Bug 17269: Deobfuscate CanvasRenderingContext2D.cpp
[WebKit-https.git] / WebCore / html / CanvasRenderingContext2D.cpp
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 "CanvasStyle.h"
37 #include "Document.h"
38 #include "ExceptionCode.h"
39 #include "Frame.h"
40 #include "GraphicsContext.h"
41 #include "HTMLCanvasElement.h"
42 #include "HTMLImageElement.h"
43 #include "HTMLNames.h"
44 #include "NotImplemented.h"
45 #include "RenderHTMLCanvas.h"
46 #include "Settings.h"
47 #include <wtf/MathExtras.h>
48
49 #if PLATFORM(QT)
50 #include <QPainter>
51 #include <QPixmap>
52 #include <QPainterPath>
53 #elif PLATFORM(CAIRO)
54 #include "CairoPath.h"
55 #include <cairo.h>
56 #endif
57
58 namespace WebCore {
59
60 using namespace HTMLNames;
61
62 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
63     : RefCounted<CanvasRenderingContext2D>(0)
64     , m_canvas(canvas)
65     , m_stateStack(1)
66 {
67 }
68
69 void CanvasRenderingContext2D::reset()
70 {
71     m_stateStack.resize(1);
72     m_stateStack.first() = State();
73 }
74
75 CanvasRenderingContext2D::State::State()
76     : m_strokeStyle(new CanvasStyle("black"))
77     , m_fillStyle(new CanvasStyle("black"))
78     , m_lineWidth(1)
79     , m_lineCap(ButtCap)
80     , m_lineJoin(MiterJoin)
81     , m_miterLimit(10)
82     , m_shadowBlur(0)
83     , m_shadowColor("black")
84     , m_globalAlpha(1)
85     , m_globalComposite(CompositeSourceOver)
86     , m_appliedStrokePattern(false)
87     , m_appliedFillPattern(false)
88 {
89 }
90
91 void CanvasRenderingContext2D::save()
92 {
93     ASSERT(m_stateStack.size() >= 1);
94     m_stateStack.append(state());
95     GraphicsContext* c = drawingContext();
96     if (!c)
97         return;
98     c->save();
99 }
100
101 void CanvasRenderingContext2D::restore()
102 {
103     ASSERT(m_stateStack.size() >= 1);
104     if (m_stateStack.size() <= 1)
105         return;
106     m_stateStack.removeLast();
107     GraphicsContext* c = drawingContext();
108     if (!c)
109         return;
110     c->restore();
111 }
112
113 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
114 {
115     return state().m_strokeStyle.get();
116 }
117
118 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
119 {
120     if (!style)
121         return;
122     state().m_strokeStyle = style;
123     GraphicsContext* c = drawingContext();
124     if (!c)
125         return;
126     state().m_strokeStyle->applyStrokeColor(c);
127     state().m_appliedStrokePattern = false;
128 }
129
130 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
131 {
132     return state().m_fillStyle.get();
133 }
134
135 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
136 {
137     if (!style)
138         return;
139     state().m_fillStyle = style;
140     GraphicsContext* c = drawingContext();
141     if (!c)
142         return;
143 #if PLATFORM(CAIRO)
144     // FIXME: hack to reduce code duplication in CanvasStyle.cpp
145     state().m_fillStyle->applyStrokeColor(c);
146 #else
147     state().m_fillStyle->applyFillColor(c);
148 #endif
149     state().m_appliedFillPattern = false;
150 }
151
152 float CanvasRenderingContext2D::lineWidth() const
153 {
154     return state().m_lineWidth;
155 }
156
157 void CanvasRenderingContext2D::setLineWidth(float width)
158 {
159     if (!(width > 0))
160         return;
161     state().m_lineWidth = width;
162     GraphicsContext* c = drawingContext();
163     if (!c)
164         return;
165     c->setStrokeThickness(width);
166 }
167
168 String CanvasRenderingContext2D::lineCap() const
169 {
170     return lineCapName(state().m_lineCap);
171 }
172
173 void CanvasRenderingContext2D::setLineCap(const String& s)
174 {
175     LineCap cap;
176     if (!parseLineCap(s, cap))
177         return;
178     state().m_lineCap = cap;
179     GraphicsContext* c = drawingContext();
180     if (!c)
181         return;
182     c->setLineCap(cap);
183 }
184
185 String CanvasRenderingContext2D::lineJoin() const
186 {
187     return lineJoinName(state().m_lineJoin);
188 }
189
190 void CanvasRenderingContext2D::setLineJoin(const String& s)
191 {
192     LineJoin join;
193     if (!parseLineJoin(s, join))
194         return;
195     state().m_lineJoin = join;
196     GraphicsContext* c = drawingContext();
197     if (!c)
198         return;
199     c->setLineJoin(join);
200 }
201
202 float CanvasRenderingContext2D::miterLimit() const
203 {
204     return state().m_miterLimit;
205 }
206
207 void CanvasRenderingContext2D::setMiterLimit(float limit)
208 {
209     if (!(limit > 0))
210         return;
211     state().m_miterLimit = limit;
212     GraphicsContext* c = drawingContext();
213     if (!c)
214         return;
215     c->setMiterLimit(limit);
216 }
217
218 float CanvasRenderingContext2D::shadowOffsetX() const
219 {
220     return state().m_shadowOffset.width();
221 }
222
223 void CanvasRenderingContext2D::setShadowOffsetX(float x)
224 {
225     state().m_shadowOffset.setWidth(x);
226     applyShadow();
227 }
228
229 float CanvasRenderingContext2D::shadowOffsetY() const
230 {
231     return state().m_shadowOffset.height();
232 }
233
234 void CanvasRenderingContext2D::setShadowOffsetY(float y)
235 {
236     state().m_shadowOffset.setHeight(y);
237     applyShadow();
238 }
239
240 float CanvasRenderingContext2D::shadowBlur() const
241 {
242     return state().m_shadowBlur;
243 }
244
245 void CanvasRenderingContext2D::setShadowBlur(float blur)
246 {
247     state().m_shadowBlur = blur;
248     applyShadow();
249 }
250
251 String CanvasRenderingContext2D::shadowColor() const
252 {
253     // FIXME: What should this return if you called setShadow with a non-string color?
254     return state().m_shadowColor;
255 }
256
257 void CanvasRenderingContext2D::setShadowColor(const String& color)
258 {
259     state().m_shadowColor = color;
260     applyShadow();
261 }
262
263 float CanvasRenderingContext2D::globalAlpha() const
264 {
265     return state().m_globalAlpha;
266 }
267
268 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
269 {
270     if (!(alpha >= 0 && alpha <= 1))
271         return;
272     state().m_globalAlpha = alpha;
273     GraphicsContext* c = drawingContext();
274     if (!c)
275         return;
276     c->setAlpha(alpha);
277 }
278
279 String CanvasRenderingContext2D::globalCompositeOperation() const
280 {
281     return compositeOperatorName(state().m_globalComposite);
282 }
283
284 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
285 {
286     CompositeOperator op;
287     if (!parseCompositeOperator(operation, op))
288         return;
289     state().m_globalComposite = op;
290     GraphicsContext* c = drawingContext();
291     if (!c)
292         return;
293     c->setCompositeOperation(op);
294 }
295
296 void CanvasRenderingContext2D::scale(float sx, float sy)
297 {
298     GraphicsContext* c = drawingContext();
299     if (!c)
300         return;
301     c->scale(FloatSize(sx, sy));
302     state().m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
303 }
304
305 void CanvasRenderingContext2D::rotate(float angleInRadians)
306 {
307     GraphicsContext* c = drawingContext();
308     if (!c)
309         return;
310     c->rotate(angleInRadians);
311     state().m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
312 }
313
314 void CanvasRenderingContext2D::translate(float tx, float ty)
315 {
316     GraphicsContext* c = drawingContext();
317     if (!c)
318         return;
319     c->translate(tx, ty);
320     state().m_path.transform(AffineTransform().translate(-tx, -ty));
321 }
322
323 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
324 {
325     GraphicsContext* c = drawingContext();
326     if (!c)
327         return;
328     
329     // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
330     if (!isfinite(m11) || !isfinite(m21) || !isfinite(dx) || 
331         !isfinite(m12) || !isfinite(m22) || !isfinite(dy))
332         return;
333     AffineTransform transform(m11, m12, m21, m22, dx, dy);
334     c->concatCTM(transform);
335     state().m_path.transform(transform.inverse());
336 }
337
338 void CanvasRenderingContext2D::setStrokeColor(const String& color)
339 {
340     setStrokeStyle(new CanvasStyle(color));
341 }
342
343 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
344 {
345     setStrokeStyle(new CanvasStyle(grayLevel, 1));
346 }
347
348 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
349 {
350     setStrokeStyle(new CanvasStyle(color, alpha));
351 }
352
353 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
354 {
355     setStrokeStyle(new CanvasStyle(grayLevel, alpha));
356 }
357
358 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
359 {
360     setStrokeStyle(new CanvasStyle(r, g, b, a));
361 }
362
363 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
364 {
365     setStrokeStyle(new CanvasStyle(c, m, y, k, a));
366 }
367
368 void CanvasRenderingContext2D::setFillColor(const String& color)
369 {
370     setFillStyle(new CanvasStyle(color));
371 }
372
373 void CanvasRenderingContext2D::setFillColor(float grayLevel)
374 {
375     setFillStyle(new CanvasStyle(grayLevel, 1));
376 }
377
378 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
379 {
380     setFillStyle(new CanvasStyle(color, 1));
381 }
382
383 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
384 {
385     setFillStyle(new CanvasStyle(grayLevel, alpha));
386 }
387
388 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
389 {
390     setFillStyle(new CanvasStyle(r, g, b, a));
391 }
392
393 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
394 {
395     setFillStyle(new CanvasStyle(c, m, y, k, a));
396 }
397
398 void CanvasRenderingContext2D::beginPath()
399 {
400     state().m_path.clear();
401 }
402
403 void CanvasRenderingContext2D::closePath()
404 {
405     state().m_path.closeSubpath();
406 }
407
408 void CanvasRenderingContext2D::moveTo(float x, float y)
409 {
410     state().m_path.moveTo(FloatPoint(x, y));
411 }
412
413 void CanvasRenderingContext2D::lineTo(float x, float y)
414 {
415     state().m_path.addLineTo(FloatPoint(x, y));
416 }
417
418 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
419 {
420     state().m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
421 }
422
423 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
424 {
425     state().m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
426 }
427
428 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
429 {
430     ec = 0;
431     if (!(r > 0)) {
432         ec = INDEX_SIZE_ERR;
433         return;
434     }
435     state().m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
436 }
437
438 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
439 {
440     ec = 0;
441     if (!(r > 0)) {
442         ec = INDEX_SIZE_ERR;
443         return;
444     }
445     state().m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
446 }
447
448 void CanvasRenderingContext2D::rect(float x, float y, float width, float height, ExceptionCode& ec)
449 {
450     ec = 0;
451     if (!(width >= 0 && height >= 0)) {
452         ec = INDEX_SIZE_ERR;
453         return;
454     }
455     state().m_path.addRect(FloatRect(x, y, width, height));
456 }
457
458 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
459 {
460     if (m_canvas)
461         if (Settings* settings = m_canvas->document()->settings())
462             if (settings->usesDashboardBackwardCompatibilityMode())
463                 state().m_path.clear();
464 }
465
466 void CanvasRenderingContext2D::fill()
467 {
468     GraphicsContext* c = drawingContext();
469     if (!c)
470         return;
471
472     c->beginPath();
473     c->addPath(state().m_path);
474     if (!state().m_path.isEmpty())
475         willDraw(state().m_path.boundingRect());
476
477 #if PLATFORM(CG)
478     if (state().m_fillStyle->gradient()) {
479         // Shading works on the entire clip region, so convert the current path to a clip.
480         c->save();
481         CGContextClip(c->platformContext());
482         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
483         c->restore();
484     } else {
485         if (state().m_fillStyle->pattern())
486             applyFillPattern();
487         CGContextFillPath(c->platformContext());
488     }
489 #elif PLATFORM(QT)
490     QPainterPath* path = state().m_path.platformPath();
491     QPainter* p = static_cast<QPainter*>(c->platformContext());
492     if (state().m_fillStyle->gradient()) {
493         p->fillPath(*path, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
494     } else {
495         if (state().m_fillStyle->pattern())
496             applyFillPattern();
497         p->fillPath(*path, p->brush());
498     }
499 #elif PLATFORM(CAIRO)
500     cairo_t* cr = c->platformContext();
501     cairo_save(cr);
502
503     if (state().m_fillStyle->gradient()) {
504         cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
505         cairo_fill(cr);
506     } else {
507         if (state().m_fillStyle->pattern())
508             applyFillPattern();
509         cairo_fill(cr);
510     }
511     cairo_restore(cr);
512 #endif
513
514     clearPathForDashboardBackwardCompatibilityMode();
515 }
516
517 void CanvasRenderingContext2D::stroke()
518 {
519     GraphicsContext* c = drawingContext();
520     if (!c)
521         return;
522     c->beginPath();
523     c->addPath(state().m_path);
524
525     if (!state().m_path.isEmpty()) {
526         // FIXME: This is insufficient, need to use CGContextReplacePathWithStrokedPath to expand to required bounds
527         float lineWidth = state().m_lineWidth;
528         float inset = lineWidth / 2;
529         FloatRect boundingRect = state().m_path.boundingRect();
530         boundingRect.inflate(inset);
531         willDraw(boundingRect);
532     }
533     
534     // FIXME: Do this through platform-independent GraphicsContext API.
535 #if PLATFORM(CG)
536     if (state().m_strokeStyle->gradient()) {
537         // Shading works on the entire clip region, so convert the current path to a clip.
538         c->save();
539         CGContextReplacePathWithStrokedPath(c->platformContext());
540         CGContextClip(c->platformContext());
541         CGContextDrawShading(c->platformContext(), state().m_strokeStyle->gradient()->platformShading());        
542         c->restore();
543     } else {
544         if (state().m_strokeStyle->pattern())
545             applyStrokePattern();
546         CGContextStrokePath(c->platformContext());
547     }
548 #elif PLATFORM(QT)
549     QPainterPath* path = state().m_path.platformPath();
550     QPainter* p = static_cast<QPainter*>(c->platformContext());
551     if (state().m_strokeStyle->gradient()) {
552         p->save();
553         p->setBrush(*(state().m_strokeStyle->gradient()->platformShading()));
554         p->strokePath(*path, p->pen());
555         p->restore();
556     } else {
557         if (state().m_strokeStyle->pattern())
558             applyStrokePattern();
559         p->strokePath(*path, p->pen());
560     }
561 #elif PLATFORM(CAIRO)
562     cairo_t* cr = c->platformContext();
563     cairo_save(cr);
564     if (state().m_strokeStyle->gradient()) {
565         cairo_set_source(cr, state().m_strokeStyle->gradient()->platformShading());
566         c->addPath(state().m_path);
567         cairo_stroke(cr);
568     } else {
569         if (state().m_strokeStyle->pattern())
570             applyStrokePattern();
571         c->addPath(state().m_path);
572         cairo_stroke(cr);
573     }
574     cairo_restore(cr);
575 #endif
576
577     clearPathForDashboardBackwardCompatibilityMode();
578 }
579
580 void CanvasRenderingContext2D::clip()
581 {
582     GraphicsContext* c = drawingContext();
583     if (!c)
584         return;
585     c->clip(state().m_path);
586     clearPathForDashboardBackwardCompatibilityMode();
587 }
588
589 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
590 {
591     GraphicsContext* c = drawingContext();
592     if (!c)
593         return false;
594     FloatPoint point(x, y);
595     // We have to invert the current transform to ensure we correctly handle the
596     // transforms applied to the current path.
597     AffineTransform ctm = c->getCTM();
598     if (!ctm.isInvertible())
599         return false;
600     FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
601     return state().m_path.contains(transformedPoint);
602 }
603
604 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height, ExceptionCode& ec)
605 {
606     ec = 0;
607     if (!(width >= 0 && height >= 0)) {
608         ec = INDEX_SIZE_ERR;
609         return;
610     }
611     GraphicsContext* c = drawingContext();
612     if (!c)
613         return;
614     FloatRect rect(x, y, width, height);
615     willDraw(rect);
616     c->clearRect(rect);
617 }
618
619 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height, ExceptionCode& ec)
620 {
621     ec = 0;
622
623     if (!(width >= 0 && height >= 0)) {
624         ec = INDEX_SIZE_ERR;
625         return;
626     }
627
628     GraphicsContext* c = drawingContext();
629     if (!c)
630         return;
631
632     FloatRect rect(x, y, width, height);
633     willDraw(rect);
634
635     // FIXME: Do this through platform-independent GraphicsContext API.
636 #if PLATFORM(CG)
637     if (state().m_fillStyle->gradient()) {
638         // Shading works on the entire clip region, so convert the rect to a clip.
639         c->save();
640         CGContextClipToRect(c->platformContext(), rect);
641         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
642         c->restore();
643     } else {
644         if (state().m_fillStyle->pattern())
645             applyFillPattern();
646         CGContextFillRect(c->platformContext(), rect);
647     }
648 #elif PLATFORM(QT)
649     QPainter* p = static_cast<QPainter*>(c->platformContext());
650     if (state().m_fillStyle->gradient()) {
651         p->fillRect(rect, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
652     } else {
653         if (state().m_fillStyle->pattern())
654             applyFillPattern();
655         p->fillRect(rect, p->brush());
656     }
657 #elif PLATFORM(CAIRO)
658     cairo_t* cr = c->platformContext();
659     cairo_save(cr);
660     if (state().m_fillStyle->gradient()) {
661         cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
662     } else {
663         if (state().m_fillStyle->pattern())
664             applyFillPattern();
665     }
666     cairo_rectangle(cr, x, y, width, height);
667     cairo_fill(cr);
668     cairo_restore(cr);
669 #endif
670 }
671
672 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, ExceptionCode& ec)
673 {
674     strokeRect(x, y, width, height, state().m_lineWidth, ec);
675 }
676
677 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
678 {
679     ec = 0;
680
681     if (!(width >= 0 && height >= 0 && lineWidth >= 0)) {
682         ec = INDEX_SIZE_ERR;
683         return;
684     }
685
686     GraphicsContext* c = drawingContext();
687     if (!c)
688         return;
689
690     FloatRect rect(x, y, width, height);
691
692     FloatRect boundingRect = rect;
693     boundingRect.inflate(lineWidth / 2);
694     willDraw(boundingRect);
695
696     // FIXME: No support for gradients!
697     if (state().m_strokeStyle->pattern())
698         applyStrokePattern();
699
700     c->strokeRect(rect, lineWidth);
701 }
702
703 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
704 {
705     state().m_shadowOffset = FloatSize(width, height);
706     state().m_shadowBlur = blur;
707     state().m_shadowColor = "";
708     applyShadow();
709 }
710
711 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
712 {
713     state().m_shadowOffset = FloatSize(width, height);
714     state().m_shadowBlur = blur;
715     state().m_shadowColor = color;
716     applyShadow();
717 }
718
719 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
720 {
721     state().m_shadowOffset = FloatSize(width, height);
722     state().m_shadowBlur = blur;
723     state().m_shadowColor = "";
724
725     GraphicsContext* c = drawingContext();
726     if (!c)
727         return;
728     // FIXME: Do this through platform-independent GraphicsContext API.
729 #if PLATFORM(CG)
730     const CGFloat components[2] = { grayLevel, 1 };
731     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
732     CGColorRef color = CGColorCreate(colorSpace, components);
733     CGColorSpaceRelease(colorSpace);
734     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
735     CGColorRelease(color);
736 #endif
737 }
738
739 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
740 {
741     state().m_shadowOffset = FloatSize(width, height);
742     state().m_shadowBlur = blur;
743     state().m_shadowColor = color;
744
745     GraphicsContext* c = drawingContext();
746     if (!c)
747         return;
748     // FIXME: Do this through platform-independent GraphicsContext API.
749 #if PLATFORM(CG)
750     RGBA32 rgba = 0; // default is transparent black
751     CSSParser::parseColor(rgba, color);
752     const CGFloat components[4] = {
753         ((rgba >> 16) & 0xFF) / 255.0f,
754         ((rgba >> 8) & 0xFF) / 255.0f,
755         (rgba & 0xFF) / 255.0f,
756         alpha
757     };
758     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
759     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
760     CGColorSpaceRelease(colorSpace);
761     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
762     CGColorRelease(shadowColor);
763 #endif
764 }
765
766 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
767 {
768     state().m_shadowOffset = FloatSize(width, height);
769     state().m_shadowBlur = blur;
770     state().m_shadowColor = "";
771
772     GraphicsContext* c = drawingContext();
773     if (!c)
774         return;
775     // FIXME: Do this through platform-independent GraphicsContext API.
776 #if PLATFORM(CG)
777     const CGFloat components[2] = { grayLevel, alpha };
778     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
779     CGColorRef color = CGColorCreate(colorSpace, components);
780     CGColorSpaceRelease(colorSpace);
781     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
782     CGColorRelease(color);
783 #endif
784 }
785
786 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
787 {
788     state().m_shadowOffset = FloatSize(width, height);
789     state().m_shadowBlur = blur;
790     state().m_shadowColor = "";
791
792     GraphicsContext* c = drawingContext();
793     if (!c)
794         return;
795     // FIXME: Do this through platform-independent GraphicsContext API.
796 #if PLATFORM(CG)
797     const CGFloat components[4] = { r, g, b, a };
798     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
799     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
800     CGColorSpaceRelease(colorSpace);
801     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
802     CGColorRelease(shadowColor);
803 #endif
804 }
805
806 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
807 {
808     state().m_shadowOffset = FloatSize(width, height);
809     state().m_shadowBlur = blur;
810     state().m_shadowColor = "";
811
812     GraphicsContext* dc = drawingContext();
813     if (!dc)
814         return;
815     // FIXME: Do this through platform-independent GraphicsContext API.
816 #if PLATFORM(CG)
817     const CGFloat components[5] = { c, m, y, k, a };
818     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
819     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
820     CGColorSpaceRelease(colorSpace);
821     CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
822     CGColorRelease(shadowColor);
823 #endif
824 }
825
826 void CanvasRenderingContext2D::clearShadow()
827 {
828     state().m_shadowOffset = FloatSize();
829     state().m_shadowBlur = 0;
830     state().m_shadowColor = "";
831     applyShadow();
832 }
833
834 void CanvasRenderingContext2D::applyShadow()
835 {
836     GraphicsContext* c = drawingContext();
837     if (!c)
838         return;
839     // FIXME: Do this through platform-independent GraphicsContext API.
840 #if PLATFORM(CG)
841     RGBA32 rgba = 0; // default is transparent black
842     if (!state().m_shadowColor.isEmpty())
843         CSSParser::parseColor(rgba, state().m_shadowColor);
844     const CGFloat components[4] = {
845         ((rgba >> 16) & 0xFF) / 255.0f,
846         ((rgba >> 8) & 0xFF) / 255.0f,
847         (rgba & 0xFF) / 255.0f,
848         ((rgba >> 24) & 0xFF) / 255.0f
849     };
850     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
851     CGColorRef color = CGColorCreate(colorSpace, components);
852     CGColorSpaceRelease(colorSpace);
853     CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
854     CGColorRelease(color);
855 #endif
856 }
857
858 static IntSize size(HTMLImageElement* image)
859 {
860     if (CachedImage* cachedImage = image->cachedImage())
861         return cachedImage->imageSize();
862     return IntSize();
863 }
864
865 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
866 {
867     ASSERT(image);
868     IntSize s = size(image);
869     ExceptionCode ec;
870     drawImage(image, x, y, s.width(), s.height(), ec);
871 }
872
873 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
874     float x, float y, float width, float height, ExceptionCode& ec)
875 {
876     ASSERT(image);
877     IntSize s = size(image);
878     drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
879 }
880
881 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
882     ExceptionCode& ec)
883 {
884     ASSERT(image);
885
886     ec = 0;
887
888     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
889     if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 
890             && dstRect.width() >= 0 && dstRect.height() >= 0)) {
891         ec = INDEX_SIZE_ERR;
892         return;
893     }
894
895     if (srcRect.isEmpty() || dstRect.isEmpty())
896         return;
897
898     GraphicsContext* c = drawingContext();
899     if (!c)
900         return;
901
902     CachedImage* cachedImage = image->cachedImage();
903     if (!cachedImage)
904         return;
905
906     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
907     FloatRect destRect = c->roundToDevicePixels(dstRect);
908     willDraw(destRect);
909     c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
910 }
911
912 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
913 {
914     ASSERT(canvas);
915     ExceptionCode ec;
916     drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
917 }
918
919 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
920     float x, float y, float width, float height, ExceptionCode& ec)
921 {
922     ASSERT(canvas);
923     drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
924 }
925
926 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
927     const FloatRect& dstRect, ExceptionCode& ec)
928 {
929     ASSERT(canvas);
930
931     ec = 0;
932
933     FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
934     if (!(srcCanvasRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 
935             && dstRect.width() >= 0 && dstRect.height() >= 0)) {
936         ec = INDEX_SIZE_ERR;
937         return;
938     }
939
940     if (srcRect.isEmpty() || dstRect.isEmpty())
941         return;
942
943     GraphicsContext* c = drawingContext();
944     if (!c)
945         return;
946         
947     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
948     FloatRect destRect = c->roundToDevicePixels(dstRect);
949         
950     // FIXME: Do this through platform-independent GraphicsContext API.
951     ImageBuffer* buffer = canvas->buffer();
952     if (!buffer)
953         return;
954     
955     willDraw(destRect);
956     c->drawImage(buffer, sourceRect, destRect);
957 }
958
959 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
960 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
961     float sx, float sy, float sw, float sh,
962     float dx, float dy, float dw, float dh,
963     const String& compositeOperation)
964 {
965     if (!image)
966         return;
967
968     CachedImage* cachedImage = image->cachedImage();
969     if (!cachedImage)
970         return;
971
972     GraphicsContext* c = drawingContext();
973     if (!c)
974         return;
975
976     CompositeOperator op;
977     if (!parseCompositeOperator(compositeOperation, op))
978         op = CompositeSourceOver;
979
980     FloatRect destRect = FloatRect(dx, dy, dw, dh);
981     willDraw(destRect);
982     c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
983 }
984
985 void CanvasRenderingContext2D::setAlpha(float alpha)
986 {
987     setGlobalAlpha(alpha);
988 }
989
990 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
991 {
992     setGlobalCompositeOperation(operation);
993 }
994
995 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
996 {
997     return new CanvasGradient(FloatPoint(x0, y0), FloatPoint(x1, y1));
998 }
999
1000 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
1001 {
1002     return new CanvasGradient(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1003 }
1004
1005 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1006     const String& repetitionType, ExceptionCode& ec)
1007 {
1008     bool repeatX, repeatY;
1009     ec = 0;
1010     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1011     if (ec)
1012         return 0;
1013     return new CanvasPattern(image ? image->cachedImage() : 0, repeatX, repeatY);
1014 }
1015
1016 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1017     const String& repetitionType, ExceptionCode& ec)
1018 {
1019     bool repeatX, repeatY;
1020     ec = 0;
1021     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1022     if (ec)
1023         return 0;
1024     // FIXME: Do this through platform-independent GraphicsContext API.
1025 #if PLATFORM(CG)
1026     CGImageRef image = canvas->createPlatformImage();
1027     if (!image)
1028         return 0;
1029     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(image, repeatX, repeatY);
1030     CGImageRelease(image);
1031     return pattern;
1032 #elif PLATFORM(CAIRO)
1033     cairo_surface_t* surface = canvas->createPlatformImage();
1034     if (!surface)
1035         return 0;
1036     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(surface, repeatX, repeatY);
1037     cairo_surface_destroy(surface);
1038     return pattern;
1039 #else
1040     notImplemented();
1041     return 0;
1042 #endif
1043 }
1044
1045 void CanvasRenderingContext2D::willDraw(const FloatRect& r)
1046 {
1047     if (!m_canvas)
1048         return;
1049     GraphicsContext* c = drawingContext();
1050     if (!c)
1051         return;
1052
1053     m_canvas->willDraw(c->getCTM().mapRect(r));
1054 }
1055
1056 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1057 {
1058     if (!m_canvas)
1059         return 0;
1060     return m_canvas->drawingContext();
1061 }
1062
1063 void CanvasRenderingContext2D::applyStrokePattern()
1064 {
1065     GraphicsContext* c = drawingContext();
1066     if (!c)
1067         return;
1068
1069 #if PLATFORM(CG)
1070     // Check for case where the pattern is already set.
1071     CGAffineTransform m = CGContextGetCTM(c->platformContext());
1072     if (state().m_appliedStrokePattern
1073             && CGAffineTransformEqualToTransform(m, state().m_strokeStylePatternTransform))
1074         return;
1075
1076     CanvasPattern* pattern = state().m_strokeStyle->pattern();
1077     if (!pattern)
1078         return;
1079
1080     CGPatternRef platformPattern = pattern->createPattern(m);
1081     if (!platformPattern)
1082         return;
1083
1084     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1085     CGContextSetStrokeColorSpace(c->platformContext(), patternSpace);
1086     CGColorSpaceRelease(patternSpace);
1087
1088     const CGFloat patternAlpha = 1;
1089     CGContextSetStrokePattern(c->platformContext(), platformPattern, &patternAlpha);
1090     CGPatternRelease(platformPattern);
1091
1092     state().m_strokeStylePatternTransform = m;
1093 #elif PLATFORM(QT)
1094     fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyStrokePattern\n");
1095 #elif PLATFORM(CAIRO)
1096     CanvasPattern* pattern = state().m_strokeStyle->pattern();
1097     if (!pattern)
1098         return;
1099
1100     cairo_t* cr = c->platformContext();
1101     cairo_matrix_t m;
1102     cairo_get_matrix(cr, &m);
1103
1104     cairo_pattern_t* platformPattern = pattern->createPattern(m);
1105     if (!platformPattern)
1106         return;
1107
1108     cairo_set_source(cr, platformPattern);
1109     cairo_pattern_destroy(platformPattern);
1110 #endif
1111     state().m_appliedStrokePattern = true;
1112 }
1113
1114 void CanvasRenderingContext2D::applyFillPattern()
1115 {
1116     GraphicsContext* c = drawingContext();
1117     if (!c)
1118         return;
1119
1120 #if PLATFORM(CG)
1121     // Check for case where the pattern is already set.
1122     CGAffineTransform m = CGContextGetCTM(c->platformContext());
1123     if (state().m_appliedFillPattern
1124             && CGAffineTransformEqualToTransform(m, state().m_fillStylePatternTransform))
1125         return;
1126
1127     CanvasPattern* pattern = state().m_fillStyle->pattern();
1128     if (!pattern)
1129         return;
1130
1131     CGPatternRef platformPattern = pattern->createPattern(m);
1132     if (!platformPattern)
1133         return;
1134
1135     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1136     CGContextSetFillColorSpace(c->platformContext(), patternSpace);
1137     CGColorSpaceRelease(patternSpace);
1138
1139     const CGFloat patternAlpha = 1;
1140     CGContextSetFillPattern(c->platformContext(), platformPattern, &patternAlpha);
1141     CGPatternRelease(platformPattern);
1142
1143     state().m_fillStylePatternTransform = m;
1144 #elif PLATFORM(QT)
1145     fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyFillPattern\n");
1146 #elif PLATFORM(CAIRO)
1147     CanvasPattern* pattern = state().m_fillStyle->pattern();
1148     if (!pattern)
1149         return;
1150
1151     cairo_t* cr = c->platformContext();
1152     cairo_matrix_t m;
1153     cairo_get_matrix(cr, &m);
1154
1155     cairo_pattern_t* platformPattern = pattern->createPattern(m);
1156     if (!platformPattern)
1157         return;
1158
1159     cairo_set_source(cr, platformPattern);
1160     cairo_pattern_destroy(platformPattern);
1161 #endif
1162     state().m_appliedFillPattern = true;
1163 }
1164
1165 } // namespace WebCore