2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "HTMLCanvasElement.h"
30 #include "CanvasGradient.h"
31 #include "CanvasPattern.h"
32 #include "CanvasRenderingContext2D.h"
33 #include "CanvasStyle.h"
36 #include "ExceptionCode.h"
38 #include "GraphicsContext.h"
39 #include "HTMLNames.h"
40 #include "ImageBuffer.h"
41 #include "MIMETypeRegistry.h"
43 #include "RenderHTMLCanvas.h"
45 #include <kjs/interpreter.h>
58 using namespace HTMLNames;
60 // These values come from the WhatWG spec.
61 static const int defaultWidth = 300;
62 static const int defaultHeight = 150;
64 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
65 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
66 // in exchange for a smaller maximum canvas size.
67 const float HTMLCanvasElement::MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels
69 HTMLCanvasElement::HTMLCanvasElement(Document* doc)
70 : HTMLElement(canvasTag, doc)
71 , m_size(defaultWidth, defaultHeight)
74 , m_ignoreReset(false)
75 , m_createdImageBuffer(false)
79 HTMLCanvasElement::~HTMLCanvasElement()
82 m_2DContext->detachCanvas();
85 #if ENABLE(DASHBOARD_SUPPORT)
87 HTMLTagStatus HTMLCanvasElement::endTagRequirement() const
89 Settings* settings = document()->settings();
90 if (settings && settings->usesDashboardBackwardCompatibilityMode())
91 return TagStatusForbidden;
93 return HTMLElement::endTagRequirement();
96 int HTMLCanvasElement::tagPriority() const
98 Settings* settings = document()->settings();
99 if (settings && settings->usesDashboardBackwardCompatibilityMode())
102 return HTMLElement::tagPriority();
107 void HTMLCanvasElement::parseMappedAttribute(MappedAttribute* attr)
109 const QualifiedName& attrName = attr->name();
110 if (attrName == widthAttr || attrName == heightAttr)
112 HTMLElement::parseMappedAttribute(attr);
115 RenderObject* HTMLCanvasElement::createRenderer(RenderArena* arena, RenderStyle* style)
117 Settings* settings = document()->settings();
118 if (settings && settings->isJavaScriptEnabled()) {
119 m_rendererIsCanvas = true;
120 return new (arena) RenderHTMLCanvas(this);
123 m_rendererIsCanvas = false;
124 return HTMLElement::createRenderer(arena, style);
127 void HTMLCanvasElement::setHeight(int value)
129 setAttribute(heightAttr, String::number(value));
132 void HTMLCanvasElement::setWidth(int value)
134 setAttribute(widthAttr, String::number(value));
137 String HTMLCanvasElement::toDataURL(const String& mimeType, ExceptionCode& ec)
139 if (!m_originClean) {
144 if (m_size.isEmpty())
145 return String("data:,");
147 if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
148 return buffer()->toDataURL("image/png");
150 return buffer()->toDataURL(mimeType);
153 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type)
157 m_2DContext = CanvasRenderingContext2D::create(this);
158 return m_2DContext.get();
163 void HTMLCanvasElement::willDraw(const FloatRect& rect)
165 m_imageBuffer->clearImage();
167 if (RenderObject* ro = renderer()) {
168 #ifdef CANVAS_INCREMENTAL_REPAINT
169 // Handle CSS triggered scaling
170 float widthScale = static_cast<float>(ro->width()) / static_cast<float>(m_size.width());
171 float heightScale = static_cast<float>(ro->height()) / static_cast<float>(m_size.height());
172 FloatRect r(rect.x() * widthScale, rect.y() * heightScale, rect.width() * widthScale, rect.height() * heightScale);
173 ro->repaintRectangle(enclosingIntRect(r));
180 m_observer->canvasChanged(this, rect);
183 void HTMLCanvasElement::reset()
189 int w = getAttribute(widthAttr).toInt(&ok);
192 int h = getAttribute(heightAttr).toInt(&ok);
196 IntSize oldSize = m_size;
197 m_size = IntSize(w, h);
199 bool hadImageBuffer = m_createdImageBuffer;
200 m_createdImageBuffer = false;
201 m_imageBuffer.clear();
203 m_2DContext->reset();
205 if (RenderObject* ro = renderer())
206 if (m_rendererIsCanvas) {
207 if (oldSize != m_size)
208 static_cast<RenderHTMLCanvas*>(ro)->canvasSizeChanged();
214 m_observer->canvasResized(this);
217 void HTMLCanvasElement::paint(GraphicsContext* context, const IntRect& r)
219 if (context->paintingDisabled())
223 Image* image = m_imageBuffer->image();
225 context->drawImage(image, r);
229 IntRect HTMLCanvasElement::convertLogicalToDevice(const FloatRect& logicalRect) const
231 return IntRect(convertLogicalToDevice(logicalRect.location()), convertLogicalToDevice(logicalRect.size()));
234 IntSize HTMLCanvasElement::convertLogicalToDevice(const FloatSize& logicalSize) const
236 float pageScaleFactor = document()->frame() ? document()->frame()->page()->chrome()->scaleFactor() : 1.0f;
237 float wf = ceilf(logicalSize.width() * pageScaleFactor);
238 float hf = ceilf(logicalSize.height() * pageScaleFactor);
240 if (!(wf >= 1 && hf >= 1 && wf * hf <= MaxCanvasArea))
243 return IntSize(static_cast<unsigned>(wf), static_cast<unsigned>(hf));
246 IntPoint HTMLCanvasElement::convertLogicalToDevice(const FloatPoint& logicalPos) const
248 float pageScaleFactor = document()->frame() ? document()->frame()->page()->chrome()->scaleFactor() : 1.0f;
249 float xf = logicalPos.x() * pageScaleFactor;
250 float yf = logicalPos.y() * pageScaleFactor;
252 return IntPoint(static_cast<unsigned>(xf), static_cast<unsigned>(yf));
255 void HTMLCanvasElement::createImageBuffer() const
257 ASSERT(!m_imageBuffer);
259 m_createdImageBuffer = true;
261 FloatSize unscaledSize(width(), height());
262 IntSize size = convertLogicalToDevice(unscaledSize);
263 if (!size.width() || !size.height())
265 m_imageBuffer.set(ImageBuffer::create(size, false).release());
268 GraphicsContext* HTMLCanvasElement::drawingContext() const
270 return buffer() ? m_imageBuffer->context() : 0;
273 ImageBuffer* HTMLCanvasElement::buffer() const
275 if (!m_createdImageBuffer)
277 return m_imageBuffer.get();
282 CGImageRef HTMLCanvasElement::createPlatformImage() const
284 GraphicsContext* context = drawingContext();
288 CGContextRef contextRef = context->platformContext();
292 CGContextFlush(contextRef);
294 return CGBitmapContextCreateImage(contextRef);
299 QPixmap HTMLCanvasElement::createPlatformImage() const
303 return *m_imageBuffer->pixmap();
306 #elif PLATFORM(CAIRO)
308 cairo_surface_t* HTMLCanvasElement::createPlatformImage() const
313 // Note that unlike CG, our returned image is not a copy or
314 // copy-on-write, but the original. This is fine, since it is only
315 // ever used as a source.
317 cairo_surface_flush(m_imageBuffer->surface());
318 cairo_surface_reference(m_imageBuffer->surface());
319 return m_imageBuffer->surface();