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