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