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