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