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