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