2011-01-30 Simon Fraser <simon.fraser@apple.com>
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / GraphicsContextCG.cpp
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #define _USE_MATH_DEFINES 1
28 #include "config.h"
29 #include "GraphicsContextCG.h"
30
31 #include "AffineTransform.h"
32 #include "FloatConversion.h"
33 #include "GraphicsContextPlatformPrivateCG.h"
34 #include "ImageBuffer.h"
35 #include "KURL.h"
36 #include "Path.h"
37 #include "Pattern.h"
38 #include "ShadowBlur.h"
39
40 #include <CoreGraphics/CoreGraphics.h>
41 #include <wtf/MathExtras.h>
42 #include <wtf/OwnArrayPtr.h>
43 #include <wtf/RetainPtr.h>
44 #include <wtf/UnusedParam.h>
45
46 #if PLATFORM(MAC) || PLATFORM(CHROMIUM)
47 #include "WebCoreSystemInterface.h"
48 #endif
49
50 #if PLATFORM(WIN)
51 #include <WebKitSystemInterface/WebKitSystemInterface.h>
52 #endif
53
54 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
55
56 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
57 // Building on 10.6 or later: kCGInterpolationMedium is defined in the CGInterpolationQuality enum.
58 #define HAVE_CG_INTERPOLATION_MEDIUM 1
59 #endif
60
61 #if !defined(TARGETING_TIGER) && !defined(TARGETING_LEOPARD)
62 // Targeting 10.6 or later: use kCGInterpolationMedium.
63 #define WTF_USE_CG_INTERPOLATION_MEDIUM 1
64 #endif
65
66 #endif
67
68 using namespace std;
69
70 namespace WebCore {
71
72 static void setCGFillColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
73 {
74     CGContextSetFillColorWithColor(context, cachedCGColor(color, colorSpace));
75 }
76
77 static void setCGStrokeColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
78 {
79     CGContextSetStrokeColorWithColor(context, cachedCGColor(color, colorSpace));
80 }
81
82 CGColorSpaceRef deviceRGBColorSpaceRef()
83 {
84     static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
85     return deviceSpace;
86 }
87
88 CGColorSpaceRef sRGBColorSpaceRef()
89 {
90     // FIXME: Windows should be able to use kCGColorSpaceSRGB, this is tracked by http://webkit.org/b/31363.
91 #if PLATFORM(WIN) || defined(BUILDING_ON_TIGER)
92     return deviceRGBColorSpaceRef();
93 #else
94     static CGColorSpaceRef sRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
95     return sRGBSpace;
96 #endif
97 }
98
99 CGColorSpaceRef linearRGBColorSpaceRef()
100 {
101     // FIXME: Windows should be able to use kCGColorSpaceGenericRGBLinear, this is tracked by http://webkit.org/b/31363.
102 #if PLATFORM(WIN) || defined(BUILDING_ON_TIGER)
103     return deviceRGBColorSpaceRef();
104 #else
105     static CGColorSpaceRef linearRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
106     return linearRGBSpace;
107 #endif
108 }
109
110 void GraphicsContext::platformInit(CGContextRef cgContext)
111 {
112     m_data = new GraphicsContextPlatformPrivate(cgContext);
113     setPaintingDisabled(!cgContext);
114     if (cgContext) {
115         // Make sure the context starts in sync with our state.
116         setPlatformFillColor(fillColor(), fillColorSpace());
117         setPlatformStrokeColor(strokeColor(), strokeColorSpace());
118     }
119 }
120
121 void GraphicsContext::platformDestroy()
122 {
123     delete m_data;
124 }
125
126 CGContextRef GraphicsContext::platformContext() const
127 {
128     ASSERT(!paintingDisabled());
129     ASSERT(m_data->m_cgContext);
130     return m_data->m_cgContext.get();
131 }
132
133 void GraphicsContext::savePlatformState()
134 {
135     // Note: Do not use this function within this class implementation, since we want to avoid the extra
136     // save of the secondary context (in GraphicsContextPlatformPrivateCG.h).
137     CGContextSaveGState(platformContext());
138     m_data->save();
139 }
140
141 void GraphicsContext::restorePlatformState()
142 {
143     // Note: Do not use this function within this class implementation, since we want to avoid the extra
144     // restore of the secondary context (in GraphicsContextPlatformPrivateCG.h).
145     CGContextRestoreGState(platformContext());
146     m_data->restore();
147     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
148 }
149
150 // Draws a filled rectangle with a stroked border.
151 void GraphicsContext::drawRect(const IntRect& rect)
152 {
153     // FIXME: this function does not handle patterns and gradients
154     // like drawPath does, it probably should.
155     if (paintingDisabled())
156         return;
157
158     CGContextRef context = platformContext();
159
160     CGContextFillRect(context, rect);
161
162     if (strokeStyle() != NoStroke) {
163         // We do a fill of four rects to simulate the stroke of a border.
164         Color oldFillColor = fillColor();
165         if (oldFillColor != strokeColor())
166             setCGFillColor(context, strokeColor(), strokeColorSpace());
167         CGRect rects[4] = {
168             FloatRect(rect.x(), rect.y(), rect.width(), 1),
169             FloatRect(rect.x(), rect.bottom() - 1, rect.width(), 1),
170             FloatRect(rect.x(), rect.y() + 1, 1, rect.height() - 2),
171             FloatRect(rect.right() - 1, rect.y() + 1, 1, rect.height() - 2)
172         };
173         CGContextFillRects(context, rects, 4);
174         if (oldFillColor != strokeColor())
175             setCGFillColor(context, oldFillColor, fillColorSpace());
176     }
177 }
178
179 // This is only used to draw borders.
180 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
181 {
182     if (paintingDisabled())
183         return;
184
185     if (strokeStyle() == NoStroke)
186         return;
187
188     float width = strokeThickness();
189
190     FloatPoint p1 = point1;
191     FloatPoint p2 = point2;
192     bool isVerticalLine = (p1.x() == p2.x());
193     
194     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
195     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
196     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
197     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
198     if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
199         if (isVerticalLine) {
200             p1.move(0, width);
201             p2.move(0, -width);
202         } else {
203             p1.move(width, 0);
204             p2.move(-width, 0);
205         }
206     }
207     
208     if (((int)width) % 2) {
209         if (isVerticalLine) {
210             // We're a vertical line.  Adjust our x.
211             p1.move(0.5f, 0.0f);
212             p2.move(0.5f, 0.0f);
213         } else {
214             // We're a horizontal line. Adjust our y.
215             p1.move(0.0f, 0.5f);
216             p2.move(0.0f, 0.5f);
217         }
218     }
219     
220     int patWidth = 0;
221     switch (strokeStyle()) {
222     case NoStroke:
223     case SolidStroke:
224         break;
225     case DottedStroke:
226         patWidth = (int)width;
227         break;
228     case DashedStroke:
229         patWidth = 3 * (int)width;
230         break;
231     }
232
233     CGContextRef context = platformContext();
234
235     if (shouldAntialias())
236         CGContextSetShouldAntialias(context, false);
237
238     if (patWidth) {
239         CGContextSaveGState(context);
240
241         // Do a rect fill of our endpoints.  This ensures we always have the
242         // appearance of being a border.  We then draw the actual dotted/dashed line.
243         setCGFillColor(context, strokeColor(), strokeColorSpace());  // The save/restore make it safe to mutate the fill color here without setting it back to the old color.
244         if (isVerticalLine) {
245             CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width));
246             CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width));
247         } else {
248             CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width));
249             CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width));
250         }
251
252         // Example: 80 pixels with a width of 30 pixels.
253         // Remainder is 20.  The maximum pixels of line we could paint
254         // will be 50 pixels.
255         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
256         int remainder = distance % patWidth;
257         int coverage = distance - remainder;
258         int numSegments = coverage / patWidth;
259
260         float patternOffset = 0.0f;
261         // Special case 1px dotted borders for speed.
262         if (patWidth == 1)
263             patternOffset = 1.0f;
264         else {
265             bool evenNumberOfSegments = !(numSegments % 2);
266             if (remainder)
267                 evenNumberOfSegments = !evenNumberOfSegments;
268             if (evenNumberOfSegments) {
269                 if (remainder) {
270                     patternOffset += patWidth - remainder;
271                     patternOffset += remainder / 2;
272                 } else
273                     patternOffset = patWidth / 2;
274             } else {
275                 if (remainder)
276                     patternOffset = (patWidth - remainder)/2;
277             }
278         }
279
280         const CGFloat dottedLine[2] = { patWidth, patWidth };
281         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
282     }
283
284     CGContextBeginPath(context);
285     CGContextMoveToPoint(context, p1.x(), p1.y());
286     CGContextAddLineToPoint(context, p2.x(), p2.y());
287
288     CGContextStrokePath(context);
289
290     if (patWidth)
291         CGContextRestoreGState(context);
292
293     if (shouldAntialias())
294         CGContextSetShouldAntialias(context, true);
295 }
296
297 // This method is only used to draw the little circles used in lists.
298 void GraphicsContext::drawEllipse(const IntRect& rect)
299 {
300     if (paintingDisabled())
301         return;
302
303     Path path;
304     path.addEllipse(rect);
305     drawPath(path);
306 }
307
308
309 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
310 {
311     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f)
312         return;
313
314     CGContextRef context = platformContext();
315     CGContextSaveGState(context);
316     CGContextBeginPath(context);
317     CGContextSetShouldAntialias(context, false);
318
319     int x = rect.x();
320     int y = rect.y();
321     float w = (float)rect.width();
322     float h = (float)rect.height();
323     float scaleFactor = h / w;
324     float reverseScaleFactor = w / h;
325
326     if (w != h)
327         scale(FloatSize(1, scaleFactor));
328
329     float hRadius = w / 2;
330     float vRadius = h / 2;
331     float fa = startAngle;
332     float falen =  fa + angleSpan;
333     float start = -fa * piFloat / 180.0f;
334     float end = -falen * piFloat / 180.0f;
335     CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true);
336
337     if (w != h)
338         scale(FloatSize(1, reverseScaleFactor));
339
340     float width = strokeThickness();
341     int patWidth = 0;
342
343     switch (strokeStyle()) {
344     case DottedStroke:
345         patWidth = (int)(width / 2);
346         break;
347     case DashedStroke:
348         patWidth = 3 * (int)(width / 2);
349         break;
350     default:
351         break;
352     }
353
354     if (patWidth) {
355         // Example: 80 pixels with a width of 30 pixels.
356         // Remainder is 20.  The maximum pixels of line we could paint
357         // will be 50 pixels.
358         int distance;
359         if (hRadius == vRadius)
360             distance = static_cast<int>((piFloat * hRadius) / 2.0f);
361         else // We are elliptical and will have to estimate the distance
362             distance = static_cast<int>((piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0f)) / 2.0f);
363
364         int remainder = distance % patWidth;
365         int coverage = distance - remainder;
366         int numSegments = coverage / patWidth;
367
368         float patternOffset = 0.0f;
369         // Special case 1px dotted borders for speed.
370         if (patWidth == 1)
371             patternOffset = 1.0f;
372         else {
373             bool evenNumberOfSegments = !(numSegments % 2);
374             if (remainder)
375                 evenNumberOfSegments = !evenNumberOfSegments;
376             if (evenNumberOfSegments) {
377                 if (remainder) {
378                     patternOffset += patWidth - remainder;
379                     patternOffset += remainder / 2.0f;
380                 } else
381                     patternOffset = patWidth / 2.0f;
382             } else {
383                 if (remainder)
384                     patternOffset = (patWidth - remainder) / 2.0f;
385             }
386         }
387
388         const CGFloat dottedLine[2] = { patWidth, patWidth };
389         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
390     }
391
392     CGContextStrokePath(context);
393
394     CGContextRestoreGState(context);
395 }
396
397 static void addConvexPolygonToPath(Path& path, size_t numberOfPoints, const FloatPoint* points)
398 {
399     ASSERT(numberOfPoints > 0);
400
401     path.moveTo(points[0]);
402     for (size_t i = 1; i < numberOfPoints; ++i)
403         path.addLineTo(points[i]);
404     path.closeSubpath();
405 }
406
407 void GraphicsContext::drawConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialiased)
408 {
409     if (paintingDisabled())
410         return;
411
412     if (numberOfPoints <= 1)
413         return;
414
415     CGContextRef context = platformContext();
416
417     if (antialiased != shouldAntialias())
418         CGContextSetShouldAntialias(context, antialiased);
419
420     Path path;
421     addConvexPolygonToPath(path, numberOfPoints, points);
422     drawPath(path);
423
424     if (antialiased != shouldAntialias())
425         CGContextSetShouldAntialias(context, shouldAntialias());
426 }
427
428 void GraphicsContext::clipConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialias)
429 {
430     if (paintingDisabled())
431         return;
432
433     if (numberOfPoints <= 1)
434         return;
435
436     CGContextRef context = platformContext();
437
438     if (antialias != shouldAntialias())
439         CGContextSetShouldAntialias(context, antialias);
440
441     Path path;
442     addConvexPolygonToPath(path, numberOfPoints, points);
443     clipPath(path, RULE_NONZERO);
444
445     if (antialias != shouldAntialias())
446         CGContextSetShouldAntialias(context, shouldAntialias());
447 }
448
449 void GraphicsContext::applyStrokePattern()
450 {
451     CGContextRef cgContext = platformContext();
452
453     RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_state.strokePattern->createPlatformPattern(getCTM()));
454     if (!platformPattern)
455         return;
456
457     RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0));
458     CGContextSetStrokeColorSpace(cgContext, patternSpace.get());
459
460     const CGFloat patternAlpha = 1;
461     CGContextSetStrokePattern(cgContext, platformPattern.get(), &patternAlpha);
462 }
463
464 void GraphicsContext::applyFillPattern()
465 {
466     CGContextRef cgContext = platformContext();
467
468     RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_state.fillPattern->createPlatformPattern(getCTM()));
469     if (!platformPattern)
470         return;
471
472     RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0));
473     CGContextSetFillColorSpace(cgContext, patternSpace.get());
474
475     const CGFloat patternAlpha = 1;
476     CGContextSetFillPattern(cgContext, platformPattern.get(), &patternAlpha);
477 }
478
479 static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPathDrawingMode& mode)
480 {
481     bool shouldFill = state.fillPattern || state.fillColor.alpha();
482     bool shouldStroke = state.strokePattern || (state.strokeStyle != NoStroke && state.strokeColor.alpha());
483     bool useEOFill = state.fillRule == RULE_EVENODD;
484
485     if (shouldFill) {
486         if (shouldStroke) {
487             if (useEOFill)
488                 mode = kCGPathEOFillStroke;
489             else
490                 mode = kCGPathFillStroke;
491         } else { // fill, no stroke
492             if (useEOFill)
493                 mode = kCGPathEOFill;
494             else
495                 mode = kCGPathFill;
496         }
497     } else {
498         // Setting mode to kCGPathStroke even if shouldStroke is false. In that case, we return false and mode will not be used,
499         // but the compiler will not complain about an uninitialized variable.
500         mode = kCGPathStroke;
501     }
502
503     return shouldFill || shouldStroke;
504 }
505
506 void GraphicsContext::drawPath(const Path& path)
507 {
508     if (paintingDisabled())
509         return;
510
511     CGContextRef context = platformContext();
512     const GraphicsContextState& state = m_state;
513
514     if (state.fillGradient || state.strokeGradient) {
515         // We don't have any optimized way to fill & stroke a path using gradients
516         // FIXME: Be smarter about this.
517         fillPath(path);
518         strokePath(path);
519         return;
520     }
521
522     CGContextBeginPath(context);
523     CGContextAddPath(context, path.platformPath());
524
525     if (state.fillPattern)
526         applyFillPattern();
527     if (state.strokePattern)
528         applyStrokePattern();
529
530     CGPathDrawingMode drawingMode;
531     if (calculateDrawingMode(state, drawingMode))
532         CGContextDrawPath(context, drawingMode);
533 }
534
535 static inline void fillPathWithFillRule(CGContextRef context, WindRule fillRule)
536 {
537     if (fillRule == RULE_EVENODD)
538         CGContextEOFillPath(context);
539     else
540         CGContextFillPath(context);
541 }
542
543 void GraphicsContext::fillPath(const Path& path)
544 {
545     if (paintingDisabled())
546         return;
547
548     CGContextRef context = platformContext();
549
550     if (m_state.fillGradient) {
551         if (hasShadow()) {
552             FloatRect rect = path.boundingRect();
553             CGLayerRef layer = CGLayerCreateWithContext(context, CGSizeMake(rect.width(), rect.height()), 0);
554             CGContextRef layerContext = CGLayerGetContext(layer);
555
556             CGContextTranslateCTM(layerContext, -rect.x(), -rect.y());
557             CGContextBeginPath(layerContext);
558             CGContextAddPath(layerContext, path.platformPath());
559             CGContextConcatCTM(layerContext, m_state.fillGradient->gradientSpaceTransform());
560
561             if (fillRule() == RULE_EVENODD)
562                 CGContextEOClip(layerContext);
563             else
564                 CGContextClip(layerContext);
565
566             m_state.fillGradient->paint(layerContext);
567             CGContextDrawLayerAtPoint(context, CGPointMake(rect.left(), rect.top()), layer);
568             CGLayerRelease(layer);
569         } else {
570             CGContextBeginPath(context);
571             CGContextAddPath(context, path.platformPath());
572             CGContextSaveGState(context);
573             CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform());
574
575             if (fillRule() == RULE_EVENODD)
576                 CGContextEOClip(context);
577             else
578                 CGContextClip(context);
579
580             m_state.fillGradient->paint(this);
581             CGContextRestoreGState(context);
582         }
583
584         return;
585     }
586
587     CGContextBeginPath(context);
588     CGContextAddPath(context, path.platformPath());
589
590     if (m_state.fillPattern)
591         applyFillPattern();
592     fillPathWithFillRule(context, fillRule());
593 }
594
595 void GraphicsContext::strokePath(const Path& path)
596 {
597     if (paintingDisabled())
598         return;
599
600     CGContextRef context = platformContext();
601
602     CGContextBeginPath(context);
603     CGContextAddPath(context, path.platformPath());
604
605     if (m_state.strokeGradient) {
606         CGContextSaveGState(context);
607         CGContextReplacePathWithStrokedPath(context);
608         CGContextClip(context);
609         CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform());
610         m_state.strokeGradient->paint(this);
611         CGContextRestoreGState(context);
612         return;
613     }
614
615     if (m_state.strokePattern)
616         applyStrokePattern();
617     CGContextStrokePath(context);
618 }
619
620 static float radiusToLegacyRadius(float radius)
621 {
622     return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius;
623 }
624
625 static bool hasBlurredShadow(const GraphicsContextState& state)
626 {
627     return state.shadowColor.isValid() && state.shadowColor.alpha() && state.shadowBlur;
628 }
629
630 void GraphicsContext::fillRect(const FloatRect& rect)
631 {
632     if (paintingDisabled())
633         return;
634
635     CGContextRef context = platformContext();
636
637     if (m_state.fillGradient) {
638         CGContextSaveGState(context);
639         if (hasShadow()) {
640             CGLayerRef layer = CGLayerCreateWithContext(context, CGSizeMake(rect.width(), rect.height()), 0);
641             CGContextRef layerContext = CGLayerGetContext(layer);
642
643             CGContextTranslateCTM(layerContext, -rect.x(), -rect.y());
644             CGContextAddRect(layerContext, rect);
645             CGContextClip(layerContext);
646
647             CGContextConcatCTM(layerContext, m_state.fillGradient->gradientSpaceTransform());
648             m_state.fillGradient->paint(layerContext);
649             CGContextDrawLayerAtPoint(context, CGPointMake(rect.left(), rect.top()), layer);
650             CGLayerRelease(layer);
651         } else {
652             CGContextClipToRect(context, rect);
653             CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform());
654             m_state.fillGradient->paint(this);
655         }
656         CGContextRestoreGState(context);
657         return;
658     }
659
660     if (m_state.fillPattern)
661         applyFillPattern();
662
663     bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
664     if (drawOwnShadow) {
665         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
666         // Turn off CG shadows.
667         CGContextSaveGState(context);
668         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
669
670         ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
671         contextShadow.drawRectShadow(this, rect, RoundedIntRect::Radii());
672     }
673
674     CGContextFillRect(context, rect);
675
676     if (drawOwnShadow)
677         CGContextRestoreGState(context);
678 }
679
680 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
681 {
682     if (paintingDisabled())
683         return;
684
685     CGContextRef context = platformContext();
686     Color oldFillColor = fillColor();
687     ColorSpace oldColorSpace = fillColorSpace();
688
689     if (oldFillColor != color || oldColorSpace != colorSpace)
690         setCGFillColor(context, color, colorSpace);
691
692     bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
693     if (drawOwnShadow) {
694         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
695         // Turn off CG shadows.
696         CGContextSaveGState(context);
697         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
698
699         ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
700         contextShadow.drawRectShadow(this, rect, RoundedIntRect::Radii());
701     }
702
703     CGContextFillRect(context, rect);
704     
705     if (drawOwnShadow)
706         CGContextRestoreGState(context);
707
708     if (oldFillColor != color || oldColorSpace != colorSpace)
709         setCGFillColor(context, oldFillColor, oldColorSpace);
710 }
711
712 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
713 {
714     if (paintingDisabled())
715         return;
716
717     CGContextRef context = platformContext();
718     Color oldFillColor = fillColor();
719     ColorSpace oldColorSpace = fillColorSpace();
720
721     if (oldFillColor != color || oldColorSpace != colorSpace)
722         setCGFillColor(context, color, colorSpace);
723
724     Path path;
725     path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
726
727     bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
728     if (drawOwnShadow) {
729         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
730
731         // Turn off CG shadows.
732         CGContextSaveGState(context);
733         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
734
735         ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
736         contextShadow.drawRectShadow(this, rect, RoundedIntRect::Radii(topLeft, topRight, bottomLeft, bottomRight));
737     }
738
739     fillPath(path);
740
741     if (drawOwnShadow)
742         CGContextRestoreGState(context);
743
744     if (oldFillColor != color || oldColorSpace != colorSpace)
745         setCGFillColor(context, oldFillColor, oldColorSpace);
746 }
747
748 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedIntRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
749 {
750     if (paintingDisabled())
751         return;
752
753     CGContextRef context = platformContext();
754
755     Path path;
756     path.addRect(rect);
757
758     if (!roundedHoleRect.radii().isZero())
759         path.addRoundedRect(roundedHoleRect.rect(), roundedHoleRect.radii().topLeft(), roundedHoleRect.radii().topRight(), roundedHoleRect.radii().bottomLeft(), roundedHoleRect.radii().bottomRight());
760     else
761         path.addRect(roundedHoleRect.rect());
762
763     WindRule oldFillRule = fillRule();
764     Color oldFillColor = fillColor();
765     ColorSpace oldFillColorSpace = fillColorSpace();
766     
767     setFillRule(RULE_EVENODD);
768     setFillColor(color, colorSpace);
769
770     // fillRectWithRoundedHole() assumes that the edges of rect are clipped out, so we only care about shadows cast around inside the hole.
771     bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms;
772     if (drawOwnShadow) {
773         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
774
775         // Turn off CG shadows.
776         CGContextSaveGState(context);
777         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
778
779         ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
780         contextShadow.drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii());
781     }
782
783     fillPath(path);
784
785     if (drawOwnShadow)
786         CGContextRestoreGState(context);
787     
788     setFillRule(oldFillRule);
789     setFillColor(oldFillColor, oldFillColorSpace);
790 }
791
792 void GraphicsContext::clip(const FloatRect& rect)
793 {
794     if (paintingDisabled())
795         return;
796     CGContextClipToRect(platformContext(), rect);
797     m_data->clip(rect);
798 }
799
800 void GraphicsContext::clipOut(const IntRect& rect)
801 {
802     if (paintingDisabled())
803         return;
804
805     CGRect rects[2] = { CGContextGetClipBoundingBox(platformContext()), rect };
806     CGContextBeginPath(platformContext());
807     CGContextAddRects(platformContext(), rects, 2);
808     CGContextEOClip(platformContext());
809 }
810
811 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
812 {
813     if (paintingDisabled())
814         return;
815
816     if (path.isEmpty())
817         return;
818
819     CGContextRef context = platformContext();
820
821     CGContextBeginPath(platformContext());
822     CGContextAddPath(platformContext(), path.platformPath());
823
824     if (clipRule == RULE_EVENODD)
825         CGContextEOClip(context);
826     else
827         CGContextClip(context);
828 }
829
830 IntRect GraphicsContext::clipBounds() const
831 {
832     return enclosingIntRect(CGContextGetClipBoundingBox(platformContext()));
833 }
834
835 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
836 {
837     if (paintingDisabled())
838         return;
839
840     clip(rect);
841     CGContextRef context = platformContext();
842
843     // Add outer ellipse
844     CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
845     // Add inner ellipse.
846     CGContextAddEllipseInRect(context, CGRectMake(rect.x() + thickness, rect.y() + thickness,
847         rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
848
849     CGContextEOClip(context);
850 }
851
852 void GraphicsContext::beginTransparencyLayer(float opacity)
853 {
854     if (paintingDisabled())
855         return;
856     CGContextRef context = platformContext();
857     CGContextSaveGState(context);
858     CGContextSetAlpha(context, opacity);
859     CGContextBeginTransparencyLayer(context, 0);
860     m_data->beginTransparencyLayer();
861     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
862 }
863
864 void GraphicsContext::endTransparencyLayer()
865 {
866     if (paintingDisabled())
867         return;
868     CGContextRef context = platformContext();
869     CGContextEndTransparencyLayer(context);
870     CGContextRestoreGState(context);
871     m_data->endTransparencyLayer();
872     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
873 }
874
875 void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
876 {
877     if (paintingDisabled())
878         return;
879     
880     // FIXME: we could avoid the shadow setup cost when we know we'll render the shadow ourselves.
881
882     CGFloat xOffset = offset.width();
883     CGFloat yOffset = offset.height();
884     CGFloat blurRadius = blur;
885     CGContextRef context = platformContext();
886
887     if (!m_state.shadowsIgnoreTransforms) {
888         CGAffineTransform userToBaseCTM = wkGetUserToBaseCTM(context);
889
890         CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b;
891         CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d;
892         CGFloat C = B;
893         CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d;
894
895         CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D)))));
896
897         // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp
898         blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0));
899
900         CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM);
901
902         xOffset = offsetInBaseSpace.width;
903         yOffset = offsetInBaseSpace.height;
904     }
905
906     // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated
907     // to the desired integer.
908     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
909     if (xOffset > 0)
910         xOffset += extraShadowOffset;
911     else if (xOffset < 0)
912         xOffset -= extraShadowOffset;
913
914     if (yOffset > 0)
915         yOffset += extraShadowOffset;
916     else if (yOffset < 0)
917         yOffset -= extraShadowOffset;
918
919     // Check for an invalid color, as this means that the color was not set for the shadow
920     // and we should therefore just use the default shadow color.
921     if (!color.isValid())
922         CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius);
923     else
924         CGContextSetShadowWithColor(context, CGSizeMake(xOffset, yOffset), blurRadius, cachedCGColor(color, colorSpace));
925 }
926
927 void GraphicsContext::clearPlatformShadow()
928 {
929     if (paintingDisabled())
930         return;
931     CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
932 }
933
934 void GraphicsContext::setMiterLimit(float limit)
935 {
936     if (paintingDisabled())
937         return;
938     CGContextSetMiterLimit(platformContext(), limit);
939 }
940
941 void GraphicsContext::setAlpha(float alpha)
942 {
943     if (paintingDisabled())
944         return;
945     CGContextSetAlpha(platformContext(), alpha);
946 }
947
948 void GraphicsContext::clearRect(const FloatRect& r)
949 {
950     if (paintingDisabled())
951         return;
952     CGContextClearRect(platformContext(), r);
953 }
954
955 void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth)
956 {
957     if (paintingDisabled())
958         return;
959
960     CGContextRef context = platformContext();
961
962     if (m_state.strokeGradient) {
963         CGContextSaveGState(context);
964         setStrokeThickness(lineWidth);
965         CGContextAddRect(context, r);
966         CGContextReplacePathWithStrokedPath(context);
967         CGContextClip(context);
968         m_state.strokeGradient->paint(this);
969         CGContextRestoreGState(context);
970         return;
971     }
972
973     if (m_state.strokePattern)
974         applyStrokePattern();
975     CGContextStrokeRectWithWidth(context, r, lineWidth);
976 }
977
978 void GraphicsContext::setLineCap(LineCap cap)
979 {
980     if (paintingDisabled())
981         return;
982     switch (cap) {
983     case ButtCap:
984         CGContextSetLineCap(platformContext(), kCGLineCapButt);
985         break;
986     case RoundCap:
987         CGContextSetLineCap(platformContext(), kCGLineCapRound);
988         break;
989     case SquareCap:
990         CGContextSetLineCap(platformContext(), kCGLineCapSquare);
991         break;
992     }
993 }
994
995 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
996 {
997     CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
998 }
999
1000 void GraphicsContext::setLineJoin(LineJoin join)
1001 {
1002     if (paintingDisabled())
1003         return;
1004     switch (join) {
1005     case MiterJoin:
1006         CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
1007         break;
1008     case RoundJoin:
1009         CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
1010         break;
1011     case BevelJoin:
1012         CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
1013         break;
1014     }
1015 }
1016
1017 void GraphicsContext::clip(const Path& path)
1018 {
1019     if (paintingDisabled())
1020         return;
1021     CGContextRef context = platformContext();
1022
1023     // CGContextClip does nothing if the path is empty, so in this case, we
1024     // instead clip against a zero rect to reduce the clipping region to
1025     // nothing - which is the intended behavior of clip() if the path is empty.    
1026     if (path.isEmpty())
1027         CGContextClipToRect(context, CGRectZero);
1028     else {
1029         CGContextBeginPath(context);
1030         CGContextAddPath(context, path.platformPath());
1031         CGContextClip(context);
1032     }
1033     m_data->clip(path);
1034 }
1035
1036 void GraphicsContext::canvasClip(const Path& path)
1037 {
1038     clip(path);
1039 }
1040
1041 void GraphicsContext::clipOut(const Path& path)
1042 {
1043     if (paintingDisabled())
1044         return;
1045
1046     CGContextBeginPath(platformContext());
1047     CGContextAddRect(platformContext(), CGContextGetClipBoundingBox(platformContext()));
1048     CGContextAddPath(platformContext(), path.platformPath());
1049     CGContextEOClip(platformContext());
1050 }
1051
1052 void GraphicsContext::scale(const FloatSize& size)
1053 {
1054     if (paintingDisabled())
1055         return;
1056     CGContextScaleCTM(platformContext(), size.width(), size.height());
1057     m_data->scale(size);
1058     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1059 }
1060
1061 void GraphicsContext::rotate(float angle)
1062 {
1063     if (paintingDisabled())
1064         return;
1065     CGContextRotateCTM(platformContext(), angle);
1066     m_data->rotate(angle);
1067     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1068 }
1069
1070 void GraphicsContext::translate(float x, float y)
1071 {
1072     if (paintingDisabled())
1073         return;
1074     CGContextTranslateCTM(platformContext(), x, y);
1075     m_data->translate(x, y);
1076     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1077 }
1078
1079 void GraphicsContext::concatCTM(const AffineTransform& transform)
1080 {
1081     if (paintingDisabled())
1082         return;
1083     CGContextConcatCTM(platformContext(), transform);
1084     m_data->concatCTM(transform);
1085     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1086 }
1087
1088 AffineTransform GraphicsContext::getCTM() const
1089 {
1090     CGAffineTransform t = CGContextGetCTM(platformContext());
1091     return AffineTransform(t.a, t.b, t.c, t.d, t.tx, t.ty);
1092 }
1093
1094 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
1095 {
1096     // It is not enough just to round to pixels in device space. The rotation part of the
1097     // affine transform matrix to device space can mess with this conversion if we have a
1098     // rotating image like the hands of the world clock widget. We just need the scale, so
1099     // we get the affine transform matrix and extract the scale.
1100
1101     if (m_data->m_userToDeviceTransformKnownToBeIdentity)
1102         return rect;
1103
1104     CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1105     if (CGAffineTransformIsIdentity(deviceMatrix)) {
1106         m_data->m_userToDeviceTransformKnownToBeIdentity = true;
1107         return rect;
1108     }
1109
1110     float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
1111     float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
1112
1113     CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
1114     CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
1115         (rect.y() + rect.height()) * deviceScaleY);
1116
1117     deviceOrigin.x = roundf(deviceOrigin.x);
1118     deviceOrigin.y = roundf(deviceOrigin.y);
1119     deviceLowerRight.x = roundf(deviceLowerRight.x);
1120     deviceLowerRight.y = roundf(deviceLowerRight.y);
1121
1122     // Don't let the height or width round to 0 unless either was originally 0
1123     if (deviceOrigin.y == deviceLowerRight.y && rect.height())
1124         deviceLowerRight.y += 1;
1125     if (deviceOrigin.x == deviceLowerRight.x && rect.width())
1126         deviceLowerRight.x += 1;
1127
1128     FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
1129     FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
1130     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
1131 }
1132
1133 void GraphicsContext::drawLineForText(const IntPoint& point, int width, bool printing)
1134 {
1135     if (paintingDisabled())
1136         return;
1137
1138     if (width <= 0)
1139         return;
1140
1141     float x = point.x();
1142     float y = point.y();
1143     float lineLength = width;
1144
1145     // Use a minimum thickness of 0.5 in user space.
1146     // See http://bugs.webkit.org/show_bug.cgi?id=4255 for details of why 0.5 is the right minimum thickness to use.
1147     float thickness = max(strokeThickness(), 0.5f);
1148
1149     bool restoreAntialiasMode = false;
1150
1151     if (!printing) {
1152         // On screen, use a minimum thickness of 1.0 in user space (later rounded to an integral number in device space).
1153         float adjustedThickness = max(thickness, 1.0f);
1154
1155         // FIXME: This should be done a better way.
1156         // We try to round all parameters to integer boundaries in device space. If rounding pixels in device space
1157         // makes our thickness more than double, then there must be a shrinking-scale factor and rounding to pixels
1158         // in device space will make the underlines too thick.
1159         CGRect lineRect = roundToDevicePixels(FloatRect(x, y, lineLength, adjustedThickness));
1160         if (lineRect.size.height < thickness * 2.0) {
1161             x = lineRect.origin.x;
1162             y = lineRect.origin.y;
1163             lineLength = lineRect.size.width;
1164             thickness = lineRect.size.height;
1165             if (shouldAntialias()) {
1166                 CGContextSetShouldAntialias(platformContext(), false);
1167                 restoreAntialiasMode = true;
1168             }
1169         }
1170     }
1171
1172     if (fillColor() != strokeColor())
1173         setCGFillColor(platformContext(), strokeColor(), strokeColorSpace());
1174     CGContextFillRect(platformContext(), CGRectMake(x, y, lineLength, thickness));
1175     if (fillColor() != strokeColor())
1176         setCGFillColor(platformContext(), fillColor(), fillColorSpace());
1177
1178     if (restoreAntialiasMode)
1179         CGContextSetShouldAntialias(platformContext(), true);
1180 }
1181
1182 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1183 {
1184     if (paintingDisabled())
1185         return;
1186
1187     RetainPtr<CFURLRef> urlRef(AdoptCF, link.createCFURL());
1188     if (!urlRef)
1189         return;
1190
1191     CGContextRef context = platformContext();
1192
1193     // Get the bounding box to handle clipping.
1194     CGRect box = CGContextGetClipBoundingBox(context);
1195
1196     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
1197     IntRect rect = destRect;
1198     rect.intersect(intBox);
1199
1200     CGPDFContextSetURLForRect(context, urlRef.get(),
1201         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
1202 }
1203
1204 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
1205 {
1206     if (paintingDisabled())
1207         return;
1208
1209     CGInterpolationQuality quality = kCGInterpolationDefault;
1210     switch (mode) {
1211     case InterpolationDefault:
1212         quality = kCGInterpolationDefault;
1213         break;
1214     case InterpolationNone:
1215         quality = kCGInterpolationNone;
1216         break;
1217     case InterpolationLow:
1218         quality = kCGInterpolationLow;
1219         break;
1220
1221     // Fall through to InterpolationHigh if kCGInterpolationMedium is not usable.
1222     case InterpolationMedium:
1223 #if USE(CG_INTERPOLATION_MEDIUM)
1224         quality = kCGInterpolationMedium;
1225         break;
1226 #endif
1227     case InterpolationHigh:
1228         quality = kCGInterpolationHigh;
1229         break;
1230     }
1231     CGContextSetInterpolationQuality(platformContext(), quality);
1232 }
1233
1234 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1235 {
1236     if (paintingDisabled())
1237         return InterpolationDefault;
1238
1239     CGInterpolationQuality quality = CGContextGetInterpolationQuality(platformContext());
1240     switch (quality) {
1241     case kCGInterpolationDefault:
1242         return InterpolationDefault;
1243     case kCGInterpolationNone:
1244         return InterpolationNone;
1245     case kCGInterpolationLow:
1246         return InterpolationLow;
1247 #if HAVE(CG_INTERPOLATION_MEDIUM)
1248     // kCGInterpolationMedium is known to be present in the CGInterpolationQuality enum.
1249     case kCGInterpolationMedium:
1250 #if USE(CG_INTERPOLATION_MEDIUM)
1251         // Only map to InterpolationMedium if targeting a system that understands it.
1252         return InterpolationMedium;
1253 #else
1254         return InterpolationDefault;
1255 #endif  // USE(CG_INTERPOLATION_MEDIUM)
1256 #endif  // HAVE(CG_INTERPOLATION_MEDIUM)
1257     case kCGInterpolationHigh:
1258         return InterpolationHigh;
1259     }
1260     return InterpolationDefault;
1261 }
1262
1263 void GraphicsContext::setAllowsFontSmoothing(bool allowsFontSmoothing)
1264 {
1265     UNUSED_PARAM(allowsFontSmoothing);
1266 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
1267     CGContextRef context = platformContext();
1268     CGContextSetAllowsFontSmoothing(context, allowsFontSmoothing);
1269 #endif
1270 }
1271
1272 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1273 {
1274     if (paintingDisabled())
1275         return;
1276
1277     // Wow, wish CG had used bits here.
1278     CGContextRef context = platformContext();
1279     switch (mode) {
1280     case TextModeInvisible:
1281         CGContextSetTextDrawingMode(context, kCGTextInvisible);
1282         break;
1283     case TextModeFill:
1284         CGContextSetTextDrawingMode(context, kCGTextFill);
1285         break;
1286     case TextModeStroke:
1287         CGContextSetTextDrawingMode(context, kCGTextStroke);
1288         break;
1289     case TextModeFill | TextModeStroke:
1290         CGContextSetTextDrawingMode(context, kCGTextFillStroke);
1291         break;
1292     case TextModeClip:
1293         CGContextSetTextDrawingMode(context, kCGTextClip);
1294         break;
1295     case TextModeFill | TextModeClip:
1296         CGContextSetTextDrawingMode(context, kCGTextFillClip);
1297         break;
1298     case TextModeStroke | TextModeClip:
1299         CGContextSetTextDrawingMode(context, kCGTextStrokeClip);
1300         break;
1301     case TextModeFill | TextModeStroke | TextModeClip:
1302         CGContextSetTextDrawingMode(context, kCGTextFillStrokeClip);
1303         break;
1304     default:
1305         break;
1306     }
1307 }
1308
1309 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1310 {
1311     if (paintingDisabled())
1312         return;
1313     setCGStrokeColor(platformContext(), color, colorSpace);
1314 }
1315
1316 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1317 {
1318     if (paintingDisabled())
1319         return;
1320     CGContextSetLineWidth(platformContext(), thickness);
1321 }
1322
1323 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1324 {
1325     if (paintingDisabled())
1326         return;
1327     setCGFillColor(platformContext(), color, colorSpace);
1328 }
1329
1330 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1331 {
1332     if (paintingDisabled())
1333         return;
1334     CGContextSetShouldAntialias(platformContext(), enable);
1335 }
1336
1337 void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
1338 {
1339     if (paintingDisabled())
1340         return;
1341     CGContextSetShouldSmoothFonts(platformContext(), enable);
1342 }
1343
1344 #ifndef BUILDING_ON_TIGER // Tiger's setPlatformCompositeOperation() is defined in GraphicsContextMac.mm.
1345 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode)
1346 {
1347     if (paintingDisabled())
1348         return;
1349
1350     CGBlendMode target = kCGBlendModeNormal;
1351     switch (mode) {
1352     case CompositeClear:
1353         target = kCGBlendModeClear;
1354         break;
1355     case CompositeCopy:
1356         target = kCGBlendModeCopy;
1357         break;
1358     case CompositeSourceOver:
1359         //kCGBlendModeNormal
1360         break;
1361     case CompositeSourceIn:
1362         target = kCGBlendModeSourceIn;
1363         break;
1364     case CompositeSourceOut:
1365         target = kCGBlendModeSourceOut;
1366         break;
1367     case CompositeSourceAtop:
1368         target = kCGBlendModeSourceAtop;
1369         break;
1370     case CompositeDestinationOver:
1371         target = kCGBlendModeDestinationOver;
1372         break;
1373     case CompositeDestinationIn:
1374         target = kCGBlendModeDestinationIn;
1375         break;
1376     case CompositeDestinationOut:
1377         target = kCGBlendModeDestinationOut;
1378         break;
1379     case CompositeDestinationAtop:
1380         target = kCGBlendModeDestinationAtop;
1381         break;
1382     case CompositeXOR:
1383         target = kCGBlendModeXOR;
1384         break;
1385     case CompositePlusDarker:
1386         target = kCGBlendModePlusDarker;
1387         break;
1388     case CompositeHighlight:
1389         // currently unsupported
1390         break;
1391     case CompositePlusLighter:
1392         target = kCGBlendModePlusLighter;
1393         break;
1394     }
1395     CGContextSetBlendMode(platformContext(), target);
1396 }
1397 #endif
1398
1399 }