2006-10-09 Oliver Hunt <ohunt@apple.com>
[WebKit-https.git] / WebCore / html / CanvasRenderingContext2D.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CanvasRenderingContext2D.h"
28
29 #include "CachedImage.h"
30 #include "CanvasGradient.h"
31 #include "CanvasPattern.h"
32 #include "CanvasStyle.h"
33 #include "ExceptionCode.h"
34 #include "GraphicsContext.h"
35 #include "HTMLCanvasElement.h"
36 #include "HTMLImageElement.h"
37 #include "HTMLNames.h"
38 #include "RenderHTMLCanvas.h"
39 #include "cssparser.h"
40
41 namespace WebCore {
42
43 using namespace HTMLNames;
44
45 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
46     : m_canvas(canvas)
47     , m_stateStack(1)
48 {
49 }
50
51 void CanvasRenderingContext2D::reset()
52 {
53     m_stateStack.resize(1);
54     m_stateStack.first() = State();
55 }
56
57 CanvasRenderingContext2D::State::State()
58     : m_strokeStyle(new CanvasStyle("black"))
59     , m_fillStyle(new CanvasStyle("black"))
60     , m_lineWidth(1)
61     , m_lineCap(ButtCap)
62     , m_lineJoin(MiterJoin)
63     , m_miterLimit(10)
64     , m_shadowBlur(0)
65     , m_shadowColor("black")
66     , m_globalAlpha(1)
67     , m_globalComposite(CompositeSourceOver)
68     , m_appliedStrokePattern(false)
69     , m_appliedFillPattern(false)
70 {
71 }
72
73 void CanvasRenderingContext2D::save()
74 {
75     ASSERT(m_stateStack.size() >= 1);
76     m_stateStack.append(state());
77     GraphicsContext* c = drawingContext();
78     if (!c)
79         return;
80     c->save();
81 }
82
83 void CanvasRenderingContext2D::restore()
84 {
85     ASSERT(m_stateStack.size() >= 1);
86     if (m_stateStack.size() <= 1)
87         return;
88     m_stateStack.removeLast();
89     GraphicsContext* c = drawingContext();
90     if (!c)
91         return;
92     c->restore();
93 }
94
95 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
96 {
97     return state().m_strokeStyle.get();
98 }
99
100 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
101 {
102     if (!style)
103         return;
104     state().m_strokeStyle = style;
105     GraphicsContext* c = drawingContext();
106     if (!c)
107         return;
108     state().m_strokeStyle->applyStrokeColor(c);
109     state().m_appliedStrokePattern = false;
110 }
111
112 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
113 {
114     return state().m_fillStyle.get();
115 }
116
117 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
118 {
119     if (!style)
120         return;
121     state().m_fillStyle = style;
122     GraphicsContext* c = drawingContext();
123     if (!c)
124         return;
125     state().m_fillStyle->applyFillColor(c);
126     state().m_appliedFillPattern = false;
127 }
128
129 float CanvasRenderingContext2D::lineWidth() const
130 {
131     return state().m_lineWidth;
132 }
133
134 void CanvasRenderingContext2D::setLineWidth(float width)
135 {
136     if (!(width > 0))
137         return;
138     state().m_lineWidth = width;
139     GraphicsContext* c = drawingContext();
140     if (!c)
141         return;
142     c->setLineWidth(width);
143 }
144
145 String CanvasRenderingContext2D::lineCap() const
146 {
147     return lineCapName(state().m_lineCap);
148 }
149
150 void CanvasRenderingContext2D::setLineCap(const String& s)
151 {
152     LineCap cap;
153     if (!parseLineCap(s, cap))
154         return;
155     state().m_lineCap = cap;
156     GraphicsContext* c = drawingContext();
157     if (!c)
158         return;
159     c->setLineCap(cap);
160 }
161
162 String CanvasRenderingContext2D::lineJoin() const
163 {
164     return lineJoinName(state().m_lineJoin);
165 }
166
167 void CanvasRenderingContext2D::setLineJoin(const String& s)
168 {
169     LineJoin join;
170     if (!parseLineJoin(s, join))
171         return;
172     state().m_lineJoin = join;
173     GraphicsContext* c = drawingContext();
174     if (!c)
175         return;
176     c->setLineJoin(join);
177 }
178
179 float CanvasRenderingContext2D::miterLimit() const
180 {
181     return state().m_miterLimit;
182 }
183
184 void CanvasRenderingContext2D::setMiterLimit(float limit)
185 {
186     if (!(limit > 0))
187         return;
188     state().m_miterLimit = limit;
189     GraphicsContext* c = drawingContext();
190     if (!c)
191         return;
192     c->setMiterLimit(limit);
193 }
194
195 float CanvasRenderingContext2D::shadowOffsetX() const
196 {
197     return state().m_shadowOffset.width();
198 }
199
200 void CanvasRenderingContext2D::setShadowOffsetX(float x)
201 {
202     state().m_shadowOffset.setWidth(x);
203     applyShadow();
204 }
205
206 float CanvasRenderingContext2D::shadowOffsetY() const
207 {
208     return state().m_shadowOffset.height();
209 }
210
211 void CanvasRenderingContext2D::setShadowOffsetY(float y)
212 {
213     state().m_shadowOffset.setHeight(y);
214     applyShadow();
215 }
216
217 float CanvasRenderingContext2D::shadowBlur() const
218 {
219     return state().m_shadowBlur;
220 }
221
222 void CanvasRenderingContext2D::setShadowBlur(float blur)
223 {
224     state().m_shadowBlur = blur;
225     applyShadow();
226 }
227
228 String CanvasRenderingContext2D::shadowColor() const
229 {
230     // FIXME: What should this return if you called setShadow with a non-string color?
231     return state().m_shadowColor;
232 }
233
234 void CanvasRenderingContext2D::setShadowColor(const String& color)
235 {
236     state().m_shadowColor = color;
237     applyShadow();
238 }
239
240 float CanvasRenderingContext2D::globalAlpha() const
241 {
242     return state().m_globalAlpha;
243 }
244
245 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
246 {
247     if (!(alpha >= 0 && alpha <= 1))
248         return;
249     state().m_globalAlpha = alpha;
250     GraphicsContext* c = drawingContext();
251     if (!c)
252         return;
253     c->setAlpha(alpha);
254 }
255
256 String CanvasRenderingContext2D::globalCompositeOperation() const
257 {
258     return compositeOperatorName(state().m_globalComposite);
259 }
260
261 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
262 {
263     CompositeOperator op;
264     if (!parseCompositeOperator(operation, op))
265         return;
266     state().m_globalComposite = op;
267     GraphicsContext* c = drawingContext();
268     if (!c)
269         return;
270     c->setCompositeOperation(op);
271 }
272
273 void CanvasRenderingContext2D::scale(float sx, float sy)
274 {
275     GraphicsContext* c = drawingContext();
276     if (!c)
277         return;
278     c->scale(FloatSize(sx, sy));
279 }
280
281 void CanvasRenderingContext2D::rotate(float angleInRadians)
282 {
283     GraphicsContext* c = drawingContext();
284     if (!c)
285         return;
286     c->rotate(angleInRadians);
287 }
288
289 void CanvasRenderingContext2D::translate(float tx, float ty)
290 {
291     GraphicsContext* c = drawingContext();
292     if (!c)
293         return;
294     c->translate(tx, ty);
295 }
296
297 void CanvasRenderingContext2D::setStrokeColor(const String& color)
298 {
299     setStrokeStyle(new CanvasStyle(color));
300 }
301
302 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
303 {
304     setStrokeStyle(new CanvasStyle(grayLevel, 1));
305 }
306
307 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
308 {
309     setStrokeStyle(new CanvasStyle(color, alpha));
310 }
311
312 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
313 {
314     setStrokeStyle(new CanvasStyle(grayLevel, alpha));
315 }
316
317 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
318 {
319     setStrokeStyle(new CanvasStyle(r, g, b, a));
320 }
321
322 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
323 {
324     setStrokeStyle(new CanvasStyle(c, m, y, k, a));
325 }
326
327 void CanvasRenderingContext2D::setFillColor(const String& color)
328 {
329     setFillStyle(new CanvasStyle(color));
330 }
331
332 void CanvasRenderingContext2D::setFillColor(float grayLevel)
333 {
334     setFillStyle(new CanvasStyle(grayLevel, 1));
335 }
336
337 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
338 {
339     setFillStyle(new CanvasStyle(color, 1));
340 }
341
342 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
343 {
344     setFillStyle(new CanvasStyle(grayLevel, alpha));
345 }
346
347 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
348 {
349     setFillStyle(new CanvasStyle(r, g, b, a));
350 }
351
352 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
353 {
354     setFillStyle(new CanvasStyle(c, m, y, k, a));
355 }
356
357 void CanvasRenderingContext2D::beginPath()
358 {
359     state().m_path.clear();
360 }
361
362 void CanvasRenderingContext2D::closePath()
363 {
364     state().m_path.closeSubpath();
365 }
366
367 void CanvasRenderingContext2D::moveTo(float x, float y)
368 {
369     state().m_path.moveTo(FloatPoint(x, y));
370 }
371
372 void CanvasRenderingContext2D::lineTo(float x, float y)
373 {
374     state().m_path.addLineTo(FloatPoint(x, y));
375 }
376
377 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
378 {
379     state().m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
380 }
381
382 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
383 {
384     state().m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
385 }
386
387 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
388 {
389     ec = 0;
390     if (!(r > 0)) {
391         ec = INDEX_SIZE_ERR;
392         return;
393     }
394     state().m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
395 }
396
397 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool clockwise, ExceptionCode& ec)
398 {
399     ec = 0;
400     if (!(r > 0)) {
401         ec = INDEX_SIZE_ERR;
402         return;
403     }
404     state().m_path.addArc(FloatPoint(x, y), r, sa, ea, clockwise);
405 }
406
407 void CanvasRenderingContext2D::rect(float x, float y, float width, float height, ExceptionCode& ec)
408 {
409     ec = 0;
410     if (!(width > 0 && height > 0)) {
411         ec = INDEX_SIZE_ERR;
412         return;
413     }
414     state().m_path.addRect(FloatRect(x, y, width, height));
415 }
416
417 void CanvasRenderingContext2D::fill()
418 {
419     GraphicsContext* c = drawingContext();
420     if (!c)
421         return;
422     // FIXME: Do this through platform-independent GraphicsContext API.
423 #if PLATFORM(CG)
424     CGContextBeginPath(c->platformContext());
425     CGContextAddPath(c->platformContext(), state().m_path.platformPath());
426
427     willDraw(CGContextGetPathBoundingBox(c->platformContext()));
428
429     if (state().m_fillStyle->gradient()) {
430         // Shading works on the entire clip region, so convert the current path to a clip.
431         c->save();
432         CGContextClip(c->platformContext());
433         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
434         c->restore();
435     } else {
436         if (state().m_fillStyle->pattern())
437             applyFillPattern();
438         CGContextFillPath(c->platformContext());
439     }
440 #endif
441 }
442
443 void CanvasRenderingContext2D::stroke()
444 {
445     GraphicsContext* c = drawingContext();
446     if (!c)
447         return;
448     // FIXME: Do this through platform-independent GraphicsContext API.
449 #if PLATFORM(CG)
450     CGContextBeginPath(c->platformContext());
451     CGContextAddPath(c->platformContext(), state().m_path.platformPath());
452
453     float lineWidth = state().m_lineWidth;
454     float inset = -lineWidth / 2;
455     CGRect boundingRect = CGRectInset(CGContextGetPathBoundingBox(c->platformContext()), inset, inset);
456
457     willDraw(boundingRect);
458
459     if (state().m_strokeStyle->gradient()) {
460         // Shading works on the entire clip region, so convert the current path to a clip.
461         c->save();
462         CGContextReplacePathWithStrokedPath(c->platformContext());
463         CGContextClip(c->platformContext());
464         CGContextDrawShading(c->platformContext(), state().m_strokeStyle->gradient()->platformShading());        
465         c->restore();
466     } else {
467         if (state().m_strokeStyle->pattern())
468             applyStrokePattern();
469         CGContextStrokePath(c->platformContext());
470     }
471 #endif
472 }
473
474 void CanvasRenderingContext2D::clip()
475 {
476     GraphicsContext* c = drawingContext();
477     if (!c)
478         return;
479     c->clip(state().m_path);
480 }
481
482 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height, ExceptionCode& ec)
483 {
484     ec = 0;
485     if (!(width > 0 && height > 0)) {
486         ec = INDEX_SIZE_ERR;
487         return;
488     }
489     GraphicsContext* c = drawingContext();
490     if (!c)
491         return;
492     FloatRect rect(x, y, width, height);
493     willDraw(rect);
494     c->clearRect(rect);
495 }
496
497 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height, ExceptionCode& ec)
498 {
499     ec = 0;
500
501     if (!(width > 0 && height > 0)) {
502         ec = INDEX_SIZE_ERR;
503         return;
504     }
505
506     GraphicsContext* c = drawingContext();
507     if (!c)
508         return;
509     // FIXME: Do this through platform-independent GraphicsContext API.
510 #if PLATFORM(CG)
511     CGRect rect = CGRectMake(x, y, width, height);
512
513     willDraw(rect);
514
515     if (state().m_fillStyle->gradient()) {
516         // Shading works on the entire clip region, so convert the rect to a clip.
517         c->save();
518         CGContextClipToRect(c->platformContext(), rect);
519         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
520         c->restore();
521     } else {
522         if (state().m_fillStyle->pattern())
523             applyFillPattern();
524         CGContextFillRect(c->platformContext(), rect);
525     }
526 #endif
527 }
528
529 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, ExceptionCode& ec)
530 {
531     strokeRect(x, y, width, height, state().m_lineWidth, ec);
532 }
533
534 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
535 {
536     ec = 0;
537
538     if (!(width > 0 && height > 0 && lineWidth > 0)) {
539         ec = INDEX_SIZE_ERR;
540         return;
541     }
542
543     GraphicsContext* c = drawingContext();
544     if (!c)
545         return;
546
547     FloatRect rect(x, y, width, height);
548
549     FloatRect boundingRect = rect;
550     boundingRect.inflate(lineWidth / 2);
551     willDraw(boundingRect);
552
553     // FIXME: No support for gradients!
554     if (state().m_strokeStyle->pattern())
555         applyStrokePattern();
556
557     c->strokeRect(rect, lineWidth);
558 }
559
560 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
561 {
562     state().m_shadowOffset = FloatSize(width, height);
563     state().m_shadowBlur = blur;
564     state().m_shadowColor = "";
565     applyShadow();
566 }
567
568 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
569 {
570     state().m_shadowOffset = FloatSize(width, height);
571     state().m_shadowBlur = blur;
572     state().m_shadowColor = color;
573     applyShadow();
574 }
575
576 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
577 {
578     state().m_shadowOffset = FloatSize(width, height);
579     state().m_shadowBlur = blur;
580     state().m_shadowColor = "";
581
582     GraphicsContext* c = drawingContext();
583     if (!c)
584         return;
585     // FIXME: Do this through platform-independent GraphicsContext API.
586 #if PLATFORM(CG)
587     const CGFloat components[2] = { grayLevel, 1 };
588     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
589     CGColorRef color = CGColorCreate(colorSpace, components);
590     CGColorSpaceRelease(colorSpace);
591     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
592     CGColorRelease(color);
593 #endif
594 }
595
596 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
597 {
598     state().m_shadowOffset = FloatSize(width, height);
599     state().m_shadowBlur = blur;
600     state().m_shadowColor = color;
601
602     GraphicsContext* c = drawingContext();
603     if (!c)
604         return;
605     // FIXME: Do this through platform-independent GraphicsContext API.
606 #if PLATFORM(CG)
607     RGBA32 rgba = CSSParser::parseColor(color);
608     const CGFloat components[4] = {
609         ((rgba >> 16) & 0xFF) / 255.0,
610         ((rgba >> 8) & 0xFF) / 255.0,
611         (rgba & 0xFF) / 255.0,
612         alpha
613     };
614     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
615     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
616     CGColorSpaceRelease(colorSpace);
617     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
618     CGColorRelease(shadowColor);
619 #endif
620 }
621
622 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
623 {
624     state().m_shadowOffset = FloatSize(width, height);
625     state().m_shadowBlur = blur;
626     state().m_shadowColor = "";
627
628     GraphicsContext* c = drawingContext();
629     if (!c)
630         return;
631     // FIXME: Do this through platform-independent GraphicsContext API.
632 #if PLATFORM(CG)
633     const CGFloat components[2] = { grayLevel, alpha };
634     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
635     CGColorRef color = CGColorCreate(colorSpace, components);
636     CGColorSpaceRelease(colorSpace);
637     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
638     CGColorRelease(color);
639 #endif
640 }
641
642 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
643 {
644     state().m_shadowOffset = FloatSize(width, height);
645     state().m_shadowBlur = blur;
646     state().m_shadowColor = "";
647
648     GraphicsContext* c = drawingContext();
649     if (!c)
650         return;
651     // FIXME: Do this through platform-independent GraphicsContext API.
652 #if PLATFORM(CG)
653     const CGFloat components[4] = { r, g, b, a };
654     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
655     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
656     CGColorSpaceRelease(colorSpace);
657     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
658     CGColorRelease(shadowColor);
659 #endif
660 }
661
662 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
663 {
664     state().m_shadowOffset = FloatSize(width, height);
665     state().m_shadowBlur = blur;
666     state().m_shadowColor = "";
667
668     GraphicsContext* dc = drawingContext();
669     if (!dc)
670         return;
671     // FIXME: Do this through platform-independent GraphicsContext API.
672 #if PLATFORM(CG)
673     const CGFloat components[5] = { c, m, y, k, a };
674     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
675     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
676     CGColorSpaceRelease(colorSpace);
677     CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
678     CGColorRelease(shadowColor);
679 #endif
680 }
681
682 void CanvasRenderingContext2D::clearShadow()
683 {
684     state().m_shadowOffset = FloatSize();
685     state().m_shadowBlur = 0;
686     state().m_shadowColor = "";
687     applyShadow();
688 }
689
690 void CanvasRenderingContext2D::applyShadow()
691 {
692     GraphicsContext* c = drawingContext();
693     if (!c)
694         return;
695     // FIXME: Do this through platform-independent GraphicsContext API.
696 #if PLATFORM(CG)
697     RGBA32 rgba = state().m_shadowColor.isEmpty() ? 0 : CSSParser::parseColor(state().m_shadowColor);
698     const CGFloat components[4] = {
699         ((rgba >> 16) & 0xFF) / 255.0,
700         ((rgba >> 8) & 0xFF) / 255.0,
701         (rgba & 0xFF) / 255.0,
702         ((rgba >> 24) & 0xFF) / 255.0
703     };
704     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
705     CGColorRef color = CGColorCreate(colorSpace, components);
706     CGColorSpaceRelease(colorSpace);
707     CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
708     CGColorRelease(color);
709 #endif
710 }
711
712 static IntSize size(HTMLImageElement* image)
713 {
714     if (CachedImage* cachedImage = image->cachedImage())
715         return cachedImage->imageSize();
716     return IntSize();
717 }
718
719 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
720 {
721     ASSERT(image);
722     IntSize s = size(image);
723     ExceptionCode ec;
724     drawImage(image, x, y, s.width(), s.height(), ec);
725 }
726
727 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
728     float x, float y, float width, float height, ExceptionCode& ec)
729 {
730     ASSERT(image);
731     IntSize s = size(image);
732     drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
733 }
734
735 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
736     ExceptionCode& ec)
737 {
738     ASSERT(image);
739
740     ec = 0;
741
742     GraphicsContext* c = drawingContext();
743     if (!c)
744         return;
745
746     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
747     if (!(imageRect.contains(srcRect) && srcRect.width() > 0 && srcRect.height() > 0 
748         && dstRect.width() > 0 && dstRect.height() > 0)) {
749         ec = INDEX_SIZE_ERR;
750         return;
751     }
752
753     CachedImage* cachedImage = image->cachedImage();
754     if (!cachedImage)
755         return;
756
757     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
758     FloatRect destRect = c->roundToDevicePixels(dstRect);
759     willDraw(destRect);
760     c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
761 }
762
763 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
764 {
765     ASSERT(canvas);
766     ExceptionCode ec;
767     drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
768 }
769
770 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
771     float x, float y, float width, float height, ExceptionCode& ec)
772 {
773     ASSERT(canvas);
774     drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
775 }
776
777 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
778     const FloatRect& dstRect, ExceptionCode& ec)
779 {
780     ASSERT(canvas);
781
782     ec = 0;
783
784     if (srcRect.isEmpty() || dstRect.isEmpty()) {
785         ec = INDEX_SIZE_ERR;
786         return;
787     }
788
789     if (!canvas)
790         return;
791
792     GraphicsContext* c = drawingContext();
793     if (!c)
794         return;
795         
796     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
797     FloatRect destRect = c->roundToDevicePixels(dstRect);
798         
799     // FIXME: Do this through platform-independent GraphicsContext API.
800 #if PLATFORM(CG)
801     CGImageRef platformImage = canvas->createPlatformImage();
802     if (!platformImage)
803         return;
804
805     willDraw(destRect);
806
807     float iw = CGImageGetWidth(platformImage);
808     float ih = CGImageGetHeight(platformImage);
809     if (sourceRect.x() == 0 && sourceRect.y() == 0 && iw == sourceRect.width() && ih == sourceRect.height()) {
810         // Fast path, yay!
811         CGContextDrawImage(c->platformContext(), destRect, platformImage);
812     } else {
813         // Slow path, boo!
814         // Create a new bitmap of the appropriate size and then draw that into our context.
815
816         size_t csw = static_cast<size_t>(ceilf(sourceRect.width()));
817         size_t csh = static_cast<size_t>(ceilf(sourceRect.height()));
818
819         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
820         size_t bytesPerRow = csw * 4;
821         void* buffer = fastMalloc(csh * bytesPerRow);
822
823         CGContextRef clippedSourceContext = CGBitmapContextCreate(buffer, csw, csh,
824             8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
825         CGColorSpaceRelease(colorSpace);
826         CGContextTranslateCTM(clippedSourceContext, -sourceRect.x(), -sourceRect.y());
827         CGContextDrawImage(clippedSourceContext, CGRectMake(0, 0, iw, ih), platformImage);
828
829         CGImageRef clippedSourceImage = CGBitmapContextCreateImage(clippedSourceContext);
830         CGContextRelease(clippedSourceContext);
831
832         CGContextDrawImage(c->platformContext(), destRect, clippedSourceImage);
833         CGImageRelease(clippedSourceImage);
834         
835         fastFree(buffer);
836     }
837
838     CGImageRelease(platformImage);
839 #endif
840 }
841
842 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
843 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
844     float sx, float sy, float sw, float sh,
845     float dx, float dy, float dw, float dh,
846     const String& compositeOperation)
847 {
848     if (!image)
849         return;
850
851     CachedImage* cachedImage = image->cachedImage();
852     if (!cachedImage)
853         return;
854
855     GraphicsContext* c = drawingContext();
856     if (!c)
857         return;
858
859     CompositeOperator op;
860     if (!parseCompositeOperator(compositeOperation, op))
861         op = CompositeSourceOver;
862
863     FloatRect destRect = FloatRect(dx, dy, dw, dh);
864     willDraw(destRect);
865     c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
866 }
867
868 void CanvasRenderingContext2D::setAlpha(float alpha)
869 {
870     setGlobalAlpha(alpha);
871 }
872
873 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
874 {
875     setGlobalCompositeOperation(operation);
876 }
877
878 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
879 {
880     return new CanvasGradient(FloatPoint(x0, y0), FloatPoint(x1, y1));
881 }
882
883 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
884 {
885     return new CanvasGradient(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
886 }
887
888 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
889     const String& repetitionType, ExceptionCode& ec)
890 {
891     bool repeatX, repeatY;
892     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
893     if (ec)
894         return 0;
895     return new CanvasPattern(image ? image->cachedImage() : 0, repeatX, repeatY);
896 }
897
898 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
899     const String& repetitionType, ExceptionCode& ec)
900 {
901     bool repeatX, repeatY;
902     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
903     if (ec)
904         return 0;
905     // FIXME: Do this through platform-independent GraphicsContext API.
906 #if PLATFORM(CG)
907     CGImageRef image = canvas->createPlatformImage();
908     if (!image)
909         return 0;
910     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(image, repeatX, repeatY);
911     CGImageRelease(image);
912     return pattern;
913 #else
914     return 0;
915 #endif
916 }
917
918 void CanvasRenderingContext2D::willDraw(const FloatRect& r)
919 {
920     if (!m_canvas)
921         return;
922     m_canvas->willDraw(r);
923 }
924
925 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
926 {
927     if (!m_canvas)
928         return 0;
929     return m_canvas->drawingContext();
930 }
931
932 void CanvasRenderingContext2D::applyStrokePattern()
933 {
934     GraphicsContext* c = drawingContext();
935     if (!c)
936         return;
937
938 #if PLATFORM(CG)
939     // Check for case where the pattern is already set.
940     CGAffineTransform m = CGContextGetCTM(c->platformContext());
941     if (state().m_appliedStrokePattern
942             && CGAffineTransformEqualToTransform(m, state().m_strokeStylePatternTransform))
943         return;
944
945     CanvasPattern* pattern = state().m_strokeStyle->pattern();
946     if (!pattern)
947         return;
948
949     CGPatternRef platformPattern = pattern->createPattern(m);
950     if (!platformPattern)
951         return;
952
953     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
954     CGContextSetStrokeColorSpace(c->platformContext(), patternSpace);
955     CGColorSpaceRelease(patternSpace);
956
957     const CGFloat patternAlpha = 1;
958     CGContextSetStrokePattern(c->platformContext(), platformPattern, &patternAlpha);
959     CGPatternRelease(platformPattern);
960
961     state().m_strokeStylePatternTransform = m;
962 #endif
963     state().m_appliedStrokePattern = true;
964 }
965
966 void CanvasRenderingContext2D::applyFillPattern()
967 {
968     GraphicsContext* c = drawingContext();
969     if (!c)
970         return;
971
972 #if PLATFORM(CG)
973     // Check for case where the pattern is already set.
974     CGAffineTransform m = CGContextGetCTM(c->platformContext());
975     if (state().m_appliedFillPattern
976             && CGAffineTransformEqualToTransform(m, state().m_fillStylePatternTransform))
977         return;
978
979     CanvasPattern* pattern = state().m_fillStyle->pattern();
980     if (!pattern)
981         return;
982
983     CGPatternRef platformPattern = pattern->createPattern(m);
984     if (!platformPattern)
985         return;
986
987     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
988     CGContextSetFillColorSpace(c->platformContext(), patternSpace);
989     CGColorSpaceRelease(patternSpace);
990
991     const CGFloat patternAlpha = 1;
992     CGContextSetFillPattern(c->platformContext(), platformPattern, &patternAlpha);
993     CGPatternRelease(platformPattern);
994
995     state().m_fillStylePatternTransform = m;
996 #endif
997     state().m_appliedFillPattern = true;
998 }
999
1000 } // namespace WebCore