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