root/trunk/WebCore/html/CanvasRenderingContext2D.cpp

Revision 35013, 39.5 kB (checked in by jmalonzo@webkit.org, 2 weeks ago)

2008-07-05 Jan Michael Alonzo <jmalonzo@webkit.org>

Rubber-stamped by Oliver Hunt

Coding style fix

  • html/CanvasRenderingContext2D.cpp: Indentation fix
  • Property svn:eol-style set to native
Line 
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Trolltech ASA
4  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "CanvasRenderingContext2D.h"
30
31 #include "AffineTransform.h"
32 #include "CSSParser.h"
33 #include "CachedImage.h"
34 #include "CanvasGradient.h"
35 #include "CanvasPattern.h"
36 #include "CanvasPixelArray.h"
37 #include "CanvasStyle.h"
38 #include "Document.h"
39 #include "ExceptionCode.h"
40 #include "FloatConversion.h"
41 #include "Frame.h"
42 #include "GraphicsContext.h"
43 #include "HTMLCanvasElement.h"
44 #include "HTMLImageElement.h"
45 #include "HTMLNames.h"
46 #include "ImageBuffer.h"
47 #include "ImageData.h"
48 #include "KURL.h"
49 #include "NotImplemented.h"
50 #include "Page.h"
51 #include "RenderHTMLCanvas.h"
52 #include "SecurityOrigin.h"
53 #include "Settings.h"
54 #include <kjs/interpreter.h>
55 #include <stdio.h>
56 #include <wtf/MathExtras.h>
57
58 #if PLATFORM(QT)
59 #include <QPainter>
60 #include <QPixmap>
61 #include <QPainterPath>
62 #elif PLATFORM(CAIRO)
63 #include "CairoPath.h"
64 #include <cairo.h>
65 #endif
66
67 namespace WebCore {
68
69 using namespace HTMLNames;
70
71 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
72     : m_canvas(canvas)
73     , m_stateStack(1)
74 {
75 }
76
77 void CanvasRenderingContext2D::ref()
78 {
79     m_canvas->ref();
80 }
81
82 void CanvasRenderingContext2D::deref()
83 {
84     m_canvas->deref();
85 }
86
87 void CanvasRenderingContext2D::reset()
88 {
89     m_stateStack.resize(1);
90     m_stateStack.first() = State();
91 }
92
93 CanvasRenderingContext2D::State::State()
94     : m_strokeStyle(CanvasStyle::create("black"))
95     , m_fillStyle(CanvasStyle::create("black"))
96     , m_lineWidth(1)
97     , m_lineCap(ButtCap)
98     , m_lineJoin(MiterJoin)
99     , m_miterLimit(10)
100     , m_shadowBlur(0)
101     , m_shadowColor("black")
102     , m_globalAlpha(1)
103     , m_globalComposite(CompositeSourceOver)
104     , m_appliedStrokePattern(false)
105     , m_appliedFillPattern(false)
106 {
107 }
108
109 void CanvasRenderingContext2D::save()
110 {
111     ASSERT(m_stateStack.size() >= 1);
112     m_stateStack.append(state());
113     GraphicsContext* c = drawingContext();
114     if (!c)
115         return;
116     c->save();
117 }
118
119 void CanvasRenderingContext2D::restore()
120 {
121     ASSERT(m_stateStack.size() >= 1);
122     if (m_stateStack.size() <= 1)
123         return;
124     m_path.transform(state().m_transform);
125     m_stateStack.removeLast();
126     m_path.transform(state().m_transform.inverse());
127     GraphicsContext* c = drawingContext();
128     if (!c)
129         return;
130     c->restore();
131 }
132
133 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
134 {
135     return state().m_strokeStyle.get();
136 }
137
138 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
139 {
140     if (!style)
141         return;
142
143     if (m_canvas->originClean()) {
144         if (CanvasPattern* pattern = style->pattern()) {
145             if (!pattern->originClean())
146                 m_canvas->setOriginTainted();
147         }
148     }
149
150     state().m_strokeStyle = style;
151     GraphicsContext* c = drawingContext();
152     if (!c)
153         return;
154     state().m_strokeStyle->applyStrokeColor(c);
155     state().m_appliedStrokePattern = false;
156 }
157
158 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
159 {
160     return state().m_fillStyle.get();
161 }
162
163 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
164 {
165     if (!style)
166         return;
167  
168     if (m_canvas->originClean()) {
169         if (CanvasPattern* pattern = style->pattern()) {
170             if (!pattern->originClean())
171                 m_canvas->setOriginTainted();
172         }
173     }
174
175     state().m_fillStyle = style;
176     GraphicsContext* c = drawingContext();
177     if (!c)
178         return;
179 #if PLATFORM(CAIRO)
180     // FIXME: hack to reduce code duplication in CanvasStyle.cpp
181     state().m_fillStyle->applyStrokeColor(c);
182 #else
183     state().m_fillStyle->applyFillColor(c);
184 #endif
185     state().m_appliedFillPattern = false;
186 }
187
188 float CanvasRenderingContext2D::lineWidth() const
189 {
190     return state().m_lineWidth;
191 }
192
193 void CanvasRenderingContext2D::setLineWidth(float width)
194 {
195     if (!(width > 0))
196         return;
197     state().m_lineWidth = width;
198     GraphicsContext* c = drawingContext();
199     if (!c)
200         return;
201     c->setStrokeThickness(width);
202 }
203
204 String CanvasRenderingContext2D::lineCap() const
205 {
206     return lineCapName(state().m_lineCap);
207 }
208
209 void CanvasRenderingContext2D::setLineCap(const String& s)
210 {
211     LineCap cap;
212     if (!parseLineCap(s, cap))
213         return;
214     state().m_lineCap = cap;
215     GraphicsContext* c = drawingContext();
216     if (!c)
217         return;
218     c->setLineCap(cap);
219 }
220
221 String CanvasRenderingContext2D::lineJoin() const
222 {
223     return lineJoinName(state().m_lineJoin);
224 }
225
226 void CanvasRenderingContext2D::setLineJoin(const String& s)
227 {
228     LineJoin join;
229     if (!parseLineJoin(s, join))
230         return;
231     state().m_lineJoin = join;
232     GraphicsContext* c = drawingContext();
233     if (!c)
234         return;
235     c->setLineJoin(join);
236 }
237
238 float CanvasRenderingContext2D::miterLimit() const
239 {
240     return state().m_miterLimit;
241 }
242
243 void CanvasRenderingContext2D::setMiterLimit(float limit)
244 {
245     if (!(limit > 0))
246         return;
247     state().m_miterLimit = limit;
248     GraphicsContext* c = drawingContext();
249     if (!c)
250         return;
251     c->setMiterLimit(limit);
252 }
253
254 float CanvasRenderingContext2D::shadowOffsetX() const
255 {
256     return state().m_shadowOffset.width();
257 }
258
259 void CanvasRenderingContext2D::setShadowOffsetX(float x)
260 {
261     state().m_shadowOffset.setWidth(x);
262     applyShadow();
263 }
264
265 float CanvasRenderingContext2D::shadowOffsetY() const
266 {
267     return state().m_shadowOffset.height();
268 }
269
270 void CanvasRenderingContext2D::setShadowOffsetY(float y)
271 {
272     state().m_shadowOffset.setHeight(y);
273     applyShadow();
274 }
275
276 float CanvasRenderingContext2D::shadowBlur() const
277 {
278     return state().m_shadowBlur;
279 }
280
281 void CanvasRenderingContext2D::setShadowBlur(float blur)
282 {
283     state().m_shadowBlur = blur;
284     applyShadow();
285 }
286
287 String CanvasRenderingContext2D::shadowColor() const
288 {
289     // FIXME: What should this return if you called setShadow with a non-string color?
290     return state().m_shadowColor;
291 }
292
293 void CanvasRenderingContext2D::setShadowColor(const String& color)
294 {
295     state().m_shadowColor = color;
296     applyShadow();
297 }
298
299 float CanvasRenderingContext2D::globalAlpha() const
300 {
301     return state().m_globalAlpha;
302 }
303
304 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
305 {
306     if (!(alpha >= 0 && alpha <= 1))
307         return;
308     state().m_globalAlpha = alpha;
309     GraphicsContext* c = drawingContext();
310     if (!c)
311         return;
312     c->setAlpha(alpha);
313 }
314
315 String CanvasRenderingContext2D::globalCompositeOperation() const
316 {
317     return compositeOperatorName(state().m_globalComposite);
318 }
319
320 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
321 {
322     CompositeOperator op;
323     if (!parseCompositeOperator(operation, op))
324         return;
325     state().m_globalComposite = op;
326     GraphicsContext* c = drawingContext();
327     if (!c)
328         return;
329     c->setCompositeOperation(op);
330 }
331
332 void CanvasRenderingContext2D::scale(float sx, float sy)
333 {
334     GraphicsContext* c = drawingContext();
335     if (!c)
336         return;
337     c->scale(FloatSize(sx, sy));
338     state().m_transform.scale(sx, sy);
339     m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
340 }
341
342 void CanvasRenderingContext2D::rotate(float angleInRadians)
343 {
344     GraphicsContext* c = drawingContext();
345     if (!c)
346         return;
347     c->rotate(angleInRadians);
348     state().m_transform.rotate(angleInRadians / piDouble * 180.0);
349     m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
350 }
351
352 void CanvasRenderingContext2D::translate(float tx, float ty)
353 {
354     GraphicsContext* c = drawingContext();
355     if (!c)
356         return;
357     c->translate(tx, ty);
358     state().m_transform.translate(tx, ty);
359     m_path.transform(AffineTransform().translate(-tx, -ty));
360 }
361
362 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
363 {
364     GraphicsContext* c = drawingContext();
365     if (!c)
366         return;
367    
368     // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
369     if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
370         !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
371         return;
372     AffineTransform transform(m11, m12, m21, m22, dx, dy);
373     c->concatCTM(transform);
374     state().m_transform.multiply(transform);
375     m_path.transform(transform.inverse());
376 }
377
378 void CanvasRenderingContext2D::setStrokeColor(const String& color)
379 {
380     setStrokeStyle(CanvasStyle::create(color));
381 }
382
383 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
384 {
385     setStrokeStyle(CanvasStyle::create(grayLevel, 1));
386 }
387
388 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
389 {
390     setStrokeStyle(CanvasStyle::create(color, alpha));
391 }
392
393 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
394 {
395     setStrokeStyle(CanvasStyle::create(grayLevel, alpha));
396 }
397
398 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
399 {
400     setStrokeStyle(CanvasStyle::create(r, g, b, a));
401 }
402
403 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
404 {
405     setStrokeStyle(CanvasStyle::create(c, m, y, k, a));
406 }
407
408 void CanvasRenderingContext2D::setFillColor(const String& color)
409 {
410     setFillStyle(CanvasStyle::create(color));
411 }
412
413 void CanvasRenderingContext2D::setFillColor(float grayLevel)
414 {
415     setFillStyle(CanvasStyle::create(grayLevel, 1));
416 }
417
418 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
419 {
420     setFillStyle(CanvasStyle::create(color, 1));
421 }
422
423 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
424 {
425     setFillStyle(CanvasStyle::create(grayLevel, alpha));
426 }
427
428 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
429 {
430     setFillStyle(CanvasStyle::create(r, g, b, a));
431 }
432
433 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
434 {
435     setFillStyle(CanvasStyle::create(c, m, y, k, a));
436 }
437
438 void CanvasRenderingContext2D::beginPath()
439 {
440     m_path.clear();
441 }
442
443 void CanvasRenderingContext2D::closePath()
444 {
445     m_path.closeSubpath();
446 }
447
448 void CanvasRenderingContext2D::moveTo(float x, float y)
449 {
450     if (!isfinite(x) | !isfinite(y))
451         return;
452     m_path.moveTo(FloatPoint(x, y));
453 }
454
455 void CanvasRenderingContext2D::lineTo(float x, float y)
456 {
457     if (!isfinite(x) | !isfinite(y))
458         return;
459     m_path.addLineTo(FloatPoint(x, y));
460 }
461
462 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
463 {
464     if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
465         return;
466     m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
467 }
468
469 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
470 {
471     if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
472         return;
473     m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
474 }
475
476 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
477 {
478     ec = 0;
479     if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r))
480         return;
481    
482     if (r < 0) {
483         ec = INDEX_SIZE_ERR;
484         return;
485     }
486    
487     m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
488 }
489
490 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
491 {
492     ec = 0;
493     if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
494         return;
495    
496     if (r < 0) {
497         ec = INDEX_SIZE_ERR;
498         return;
499     }
500    
501     m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
502 }
503    
504 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
505 {
506     if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
507         return false;
508    
509     if (width < 0) {
510         width = -width;
511         x -= width;
512     }
513    
514     if (height < 0) {
515         height = -height;
516         y -= height;
517     }
518    
519     return true;
520 }
521
522 void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
523 {
524     if (!validateRectForCanvas(x, y, width, height))
525         return;
526        
527     m_path.addRect(FloatRect(x, y, width, height));
528 }
529
530 #if ENABLE(DASHBOARD_SUPPORT)
531 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
532 {
533     if (Settings* settings = m_canvas->document()->settings())
534         if (settings->usesDashboardBackwardCompatibilityMode())
535             m_path.clear();
536 }
537 #endif
538
539 void CanvasRenderingContext2D::fill()
540 {
541     GraphicsContext* c = drawingContext();
542     if (!c)
543         return;
544
545     c->beginPath();
546     c->addPath(m_path);
547     if (!m_path.isEmpty())
548         willDraw(m_path.boundingRect());
549
550 #if PLATFORM(CG)
551     if (state().m_fillStyle->canvasGradient()) {
552         // Shading works on the entire clip region, so convert the current path to a clip.
553         c->save();
554         CGContextClip(c->platformContext());
555         CGContextDrawShading