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