2006-10-09 Oliver Hunt <ohunt@apple.com>
[WebKit-https.git] / WebCore / html / HTMLCanvasElement.cpp
1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, 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 "Document.h"
34 #include "Frame.h"
35 #include "GraphicsContext.h"
36 #include "HTMLNames.h"
37 #include "RenderHTMLCanvas.h"
38 #include "Screen.h"
39 #include <math.h>
40
41 namespace WebCore {
42
43 using namespace HTMLNames;
44
45 // These value come from the specification.
46 const int defaultWidth = 300;
47 const int defaultHeight = 150;
48
49 HTMLCanvasElement::HTMLCanvasElement(Document* doc)
50     : HTMLElement(canvasTag, doc)
51     , m_size(defaultWidth, defaultHeight)
52     , m_createdDrawingContext(false)
53     , m_data(0)
54     , m_drawingContext(0)
55 {
56 }
57
58 HTMLCanvasElement::~HTMLCanvasElement()
59 {
60     if (m_2DContext)
61         m_2DContext->detachCanvas();
62     fastFree(m_data);
63     delete m_drawingContext;
64 }
65
66 void HTMLCanvasElement::parseMappedAttribute(MappedAttribute* attr)
67 {
68     const QualifiedName& attrName = attr->name();
69     if (attrName == widthAttr || attrName == heightAttr)
70         reset();
71     HTMLElement::parseMappedAttribute(attr);
72 }
73
74 RenderObject* HTMLCanvasElement::createRenderer(RenderArena *arena, RenderStyle *style)
75 {
76     RenderHTMLCanvas* r = new (arena) RenderHTMLCanvas(this);
77     r->setIntrinsicWidth(width());
78     r->setIntrinsicHeight(height());
79     return r;
80 }
81
82 void HTMLCanvasElement::setHeight(int value)
83 {
84     setAttribute(heightAttr, String::number(value));
85 }
86
87 void HTMLCanvasElement::setWidth(int value)
88 {
89     setAttribute(widthAttr, String::number(value));
90 }
91
92 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type)
93 {
94     if (type == "2d") {
95         if (!m_2DContext)
96             m_2DContext = new CanvasRenderingContext2D(this);
97         return m_2DContext.get();
98     }
99     return 0;
100 }
101
102 void HTMLCanvasElement::willDraw(const FloatRect&)
103 {
104     // FIXME: Change to repaint just the dirty rect for speed.
105     // Until we start doing this, we won't know if the rects passed in are
106     // accurate. Also don't forget to take into account the transform
107     // on the context when determining what needs to be repainted.
108     if (renderer())
109         renderer()->repaint();
110 }
111
112 void HTMLCanvasElement::reset()
113 {
114     bool ok;
115     int w = getAttribute(widthAttr).toInt(&ok);
116     if (!ok)
117         w = defaultWidth;
118     int h = getAttribute(heightAttr).toInt(&ok);
119     if (!ok)
120         h = defaultHeight;
121     m_size = IntSize(w, h);
122
123     RenderHTMLCanvas* r = static_cast<RenderHTMLCanvas*>(renderer());
124     if (r) {
125         r->setIntrinsicWidth(w);
126         r->setIntrinsicHeight(h);
127         r->repaint();
128     }
129
130     m_createdDrawingContext = false;
131     fastFree(m_data);
132     m_data = 0;
133     delete m_drawingContext;
134     m_drawingContext = 0;
135 }
136
137 void HTMLCanvasElement::paint(GraphicsContext* p, const IntRect& r)
138 {
139     if (p->paintingDisabled())
140         return;
141 #if PLATFORM(CG)
142     if (CGImageRef image = createPlatformImage()) {
143         CGContextDrawImage(p->platformContext(), p->roundToDevicePixels(r), image);
144         CGImageRelease(image);
145     }
146 #endif
147 }
148
149 void HTMLCanvasElement::createDrawingContext() const
150 {
151     ASSERT(!m_createdDrawingContext);
152     ASSERT(!m_data);
153
154     m_createdDrawingContext = true;
155
156     float unscaledWidth = width();
157     float unscaledHeight = height();
158     float pageScaleFactor = document()->frame() ? scaleFactor(document()->frame()->page()) : 1.0f;
159     float wf = ceilf(unscaledWidth * pageScaleFactor);
160     float hf = ceilf(unscaledHeight * pageScaleFactor);
161     
162     if (!(wf > 0 && wf < UINT_MAX && hf > 0 && hf < UINT_MAX))
163         return;
164         
165     unsigned w = static_cast<unsigned>(wf);
166     unsigned h = static_cast<unsigned>(hf);
167     
168     size_t bytesPerRow = w * 4;
169     if (bytesPerRow / 4 != w) // check for overflow
170         return;
171     m_data = fastCalloc(h, bytesPerRow);
172     if (!m_data)
173         return;
174 #if PLATFORM(CG)
175     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
176     CGContextRef bitmapContext = CGBitmapContextCreate(m_data, w, h, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
177     CGContextScaleCTM(bitmapContext, w / unscaledWidth, h / unscaledHeight);
178     CGColorSpaceRelease(colorSpace);
179     m_drawingContext = new GraphicsContext(bitmapContext);
180     CGContextRelease(bitmapContext);
181 #endif
182 }
183
184 GraphicsContext* HTMLCanvasElement::drawingContext() const
185 {
186     if (!m_createdDrawingContext)
187         createDrawingContext();
188     return m_drawingContext;
189 }
190
191 #if PLATFORM(CG)
192
193 CGImageRef HTMLCanvasElement::createPlatformImage() const
194 {
195     GraphicsContext* context = drawingContext();
196     if (!context)
197         return 0;
198     CGContextFlush(context->platformContext());
199     return CGBitmapContextCreateImage(context->platformContext());
200 }
201
202 #endif
203
204 }