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