Extend platform layer so it can pass blend modes to the compositing calls
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / GraphicsContextCG.cpp
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 "ImageOrientation.h"
36 #include "KURL.h"
37 #include "Path.h"
38 #include "Pattern.h"
39 #include "ShadowBlur.h"
40 #include "Timer.h"
41 #include <CoreGraphics/CoreGraphics.h>
42 #include <wtf/MathExtras.h>
43 #include <wtf/OwnArrayPtr.h>
44 #include <wtf/RetainPtr.h>
45 #include <wtf/UnusedParam.h>
46
47 #if PLATFORM(MAC) || PLATFORM(CHROMIUM)
48 #include "WebCoreSystemInterface.h"
49 #endif
50
51 #if PLATFORM(WIN)
52 #include <WebKitSystemInterface/WebKitSystemInterface.h>
53 #endif
54
55 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
56
57 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
58 // Building on 10.6 or later: kCGInterpolationMedium is defined in the CGInterpolationQuality enum.
59 #define HAVE_CG_INTERPOLATION_MEDIUM 1
60 #endif
61
62 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
63 // Targeting 10.6 or later: use kCGInterpolationMedium.
64 #define WTF_USE_CG_INTERPOLATION_MEDIUM 1
65 #endif
66
67 #endif
68
69 extern "C" {
70     CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
71     CG_EXTERN CGAffineTransform CGContextGetBaseCTM(CGContextRef);
72 };
73
74 using namespace std;
75
76 // FIXME: The following using declaration should be in <wtf/HashFunctions.h>.
77 using WTF::pairIntHash;
78
79 // FIXME: The following using declaration should be in <wtf/HashTraits.h>.
80 using WTF::GenericHashTraits;
81
82 #define CACHE_SUBIMAGES 1
83
84 namespace WebCore {
85
86 #if !CACHE_SUBIMAGES
87
88 static inline RetainPtr<CGImageRef> subimage(CGImageRef image, const FloatRect& rect)
89 {
90     return adoptCF(CGImageCreateWithImageInRect(image, rect));
91 }
92
93 #else // CACHE_SUBIMAGES
94
95 static const double subimageCacheClearDelay = 1;
96 static const int maxSubimageCacheSize = 300;
97
98 struct SubimageCacheEntry {
99     RetainPtr<CGImageRef> image;
100     FloatRect rect;
101     RetainPtr<CGImageRef> subimage;
102 };
103
104 struct SubimageCacheEntryTraits : GenericHashTraits<SubimageCacheEntry> {
105     typedef HashTraits<RetainPtr<CGImageRef> > ImageTraits;
106
107     static const bool emptyValueIsZero = true;
108
109     static const bool hasIsEmptyValueFunction = true;
110     static bool isEmptyValue(const SubimageCacheEntry& value) { return !value.image; }
111
112     static void constructDeletedValue(SubimageCacheEntry& slot) { ImageTraits::constructDeletedValue(slot.image); }
113     static bool isDeletedValue(const SubimageCacheEntry& value) { return ImageTraits::isDeletedValue(value.image); }
114 };
115
116 struct SubimageCacheHash {
117     static unsigned hash(CGImageRef image, const FloatRect& rect)
118     {
119         return pairIntHash(PtrHash<CGImageRef>::hash(image),
120             (static_cast<unsigned>(rect.x()) << 16) | static_cast<unsigned>(rect.y()));
121     }
122     static unsigned hash(const SubimageCacheEntry& key)
123     {
124         return hash(key.image.get(), key.rect);
125     }
126     static bool equal(const SubimageCacheEntry& a, const SubimageCacheEntry& b)
127     {
128         return a.image == b.image && a.rect == b.rect;
129     }
130     static const bool safeToCompareToEmptyOrDeleted = true;
131 };
132
133 typedef HashSet<SubimageCacheEntry, SubimageCacheHash, SubimageCacheEntryTraits> SubimageCache;
134
135 struct SubimageCacheWithTimer {
136     SubimageCache cache;
137     DeferrableOneShotTimer<SubimageCacheWithTimer> timer;
138
139     SubimageCacheWithTimer()
140         : timer(this, &SubimageCacheWithTimer::invalidateCacheTimerFired, subimageCacheClearDelay)
141     {
142     }
143
144     void invalidateCacheTimerFired(DeferrableOneShotTimer<SubimageCacheWithTimer>*);
145 };
146
147 static SubimageCacheWithTimer& subimageCache()
148 {
149     static SubimageCacheWithTimer& cache = *new SubimageCacheWithTimer;
150     return cache;
151 }
152
153 void SubimageCacheWithTimer::invalidateCacheTimerFired(DeferrableOneShotTimer<SubimageCacheWithTimer>*)
154 {
155     subimageCache().cache.clear();
156 }
157
158 struct SubimageRequest {
159     CGImageRef image;
160     const FloatRect& rect;
161     SubimageRequest(CGImageRef image, const FloatRect& rect) : image(image), rect(rect) { }
162 };
163
164 struct SubimageCacheAdder {
165     static unsigned hash(const SubimageRequest& value)
166     {
167         return SubimageCacheHash::hash(value.image, value.rect);
168     }
169     static bool equal(const SubimageCacheEntry& a, const SubimageRequest& b)
170     {
171         return a.image == b.image && a.rect == b.rect;
172     }
173     static void translate(SubimageCacheEntry& entry, const SubimageRequest& request, unsigned /*hashCode*/)
174     {
175         entry.image = request.image;
176         entry.rect = request.rect;
177         entry.subimage = adoptCF(CGImageCreateWithImageInRect(request.image, request.rect));
178     }
179 };
180
181 static RetainPtr<CGImageRef> subimage(CGImageRef image, const FloatRect& rect)
182 {
183     SubimageCacheWithTimer& cache = subimageCache();
184     cache.timer.restart();
185     if (cache.cache.size() == maxSubimageCacheSize)
186         cache.cache.remove(cache.cache.begin());
187     ASSERT(cache.cache.size() < maxSubimageCacheSize);
188     return cache.cache.add<SubimageRequest, SubimageCacheAdder>(SubimageRequest(image, rect)).iterator->subimage;
189 }
190
191 #endif // CACHE_SUBIMAGES
192
193 static void setCGFillColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
194 {
195     CGContextSetFillColorWithColor(context, cachedCGColor(color, colorSpace));
196 }
197
198 static void setCGStrokeColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
199 {
200     CGContextSetStrokeColorWithColor(context, cachedCGColor(color, colorSpace));
201 }
202
203 CGColorSpaceRef deviceRGBColorSpaceRef()
204 {
205     static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
206     return deviceSpace;
207 }
208
209 CGColorSpaceRef sRGBColorSpaceRef()
210 {
211     // FIXME: Windows should be able to use kCGColorSpaceSRGB, this is tracked by http://webkit.org/b/31363.
212 #if PLATFORM(WIN)
213     return deviceRGBColorSpaceRef();
214 #else
215     static CGColorSpaceRef sRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
216     return sRGBSpace;
217 #endif
218 }
219
220 #if PLATFORM(WIN)
221 CGColorSpaceRef linearRGBColorSpaceRef()
222 {
223     // FIXME: Windows should be able to use linear sRGB, this is tracked by http://webkit.org/b/80000.
224     return deviceRGBColorSpaceRef();
225 }
226 #endif
227
228 void GraphicsContext::platformInit(CGContextRef cgContext)
229 {
230     m_data = new GraphicsContextPlatformPrivate(cgContext);
231     setPaintingDisabled(!cgContext);
232     if (cgContext) {
233         // Make sure the context starts in sync with our state.
234         setPlatformFillColor(fillColor(), fillColorSpace());
235         setPlatformStrokeColor(strokeColor(), strokeColorSpace());
236     }
237 }
238
239 void GraphicsContext::platformDestroy()
240 {
241     delete m_data;
242 }
243
244 CGContextRef GraphicsContext::platformContext() const
245 {
246     ASSERT(!paintingDisabled());
247     ASSERT(m_data->m_cgContext);
248     return m_data->m_cgContext.get();
249 }
250
251 void GraphicsContext::savePlatformState()
252 {
253     // Note: Do not use this function within this class implementation, since we want to avoid the extra
254     // save of the secondary context (in GraphicsContextPlatformPrivateCG.h).
255     CGContextSaveGState(platformContext());
256     m_data->save();
257 }
258
259 void GraphicsContext::restorePlatformState()
260 {
261     // Note: Do not use this function within this class implementation, since we want to avoid the extra
262     // restore of the secondary context (in GraphicsContextPlatformPrivateCG.h).
263     CGContextRestoreGState(platformContext());
264     m_data->restore();
265     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
266 }
267
268 void GraphicsContext::drawNativeImage(NativeImagePtr imagePtr, const FloatSize& imageSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
269 {
270     RetainPtr<CGImageRef> image(imagePtr);
271
272     float currHeight = orientation.usesWidthAsHeight() ? CGImageGetWidth(image.get()) : CGImageGetHeight(image.get());
273
274     if (currHeight <= srcRect.y())
275         return;
276
277     CGContextRef context = platformContext();
278     CGContextSaveGState(context);
279
280     bool shouldUseSubimage = false;
281
282     // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image
283     // and then set a clip to the portion that we want to display.
284     FloatRect adjustedDestRect = destRect;
285
286     if (srcRect.size() != imageSize) {
287         CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context);
288         // When the image is scaled using high-quality interpolation, we create a temporary CGImage
289         // containing only the portion we want to display. We need to do this because high-quality
290         // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed
291         // into the destination rect. See <rdar://problem/6112909>.
292         shouldUseSubimage = (interpolationQuality != kCGInterpolationNone) && (srcRect.size() != destRect.size() || !getCTM().isIdentityOrTranslationOrFlipped());
293         float xScale = srcRect.width() / destRect.width();
294         float yScale = srcRect.height() / destRect.height();
295         if (shouldUseSubimage) {
296             FloatRect subimageRect = srcRect;
297             float leftPadding = srcRect.x() - floorf(srcRect.x());
298             float topPadding = srcRect.y() - floorf(srcRect.y());
299
300             subimageRect.move(-leftPadding, -topPadding);
301             adjustedDestRect.move(-leftPadding / xScale, -topPadding / yScale);
302
303             subimageRect.setWidth(ceilf(subimageRect.width() + leftPadding));
304             adjustedDestRect.setWidth(subimageRect.width() / xScale);
305
306             subimageRect.setHeight(ceilf(subimageRect.height() + topPadding));
307             adjustedDestRect.setHeight(subimageRect.height() / yScale);
308
309             image = subimage(image.get(), subimageRect);
310             if (currHeight < srcRect.maxY()) {
311                 ASSERT(CGImageGetHeight(image.get()) == currHeight - CGRectIntegral(srcRect).origin.y);
312                 adjustedDestRect.setHeight(CGImageGetHeight(image.get()) / yScale);
313             }
314         } else {
315             adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale));
316             adjustedDestRect.setSize(FloatSize(imageSize.width() / xScale, imageSize.height() / yScale));
317         }
318
319         if (!destRect.contains(adjustedDestRect))
320             CGContextClipToRect(context, destRect);
321     }
322
323     // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly.
324     if (!shouldUseSubimage && currHeight < imageSize.height())
325         adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / imageSize.height());
326
327     setPlatformCompositeOperation(op, blendMode);
328
329     // ImageOrientation expects the origin to be at (0, 0)
330     CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.y());
331     adjustedDestRect.setLocation(FloatPoint());
332
333     if (orientation != DefaultImageOrientation) {
334         CGContextConcatCTM(context, orientation.transformFromDefault(adjustedDestRect.size()));
335         if (orientation.usesWidthAsHeight()) {
336             // The destination rect will have it's width and height already reversed for the orientation of
337             // the image, as it was needed for page layout, so we need to reverse it back here.
338             adjustedDestRect = FloatRect(adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.height(), adjustedDestRect.width());
339         }
340     }
341     
342     // Flip the coords.
343     CGContextTranslateCTM(context, 0, adjustedDestRect.height());
344     CGContextScaleCTM(context, 1, -1);
345
346     // Adjust the color space.
347     image = Image::imageWithColorSpace(image.get(), styleColorSpace);
348
349     // Draw the image.
350     CGContextDrawImage(context, adjustedDestRect, image.get());
351
352     CGContextRestoreGState(context);
353 }
354
355 // Draws a filled rectangle with a stroked border.
356 void GraphicsContext::drawRect(const IntRect& rect)
357 {
358     // FIXME: this function does not handle patterns and gradients
359     // like drawPath does, it probably should.
360     if (paintingDisabled())
361         return;
362
363     ASSERT(!rect.isEmpty());
364
365     CGContextRef context = platformContext();
366
367     CGContextFillRect(context, rect);
368
369     if (strokeStyle() != NoStroke) {
370         // We do a fill of four rects to simulate the stroke of a border.
371         Color oldFillColor = fillColor();
372         if (oldFillColor != strokeColor())
373             setCGFillColor(context, strokeColor(), strokeColorSpace());
374         CGRect rects[4] = {
375             FloatRect(rect.x(), rect.y(), rect.width(), 1),
376             FloatRect(rect.x(), rect.maxY() - 1, rect.width(), 1),
377             FloatRect(rect.x(), rect.y() + 1, 1, rect.height() - 2),
378             FloatRect(rect.maxX() - 1, rect.y() + 1, 1, rect.height() - 2)
379         };
380         CGContextFillRects(context, rects, 4);
381         if (oldFillColor != strokeColor())
382             setCGFillColor(context, oldFillColor, fillColorSpace());
383     }
384 }
385
386 // This is only used to draw borders.
387 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
388 {
389     if (paintingDisabled())
390         return;
391
392     if (strokeStyle() == NoStroke)
393         return;
394
395     float width = strokeThickness();
396
397     FloatPoint p1 = point1;
398     FloatPoint p2 = point2;
399     bool isVerticalLine = (p1.x() == p2.x());
400     
401     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
402     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
403     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
404     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
405     if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
406         if (isVerticalLine) {
407             p1.move(0, width);
408             p2.move(0, -width);
409         } else {
410             p1.move(width, 0);
411             p2.move(-width, 0);
412         }
413     }
414     
415     if (((int)width) % 2) {
416         if (isVerticalLine) {
417             // We're a vertical line.  Adjust our x.
418             p1.move(0.5f, 0.0f);
419             p2.move(0.5f, 0.0f);
420         } else {
421             // We're a horizontal line. Adjust our y.
422             p1.move(0.0f, 0.5f);
423             p2.move(0.0f, 0.5f);
424         }
425     }
426     
427     int patWidth = 0;
428     switch (strokeStyle()) {
429     case NoStroke:
430     case SolidStroke:
431 #if ENABLE(CSS3_TEXT)
432     case DoubleStroke:
433     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=94112 - Needs platform support.
434 #endif // CSS3_TEXT
435         break;
436     case DottedStroke:
437         patWidth = (int)width;
438         break;
439     case DashedStroke:
440         patWidth = 3 * (int)width;
441         break;
442     }
443
444     CGContextRef context = platformContext();
445
446     if (shouldAntialias())
447         CGContextSetShouldAntialias(context, false);
448
449     if (patWidth) {
450         CGContextSaveGState(context);
451
452         // Do a rect fill of our endpoints.  This ensures we always have the
453         // appearance of being a border.  We then draw the actual dotted/dashed line.
454         setCGFillColor(context, strokeColor(), strokeColorSpace());  // The save/restore make it safe to mutate the fill color here without setting it back to the old color.
455         if (isVerticalLine) {
456             CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width));
457             CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width));
458         } else {
459             CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width));
460             CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width));
461         }
462
463         // Example: 80 pixels with a width of 30 pixels.
464         // Remainder is 20.  The maximum pixels of line we could paint
465         // will be 50 pixels.
466         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
467         int remainder = distance % patWidth;
468         int coverage = distance - remainder;
469         int numSegments = coverage / patWidth;
470
471         float patternOffset = 0.0f;
472         // Special case 1px dotted borders for speed.
473         if (patWidth == 1)
474             patternOffset = 1.0f;
475         else {
476             bool evenNumberOfSegments = !(numSegments % 2);
477             if (remainder)
478                 evenNumberOfSegments = !evenNumberOfSegments;
479             if (evenNumberOfSegments) {
480                 if (remainder) {
481                     patternOffset += patWidth - remainder;
482                     patternOffset += remainder / 2;
483                 } else
484                     patternOffset = patWidth / 2;
485             } else {
486                 if (remainder)
487                     patternOffset = (patWidth - remainder)/2;
488             }
489         }
490
491         const CGFloat dottedLine[2] = { static_cast<CGFloat>(patWidth), static_cast<CGFloat>(patWidth) };
492         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
493     }
494
495     CGContextBeginPath(context);
496     CGContextMoveToPoint(context, p1.x(), p1.y());
497     CGContextAddLineToPoint(context, p2.x(), p2.y());
498
499     CGContextStrokePath(context);
500
501     if (patWidth)
502         CGContextRestoreGState(context);
503
504     if (shouldAntialias())
505         CGContextSetShouldAntialias(context, true);
506 }
507
508 // This method is only used to draw the little circles used in lists.
509 void GraphicsContext::drawEllipse(const IntRect& rect)
510 {
511     if (paintingDisabled())
512         return;
513
514     Path path;
515     path.addEllipse(rect);
516     drawPath(path);
517 }
518
519
520 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
521 {
522     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f)
523         return;
524
525     CGContextRef context = platformContext();
526     CGContextSaveGState(context);
527     CGContextBeginPath(context);
528     CGContextSetShouldAntialias(context, false);
529
530     int x = rect.x();
531     int y = rect.y();
532     float w = (float)rect.width();
533     float h = (float)rect.height();
534     float scaleFactor = h / w;
535     float reverseScaleFactor = w / h;
536
537     if (w != h)
538         scale(FloatSize(1, scaleFactor));
539
540     float hRadius = w / 2;
541     float vRadius = h / 2;
542     float fa = startAngle;
543     float falen =  fa + angleSpan;
544     float start = -fa * piFloat / 180.0f;
545     float end = -falen * piFloat / 180.0f;
546     CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true);
547
548     if (w != h)
549         scale(FloatSize(1, reverseScaleFactor));
550
551     float width = strokeThickness();
552     int patWidth = 0;
553
554     switch (strokeStyle()) {
555     case DottedStroke:
556         patWidth = (int)(width / 2);
557         break;
558     case DashedStroke:
559         patWidth = 3 * (int)(width / 2);
560         break;
561     default:
562         break;
563     }
564
565     if (patWidth) {
566         // Example: 80 pixels with a width of 30 pixels.
567         // Remainder is 20.  The maximum pixels of line we could paint
568         // will be 50 pixels.
569         int distance;
570         if (hRadius == vRadius)
571             distance = static_cast<int>((piFloat * hRadius) / 2.0f);
572         else // We are elliptical and will have to estimate the distance
573             distance = static_cast<int>((piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0f)) / 2.0f);
574
575         int remainder = distance % patWidth;
576         int coverage = distance - remainder;
577         int numSegments = coverage / patWidth;
578
579         float patternOffset = 0.0f;
580         // Special case 1px dotted borders for speed.
581         if (patWidth == 1)
582             patternOffset = 1.0f;
583         else {
584             bool evenNumberOfSegments = !(numSegments % 2);
585             if (remainder)
586                 evenNumberOfSegments = !evenNumberOfSegments;
587             if (evenNumberOfSegments) {
588                 if (remainder) {
589                     patternOffset += patWidth - remainder;
590                     patternOffset += remainder / 2.0f;
591                 } else
592                     patternOffset = patWidth / 2.0f;
593             } else {
594                 if (remainder)
595                     patternOffset = (patWidth - remainder) / 2.0f;
596             }
597         }
598
599         const CGFloat dottedLine[2] = { static_cast<CGFloat>(patWidth), static_cast<CGFloat>(patWidth) };
600         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
601     }
602
603     CGContextStrokePath(context);
604
605     CGContextRestoreGState(context);
606 }
607
608 static void addConvexPolygonToPath(Path& path, size_t numberOfPoints, const FloatPoint* points)
609 {
610     ASSERT(numberOfPoints > 0);
611
612     path.moveTo(points[0]);
613     for (size_t i = 1; i < numberOfPoints; ++i)
614         path.addLineTo(points[i]);
615     path.closeSubpath();
616 }
617
618 void GraphicsContext::drawConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialiased)
619 {
620     if (paintingDisabled())
621         return;
622
623     if (numberOfPoints <= 1)
624         return;
625
626     CGContextRef context = platformContext();
627
628     if (antialiased != shouldAntialias())
629         CGContextSetShouldAntialias(context, antialiased);
630
631     Path path;
632     addConvexPolygonToPath(path, numberOfPoints, points);
633     drawPath(path);
634
635     if (antialiased != shouldAntialias())
636         CGContextSetShouldAntialias(context, shouldAntialias());
637 }
638
639 void GraphicsContext::clipConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialias)
640 {
641     if (paintingDisabled())
642         return;
643
644     if (numberOfPoints <= 1)
645         return;
646
647     CGContextRef context = platformContext();
648
649     if (antialias != shouldAntialias())
650         CGContextSetShouldAntialias(context, antialias);
651
652     Path path;
653     addConvexPolygonToPath(path, numberOfPoints, points);
654     clipPath(path, RULE_NONZERO);
655
656     if (antialias != shouldAntialias())
657         CGContextSetShouldAntialias(context, shouldAntialias());
658 }
659
660 void GraphicsContext::applyStrokePattern()
661 {
662     CGContextRef cgContext = platformContext();
663     AffineTransform userToBaseCTM = AffineTransform(wkGetUserToBaseCTM(cgContext));
664
665     RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_state.strokePattern->createPlatformPattern(userToBaseCTM));
666     if (!platformPattern)
667         return;
668
669     RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0));
670     CGContextSetStrokeColorSpace(cgContext, patternSpace.get());
671
672     const CGFloat patternAlpha = 1;
673     CGContextSetStrokePattern(cgContext, platformPattern.get(), &patternAlpha);
674 }
675
676 void GraphicsContext::applyFillPattern()
677 {
678     CGContextRef cgContext = platformContext();
679     AffineTransform userToBaseCTM = AffineTransform(wkGetUserToBaseCTM(cgContext));
680
681     RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_state.fillPattern->createPlatformPattern(userToBaseCTM));
682     if (!platformPattern)
683         return;
684
685     RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0));
686     CGContextSetFillColorSpace(cgContext, patternSpace.get());
687
688     const CGFloat patternAlpha = 1;
689     CGContextSetFillPattern(cgContext, platformPattern.get(), &patternAlpha);
690 }
691
692 static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPathDrawingMode& mode)
693 {
694     bool shouldFill = state.fillPattern || state.fillColor.alpha();
695     bool shouldStroke = state.strokePattern || (state.strokeStyle != NoStroke && state.strokeColor.alpha());
696     bool useEOFill = state.fillRule == RULE_EVENODD;
697
698     if (shouldFill) {
699         if (shouldStroke) {
700             if (useEOFill)
701                 mode = kCGPathEOFillStroke;
702             else
703                 mode = kCGPathFillStroke;
704         } else { // fill, no stroke
705             if (useEOFill)
706                 mode = kCGPathEOFill;
707             else
708                 mode = kCGPathFill;
709         }
710     } else {
711         // Setting mode to kCGPathStroke even if shouldStroke is false. In that case, we return false and mode will not be used,
712         // but the compiler will not complain about an uninitialized variable.
713         mode = kCGPathStroke;
714     }
715
716     return shouldFill || shouldStroke;
717 }
718
719 void GraphicsContext::drawPath(const Path& path)
720 {
721     if (paintingDisabled() || path.isEmpty())
722         return;
723
724     CGContextRef context = platformContext();
725     const GraphicsContextState& state = m_state;
726
727     if (state.fillGradient || state.strokeGradient) {
728         // We don't have any optimized way to fill & stroke a path using gradients
729         // FIXME: Be smarter about this.
730         fillPath(path);
731         strokePath(path);
732         return;
733     }
734
735     CGContextBeginPath(context);
736     CGContextAddPath(context, path.platformPath());
737
738     if (state.fillPattern)
739         applyFillPattern();
740     if (state.strokePattern)
741         applyStrokePattern();
742
743     CGPathDrawingMode drawingMode;
744     if (calculateDrawingMode(state, drawingMode))
745         CGContextDrawPath(context, drawingMode);
746 }
747
748 static inline void fillPathWithFillRule(CGContextRef context, WindRule fillRule)
749 {
750     if (fillRule == RULE_EVENODD)
751         CGContextEOFillPath(context);
752     else
753         CGContextFillPath(context);
754 }
755
756 void GraphicsContext::fillPath(const Path& path)
757 {
758     if (paintingDisabled() || path.isEmpty())
759         return;
760
761     CGContextRef context = platformContext();
762
763     if (m_state.fillGradient) {
764         if (hasShadow()) {
765             FloatRect rect = path.fastBoundingRect();
766             FloatSize layerSize = getCTM().mapSize(rect.size());
767
768             CGLayerRef layer = CGLayerCreateWithContext(context, layerSize, 0);
769             CGContextRef layerContext = CGLayerGetContext(layer);
770
771             CGContextScaleCTM(layerContext, layerSize.width() / rect.width(), layerSize.height() / rect.height());
772             CGContextTranslateCTM(layerContext, -rect.x(), -rect.y());
773             CGContextBeginPath(layerContext);
774             CGContextAddPath(layerContext, path.platformPath());
775             CGContextConcatCTM(layerContext, m_state.fillGradient->gradientSpaceTransform());
776
777             if (fillRule() == RULE_EVENODD)
778                 CGContextEOClip(layerContext);
779             else
780                 CGContextClip(layerContext);
781
782             m_state.fillGradient->paint(layerContext);
783             CGContextDrawLayerInRect(context, rect, layer);
784             CGLayerRelease(layer);
785         } else {
786             CGContextBeginPath(context);
787             CGContextAddPath(context, path.platformPath());
788             CGContextSaveGState(context);
789             CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform());
790
791             if (fillRule() == RULE_EVENODD)
792                 CGContextEOClip(context);
793             else
794                 CGContextClip(context);
795
796             m_state.fillGradient->paint(this);
797             CGContextRestoreGState(context);
798         }
799
800         return;
801     }
802
803     CGContextBeginPath(context);
804     CGContextAddPath(context, path.platformPath());
805
806     if (m_state.fillPattern)
807         applyFillPattern();
808     fillPathWithFillRule(context, fillRule());
809 }
810
811 void GraphicsContext::strokePath(const Path& path)
812 {
813     if (paintingDisabled() || path.isEmpty())
814         return;
815
816     CGContextRef context = platformContext();
817
818     CGContextBeginPath(context);
819     CGContextAddPath(context, path.platformPath());
820
821     if (m_state.strokeGradient) {
822         if (hasShadow()) {
823             FloatRect rect = path.fastBoundingRect();
824             float lineWidth = strokeThickness();
825             float doubleLineWidth = lineWidth * 2;
826             float adjustedWidth = ceilf(rect.width() + doubleLineWidth);
827             float adjustedHeight = ceilf(rect.height() + doubleLineWidth);
828
829             FloatSize layerSize = getCTM().mapSize(FloatSize(adjustedWidth, adjustedHeight));
830
831             CGLayerRef layer = CGLayerCreateWithContext(context, layerSize, 0);
832             CGContextRef layerContext = CGLayerGetContext(layer);
833             CGContextSetLineWidth(layerContext, lineWidth);
834
835             // Compensate for the line width, otherwise the layer's top-left corner would be
836             // aligned with the rect's top-left corner. This would result in leaving pixels out of
837             // the layer on the left and top sides.
838             float translationX = lineWidth - rect.x();
839             float translationY = lineWidth - rect.y();
840             CGContextScaleCTM(layerContext, layerSize.width() / adjustedWidth, layerSize.height() / adjustedHeight);
841             CGContextTranslateCTM(layerContext, translationX, translationY);
842
843             CGContextAddPath(layerContext, path.platformPath());
844             CGContextReplacePathWithStrokedPath(layerContext);
845             CGContextClip(layerContext);
846             CGContextConcatCTM(layerContext, m_state.strokeGradient->gradientSpaceTransform());
847             m_state.strokeGradient->paint(layerContext);
848
849             float destinationX = roundf(rect.x() - lineWidth);
850             float destinationY = roundf(rect.y() - lineWidth);
851             CGContextDrawLayerInRect(context, CGRectMake(destinationX, destinationY, adjustedWidth, adjustedHeight), layer);
852             CGLayerRelease(layer);
853         } else {
854             CGContextSaveGState(context);
855             CGContextReplacePathWithStrokedPath(context);
856             CGContextClip(context);
857             CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform());
858             m_state.strokeGradient->paint(this);
859             CGContextRestoreGState(context);
860         }
861         return;
862     }
863
864     if (m_state.strokePattern)
865         applyStrokePattern();
866     CGContextStrokePath(context);
867 }
868
869 static float radiusToLegacyRadius(float radius)
870 {
871     return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius;
872 }
873
874 static bool hasBlurredShadow(const GraphicsContextState& state)
875 {
876     return state.shadowColor.isValid() && state.shadowColor.alpha() && state.shadowBlur;
877 }
878
879 void GraphicsContext::fillRect(const FloatRect& rect)
880 {
881     if (paintingDisabled())
882         return;
883
884     CGContextRef context = platformContext();
885
886     if (m_state.fillGradient) {
887         CGContextSaveGState(context);
888         if (hasShadow()) {
889             FloatSize layerSize = getCTM().mapSize(rect.size());
890
891             CGLayerRef layer = CGLayerCreateWithContext(context, layerSize, 0);
892             CGContextRef layerContext = CGLayerGetContext(layer);
893
894             CGContextScaleCTM(layerContext, layerSize.width() / rect.width(), layerSize.height() / rect.height());
895             CGContextTranslateCTM(layerContext, -rect.x(), -rect.y());
896             CGContextAddRect(layerContext, rect);
897             CGContextClip(layerContext);
898
899             CGContextConcatCTM(layerContext, m_state.fillGradient->gradientSpaceTransform());
900             m_state.fillGradient->paint(layerContext);
901             CGContextDrawLayerInRect(context, rect, layer);
902             CGLayerRelease(layer);
903         } else {
904             CGContextClipToRect(context, rect);
905             CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform());
906             m_state.fillGradient->paint(this);
907         }
908         CGContextRestoreGState(context);
909         return;
910     }
911
912     if (m_state.fillPattern)
913         applyFillPattern();
914
915     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
916     if (drawOwnShadow) {
917         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
918         // Turn off CG shadows.
919         CGContextSaveGState(context);
920         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
921
922         ShadowBlur contextShadow(FloatSize(shadowBlur, shadowBlur), m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
923         contextShadow.drawRectShadow(this, rect, RoundedRect::Radii());
924     }
925
926     CGContextFillRect(context, rect);
927
928     if (drawOwnShadow)
929         CGContextRestoreGState(context);
930 }
931
932 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
933 {
934     if (paintingDisabled())
935         return;
936
937     CGContextRef context = platformContext();
938     Color oldFillColor = fillColor();
939     ColorSpace oldColorSpace = fillColorSpace();
940
941     if (oldFillColor != color || oldColorSpace != colorSpace)
942         setCGFillColor(context, color, colorSpace);
943
944     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
945     if (drawOwnShadow) {
946         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
947         // Turn off CG shadows.
948         CGContextSaveGState(context);
949         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
950
951         ShadowBlur contextShadow(FloatSize(shadowBlur, shadowBlur), m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
952         contextShadow.drawRectShadow(this, rect, RoundedRect::Radii());
953     }
954
955     CGContextFillRect(context, rect);
956     
957     if (drawOwnShadow)
958         CGContextRestoreGState(context);
959
960     if (oldFillColor != color || oldColorSpace != colorSpace)
961         setCGFillColor(context, oldFillColor, oldColorSpace);
962 }
963
964 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
965 {
966     if (paintingDisabled())
967         return;
968
969     CGContextRef context = platformContext();
970     Color oldFillColor = fillColor();
971     ColorSpace oldColorSpace = fillColorSpace();
972
973     if (oldFillColor != color || oldColorSpace != colorSpace)
974         setCGFillColor(context, color, colorSpace);
975
976     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
977     if (drawOwnShadow) {
978         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
979
980         // Turn off CG shadows.
981         CGContextSaveGState(context);
982         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
983
984         ShadowBlur contextShadow(FloatSize(shadowBlur, shadowBlur), m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
985         contextShadow.drawRectShadow(this, rect, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight));
986     }
987
988     bool equalWidths = (topLeft.width() == topRight.width() && topRight.width() == bottomLeft.width() && bottomLeft.width() == bottomRight.width());
989     bool equalHeights = (topLeft.height() == bottomLeft.height() && bottomLeft.height() == topRight.height() && topRight.height() == bottomRight.height());
990     bool hasCustomFill = m_state.fillGradient || m_state.fillPattern;
991     if (!hasCustomFill && equalWidths && equalHeights && topLeft.width() * 2 == rect.width() && topLeft.height() * 2 == rect.height())
992         CGContextFillEllipseInRect(context, rect);
993     else {
994         Path path;
995         path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
996         fillPath(path);
997     }
998
999     if (drawOwnShadow)
1000         CGContextRestoreGState(context);
1001
1002     if (oldFillColor != color || oldColorSpace != colorSpace)
1003         setCGFillColor(context, oldFillColor, oldColorSpace);
1004 }
1005
1006 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
1007 {
1008     if (paintingDisabled())
1009         return;
1010
1011     CGContextRef context = platformContext();
1012
1013     Path path;
1014     path.addRect(rect);
1015
1016     if (!roundedHoleRect.radii().isZero())
1017         path.addRoundedRect(roundedHoleRect);
1018     else
1019         path.addRect(roundedHoleRect.rect());
1020
1021     WindRule oldFillRule = fillRule();
1022     Color oldFillColor = fillColor();
1023     ColorSpace oldFillColorSpace = fillColorSpace();
1024     
1025     setFillRule(RULE_EVENODD);
1026     setFillColor(color, colorSpace);
1027
1028     // fillRectWithRoundedHole() assumes that the edges of rect are clipped out, so we only care about shadows cast around inside the hole.
1029     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms;
1030     if (drawOwnShadow) {
1031         float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur;
1032
1033         // Turn off CG shadows.
1034         CGContextSaveGState(context);
1035         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
1036
1037         ShadowBlur contextShadow(FloatSize(shadowBlur, shadowBlur), m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace);
1038         contextShadow.drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii());
1039     }
1040
1041     fillPath(path);
1042
1043     if (drawOwnShadow)
1044         CGContextRestoreGState(context);
1045     
1046     setFillRule(oldFillRule);
1047     setFillColor(oldFillColor, oldFillColorSpace);
1048 }
1049
1050 void GraphicsContext::clip(const FloatRect& rect)
1051 {
1052     if (paintingDisabled())
1053         return;
1054     CGContextClipToRect(platformContext(), rect);
1055     m_data->clip(rect);
1056 }
1057
1058 void GraphicsContext::clipOut(const IntRect& rect)
1059 {
1060     if (paintingDisabled())
1061         return;
1062
1063     // FIXME: Using CGRectInfinite is much faster than getting the clip bounding box. However, due
1064     // to <rdar://problem/12584492>, CGRectInfinite can't be used with an accelerated context that
1065     // has certain transforms that aren't just a translation or a scale.
1066     const AffineTransform& ctm = getCTM();
1067     bool canUseCGRectInfinite = !isAcceleratedContext() || (!ctm.b() && !ctm.c());
1068     CGRect rects[2] = { canUseCGRectInfinite ? CGRectInfinite : CGContextGetClipBoundingBox(platformContext()), rect };
1069     CGContextBeginPath(platformContext());
1070     CGContextAddRects(platformContext(), rects, 2);
1071     CGContextEOClip(platformContext());
1072 }
1073
1074 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
1075 {
1076     if (paintingDisabled())
1077         return;
1078
1079     // Why does clipping to an empty path do nothing?
1080     // Why is this different from GraphicsContext::clip(const Path&).
1081     if (path.isEmpty())
1082         return;
1083
1084     CGContextRef context = platformContext();
1085
1086     CGContextBeginPath(platformContext());
1087     CGContextAddPath(platformContext(), path.platformPath());
1088
1089     if (clipRule == RULE_EVENODD)
1090         CGContextEOClip(context);
1091     else
1092         CGContextClip(context);
1093 }
1094
1095 IntRect GraphicsContext::clipBounds() const
1096 {
1097     return enclosingIntRect(CGContextGetClipBoundingBox(platformContext()));
1098 }
1099
1100 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
1101 {
1102     if (paintingDisabled())
1103         return;
1104
1105     clip(rect);
1106     CGContextRef context = platformContext();
1107
1108     // Add outer ellipse
1109     CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
1110     // Add inner ellipse.
1111     CGContextAddEllipseInRect(context, CGRectMake(rect.x() + thickness, rect.y() + thickness,
1112         rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1113
1114     CGContextEOClip(context);
1115 }
1116
1117 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
1118 {
1119     if (paintingDisabled())
1120         return;
1121
1122     save();
1123
1124     CGContextRef context = platformContext();
1125     CGContextSetAlpha(context, opacity);
1126     CGContextBeginTransparencyLayer(context, 0);
1127     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1128 }
1129
1130 void GraphicsContext::endPlatformTransparencyLayer()
1131 {
1132     if (paintingDisabled())
1133         return;
1134     CGContextRef context = platformContext();
1135     CGContextEndTransparencyLayer(context);
1136
1137     restore();
1138 }
1139
1140 bool GraphicsContext::supportsTransparencyLayers()
1141 {
1142     return true;
1143 }
1144
1145 static void applyShadowOffsetWorkaroundIfNeeded(const GraphicsContext& context, CGFloat& xOffset, CGFloat& yOffset)
1146 {
1147 #if !PLATFORM(IOS)
1148     if (context.isAcceleratedContext())
1149         return;
1150
1151 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
1152     if (wkCGContextDrawsWithCorrectShadowOffsets(context.platformContext()))
1153         return;
1154 #endif
1155
1156     // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated
1157     // to the desired integer. Also see: <rdar://problem/10056277>
1158     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
1159     if (xOffset > 0)
1160         xOffset += extraShadowOffset;
1161     else if (xOffset < 0)
1162         xOffset -= extraShadowOffset;
1163
1164     if (yOffset > 0)
1165         yOffset += extraShadowOffset;
1166     else if (yOffset < 0)
1167         yOffset -= extraShadowOffset;
1168 #endif
1169 }
1170
1171 void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
1172 {
1173     if (paintingDisabled())
1174         return;
1175     
1176     // FIXME: we could avoid the shadow setup cost when we know we'll render the shadow ourselves.
1177
1178     CGFloat xOffset = offset.width();
1179     CGFloat yOffset = offset.height();
1180     CGFloat blurRadius = blur;
1181     CGContextRef context = platformContext();
1182
1183     if (!m_state.shadowsIgnoreTransforms) {
1184         CGAffineTransform userToBaseCTM = wkGetUserToBaseCTM(context);
1185
1186         CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b;
1187         CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d;
1188         CGFloat C = B;
1189         CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d;
1190
1191         CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D)))));
1192
1193         blurRadius = blur * smallEigenvalue;
1194
1195         CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM);
1196
1197         xOffset = offsetInBaseSpace.width;
1198         yOffset = offsetInBaseSpace.height;
1199     }
1200
1201     // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp
1202     blurRadius = min(blurRadius, narrowPrecisionToCGFloat(1000.0));
1203
1204     applyShadowOffsetWorkaroundIfNeeded(*this, xOffset, yOffset);
1205
1206     // Check for an invalid color, as this means that the color was not set for the shadow
1207     // and we should therefore just use the default shadow color.
1208     if (!color.isValid())
1209         CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius);
1210     else
1211         CGContextSetShadowWithColor(context, CGSizeMake(xOffset, yOffset), blurRadius, cachedCGColor(color, colorSpace));
1212 }
1213
1214 void GraphicsContext::clearPlatformShadow()
1215 {
1216     if (paintingDisabled())
1217         return;
1218     CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
1219 }
1220
1221 void GraphicsContext::setMiterLimit(float limit)
1222 {
1223     if (paintingDisabled())
1224         return;
1225     CGContextSetMiterLimit(platformContext(), limit);
1226 }
1227
1228 void GraphicsContext::setAlpha(float alpha)
1229 {
1230     if (paintingDisabled())
1231         return;
1232     CGContextSetAlpha(platformContext(), alpha);
1233 }
1234
1235 void GraphicsContext::clearRect(const FloatRect& r)
1236 {
1237     if (paintingDisabled())
1238         return;
1239     CGContextClearRect(platformContext(), r);
1240 }
1241
1242 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1243 {
1244     if (paintingDisabled())
1245         return;
1246
1247     CGContextRef context = platformContext();
1248
1249     if (m_state.strokeGradient) {
1250         if (hasShadow()) {
1251             const float doubleLineWidth = lineWidth * 2;
1252             float adjustedWidth = ceilf(rect.width() + doubleLineWidth);
1253             float adjustedHeight = ceilf(rect.height() + doubleLineWidth);
1254             FloatSize layerSize = getCTM().mapSize(FloatSize(adjustedWidth, adjustedHeight));
1255
1256             CGLayerRef layer = CGLayerCreateWithContext(context, layerSize, 0);
1257
1258             CGContextRef layerContext = CGLayerGetContext(layer);
1259             m_state.strokeThickness = lineWidth;
1260             CGContextSetLineWidth(layerContext, lineWidth);
1261
1262             // Compensate for the line width, otherwise the layer's top-left corner would be
1263             // aligned with the rect's top-left corner. This would result in leaving pixels out of
1264             // the layer on the left and top sides.
1265             const float translationX = lineWidth - rect.x();
1266             const float translationY = lineWidth - rect.y();
1267             CGContextScaleCTM(layerContext, layerSize.width() / adjustedWidth, layerSize.height() / adjustedHeight);
1268             CGContextTranslateCTM(layerContext, translationX, translationY);
1269
1270             CGContextAddRect(layerContext, rect);
1271             CGContextReplacePathWithStrokedPath(layerContext);
1272             CGContextClip(layerContext);
1273             CGContextConcatCTM(layerContext, m_state.strokeGradient->gradientSpaceTransform());
1274             m_state.strokeGradient->paint(layerContext);
1275
1276             const float destinationX = roundf(rect.x() - lineWidth);
1277             const float destinationY = roundf(rect.y() - lineWidth);
1278             CGContextDrawLayerInRect(context, CGRectMake(destinationX, destinationY, adjustedWidth, adjustedHeight), layer);
1279             CGLayerRelease(layer);
1280         } else {
1281             CGContextSaveGState(context);
1282             setStrokeThickness(lineWidth);
1283             CGContextAddRect(context, rect);
1284             CGContextReplacePathWithStrokedPath(context);
1285             CGContextClip(context);
1286             CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform());
1287             m_state.strokeGradient->paint(this);
1288             CGContextRestoreGState(context);
1289         }
1290         return;
1291     }
1292
1293     if (m_state.strokePattern)
1294         applyStrokePattern();
1295     CGContextStrokeRectWithWidth(context, rect, lineWidth);
1296 }
1297
1298 void GraphicsContext::setLineCap(LineCap cap)
1299 {
1300     if (paintingDisabled())
1301         return;
1302     switch (cap) {
1303     case ButtCap:
1304         CGContextSetLineCap(platformContext(), kCGLineCapButt);
1305         break;
1306     case RoundCap:
1307         CGContextSetLineCap(platformContext(), kCGLineCapRound);
1308         break;
1309     case SquareCap:
1310         CGContextSetLineCap(platformContext(), kCGLineCapSquare);
1311         break;
1312     }
1313 }
1314
1315 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1316 {
1317     CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
1318 }
1319
1320 void GraphicsContext::setLineJoin(LineJoin join)
1321 {
1322     if (paintingDisabled())
1323         return;
1324     switch (join) {
1325     case MiterJoin:
1326         CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
1327         break;
1328     case RoundJoin:
1329         CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
1330         break;
1331     case BevelJoin:
1332         CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
1333         break;
1334     }
1335 }
1336
1337 void GraphicsContext::clip(const Path& path)
1338 {
1339     if (paintingDisabled())
1340         return;
1341     CGContextRef context = platformContext();
1342
1343     // CGContextClip does nothing if the path is empty, so in this case, we
1344     // instead clip against a zero rect to reduce the clipping region to
1345     // nothing - which is the intended behavior of clip() if the path is empty.    
1346     if (path.isEmpty())
1347         CGContextClipToRect(context, CGRectZero);
1348     else {
1349         CGContextBeginPath(context);
1350         CGContextAddPath(context, path.platformPath());
1351         CGContextClip(context);
1352     }
1353     m_data->clip(path);
1354 }
1355
1356 void GraphicsContext::canvasClip(const Path& path)
1357 {
1358     clip(path);
1359 }
1360
1361 void GraphicsContext::clipOut(const Path& path)
1362 {
1363     if (paintingDisabled())
1364         return;
1365
1366     CGContextBeginPath(platformContext());
1367     CGContextAddRect(platformContext(), CGContextGetClipBoundingBox(platformContext()));
1368     if (!path.isEmpty())
1369         CGContextAddPath(platformContext(), path.platformPath());
1370     CGContextEOClip(platformContext());
1371 }
1372
1373 void GraphicsContext::scale(const FloatSize& size)
1374 {
1375     if (paintingDisabled())
1376         return;
1377     CGContextScaleCTM(platformContext(), size.width(), size.height());
1378     m_data->scale(size);
1379     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1380 }
1381
1382 void GraphicsContext::rotate(float angle)
1383 {
1384     if (paintingDisabled())
1385         return;
1386     CGContextRotateCTM(platformContext(), angle);
1387     m_data->rotate(angle);
1388     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1389 }
1390
1391 void GraphicsContext::translate(float x, float y)
1392 {
1393     if (paintingDisabled())
1394         return;
1395     CGContextTranslateCTM(platformContext(), x, y);
1396     m_data->translate(x, y);
1397     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1398 }
1399
1400 void GraphicsContext::concatCTM(const AffineTransform& transform)
1401 {
1402     if (paintingDisabled())
1403         return;
1404     CGContextConcatCTM(platformContext(), transform);
1405     m_data->concatCTM(transform);
1406     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1407 }
1408
1409 void GraphicsContext::setCTM(const AffineTransform& transform)
1410 {
1411     if (paintingDisabled())
1412         return;
1413     CGContextSetCTM(platformContext(), transform);
1414     m_data->setCTM(transform);
1415     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1416 }
1417
1418 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
1419 {
1420     if (paintingDisabled())
1421         return AffineTransform();
1422
1423     // The CTM usually includes the deviceScaleFactor except in WebKit 1 when the
1424     // content is non-composited, since the scale factor is integrated at a lower
1425     // level. To guarantee the deviceScale is included, we can use this CG API.
1426     if (includeScale == DefinitelyIncludeDeviceScale)
1427         return CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1428
1429     return CGContextGetCTM(platformContext());
1430 }
1431
1432 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
1433 {
1434 #if PLATFORM(CHROMIUM)
1435     return rect;
1436 #else
1437     // It is not enough just to round to pixels in device space. The rotation part of the
1438     // affine transform matrix to device space can mess with this conversion if we have a
1439     // rotating image like the hands of the world clock widget. We just need the scale, so
1440     // we get the affine transform matrix and extract the scale.
1441
1442     if (m_data->m_userToDeviceTransformKnownToBeIdentity)
1443         return roundedIntRect(rect);
1444
1445     CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1446     if (CGAffineTransformIsIdentity(deviceMatrix)) {
1447         m_data->m_userToDeviceTransformKnownToBeIdentity = true;
1448         return roundedIntRect(rect);
1449     }
1450
1451     float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
1452     float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
1453
1454     CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
1455     CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
1456         (rect.y() + rect.height()) * deviceScaleY);
1457
1458     deviceOrigin.x = roundf(deviceOrigin.x);
1459     deviceOrigin.y = roundf(deviceOrigin.y);
1460     if (roundingMode == RoundAllSides) {
1461         deviceLowerRight.x = roundf(deviceLowerRight.x);
1462         deviceLowerRight.y = roundf(deviceLowerRight.y);
1463     } else {
1464         deviceLowerRight.x = deviceOrigin.x + roundf(rect.width() * deviceScaleX);
1465         deviceLowerRight.y = deviceOrigin.y + roundf(rect.height() * deviceScaleY);
1466     }
1467
1468     // Don't let the height or width round to 0 unless either was originally 0
1469     if (deviceOrigin.y == deviceLowerRight.y && rect.height())
1470         deviceLowerRight.y += 1;
1471     if (deviceOrigin.x == deviceLowerRight.x && rect.width())
1472         deviceLowerRight.x += 1;
1473
1474     FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
1475     FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
1476     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
1477 #endif
1478 }
1479
1480 void GraphicsContext::drawLineForText(const FloatPoint& point, float width, bool printing)
1481 {
1482     if (paintingDisabled())
1483         return;
1484
1485     if (width <= 0)
1486         return;
1487
1488     float x = point.x();
1489     float y = point.y();
1490     float lineLength = width;
1491
1492     // Use a minimum thickness of 0.5 in user space.
1493     // See http://bugs.webkit.org/show_bug.cgi?id=4255 for details of why 0.5 is the right minimum thickness to use.
1494     float thickness = max(strokeThickness(), 0.5f);
1495
1496     bool restoreAntialiasMode = false;
1497
1498     if (!printing) {
1499         // On screen, use a minimum thickness of 1.0 in user space (later rounded to an integral number in device space).
1500         float adjustedThickness = max(thickness, 1.0f);
1501
1502         // FIXME: This should be done a better way.
1503         // We try to round all parameters to integer boundaries in device space. If rounding pixels in device space
1504         // makes our thickness more than double, then there must be a shrinking-scale factor and rounding to pixels
1505         // in device space will make the underlines too thick.
1506         CGRect lineRect = roundToDevicePixels(FloatRect(x, y, lineLength, adjustedThickness), RoundOriginAndDimensions);
1507         if (lineRect.size.height < thickness * 2.0) {
1508             x = lineRect.origin.x;
1509             y = lineRect.origin.y;
1510             lineLength = lineRect.size.width;
1511             thickness = lineRect.size.height;
1512             if (shouldAntialias()) {
1513                 CGContextSetShouldAntialias(platformContext(), false);
1514                 restoreAntialiasMode = true;
1515             }
1516         }
1517     }
1518
1519     if (fillColor() != strokeColor())
1520         setCGFillColor(platformContext(), strokeColor(), strokeColorSpace());
1521     CGContextFillRect(platformContext(), CGRectMake(x, y, lineLength, thickness));
1522     if (fillColor() != strokeColor())
1523         setCGFillColor(platformContext(), fillColor(), fillColorSpace());
1524
1525     if (restoreAntialiasMode)
1526         CGContextSetShouldAntialias(platformContext(), true);
1527 }
1528
1529 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1530 {
1531     if (paintingDisabled())
1532         return;
1533
1534     RetainPtr<CFURLRef> urlRef(AdoptCF, link.createCFURL());
1535     if (!urlRef)
1536         return;
1537
1538     CGContextRef context = platformContext();
1539
1540     // Get the bounding box to handle clipping.
1541     CGRect box = CGContextGetClipBoundingBox(context);
1542
1543     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
1544     IntRect rect = destRect;
1545     rect.intersect(intBox);
1546
1547     CGPDFContextSetURLForRect(context, urlRef.get(),
1548         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
1549 }
1550
1551 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
1552 {
1553     if (paintingDisabled())
1554         return;
1555
1556     CGInterpolationQuality quality = kCGInterpolationDefault;
1557     switch (mode) {
1558     case InterpolationDefault:
1559         quality = kCGInterpolationDefault;
1560         break;
1561     case InterpolationNone:
1562         quality = kCGInterpolationNone;
1563         break;
1564     case InterpolationLow:
1565         quality = kCGInterpolationLow;
1566         break;
1567
1568     // Fall through to InterpolationHigh if kCGInterpolationMedium is not usable.
1569     case InterpolationMedium:
1570 #if USE(CG_INTERPOLATION_MEDIUM)
1571         quality = kCGInterpolationMedium;
1572         break;
1573 #endif
1574     case InterpolationHigh:
1575         quality = kCGInterpolationHigh;
1576         break;
1577     }
1578     CGContextSetInterpolationQuality(platformContext(), quality);
1579 }
1580
1581 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1582 {
1583     if (paintingDisabled())
1584         return InterpolationDefault;
1585
1586     CGInterpolationQuality quality = CGContextGetInterpolationQuality(platformContext());
1587     switch (quality) {
1588     case kCGInterpolationDefault:
1589         return InterpolationDefault;
1590     case kCGInterpolationNone:
1591         return InterpolationNone;
1592     case kCGInterpolationLow:
1593         return InterpolationLow;
1594 #if HAVE(CG_INTERPOLATION_MEDIUM)
1595     // kCGInterpolationMedium is known to be present in the CGInterpolationQuality enum.
1596     case kCGInterpolationMedium:
1597 #if USE(CG_INTERPOLATION_MEDIUM)
1598         // Only map to InterpolationMedium if targeting a system that understands it.
1599         return InterpolationMedium;
1600 #else
1601         return InterpolationDefault;
1602 #endif  // USE(CG_INTERPOLATION_MEDIUM)
1603 #endif  // HAVE(CG_INTERPOLATION_MEDIUM)
1604     case kCGInterpolationHigh:
1605         return InterpolationHigh;
1606     }
1607     return InterpolationDefault;
1608 }
1609
1610 void GraphicsContext::setAllowsFontSmoothing(bool allowsFontSmoothing)
1611 {
1612     UNUSED_PARAM(allowsFontSmoothing);
1613 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1614     CGContextRef context = platformContext();
1615     CGContextSetAllowsFontSmoothing(context, allowsFontSmoothing);
1616 #endif
1617 }
1618
1619 void GraphicsContext::setIsCALayerContext(bool isLayerContext)
1620 {
1621     if (isLayerContext)
1622         m_data->m_contextFlags |= IsLayerCGContext;
1623     else
1624         m_data->m_contextFlags &= ~IsLayerCGContext;
1625 }
1626
1627 bool GraphicsContext::isCALayerContext() const
1628 {
1629     return m_data->m_contextFlags & IsLayerCGContext;
1630 }
1631
1632 void GraphicsContext::setIsAcceleratedContext(bool isAccelerated)
1633 {
1634     if (isAccelerated)
1635         m_data->m_contextFlags |= IsAcceleratedCGContext;
1636     else
1637         m_data->m_contextFlags &= ~IsAcceleratedCGContext;
1638 }
1639
1640 bool GraphicsContext::isAcceleratedContext() const
1641 {
1642     return m_data->m_contextFlags & IsAcceleratedCGContext;
1643 }
1644
1645 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1646 {
1647     if (paintingDisabled())
1648         return;
1649
1650     CGContextRef context = platformContext();
1651     switch (mode) {
1652     case TextModeFill:
1653         CGContextSetTextDrawingMode(context, kCGTextFill);
1654         break;
1655     case TextModeStroke:
1656         CGContextSetTextDrawingMode(context, kCGTextStroke);
1657         break;
1658     case TextModeFill | TextModeStroke:
1659         CGContextSetTextDrawingMode(context, kCGTextFillStroke);
1660         break;
1661     default:
1662         break;
1663     }
1664 }
1665
1666 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1667 {
1668     if (paintingDisabled())
1669         return;
1670     setCGStrokeColor(platformContext(), color, colorSpace);
1671 }
1672
1673 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1674 {
1675     if (paintingDisabled())
1676         return;
1677     CGContextSetLineWidth(platformContext(), thickness);
1678 }
1679
1680 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1681 {
1682     if (paintingDisabled())
1683         return;
1684     setCGFillColor(platformContext(), color, colorSpace);
1685 }
1686
1687 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1688 {
1689     if (paintingDisabled())
1690         return;
1691     CGContextSetShouldAntialias(platformContext(), enable);
1692 }
1693
1694 void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
1695 {
1696     if (paintingDisabled())
1697         return;
1698     CGContextSetShouldSmoothFonts(platformContext(), enable);
1699 }
1700
1701 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode, BlendMode)
1702 {
1703     if (paintingDisabled())
1704         return;
1705
1706     CGBlendMode target = kCGBlendModeNormal;
1707     switch (mode) {
1708     case CompositeClear:
1709         target = kCGBlendModeClear;
1710         break;
1711     case CompositeCopy:
1712         target = kCGBlendModeCopy;
1713         break;
1714     case CompositeSourceOver:
1715         //kCGBlendModeNormal
1716         break;
1717     case CompositeSourceIn:
1718         target = kCGBlendModeSourceIn;
1719         break;
1720     case CompositeSourceOut:
1721         target = kCGBlendModeSourceOut;
1722         break;
1723     case CompositeSourceAtop:
1724         target = kCGBlendModeSourceAtop;
1725         break;
1726     case CompositeDestinationOver:
1727         target = kCGBlendModeDestinationOver;
1728         break;
1729     case CompositeDestinationIn:
1730         target = kCGBlendModeDestinationIn;
1731         break;
1732     case CompositeDestinationOut:
1733         target = kCGBlendModeDestinationOut;
1734         break;
1735     case CompositeDestinationAtop:
1736         target = kCGBlendModeDestinationAtop;
1737         break;
1738     case CompositeXOR:
1739         target = kCGBlendModeXOR;
1740         break;
1741     case CompositePlusDarker:
1742         target = kCGBlendModePlusDarker;
1743         break;
1744     case CompositePlusLighter:
1745         target = kCGBlendModePlusLighter;
1746         break;
1747     case CompositeDifference:
1748         target = kCGBlendModeDifference;
1749         break;
1750     }
1751     CGContextSetBlendMode(platformContext(), target);
1752 }
1753
1754 void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
1755 {
1756     // CoreGraphics expects the base CTM of a HiDPI context to have the scale factor applied to it.
1757     // Failing to change the base level CTM will cause certain CG features, such as focus rings,
1758     // to draw with a scale factor of 1 rather than the actual scale factor.
1759     wkSetBaseCTM(platformContext(), CGAffineTransformScale(CGContextGetBaseCTM(platformContext()), deviceScaleFactor, deviceScaleFactor));
1760 }
1761
1762 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1763 {
1764     if (paintingDisabled())
1765         return;
1766
1767     // CGContextFillEllipseInRect only supports solid colors.
1768     if (m_state.fillGradient || m_state.fillPattern) {
1769         fillEllipseAsPath(ellipse);
1770         return;
1771     }
1772
1773     CGContextRef context = platformContext();
1774     CGContextFillEllipseInRect(context, ellipse);
1775 }
1776
1777 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1778 {
1779     if (paintingDisabled())
1780         return;
1781
1782     // CGContextStrokeEllipseInRect only supports solid colors.
1783     if (m_state.strokeGradient || m_state.strokePattern) {
1784         strokeEllipseAsPath(ellipse);
1785         return;
1786     }
1787
1788     CGContextRef context = platformContext();
1789     CGContextStrokeEllipseInRect(context, ellipse);
1790 }
1791
1792 }