Host GraphicsContext's CTM inside GraphicsContextState
[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, ColorSpace colorSpace)
67 {
68     CGContextSetFillColorWithColor(context, cachedCGColor(color, colorSpace));
69 }
70
71 static void setCGStrokeColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
72 {
73     CGContextSetStrokeColorWithColor(context, cachedCGColor(color, colorSpace));
74 }
75
76 CGColorSpaceRef deviceRGBColorSpaceRef()
77 {
78     static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
79     return deviceSpace;
80 }
81
82 CGColorSpaceRef sRGBColorSpaceRef()
83 {
84 #if PLATFORM(IOS)
85     return deviceRGBColorSpaceRef();
86 #else
87     static CGColorSpaceRef sRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
88 #if PLATFORM(WIN)
89     // Out-of-date CG installations will not honor kCGColorSpaceSRGB. This logic avoids
90     // causing a crash under those conditions. Since the default color space in Windows
91     // is sRGB, this all works out nicely.
92     if (!sRGBSpace)
93         sRGBSpace = deviceRGBColorSpaceRef();
94 #endif // PLATFORM(WIN)
95     return sRGBSpace;
96 #endif // PLATFORM(IOS)
97 }
98
99 #if PLATFORM(WIN) || PLATFORM(IOS)
100 CGColorSpaceRef linearRGBColorSpaceRef()
101 {
102     // FIXME: Windows should be able to use linear sRGB, this is tracked by http://webkit.org/b/80000.
103     return deviceRGBColorSpaceRef();
104 }
105 #endif
106
107 void GraphicsContext::resetPlatformCTM()
108 {
109     m_state.ctm = CGContextGetCTM(platformContext());
110 #if PLATFORM(WIN)
111     m_state.userToDeviceSpaceCTM = static_cast<AffineTransform>(CGContextGetUserSpaceToDeviceSpaceTransform(platformContext())) * m_state.ctm.inverse();
112 #else
113     m_state.userToDeviceSpaceCTM = CGContextGetDefaultUserSpaceToDeviceSpaceTransform(platformContext());
114 #endif
115 }
116
117 void GraphicsContext::platformInit(CGContextRef cgContext)
118 {
119     m_data = new GraphicsContextPlatformPrivate(cgContext);
120     setPaintingDisabled(!cgContext);
121     if (cgContext) {
122         // Make sure the context starts in sync with our state.
123         setPlatformFillColor(fillColor(), fillColorSpace());
124         setPlatformStrokeColor(strokeColor(), strokeColorSpace());
125         setPlatformStrokeThickness(strokeThickness());
126         resetPlatformCTM();
127     }
128 }
129
130 void GraphicsContext::platformDestroy()
131 {
132     delete m_data;
133 }
134
135 CGContextRef GraphicsContext::platformContext() const
136 {
137     ASSERT(!paintingDisabled());
138     ASSERT(m_data->m_cgContext);
139     return m_data->m_cgContext.get();
140 }
141
142 void GraphicsContext::savePlatformState()
143 {
144     // Note: Do not use this function within this class implementation, since we want to avoid the extra
145     // save of the secondary context (in GraphicsContextPlatformPrivateCG.h).
146     CGContextSaveGState(platformContext());
147     m_data->save();
148 }
149
150 void GraphicsContext::restorePlatformState()
151 {
152     // Note: Do not use this function within this class implementation, since we want to avoid the extra
153     // restore of the secondary context (in GraphicsContextPlatformPrivateCG.h).
154     CGContextRestoreGState(platformContext());
155     m_data->restore();
156     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
157 }
158
159 void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSize& imageSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
160 {
161     RetainPtr<CGImageRef> image(imagePtr);
162
163     float currHeight = orientation.usesWidthAsHeight() ? CGImageGetWidth(image.get()) : CGImageGetHeight(image.get());
164     if (currHeight <= srcRect.y())
165         return;
166
167     CGContextRef context = platformContext();
168     GraphicsContextStateSaver stateSaver(*this);
169
170 #if PLATFORM(IOS)
171     // Anti-aliasing is on by default on the iPhone. Need to turn it off when drawing images.
172     CGContextSetShouldAntialias(context, false);
173 #endif
174
175     bool shouldUseSubimage = false;
176
177     // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image
178     // and then set a clip to the portion that we want to display.
179     FloatRect adjustedDestRect = destRect;
180
181     if (srcRect.size() != imageSize) {
182         CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context);
183         // When the image is scaled using high-quality interpolation, we create a temporary CGImage
184         // containing only the portion we want to display. We need to do this because high-quality
185         // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed
186         // into the destination rect. See <rdar://problem/6112909>.
187         shouldUseSubimage = (interpolationQuality != kCGInterpolationNone) && (srcRect.size() != destRect.size() || !getCTM().isIdentityOrTranslationOrFlipped());
188         float xScale = srcRect.width() / destRect.width();
189         float yScale = srcRect.height() / destRect.height();
190         if (shouldUseSubimage) {
191             FloatRect subimageRect = srcRect;
192             float leftPadding = srcRect.x() - floorf(srcRect.x());
193             float topPadding = srcRect.y() - floorf(srcRect.y());
194
195             subimageRect.move(-leftPadding, -topPadding);
196             adjustedDestRect.move(-leftPadding / xScale, -topPadding / yScale);
197
198             subimageRect.setWidth(ceilf(subimageRect.width() + leftPadding));
199             adjustedDestRect.setWidth(subimageRect.width() / xScale);
200
201             subimageRect.setHeight(ceilf(subimageRect.height() + topPadding));
202             adjustedDestRect.setHeight(subimageRect.height() / yScale);
203
204 #if CACHE_SUBIMAGES
205             image = subimageCache().getSubimage(image.get(), subimageRect);
206 #else
207             image = adoptCF(CGImageCreateWithImageInRect(image.get(), subimageRect));
208 #endif
209             if (currHeight < srcRect.maxY()) {
210                 ASSERT(CGImageGetHeight(image.get()) == currHeight - CGRectIntegral(srcRect).origin.y);
211                 adjustedDestRect.setHeight(CGImageGetHeight(image.get()) / yScale);
212             }
213         } else {
214             adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale));
215             adjustedDestRect.setSize(FloatSize(imageSize.width() / xScale, imageSize.height() / yScale));
216         }
217
218         if (!destRect.contains(adjustedDestRect))
219             CGContextClipToRect(context, destRect);
220     }
221
222     // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly.
223     if (!shouldUseSubimage && currHeight < imageSize.height())
224         adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / imageSize.height());
225
226 #if PLATFORM(IOS)
227     // Align to pixel boundaries
228     adjustedDestRect = roundToDevicePixels(adjustedDestRect);
229 #endif
230
231     setPlatformCompositeOperation(op, blendMode);
232
233     // ImageOrientation expects the origin to be at (0, 0)
234     CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.y());
235     adjustedDestRect.setLocation(FloatPoint());
236
237     if (orientation != DefaultImageOrientation) {
238         CGContextConcatCTM(context, orientation.transformFromDefault(adjustedDestRect.size()));
239         if (orientation.usesWidthAsHeight()) {
240             // The destination rect will have it's width and height already reversed for the orientation of
241             // the image, as it was needed for page layout, so we need to reverse it back here.
242             adjustedDestRect = FloatRect(adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.height(), adjustedDestRect.width());
243         }
244     }
245     
246     // Flip the coords.
247     CGContextTranslateCTM(context, 0, adjustedDestRect.height());
248     CGContextScaleCTM(context, 1, -1);
249
250     // Adjust the color space.
251     image = Image::imageWithColorSpace(image.get(), styleColorSpace);
252
253     // Draw the image.
254     CGContextDrawImage(context, adjustedDestRect, image.get());
255 }
256
257 static void drawPatternCallback(void* info, CGContextRef context)
258 {
259     CGImageRef image = (CGImageRef)info;
260     CGFloat height = CGImageGetHeight(image);
261 #if PLATFORM(IOS)
262     CGContextScaleCTM(context, 1, -1);
263     CGContextTranslateCTM(context, 0, -height);
264 #endif
265     CGContextDrawImage(context, GraphicsContext(context).roundToDevicePixels(FloatRect(0, 0, CGImageGetWidth(image), height)), image);
266 }
267
268 static void patternReleaseCallback(void* info)
269 {
270     auto image = static_cast<CGImageRef>(info);
271     callOnMainThread([image] {
272         CGImageRelease(image);
273     });
274 }
275
276 void GraphicsContext::drawPattern(Image& image, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
277 {
278     if (!patternTransform.isInvertible())
279         return;
280
281     CGContextRef context = platformContext();
282     GraphicsContextStateSaver stateSaver(*this);
283     CGContextClipToRect(context, destRect);
284
285     setPlatformCompositeOperation(op, blendMode);
286
287     CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height());
288     CGContextScaleCTM(context, 1, -1);
289     
290     // Compute the scaled tile size.
291     float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d());
292     
293     // 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
294     // the origin).
295     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.
296     float adjustedY = destRect.height() - (phase.y() - destRect.y() + tileRect.y() * narrowPrecisionToFloat(patternTransform.d()) + scaledTileHeight);
297
298     CGImageRef tileImage = image.nativeImageForCurrentFrame();
299     float h = CGImageGetHeight(tileImage);
300
301     RetainPtr<CGImageRef> subImage;
302 #if PLATFORM(IOS)
303     FloatSize imageSize = image.originalSize();
304 #else
305     FloatSize imageSize = image.size();
306 #endif
307     if (tileRect.size() == imageSize)
308         subImage = tileImage;
309     else {
310         // Copying a sub-image out of a partially-decoded image stops the decoding of the original image. It should never happen
311         // because sub-images are only used for border-image, which only renders when the image is fully decoded.
312         ASSERT(h == image.height());
313         subImage = adoptCF(CGImageCreateWithImageInRect(tileImage, tileRect));
314     }
315
316     // Adjust the color space.
317     subImage = Image::imageWithColorSpace(subImage.get(), styleColorSpace);
318
319     // If we need to paint gaps between tiles because we have a partially loaded image or non-zero spacing,
320     // fall back to the less efficient CGPattern-based mechanism.
321     float scaledTileWidth = tileRect.width() * narrowPrecisionToFloat(patternTransform.a());
322     float w = CGImageGetWidth(tileImage);
323     if (w == image.size().width() && h == image.size().height() && !spacing.width() && !spacing.height())
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(0));
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));
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(), strokeColorSpace());
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, fillColorSpace());
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(), strokeColorSpace());
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(wkGetUserToBaseCTM(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(wkGetUserToBaseCTM(cgContext));
596
597     RetainPtr<CGPatternRef> platformPattern = adoptCF(m_state.fillPattern->createPlatformPattern(userToBaseCTM));
598     if (!platformPattern)
599         return;
600
601     RetainPtr<CGColorSpaceRef> patternSpace = adoptCF(CGColorSpaceCreatePattern(0));
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, nullptr);
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, ColorSpace colorSpace)
832 {
833     if (paintingDisabled())
834         return;
835
836     CGContextRef context = platformContext();
837     Color oldFillColor = fillColor();
838     ColorSpace oldColorSpace = fillColorSpace();
839
840     if (oldFillColor != color || oldColorSpace != colorSpace)
841         setCGFillColor(context, color, colorSpace);
842
843     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
844     CGContextStateSaver stateSaver(context, drawOwnShadow);
845     if (drawOwnShadow) {
846         // Turn off CG shadows.
847         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
848
849         ShadowBlur contextShadow(m_state);
850         contextShadow.drawRectShadow(*this, FloatRoundedRect(rect));
851     }
852
853     CGContextFillRect(context, rect);
854     
855     if (drawOwnShadow)
856         stateSaver.restore();
857
858     if (oldFillColor != color || oldColorSpace != colorSpace)
859         setCGFillColor(context, oldFillColor, oldColorSpace);
860 }
861
862 void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace colorSpace)
863 {
864     if (paintingDisabled())
865         return;
866
867     CGContextRef context = platformContext();
868     Color oldFillColor = fillColor();
869     ColorSpace oldColorSpace = fillColorSpace();
870
871     if (oldFillColor != color || oldColorSpace != colorSpace)
872         setCGFillColor(context, color, colorSpace);
873
874     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet.
875     CGContextStateSaver stateSaver(context, drawOwnShadow);
876     if (drawOwnShadow) {
877         // Turn off CG shadows.
878         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
879
880         ShadowBlur contextShadow(m_state);
881         contextShadow.drawRectShadow(*this, rect);
882     }
883
884     const FloatRect& r = rect.rect();
885     const FloatRoundedRect::Radii& radii = rect.radii();
886     bool equalWidths = (radii.topLeft().width() == radii.topRight().width() && radii.topRight().width() == radii.bottomLeft().width() && radii.bottomLeft().width() == radii.bottomRight().width());
887     bool equalHeights = (radii.topLeft().height() == radii.bottomLeft().height() && radii.bottomLeft().height() == radii.topRight().height() && radii.topRight().height() == radii.bottomRight().height());
888     bool hasCustomFill = m_state.fillGradient || m_state.fillPattern;
889     if (!hasCustomFill && equalWidths && equalHeights && radii.topLeft().width() * 2 == r.width() && radii.topLeft().height() * 2 == r.height())
890         CGContextFillEllipseInRect(context, r);
891     else {
892         Path path;
893         path.addRoundedRect(rect);
894         fillPath(path);
895     }
896
897     if (drawOwnShadow)
898         stateSaver.restore();
899
900     if (oldFillColor != color || oldColorSpace != colorSpace)
901         setCGFillColor(context, oldFillColor, oldColorSpace);
902 }
903
904 void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
905 {
906     if (paintingDisabled())
907         return;
908
909     CGContextRef context = platformContext();
910
911     Path path;
912     path.addRect(rect);
913
914     if (!roundedHoleRect.radii().isZero())
915         path.addRoundedRect(roundedHoleRect);
916     else
917         path.addRect(roundedHoleRect.rect());
918
919     WindRule oldFillRule = fillRule();
920     Color oldFillColor = fillColor();
921     ColorSpace oldFillColorSpace = fillColorSpace();
922     
923     setFillRule(RULE_EVENODD);
924     setFillColor(color, colorSpace);
925
926     // fillRectWithRoundedHole() assumes that the edges of rect are clipped out, so we only care about shadows cast around inside the hole.
927     bool drawOwnShadow = !isAcceleratedContext() && hasBlurredShadow() && !m_state.shadowsIgnoreTransforms;
928     CGContextStateSaver stateSaver(context, drawOwnShadow);
929     if (drawOwnShadow) {
930         // Turn off CG shadows.
931         CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
932
933         ShadowBlur contextShadow(m_state);
934         contextShadow.drawInsetShadow(*this, rect, roundedHoleRect);
935     }
936
937     fillPath(path);
938
939     if (drawOwnShadow)
940         stateSaver.restore();
941     
942     setFillRule(oldFillRule);
943     setFillColor(oldFillColor, oldFillColorSpace);
944 }
945
946 void GraphicsContext::clip(const FloatRect& rect)
947 {
948     if (paintingDisabled())
949         return;
950     CGContextClipToRect(platformContext(), rect);
951     m_data->clip(rect);
952 }
953
954 void GraphicsContext::clipOut(const FloatRect& rect)
955 {
956     if (paintingDisabled())
957         return;
958
959     // FIXME: Using CGRectInfinite is much faster than getting the clip bounding box. However, due
960     // to <rdar://problem/12584492>, CGRectInfinite can't be used with an accelerated context that
961     // has certain transforms that aren't just a translation or a scale. And due to <rdar://problem/14634453>
962     // we cannot use it in for a printing context either.
963     const AffineTransform& ctm = getCTM();
964     bool canUseCGRectInfinite = !wkCGContextIsPDFContext(platformContext()) && (!isAcceleratedContext() || (!ctm.b() && !ctm.c()));
965     CGRect rects[2] = { canUseCGRectInfinite ? CGRectInfinite : CGContextGetClipBoundingBox(platformContext()), rect };
966     CGContextBeginPath(platformContext());
967     CGContextAddRects(platformContext(), rects, 2);
968     CGContextEOClip(platformContext());
969 }
970
971 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
972 {
973     if (paintingDisabled())
974         return;
975
976     // Why does clipping to an empty path do nothing?
977     // Why is this different from GraphicsContext::clip(const Path&).
978     if (path.isEmpty())
979         return;
980
981     CGContextRef context = platformContext();
982
983     CGContextBeginPath(platformContext());
984     CGContextAddPath(platformContext(), path.platformPath());
985
986     if (clipRule == RULE_EVENODD)
987         CGContextEOClip(context);
988     else
989         CGContextClip(context);
990 }
991
992 IntRect GraphicsContext::clipBounds() const
993 {
994     return enclosingIntRect(CGContextGetClipBoundingBox(platformContext()));
995 }
996
997 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
998 {
999     if (paintingDisabled())
1000         return;
1001
1002     save();
1003
1004     CGContextRef context = platformContext();
1005     CGContextSetAlpha(context, opacity);
1006     CGContextBeginTransparencyLayer(context, 0);
1007     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1008 }
1009
1010 void GraphicsContext::endPlatformTransparencyLayer()
1011 {
1012     if (paintingDisabled())
1013         return;
1014     CGContextRef context = platformContext();
1015     CGContextEndTransparencyLayer(context);
1016
1017     restore();
1018 }
1019
1020 bool GraphicsContext::supportsTransparencyLayers()
1021 {
1022     return true;
1023 }
1024
1025 static void applyShadowOffsetWorkaroundIfNeeded(const GraphicsContext& context, CGFloat& xOffset, CGFloat& yOffset)
1026 {
1027 #if PLATFORM(IOS)
1028     UNUSED_PARAM(context);
1029     UNUSED_PARAM(xOffset);
1030     UNUSED_PARAM(yOffset);
1031 #else
1032     if (context.isAcceleratedContext())
1033         return;
1034
1035     if (wkCGContextDrawsWithCorrectShadowOffsets(context.platformContext()))
1036         return;
1037
1038     // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated
1039     // to the desired integer. Also see: <rdar://problem/10056277>
1040     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
1041     if (xOffset > 0)
1042         xOffset += extraShadowOffset;
1043     else if (xOffset < 0)
1044         xOffset -= extraShadowOffset;
1045
1046     if (yOffset > 0)
1047         yOffset += extraShadowOffset;
1048     else if (yOffset < 0)
1049         yOffset -= extraShadowOffset;
1050 #endif
1051 }
1052
1053 void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
1054 {
1055     if (paintingDisabled())
1056         return;
1057     
1058     // FIXME: we could avoid the shadow setup cost when we know we'll render the shadow ourselves.
1059
1060     CGFloat xOffset = offset.width();
1061     CGFloat yOffset = offset.height();
1062     CGFloat blurRadius = blur;
1063     CGContextRef context = platformContext();
1064
1065     if (!m_state.shadowsIgnoreTransforms) {
1066         CGAffineTransform userToBaseCTM = wkGetUserToBaseCTM(context);
1067
1068         CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b;
1069         CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d;
1070         CGFloat C = B;
1071         CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d;
1072
1073         CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D)))));
1074
1075         blurRadius = blur * smallEigenvalue;
1076
1077         CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM);
1078
1079         xOffset = offsetInBaseSpace.width;
1080         yOffset = offsetInBaseSpace.height;
1081     }
1082
1083     // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp
1084     blurRadius = std::min(blurRadius, narrowPrecisionToCGFloat(1000.0));
1085
1086     applyShadowOffsetWorkaroundIfNeeded(*this, xOffset, yOffset);
1087
1088     // Check for an invalid color, as this means that the color was not set for the shadow
1089     // and we should therefore just use the default shadow color.
1090     if (!color.isValid())
1091         CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius);
1092     else
1093         CGContextSetShadowWithColor(context, CGSizeMake(xOffset, yOffset), blurRadius, cachedCGColor(color, colorSpace));
1094 }
1095
1096 void GraphicsContext::clearPlatformShadow()
1097 {
1098     if (paintingDisabled())
1099         return;
1100     CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
1101 }
1102
1103 void GraphicsContext::setMiterLimit(float limit)
1104 {
1105     if (paintingDisabled())
1106         return;
1107     CGContextSetMiterLimit(platformContext(), limit);
1108 }
1109
1110 void GraphicsContext::clearRect(const FloatRect& r)
1111 {
1112     if (paintingDisabled())
1113         return;
1114     CGContextClearRect(platformContext(), r);
1115 }
1116
1117 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1118 {
1119     if (paintingDisabled())
1120         return;
1121
1122     CGContextRef context = platformContext();
1123
1124     if (m_state.strokeGradient) {
1125         if (hasShadow()) {
1126             const float doubleLineWidth = lineWidth * 2;
1127             float adjustedWidth = ceilf(rect.width() + doubleLineWidth);
1128             float adjustedHeight = ceilf(rect.height() + doubleLineWidth);
1129             FloatSize layerSize = getCTM().mapSize(FloatSize(adjustedWidth, adjustedHeight));
1130
1131             CGLayerRef layer = CGLayerCreateWithContext(context, layerSize, 0);
1132
1133             CGContextRef layerContext = CGLayerGetContext(layer);
1134             m_state.strokeThickness = lineWidth;
1135             CGContextSetLineWidth(layerContext, lineWidth);
1136
1137             // Compensate for the line width, otherwise the layer's top-left corner would be
1138             // aligned with the rect's top-left corner. This would result in leaving pixels out of
1139             // the layer on the left and top sides.
1140             const float translationX = lineWidth - rect.x();
1141             const float translationY = lineWidth - rect.y();
1142             CGContextScaleCTM(layerContext, layerSize.width() / adjustedWidth, layerSize.height() / adjustedHeight);
1143             CGContextTranslateCTM(layerContext, translationX, translationY);
1144
1145             CGContextAddRect(layerContext, rect);
1146             CGContextReplacePathWithStrokedPath(layerContext);
1147             CGContextClip(layerContext);
1148             CGContextConcatCTM(layerContext, m_state.strokeGradient->gradientSpaceTransform());
1149             m_state.strokeGradient->paint(layerContext);
1150
1151             const float destinationX = roundf(rect.x() - lineWidth);
1152             const float destinationY = roundf(rect.y() - lineWidth);
1153             CGContextDrawLayerInRect(context, CGRectMake(destinationX, destinationY, adjustedWidth, adjustedHeight), layer);
1154             CGLayerRelease(layer);
1155         } else {
1156             CGContextStateSaver stateSaver(context);
1157             setStrokeThickness(lineWidth);
1158             CGContextAddRect(context, rect);
1159             CGContextReplacePathWithStrokedPath(context);
1160             CGContextClip(context);
1161             CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform());
1162             m_state.strokeGradient->paint(this);
1163         }
1164         return;
1165     }
1166
1167     if (m_state.strokePattern)
1168         applyStrokePattern();
1169
1170     // Using CGContextAddRect and CGContextStrokePath to stroke rect rather than
1171     // convenience functions (CGContextStrokeRect/CGContextStrokeRectWithWidth).
1172     // The convenience functions currently (in at least OSX 10.9.4) fail to
1173     // apply some attributes of the graphics state in certain cases
1174     // (as identified in https://bugs.webkit.org/show_bug.cgi?id=132948)
1175     CGContextStateSaver stateSaver(context);
1176     setStrokeThickness(lineWidth);
1177
1178     CGContextAddRect(context, rect);
1179     CGContextStrokePath(context);
1180 }
1181
1182 void GraphicsContext::setLineCap(LineCap cap)
1183 {
1184     if (paintingDisabled())
1185         return;
1186     switch (cap) {
1187     case ButtCap:
1188         CGContextSetLineCap(platformContext(), kCGLineCapButt);
1189         break;
1190     case RoundCap:
1191         CGContextSetLineCap(platformContext(), kCGLineCapRound);
1192         break;
1193     case SquareCap:
1194         CGContextSetLineCap(platformContext(), kCGLineCapSquare);
1195         break;
1196     }
1197 }
1198
1199 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1200 {
1201     if (dashOffset < 0) {
1202         float length = 0;
1203         for (size_t i = 0; i < dashes.size(); ++i)
1204             length += static_cast<float>(dashes[i]);
1205         if (length)
1206             dashOffset = fmod(dashOffset, length) + length;
1207     }
1208     CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
1209 }
1210
1211 void GraphicsContext::setLineJoin(LineJoin join)
1212 {
1213     if (paintingDisabled())
1214         return;
1215     switch (join) {
1216     case MiterJoin:
1217         CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
1218         break;
1219     case RoundJoin:
1220         CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
1221         break;
1222     case BevelJoin:
1223         CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
1224         break;
1225     }
1226 }
1227
1228 void GraphicsContext::clip(const Path& path, WindRule fillRule)
1229 {
1230     if (paintingDisabled())
1231         return;
1232     CGContextRef context = platformContext();
1233
1234     // CGContextClip does nothing if the path is empty, so in this case, we
1235     // instead clip against a zero rect to reduce the clipping region to
1236     // nothing - which is the intended behavior of clip() if the path is empty.    
1237     if (path.isEmpty())
1238         CGContextClipToRect(context, CGRectZero);
1239     else {
1240         CGContextBeginPath(context);
1241         CGContextAddPath(context, path.platformPath());
1242         if (fillRule == RULE_NONZERO)
1243             CGContextClip(context);
1244         else
1245             CGContextEOClip(context);
1246     }
1247     m_data->clip(path);
1248 }
1249
1250 void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
1251 {
1252     clip(path, fillRule);
1253 }
1254
1255 void GraphicsContext::clipOut(const Path& path)
1256 {
1257     if (paintingDisabled())
1258         return;
1259
1260     CGContextBeginPath(platformContext());
1261     CGContextAddRect(platformContext(), CGContextGetClipBoundingBox(platformContext()));
1262     if (!path.isEmpty())
1263         CGContextAddPath(platformContext(), path.platformPath());
1264     CGContextEOClip(platformContext());
1265 }
1266
1267 void GraphicsContext::scalePlatformCTM(float x, float y)
1268 {
1269     if (paintingDisabled())
1270         return;
1271     CGContextScaleCTM(platformContext(), x, y);
1272     m_data->scalePlatformCTM(x, y);
1273     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1274 }
1275
1276 void GraphicsContext::rotatePlatformCTM(float angle)
1277 {
1278     if (paintingDisabled())
1279         return;
1280     CGContextRotateCTM(platformContext(), angle);
1281     m_data->rotatePlatformCTM(angle);
1282     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1283 }
1284
1285 void GraphicsContext::translatePlatformCTM(float x, float y)
1286 {
1287     if (paintingDisabled())
1288         return;
1289     CGContextTranslateCTM(platformContext(), x, y);
1290     m_data->translatePlatformCTM(x, y);
1291     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1292 }
1293
1294 void GraphicsContext::concatPlatformCTM(const AffineTransform& transform)
1295 {
1296     if (paintingDisabled())
1297         return;
1298     CGContextConcatCTM(platformContext(), transform);
1299     m_data->concatPlatformCTM(transform);
1300     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1301 }
1302
1303 void GraphicsContext::setPlatformCTM(const AffineTransform& transform)
1304 {
1305     if (paintingDisabled())
1306         return;
1307     CGContextSetCTM(platformContext(), transform);
1308     m_data->setPlatformCTM(transform);
1309     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1310 }
1311
1312 AffineTransform GraphicsContext::getPlatformCTM(IncludeDeviceScale includeScale) const
1313 {
1314     if (paintingDisabled())
1315         return AffineTransform();
1316
1317     // The CTM usually includes the deviceScaleFactor except in WebKit 1 when the
1318     // content is non-composited, since the scale factor is integrated at a lower
1319     // level. To guarantee the deviceScale is included, we can use this CG API.
1320     if (includeScale == DefinitelyIncludeDeviceScale)
1321         return CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1322
1323     return CGContextGetCTM(platformContext());
1324 }
1325
1326 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
1327 {
1328     // It is not enough just to round to pixels in device space. The rotation part of the
1329     // affine transform matrix to device space can mess with this conversion if we have a
1330     // rotating image like the hands of the world clock widget. We just need the scale, so
1331     // we get the affine transform matrix and extract the scale.
1332
1333     if (m_data->m_userToDeviceTransformKnownToBeIdentity)
1334         return roundedIntRect(rect);
1335
1336     CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1337     if (CGAffineTransformIsIdentity(deviceMatrix)) {
1338         m_data->m_userToDeviceTransformKnownToBeIdentity = true;
1339         return roundedIntRect(rect);
1340     }
1341
1342     float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
1343     float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
1344
1345     CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
1346     CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
1347         (rect.y() + rect.height()) * deviceScaleY);
1348
1349     deviceOrigin.x = roundf(deviceOrigin.x);
1350     deviceOrigin.y = roundf(deviceOrigin.y);
1351     if (roundingMode == RoundAllSides) {
1352         deviceLowerRight.x = roundf(deviceLowerRight.x);
1353         deviceLowerRight.y = roundf(deviceLowerRight.y);
1354     } else {
1355         deviceLowerRight.x = deviceOrigin.x + roundf(rect.width() * deviceScaleX);
1356         deviceLowerRight.y = deviceOrigin.y + roundf(rect.height() * deviceScaleY);
1357     }
1358
1359     // Don't let the height or width round to 0 unless either was originally 0
1360     if (deviceOrigin.y == deviceLowerRight.y && rect.height())
1361         deviceLowerRight.y += 1;
1362     if (deviceOrigin.x == deviceLowerRight.x && rect.width())
1363         deviceLowerRight.x += 1;
1364
1365     FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
1366     FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
1367     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
1368 }
1369
1370 FloatRect GraphicsContext::computeLineBoundsForText(const FloatPoint& point, float width, bool printing)
1371 {
1372     bool dummyBool;
1373     Color dummyColor;
1374     return computeLineBoundsAndAntialiasingModeForText(point, width, printing, dummyBool, dummyColor);
1375 }
1376
1377 void GraphicsContext::drawLineForText(const FloatPoint& point, float width, bool printing, bool doubleLines)
1378 {
1379     DashArray widths;
1380     widths.append(width);
1381     widths.append(0);
1382     drawLinesForText(point, widths, printing, doubleLines);
1383 }
1384
1385 void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines)
1386 {
1387     if (paintingDisabled())
1388         return;
1389
1390     if (widths.size() <= 0)
1391         return;
1392
1393     Color localStrokeColor(strokeColor());
1394
1395     bool shouldAntialiasLine;
1396     FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, shouldAntialiasLine, localStrokeColor);
1397     bool fillColorIsNotEqualToStrokeColor = fillColor() != localStrokeColor;
1398     
1399     Vector<CGRect, 4> dashBounds;
1400     ASSERT(!(widths.size() % 2));
1401     dashBounds.reserveInitialCapacity(dashBounds.size() / 2);
1402     for (size_t i = 0; i < widths.size(); i += 2)
1403         dashBounds.append(CGRectMake(bounds.x() + widths[i], bounds.y(), widths[i+1] - widths[i], bounds.height()));
1404
1405     if (doubleLines) {
1406         // The space between double underlines is equal to the height of the underline
1407         for (size_t i = 0; i < widths.size(); i += 2)
1408             dashBounds.append(CGRectMake(bounds.x() + widths[i], bounds.y() + 2 * bounds.height(), widths[i+1] - widths[i], bounds.height()));
1409     }
1410
1411     if (fillColorIsNotEqualToStrokeColor)
1412         setCGFillColor(platformContext(), localStrokeColor, strokeColorSpace());
1413
1414     CGContextFillRects(platformContext(), dashBounds.data(), dashBounds.size());
1415
1416     if (fillColorIsNotEqualToStrokeColor)
1417         setCGFillColor(platformContext(), fillColor(), fillColorSpace());
1418 }
1419
1420 void GraphicsContext::setURLForRect(const URL& link, const IntRect& destRect)
1421 {
1422 #if !PLATFORM(IOS)
1423     if (paintingDisabled())
1424         return;
1425
1426     RetainPtr<CFURLRef> urlRef = link.createCFURL();
1427     if (!urlRef)
1428         return;
1429
1430     CGContextRef context = platformContext();
1431
1432     // Get the bounding box to handle clipping.
1433     CGRect box = CGContextGetClipBoundingBox(context);
1434
1435     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
1436     IntRect rect = destRect;
1437     rect.intersect(intBox);
1438
1439     CGPDFContextSetURLForRect(context, urlRef.get(),
1440         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
1441 #else
1442     UNUSED_PARAM(link);
1443     UNUSED_PARAM(destRect);
1444 #endif
1445 }
1446
1447 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
1448 {
1449     if (paintingDisabled())
1450         return;
1451
1452     CGInterpolationQuality quality = kCGInterpolationDefault;
1453     switch (mode) {
1454     case InterpolationDefault:
1455         quality = kCGInterpolationDefault;
1456         break;
1457     case InterpolationNone:
1458         quality = kCGInterpolationNone;
1459         break;
1460     case InterpolationLow:
1461         quality = kCGInterpolationLow;
1462         break;
1463     case InterpolationMedium:
1464         quality = kCGInterpolationMedium;
1465         break;
1466     case InterpolationHigh:
1467         quality = kCGInterpolationHigh;
1468         break;
1469     }
1470     CGContextSetInterpolationQuality(platformContext(), quality);
1471 }
1472
1473 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1474 {
1475     if (paintingDisabled())
1476         return InterpolationDefault;
1477
1478     CGInterpolationQuality quality = CGContextGetInterpolationQuality(platformContext());
1479     switch (quality) {
1480     case kCGInterpolationDefault:
1481         return InterpolationDefault;
1482     case kCGInterpolationNone:
1483         return InterpolationNone;
1484     case kCGInterpolationLow:
1485         return InterpolationLow;
1486     case kCGInterpolationMedium:
1487         return InterpolationMedium;
1488     case kCGInterpolationHigh:
1489         return InterpolationHigh;
1490     }
1491     return InterpolationDefault;
1492 }
1493
1494 void GraphicsContext::setIsCALayerContext(bool isLayerContext)
1495 {
1496     if (isLayerContext)
1497         m_data->m_contextFlags |= IsLayerCGContext;
1498     else
1499         m_data->m_contextFlags &= ~IsLayerCGContext;
1500 }
1501
1502 bool GraphicsContext::isCALayerContext() const
1503 {
1504     return m_data->m_contextFlags & IsLayerCGContext;
1505 }
1506
1507 void GraphicsContext::setIsAcceleratedContext(bool isAccelerated)
1508 {
1509     if (isAccelerated)
1510         m_data->m_contextFlags |= IsAcceleratedCGContext;
1511     else
1512         m_data->m_contextFlags &= ~IsAcceleratedCGContext;
1513 }
1514
1515 bool GraphicsContext::isAcceleratedContext() const
1516 {
1517     return m_data->m_contextFlags & IsAcceleratedCGContext;
1518 }
1519
1520 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1521 {
1522     if (paintingDisabled())
1523         return;
1524
1525     CGContextRef context = platformContext();
1526     switch (mode) {
1527     case TextModeFill:
1528         CGContextSetTextDrawingMode(context, kCGTextFill);
1529         break;
1530     case TextModeStroke:
1531         CGContextSetTextDrawingMode(context, kCGTextStroke);
1532         break;
1533     case TextModeFill | TextModeStroke:
1534         CGContextSetTextDrawingMode(context, kCGTextFillStroke);
1535         break;
1536     default:
1537         break;
1538     }
1539 }
1540
1541 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1542 {
1543     if (paintingDisabled())
1544         return;
1545     setCGStrokeColor(platformContext(), color, colorSpace);
1546 }
1547
1548 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1549 {
1550     if (paintingDisabled())
1551         return;
1552     CGContextSetLineWidth(platformContext(), std::max(thickness, 0.f));
1553 }
1554
1555 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1556 {
1557     if (paintingDisabled())
1558         return;
1559     setCGFillColor(platformContext(), color, colorSpace);
1560 }
1561
1562 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1563 {
1564     if (paintingDisabled())
1565         return;
1566     CGContextSetShouldAntialias(platformContext(), enable);
1567 }
1568
1569 void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
1570 {
1571     if (paintingDisabled())
1572         return;
1573     CGContextSetShouldSmoothFonts(platformContext(), enable);
1574 }
1575
1576 void GraphicsContext::setPlatformAlpha(float alpha)
1577 {
1578     if (paintingDisabled())
1579         return;
1580     CGContextSetAlpha(platformContext(), alpha);
1581 }
1582
1583 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator mode, BlendMode blendMode)
1584 {
1585     if (paintingDisabled())
1586         return;
1587
1588     CGBlendMode target = kCGBlendModeNormal;
1589     if (blendMode != BlendModeNormal) {
1590         switch (blendMode) {
1591         case BlendModeMultiply:
1592             target = kCGBlendModeMultiply;
1593             break;
1594         case BlendModeScreen:
1595             target = kCGBlendModeScreen;
1596             break;
1597         case BlendModeOverlay:
1598             target = kCGBlendModeOverlay;
1599             break;
1600         case BlendModeDarken:
1601             target = kCGBlendModeDarken;
1602             break;
1603         case BlendModeLighten:
1604             target = kCGBlendModeLighten;
1605             break;
1606         case BlendModeColorDodge:
1607             target = kCGBlendModeColorDodge;
1608             break;
1609         case BlendModeColorBurn:
1610             target = kCGBlendModeColorBurn;
1611             break;
1612         case BlendModeHardLight:
1613             target = kCGBlendModeHardLight;
1614             break;
1615         case BlendModeSoftLight:
1616             target = kCGBlendModeSoftLight;
1617             break;
1618         case BlendModeDifference:
1619             target = kCGBlendModeDifference;
1620             break;
1621         case BlendModeExclusion:
1622             target = kCGBlendModeExclusion;
1623             break;
1624         case BlendModeHue:
1625             target = kCGBlendModeHue;
1626             break;
1627         case BlendModeSaturation:
1628             target = kCGBlendModeSaturation;
1629             break;
1630         case BlendModeColor:
1631             target = kCGBlendModeColor;
1632             break;
1633         case BlendModeLuminosity:
1634             target = kCGBlendModeLuminosity;
1635             break;
1636         case BlendModePlusDarker:
1637             target = kCGBlendModePlusDarker;
1638             break;
1639         case BlendModePlusLighter:
1640             target = kCGBlendModePlusLighter;
1641             break;
1642         default:
1643             break;
1644         }
1645     } else {
1646         switch (mode) {
1647         case CompositeClear:
1648             target = kCGBlendModeClear;
1649             break;
1650         case CompositeCopy:
1651             target = kCGBlendModeCopy;
1652             break;
1653         case CompositeSourceOver:
1654             // kCGBlendModeNormal
1655             break;
1656         case CompositeSourceIn:
1657             target = kCGBlendModeSourceIn;
1658             break;
1659         case CompositeSourceOut:
1660             target = kCGBlendModeSourceOut;
1661             break;
1662         case CompositeSourceAtop:
1663             target = kCGBlendModeSourceAtop;
1664             break;
1665         case CompositeDestinationOver:
1666             target = kCGBlendModeDestinationOver;
1667             break;
1668         case CompositeDestinationIn:
1669             target = kCGBlendModeDestinationIn;
1670             break;
1671         case CompositeDestinationOut:
1672             target = kCGBlendModeDestinationOut;
1673             break;
1674         case CompositeDestinationAtop:
1675             target = kCGBlendModeDestinationAtop;
1676             break;
1677         case CompositeXOR:
1678             target = kCGBlendModeXOR;
1679             break;
1680         case CompositePlusDarker:
1681             target = kCGBlendModePlusDarker;
1682             break;
1683         case CompositePlusLighter:
1684             target = kCGBlendModePlusLighter;
1685             break;
1686         case CompositeDifference:
1687             target = kCGBlendModeDifference;
1688             break;
1689         }
1690     }
1691     CGContextSetBlendMode(platformContext(), target);
1692 }
1693
1694 void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
1695 {
1696     // CoreGraphics expects the base CTM of a HiDPI context to have the scale factor applied to it.
1697     // Failing to change the base level CTM will cause certain CG features, such as focus rings,
1698     // to draw with a scale factor of 1 rather than the actual scale factor.
1699     CGContextSetBaseCTM(platformContext(), CGAffineTransformScale(CGContextGetBaseCTM(platformContext()), deviceScaleFactor, deviceScaleFactor));
1700 }
1701
1702 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1703 {
1704     if (paintingDisabled())
1705         return;
1706
1707     // CGContextFillEllipseInRect only supports solid colors.
1708     if (m_state.fillGradient || m_state.fillPattern) {
1709         fillEllipseAsPath(ellipse);
1710         return;
1711     }
1712
1713     CGContextRef context = platformContext();
1714     CGContextFillEllipseInRect(context, ellipse);
1715 }
1716
1717 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1718 {
1719     if (paintingDisabled())
1720         return;
1721
1722     // CGContextStrokeEllipseInRect only supports solid colors.
1723     if (m_state.strokeGradient || m_state.strokePattern) {
1724         strokeEllipseAsPath(ellipse);
1725         return;
1726     }
1727
1728     CGContextRef context = platformContext();
1729     CGContextStrokeEllipseInRect(context, ellipse);
1730 }
1731
1732 }