aab0c0b647e51d82734d5c0d38b1823445ce600d
[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     // FIXME: Do this through platform-independent GraphicsContext API.
472 #if PLATFORM(CG)
473     CGContextBeginPath(c->platformContext());
474     CGContextAddPath(c->platformContext(), state().m_path.platformPath());
475
476     if (!state().m_path.isEmpty())
477         willDraw(CGContextGetPathBoundingBox(c->platformContext()));
478
479     if (state().m_fillStyle->gradient()) {
480         // Shading works on the entire clip region, so convert the current path to a clip.
481         c->save();
482         CGContextClip(c->platformContext());
483         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
484         c->restore();
485     } else {
486         if (state().m_fillStyle->pattern())
487             applyFillPattern();
488         CGContextFillPath(c->platformContext());
489     }
490 #elif PLATFORM(QT)
491     QPainterPath* path = state().m_path.platformPath();
492     QPainter* p = static_cast<QPainter*>(c->platformContext());
493     willDraw(path->controlPointRect());
494     if (state().m_fillStyle->gradient()) {
495         p->fillPath(*path, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
496     } else {
497         if (state().m_fillStyle->pattern())
498             applyFillPattern();
499         p->fillPath(*path, p->brush());
500     }
501 #elif PLATFORM(CAIRO)
502     cairo_t* cr = c->platformContext();
503     cairo_save(cr);
504     willDraw(state().m_path.boundingRect());
505     if (state().m_fillStyle->gradient()) {
506         cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
507         c->addPath(state().m_path);
508         cairo_fill(cr);
509     } else {
510         if (state().m_fillStyle->pattern())
511             applyFillPattern();
512         c->addPath(state().m_path);
513         cairo_fill(cr);
514     }
515     cairo_restore(cr);
516 #endif
517
518     clearPathForDashboardBackwardCompatibilityMode();
519 }
520
521 void CanvasRenderingContext2D::stroke()
522 {
523     GraphicsContext* c = drawingContext();
524     if (!c)
525         return;
526     // FIXME: Do this through platform-independent GraphicsContext API.
527 #if PLATFORM(CG)
528     CGContextBeginPath(c->platformContext());
529     CGContextAddPath(c->platformContext(), state().m_path.platformPath());
530
531     if (!state().m_path.isEmpty()) {
532         float lineWidth = state().m_lineWidth;
533         float inset = -lineWidth / 2;
534         CGRect boundingRect = CGRectInset(CGContextGetPathBoundingBox(c->platformContext()), inset, inset);
535         willDraw(boundingRect);
536     }
537
538     if (state().m_strokeStyle->gradient()) {
539         // Shading works on the entire clip region, so convert the current path to a clip.
540         c->save();
541         CGContextReplacePathWithStrokedPath(c->platformContext());
542         CGContextClip(c->platformContext());
543         CGContextDrawShading(c->platformContext(), state().m_strokeStyle->gradient()->platformShading());        
544         c->restore();
545     } else {
546         if (state().m_strokeStyle->pattern())
547             applyStrokePattern();
548         CGContextStrokePath(c->platformContext());
549     }
550 #elif PLATFORM(QT)
551     QPainterPath* path = state().m_path.platformPath();
552     QPainter* p = static_cast<QPainter*>(c->platformContext());
553     willDraw(path->controlPointRect());
554     if (state().m_strokeStyle->gradient()) {
555         p->save();
556         p->setBrush(*(state().m_strokeStyle->gradient()->platformShading()));
557         p->strokePath(*path, p->pen());
558         p->restore();
559     } else {
560         if (state().m_strokeStyle->pattern())
561             applyStrokePattern();
562         p->strokePath(*path, p->pen());
563     }
564 #elif PLATFORM(CAIRO)
565     cairo_t* cr = c->platformContext();
566     cairo_save(cr);
567     // FIXME: consider inset, as in CG
568     willDraw(state().m_path.boundingRect());
569     if (state().m_strokeStyle->gradient()) {
570         cairo_set_source(cr, state().m_strokeStyle->gradient()->platformShading());
571         c->addPath(state().m_path);
572         cairo_stroke(cr);
573     } else {
574         if (state().m_strokeStyle->pattern())
575             applyStrokePattern();
576         c->addPath(state().m_path);
577         cairo_stroke(cr);
578     }
579     cairo_restore(cr);
580 #endif
581
582     clearPathForDashboardBackwardCompatibilityMode();
583 }
584
585 void CanvasRenderingContext2D::clip()
586 {
587     GraphicsContext* c = drawingContext();
588     if (!c)
589         return;
590     c->clip(state().m_path);
591     clearPathForDashboardBackwardCompatibilityMode();
592 }
593
594 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
595 {
596     GraphicsContext* c = drawingContext();
597     if (!c)
598         return false;
599     FloatPoint point(x, y);
600     // We have to invert the current transform to ensure we correctly handle the
601     // transforms applied to the current path.
602     AffineTransform ctm = c->getCTM();
603     if (!ctm.isInvertible())
604         return false;
605     FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
606     return state().m_path.contains(transformedPoint);
607 }
608
609 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height, ExceptionCode& ec)
610 {
611     ec = 0;
612     if (!(width >= 0 && height >= 0)) {
613         ec = INDEX_SIZE_ERR;
614         return;
615     }
616     GraphicsContext* c = drawingContext();
617     if (!c)
618         return;
619     FloatRect rect(x, y, width, height);
620     willDraw(rect);
621     c->clearRect(rect);
622 }
623
624 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height, ExceptionCode& ec)
625 {
626     ec = 0;
627
628     if (!(width >= 0 && height >= 0)) {
629         ec = INDEX_SIZE_ERR;
630         return;
631     }
632
633     GraphicsContext* c = drawingContext();
634     if (!c)
635         return;
636     // FIXME: Do this through platform-independent GraphicsContext API.
637 #if PLATFORM(CG)
638     CGRect rect = CGRectMake(x, y, width, height);
639
640     willDraw(rect);
641
642     if (state().m_fillStyle->gradient()) {
643         // Shading works on the entire clip region, so convert the rect to a clip.
644         c->save();
645         CGContextClipToRect(c->platformContext(), rect);
646         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
647         c->restore();
648     } else {
649         if (state().m_fillStyle->pattern())
650             applyFillPattern();
651         CGContextFillRect(c->platformContext(), rect);
652     }
653 #elif PLATFORM(QT)
654     QRectF rect(x, y, width, height);
655     willDraw(rect);
656     QPainter* p = static_cast<QPainter*>(c->platformContext());
657     if (state().m_fillStyle->gradient()) {
658         p->fillRect(rect, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
659     } else {
660         if (state().m_fillStyle->pattern())
661             applyFillPattern();
662         p->fillRect(rect, p->brush());
663     }
664 #elif PLATFORM(CAIRO)
665     FloatRect rect(x, y, width, height);
666     willDraw(rect);
667     cairo_t* cr = c->platformContext();
668     cairo_save(cr);
669     if (state().m_fillStyle->gradient()) {
670         cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
671     } else {
672         if (state().m_fillStyle->pattern())
673             applyFillPattern();
674     }
675     cairo_rectangle(cr, x, y, width, height);
676     cairo_fill(cr);
677     cairo_restore(cr);
678 #endif
679 }
680
681 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, ExceptionCode& ec)
682 {
683     strokeRect(x, y, width, height, state().m_lineWidth, ec);
684 }
685
686 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
687 {
688     ec = 0;
689
690     if (!(width >= 0 && height >= 0 && lineWidth >= 0)) {
691         ec = INDEX_SIZE_ERR;
692         return;
693     }
694
695     GraphicsContext* c = drawingContext();
696     if (!c)
697         return;
698
699     FloatRect rect(x, y, width, height);
700
701     FloatRect boundingRect = rect;
702     boundingRect.inflate(lineWidth / 2);
703     willDraw(boundingRect);
704
705     // FIXME: No support for gradients!
706     if (state().m_strokeStyle->pattern())
707         applyStrokePattern();
708
709     c->strokeRect(rect, lineWidth);
710 }
711
712 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
713 {
714     state().m_shadowOffset = FloatSize(width, height);
715     state().m_shadowBlur = blur;
716     state().m_shadowColor = "";
717     applyShadow();
718 }
719
720 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
721 {
722     state().m_shadowOffset = FloatSize(width, height);
723     state().m_shadowBlur = blur;
724     state().m_shadowColor = color;
725     applyShadow();
726 }
727
728 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
729 {
730     state().m_shadowOffset = FloatSize(width, height);
731     state().m_shadowBlur = blur;
732     state().m_shadowColor = "";
733
734     GraphicsContext* c = drawingContext();
735     if (!c)
736         return;
737     // FIXME: Do this through platform-independent GraphicsContext API.
738 #if PLATFORM(CG)
739     const CGFloat components[2] = { grayLevel, 1 };
740     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
741     CGColorRef color = CGColorCreate(colorSpace, components);
742     CGColorSpaceRelease(colorSpace);
743     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
744     CGColorRelease(color);
745 #endif
746 }
747
748 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
749 {
750     state().m_shadowOffset = FloatSize(width, height);
751     state().m_shadowBlur = blur;
752     state().m_shadowColor = color;
753
754     GraphicsContext* c = drawingContext();
755     if (!c)
756         return;
757     // FIXME: Do this through platform-independent GraphicsContext API.
758 #if PLATFORM(CG)
759     RGBA32 rgba = 0; // default is transparent black
760     CSSParser::parseColor(rgba, color);
761     const CGFloat components[4] = {
762         ((rgba >> 16) & 0xFF) / 255.0f,
763         ((rgba >> 8) & 0xFF) / 255.0f,
764         (rgba & 0xFF) / 255.0f,
765         alpha
766     };
767     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
768     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
769     CGColorSpaceRelease(colorSpace);
770     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
771     CGColorRelease(shadowColor);
772 #endif
773 }
774
775 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
776 {
777     state().m_shadowOffset = FloatSize(width, height);
778     state().m_shadowBlur = blur;
779     state().m_shadowColor = "";
780
781     GraphicsContext* c = drawingContext();
782     if (!c)
783         return;
784     // FIXME: Do this through platform-independent GraphicsContext API.
785 #if PLATFORM(CG)
786     const CGFloat components[2] = { grayLevel, alpha };
787     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
788     CGColorRef color = CGColorCreate(colorSpace, components);
789     CGColorSpaceRelease(colorSpace);
790     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
791     CGColorRelease(color);
792 #endif
793 }
794
795 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
796 {
797     state().m_shadowOffset = FloatSize(width, height);
798     state().m_shadowBlur = blur;
799     state().m_shadowColor = "";
800
801     GraphicsContext* c = drawingContext();
802     if (!c)
803         return;
804     // FIXME: Do this through platform-independent GraphicsContext API.
805 #if PLATFORM(CG)
806     const CGFloat components[4] = { r, g, b, a };
807     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
808     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
809     CGColorSpaceRelease(colorSpace);
810     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
811     CGColorRelease(shadowColor);
812 #endif
813 }
814
815 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
816 {
817     state().m_shadowOffset = FloatSize(width, height);
818     state().m_shadowBlur = blur;
819     state().m_shadowColor = "";
820
821     GraphicsContext* dc = drawingContext();
822     if (!dc)
823         return;
824     // FIXME: Do this through platform-independent GraphicsContext API.
825 #if PLATFORM(CG)
826     const CGFloat components[5] = { c, m, y, k, a };
827     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
828     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
829     CGColorSpaceRelease(colorSpace);
830     CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
831     CGColorRelease(shadowColor);
832 #endif
833 }
834
835 void CanvasRenderingContext2D::clearShadow()
836 {
837     state().m_shadowOffset = FloatSize();
838     state().m_shadowBlur = 0;
839     state().m_shadowColor = "";
840     applyShadow();
841 }
842
843 void CanvasRenderingContext2D::applyShadow()
844 {
845     GraphicsContext* c = drawingContext();
846     if (!c)
847         return;
848     // FIXME: Do this through platform-independent GraphicsContext API.
849 #if PLATFORM(CG)
850     RGBA32 rgba = 0; // default is transparent black
851     if (!state().m_shadowColor.isEmpty())
852         CSSParser::parseColor(rgba, state().m_shadowColor);
853     const CGFloat components[4] = {
854         ((rgba >> 16) & 0xFF) / 255.0f,
855         ((rgba >> 8) & 0xFF) / 255.0f,
856         (rgba & 0xFF) / 255.0f,
857         ((rgba >> 24) & 0xFF) / 255.0f
858     };
859     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
860     CGColorRef color = CGColorCreate(colorSpace, components);
861     CGColorSpaceRelease(colorSpace);
862     CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
863     CGColorRelease(color);
864 #endif
865 }
866
867 static IntSize size(HTMLImageElement* image)
868 {
869     if (CachedImage* cachedImage = image->cachedImage())
870         return cachedImage->imageSize();
871     return IntSize();
872 }
873
874 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
875 {
876     ASSERT(image);
877     IntSize s = size(image);
878     ExceptionCode ec;
879     drawImage(image, x, y, s.width(), s.height(), ec);
880 }
881
882 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
883     float x, float y, float width, float height, ExceptionCode& ec)
884 {
885     ASSERT(image);
886     IntSize s = size(image);
887     drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
888 }
889
890 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
891     ExceptionCode& ec)
892 {
893     ASSERT(image);
894
895     ec = 0;
896
897     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
898     if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 
899             && dstRect.width() >= 0 && dstRect.height() >= 0)) {
900         ec = INDEX_SIZE_ERR;
901         return;
902     }
903
904     if (srcRect.isEmpty() || dstRect.isEmpty())
905         return;
906
907     GraphicsContext* c = drawingContext();
908     if (!c)
909         return;
910
911     CachedImage* cachedImage = image->cachedImage();
912     if (!cachedImage)
913         return;
914
915     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
916     FloatRect destRect = c->roundToDevicePixels(dstRect);
917     willDraw(destRect);
918     c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
919 }
920
921 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
922 {
923     ASSERT(canvas);
924     ExceptionCode ec;
925     drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
926 }
927
928 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
929     float x, float y, float width, float height, ExceptionCode& ec)
930 {
931     ASSERT(canvas);
932     drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
933 }
934
935 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
936     const FloatRect& dstRect, ExceptionCode& ec)
937 {
938     ASSERT(canvas);
939
940     ec = 0;
941
942     FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
943     if (!(srcCanvasRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 
944             && dstRect.width() >= 0 && dstRect.height() >= 0)) {
945         ec = INDEX_SIZE_ERR;
946         return;
947     }
948
949     if (srcRect.isEmpty() || dstRect.isEmpty())
950         return;
951
952     GraphicsContext* c = drawingContext();
953     if (!c)
954         return;
955         
956     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
957     FloatRect destRect = c->roundToDevicePixels(dstRect);
958         
959     // FIXME: Do this through platform-independent GraphicsContext API.
960     ImageBuffer* buffer = canvas->buffer();
961     if (!buffer)
962         return;
963     
964     willDraw(destRect);
965     c->drawImage(buffer, sourceRect, destRect);
966 }
967
968 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
969 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
970     float sx, float sy, float sw, float sh,
971     float dx, float dy, float dw, float dh,
972     const String& compositeOperation)
973 {
974     if (!image)
975         return;
976
977     CachedImage* cachedImage = image->cachedImage();
978     if (!cachedImage)
979         return;
980
981     GraphicsContext* c = drawingContext();
982     if (!c)
983         return;
984
985     CompositeOperator op;
986     if (!parseCompositeOperator(compositeOperation, op))
987         op = CompositeSourceOver;
988
989     FloatRect destRect = FloatRect(dx, dy, dw, dh);
990     willDraw(destRect);
991     c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
992 }
993
994 void CanvasRenderingContext2D::setAlpha(float alpha)
995 {
996     setGlobalAlpha(alpha);
997 }
998
999 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1000 {
1001     setGlobalCompositeOperation(operation);
1002 }
1003
1004 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1005 {
1006     return new CanvasGradient(FloatPoint(x0, y0), FloatPoint(x1, y1));
1007 }
1008
1009 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
1010 {
1011     return new CanvasGradient(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1012 }
1013
1014 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1015     const String& repetitionType, ExceptionCode& ec)
1016 {
1017     bool repeatX, repeatY;
1018     ec = 0;
1019     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1020     if (ec)
1021         return 0;
1022     return new CanvasPattern(image ? image->cachedImage() : 0, repeatX, repeatY);
1023 }
1024
1025 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1026     const String& repetitionType, ExceptionCode& ec)
1027 {
1028     bool repeatX, repeatY;
1029     ec = 0;
1030     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1031     if (ec)
1032         return 0;
1033     // FIXME: Do this through platform-independent GraphicsContext API.
1034 #if PLATFORM(CG)
1035     CGImageRef image = canvas->createPlatformImage();
1036     if (!image)
1037         return 0;
1038     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(image, repeatX, repeatY);
1039     CGImageRelease(image);
1040     return pattern;
1041 #elif PLATFORM(CAIRO)
1042     cairo_surface_t* surface = canvas->createPlatformImage();
1043     if (!surface)
1044         return 0;
1045     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(surface, repeatX, repeatY);
1046     cairo_surface_destroy(surface);
1047     return pattern;
1048 #else
1049     notImplemented();
1050     return 0;
1051 #endif
1052 }
1053
1054 void CanvasRenderingContext2D::willDraw(const FloatRect& r)
1055 {
1056     if (!m_canvas)
1057         return;
1058     GraphicsContext* c = drawingContext();
1059     if (!c)
1060         return;
1061
1062     m_canvas->willDraw(c->getCTM().mapRect(r));
1063 }
1064
1065 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1066 {
1067     if (!m_canvas)
1068         return 0;
1069     return m_canvas->drawingContext();
1070 }
1071
1072 void CanvasRenderingContext2D::applyStrokePattern()
1073 {
1074     GraphicsContext* c = drawingContext();
1075     if (!c)
1076         return;
1077
1078 #if PLATFORM(CG)
1079     // Check for case where the pattern is already set.
1080     CGAffineTransform m = CGContextGetCTM(c->platformContext());
1081     if (state().m_appliedStrokePattern
1082             && CGAffineTransformEqualToTransform(m, state().m_strokeStylePatternTransform))
1083         return;
1084
1085     CanvasPattern* pattern = state().m_strokeStyle->pattern();
1086     if (!pattern)
1087         return;
1088
1089     CGPatternRef platformPattern = pattern->createPattern(m);
1090     if (!platformPattern)
1091         return;
1092
1093     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1094     CGContextSetStrokeColorSpace(c->platformContext(), patternSpace);
1095     CGColorSpaceRelease(patternSpace);
1096
1097     const CGFloat patternAlpha = 1;
1098     CGContextSetStrokePattern(c->platformContext(), platformPattern, &patternAlpha);
1099     CGPatternRelease(platformPattern);
1100
1101     state().m_strokeStylePatternTransform = m;
1102 #elif PLATFORM(QT)
1103     fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyStrokePattern\n");
1104 #elif PLATFORM(CAIRO)
1105     CanvasPattern* pattern = state().m_strokeStyle->pattern();
1106     if (!pattern)
1107         return;
1108
1109     cairo_t* cr = c->platformContext();
1110     cairo_matrix_t m;
1111     cairo_get_matrix(cr, &m);
1112
1113     cairo_pattern_t* platformPattern = pattern->createPattern(m);
1114     if (!platformPattern)
1115         return;
1116
1117     cairo_set_source(cr, platformPattern);
1118     cairo_pattern_destroy(platformPattern);
1119 #endif
1120     state().m_appliedStrokePattern = true;
1121 }
1122
1123 void CanvasRenderingContext2D::applyFillPattern()
1124 {
1125     GraphicsContext* c = drawingContext();
1126     if (!c)
1127         return;
1128
1129 #if PLATFORM(CG)
1130     // Check for case where the pattern is already set.
1131     CGAffineTransform m = CGContextGetCTM(c->platformContext());
1132     if (state().m_appliedFillPattern
1133             && CGAffineTransformEqualToTransform(m, state().m_fillStylePatternTransform))
1134         return;
1135
1136     CanvasPattern* pattern = state().m_fillStyle->pattern();
1137     if (!pattern)
1138         return;
1139
1140     CGPatternRef platformPattern = pattern->createPattern(m);
1141     if (!platformPattern)
1142         return;
1143
1144     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1145     CGContextSetFillColorSpace(c->platformContext(), patternSpace);
1146     CGColorSpaceRelease(patternSpace);
1147
1148     const CGFloat patternAlpha = 1;
1149     CGContextSetFillPattern(c->platformContext(), platformPattern, &patternAlpha);
1150     CGPatternRelease(platformPattern);
1151
1152     state().m_fillStylePatternTransform = m;
1153 #elif PLATFORM(QT)
1154     fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyFillPattern\n");
1155 #elif PLATFORM(CAIRO)
1156     CanvasPattern* pattern = state().m_fillStyle->pattern();
1157     if (!pattern)
1158         return;
1159
1160     cairo_t* cr = c->platformContext();
1161     cairo_matrix_t m;
1162     cairo_get_matrix(cr, &m);
1163
1164     cairo_pattern_t* platformPattern = pattern->createPattern(m);
1165     if (!platformPattern)
1166         return;
1167
1168     cairo_set_source(cr, platformPattern);
1169     cairo_pattern_destroy(platformPattern);
1170 #endif
1171     state().m_appliedFillPattern = true;
1172 }
1173
1174 } // namespace WebCore