9f99053062e587aa3a7e535d00e640e3291746dd
[WebKit-https.git] / WebCore / html / HTMLCanvasElement.cpp
1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "HTMLCanvasElement.h"
28
29 #include "CanvasGradient.h"
30 #include "CanvasPattern.h"
31 #include "CanvasRenderingContext2D.h"
32 #include "CanvasStyle.h"
33 #include "Chrome.h"
34 #include "Document.h"
35 #include "Frame.h"
36 #include "GraphicsContext.h"
37 #include "HTMLNames.h"
38 #include "Page.h"
39 #include "RenderHTMLCanvas.h"
40 #include "Settings.h"
41
42 #if PLATFORM(QT)
43 #include <QPainter>
44 #include <QPixmap>
45 #endif
46
47 #include <math.h>
48
49
50 namespace WebCore {
51
52 using namespace HTMLNames;
53
54 // These values come from the WhatWG spec.
55 const int defaultWidth = 300;
56 const int defaultHeight = 150;
57
58 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it 
59 // reaches that limit we limit by area instead, giving us larger max dimensions, in exchange 
60 // for reduce maximum canvas size.
61 const float maxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels
62
63 HTMLCanvasElement::HTMLCanvasElement(Document* doc)
64     : HTMLElement(canvasTag, doc)
65     , m_size(defaultWidth, defaultHeight)
66     , m_createdDrawingContext(false)
67     , m_data(0)
68 #if PLATFORM(QT)
69     , m_painter(0)
70 #endif
71     , m_drawingContext(0)
72 {
73 }
74
75 HTMLCanvasElement::~HTMLCanvasElement()
76 {
77     if (m_2DContext)
78         m_2DContext->detachCanvas();
79 #if PLATFORM(CG)
80     fastFree(m_data);
81 #elif PLATFORM(QT)
82     delete m_painter;
83     delete m_data;
84 #endif
85     delete m_drawingContext;
86 }
87
88 HTMLTagStatus HTMLCanvasElement::endTagRequirement() const 
89
90     if (document()->frame() && document()->frame()->settings()->usesDashboardBackwardCompatibilityMode())
91         return TagStatusForbidden; 
92
93     return HTMLElement::endTagRequirement();
94 }
95
96 int HTMLCanvasElement::tagPriority() const 
97
98     if (document()->frame() && document()->frame()->settings()->usesDashboardBackwardCompatibilityMode())
99         return 0; 
100
101     return HTMLElement::tagPriority();
102 }
103
104 void HTMLCanvasElement::parseMappedAttribute(MappedAttribute* attr)
105 {
106     const QualifiedName& attrName = attr->name();
107     if (attrName == widthAttr || attrName == heightAttr)
108         reset();
109     HTMLElement::parseMappedAttribute(attr);
110 }
111
112 RenderObject* HTMLCanvasElement::createRenderer(RenderArena* arena, RenderStyle* style)
113 {
114     if (document()->frame() && document()->frame()->settings()->isJavaScriptEnabled()) {
115         m_rendererIsCanvas = true;
116         return new (arena) RenderHTMLCanvas(this);
117     }
118
119     m_rendererIsCanvas = false;
120     return HTMLElement::createRenderer(arena, style);
121 }
122
123 void HTMLCanvasElement::setHeight(int value)
124 {
125     setAttribute(heightAttr, String::number(value));
126 }
127
128 void HTMLCanvasElement::setWidth(int value)
129 {
130     setAttribute(widthAttr, String::number(value));
131 }
132
133 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type)
134 {
135     if (type == "2d") {
136         if (!m_2DContext)
137             m_2DContext = new CanvasRenderingContext2D(this);
138         return m_2DContext.get();
139     }
140     return 0;
141 }
142
143 void HTMLCanvasElement::willDraw(const FloatRect&)
144 {
145     // FIXME: Change to repaint just the dirty rect for speed.
146     // Until we start doing this, we won't know if the rects passed in are
147     // accurate. Also don't forget to take into account the transform
148     // on the context when determining what needs to be repainted.
149     if (renderer())
150         renderer()->repaint();
151 }
152
153 void HTMLCanvasElement::reset()
154 {
155     bool ok;
156     int w = getAttribute(widthAttr).toInt(&ok);
157     if (!ok)
158         w = defaultWidth;
159     int h = getAttribute(heightAttr).toInt(&ok);
160     if (!ok)
161         h = defaultHeight;
162
163     IntSize oldSize = m_size;
164     m_size = IntSize(w, h);
165
166     bool hadDrawingContext = m_createdDrawingContext;
167     m_createdDrawingContext = false;
168 #if PLATFORM(CG)
169     fastFree(m_data);
170 #elif PLATFORM(QT)
171     delete m_painter;
172     delete m_data;
173 #endif
174     m_data = 0;
175     delete m_drawingContext;
176     m_drawingContext = 0;
177
178     if (RenderObject* ro = renderer())
179         if (m_rendererIsCanvas) {
180             if (oldSize != m_size)
181                 static_cast<RenderHTMLCanvas*>(ro)->canvasSizeChanged();
182             if (hadDrawingContext)
183                 ro->repaint();
184         }
185 }
186
187 void HTMLCanvasElement::paint(GraphicsContext* p, const IntRect& r)
188 {
189     if (p->paintingDisabled())
190         return;
191 #if PLATFORM(CG)
192     if (CGImageRef image = createPlatformImage()) {
193         CGContextDrawImage(p->platformContext(), p->roundToDevicePixels(r), image);
194         CGImageRelease(image);
195     }
196 #elif PLATFORM(QT)
197     if (m_data) {
198         if (m_painter->isActive())
199             m_painter->end();
200         static_cast<QPainter*>(p->platformContext())->drawPixmap(r, *m_data);
201     }
202 #endif
203 }
204
205 void HTMLCanvasElement::createDrawingContext() const
206 {
207     ASSERT(!m_createdDrawingContext);
208     ASSERT(!m_data);
209
210     m_createdDrawingContext = true;
211
212     float unscaledWidth = width();
213     float unscaledHeight = height();
214     float pageScaleFactor = document()->frame() ? document()->frame()->page()->chrome()->scaleFactor() : 1.0f;
215     float wf = ceilf(unscaledWidth * pageScaleFactor);
216     float hf = ceilf(unscaledHeight * pageScaleFactor);
217     
218     if (!(wf >= 1 && hf >= 1 && wf * hf <= maxCanvasArea))
219         return;
220         
221     unsigned w = static_cast<unsigned>(wf);
222     unsigned h = static_cast<unsigned>(hf);
223     
224     size_t bytesPerRow = w * 4;
225     if (bytesPerRow / 4 != w) // check for overflow
226         return;
227 #if PLATFORM(CG)
228     m_data = fastCalloc(h, bytesPerRow);
229 #elif PLATFORM(QT)
230     m_data = new QPixmap(w, h);
231 #endif
232     if (!m_data)
233         return;
234 #if PLATFORM(CG)
235     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
236     CGContextRef bitmapContext = CGBitmapContextCreate(m_data, w, h, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
237     CGContextScaleCTM(bitmapContext, w / unscaledWidth, h / unscaledHeight);
238     CGColorSpaceRelease(colorSpace);
239     m_drawingContext = new GraphicsContext(bitmapContext);
240     CGContextRelease(bitmapContext);
241 #elif PLATFORM(QT)
242     m_data->fill(Qt::white);
243     m_painter = new QPainter(m_data);
244     m_drawingContext = new GraphicsContext(m_painter);
245 #endif
246 }
247
248 GraphicsContext* HTMLCanvasElement::drawingContext() const
249 {
250     if (!m_createdDrawingContext)
251         createDrawingContext();
252     return m_drawingContext;
253 }
254
255 #if PLATFORM(CG)
256
257 CGImageRef HTMLCanvasElement::createPlatformImage() const
258 {
259     GraphicsContext* context = drawingContext();
260     if (!context)
261         return 0;
262     
263     CGContextRef contextRef = context->platformContext();
264     if (!contextRef)
265         return 0;
266     
267     CGContextFlush(contextRef);
268     
269     return CGBitmapContextCreateImage(contextRef);
270 }
271 #elif PLATFORM(QT)
272 QPixmap HTMLCanvasElement::createPlatformImage() const
273 {
274     if (m_data)
275         return *m_data;
276     return QPixmap();
277 }
278
279 #endif
280
281 }