Remove pre-Sierra-OS-specific code in WebCore
[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     double startTime = currentTime();
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 " << 1000.0 * (currentTime() - startTime) << "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         WTFLogAlways("Getting the clip bounds not yet supported with display lists");
1147         return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists.
1148     }
1149
1150     return enclosingIntRect(CGContextGetClipBoundingBox(platformContext()));
1151 }
1152
1153 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
1154 {
1155     if (paintingDisabled())
1156         return;
1157
1158     ASSERT(hasPlatformContext());
1159
1160     save();
1161
1162     CGContextRef context = platformContext();
1163     CGContextSetAlpha(context, opacity);
1164     CGContextBeginTransparencyLayer(context, 0);
1165     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1166 }
1167
1168 void GraphicsContext::endPlatformTransparencyLayer()
1169 {
1170     if (paintingDisabled())
1171         return;
1172
1173     ASSERT(hasPlatformContext());
1174
1175     CGContextRef context = platformContext();
1176     CGContextEndTransparencyLayer(context);
1177
1178     restore();
1179 }
1180
1181 bool GraphicsContext::supportsTransparencyLayers()
1182 {
1183     return true;
1184 }
1185
1186 static void applyShadowOffsetWorkaroundIfNeeded(const GraphicsContext& context, CGFloat& xOffset, CGFloat& yOffset)
1187 {
1188 #if PLATFORM(IOS) || PLATFORM(WIN)
1189     UNUSED_PARAM(context);
1190     UNUSED_PARAM(xOffset);
1191     UNUSED_PARAM(yOffset);
1192 #else
1193     if (context.isAcceleratedContext())
1194         return;
1195
1196     if (CGContextDrawsWithCorrectShadowOffsets(context.platformContext()))
1197         return;
1198
1199     // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated
1200     // to the desired integer. Also see: <rdar://problem/10056277>
1201     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
1202     if (xOffset > 0)
1203         xOffset += extraShadowOffset;
1204     else if (xOffset < 0)
1205         xOffset -= extraShadowOffset;
1206
1207     if (yOffset > 0)
1208         yOffset += extraShadowOffset;
1209     else if (yOffset < 0)
1210         yOffset -= extraShadowOffset;
1211 #endif
1212 }
1213
1214 void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, const Color& color)
1215 {
1216     if (paintingDisabled())
1217         return;
1218
1219     ASSERT(hasPlatformContext());
1220     
1221     // FIXME: we could avoid the shadow setup cost when we know we'll render the shadow ourselves.
1222
1223     CGFloat xOffset = offset.width();
1224     CGFloat yOffset = offset.height();
1225     CGFloat blurRadius = blur;
1226     CGContextRef context = platformContext();
1227
1228     if (!m_state.shadowsIgnoreTransforms) {
1229         CGAffineTransform userToBaseCTM = getUserToBaseCTM(context);
1230
1231         CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b;
1232         CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d;
1233         CGFloat C = B;
1234         CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d;
1235
1236         CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D)))));
1237
1238         blurRadius = blur * smallEigenvalue;
1239
1240         CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM);
1241
1242         xOffset = offsetInBaseSpace.width;
1243         yOffset = offsetInBaseSpace.height;
1244     }
1245
1246     // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp
1247     blurRadius = std::min(blurRadius, narrowPrecisionToCGFloat(1000.0));
1248
1249     applyShadowOffsetWorkaroundIfNeeded(*this, xOffset, yOffset);
1250
1251     // Check for an invalid color, as this means that the color was not set for the shadow
1252     // and we should therefore just use the default shadow color.
1253     if (!color.isValid())
1254         CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius);
1255     else
1256         CGContextSetShadowWithColor(context, CGSizeMake(xOffset, yOffset), blurRadius, cachedCGColor(color));
1257 }
1258
1259 void GraphicsContext::clearPlatformShadow()
1260 {
1261     if (paintingDisabled())
1262         return;
1263     CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
1264 }
1265
1266 void GraphicsContext::setMiterLimit(float limit)
1267 {
1268     if (paintingDisabled())
1269         return;
1270
1271     if (m_impl) {
1272         // Maybe this should be part of the state.
1273         m_impl->setMiterLimit(limit);
1274         return;
1275     }
1276
1277     CGContextSetMiterLimit(platformContext(), limit);
1278 }
1279
1280 void GraphicsContext::clearRect(const FloatRect& r)
1281 {
1282     if (paintingDisabled())
1283         return;
1284
1285     if (m_impl) {
1286         m_impl->clearRect(r);
1287         return;
1288     }
1289
1290     CGContextClearRect(platformContext(), r);
1291 }
1292
1293 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1294 {
1295     if (paintingDisabled())
1296         return;
1297
1298     if (m_impl) {
1299         m_impl->strokeRect(rect, lineWidth);
1300         return;
1301     }
1302
1303     CGContextRef context = platformContext();
1304
1305     if (m_state.strokeGradient) {
1306         if (hasShadow()) {
1307             const float doubleLineWidth = lineWidth * 2;
1308             float adjustedWidth = ceilf(rect.width() + doubleLineWidth);
1309             float adjustedHeight = ceilf(rect.height() + doubleLineWidth);
1310             FloatSize layerSize = getCTM().mapSize(FloatSize(adjustedWidth, adjustedHeight));
1311
1312             CGLayerRef layer = CGLayerCreateWithContext(context, layerSize, 0);
1313
1314             CGContextRef layerContext = CGLayerGetContext(layer);
1315             m_state.strokeThickness = lineWidth;
1316             CGContextSetLineWidth(layerContext, lineWidth);
1317
1318             // Compensate for the line width, otherwise the layer's top-left corner would be
1319             // aligned with the rect's top-left corner. This would result in leaving pixels out of
1320             // the layer on the left and top sides.
1321             const float translationX = lineWidth - rect.x();
1322             const float translationY = lineWidth - rect.y();
1323             CGContextScaleCTM(layerContext, layerSize.width() / adjustedWidth, layerSize.height() / adjustedHeight);
1324             CGContextTranslateCTM(layerContext, translationX, translationY);
1325
1326             CGContextAddRect(layerContext, rect);
1327             CGContextReplacePathWithStrokedPath(layerContext);
1328             CGContextClip(layerContext);
1329             CGContextConcatCTM(layerContext, m_state.strokeGradient->gradientSpaceTransform());
1330             m_state.strokeGradient->paint(layerContext);
1331
1332             const float destinationX = roundf(rect.x() - lineWidth);
1333             const float destinationY = roundf(rect.y() - lineWidth);
1334             CGContextDrawLayerInRect(context, CGRectMake(destinationX, destinationY, adjustedWidth, adjustedHeight), layer);
1335             CGLayerRelease(layer);
1336         } else {
1337             CGContextStateSaver stateSaver(context);
1338             setStrokeThickness(lineWidth);
1339             CGContextAddRect(context, rect);
1340             CGContextReplacePathWithStrokedPath(context);
1341             CGContextClip(context);
1342             CGContextConcatCTM(context, m_state.strokeGradient->gradientSpaceTransform());
1343             m_state.strokeGradient->paint(*this);
1344         }
1345         return;
1346     }
1347
1348     if (m_state.strokePattern)
1349         applyStrokePattern();
1350
1351     // Using CGContextAddRect and CGContextStrokePath to stroke rect rather than
1352     // convenience functions (CGContextStrokeRect/CGContextStrokeRectWithWidth).
1353     // The convenience functions currently (in at least OSX 10.9.4) fail to
1354     // apply some attributes of the graphics state in certain cases
1355     // (as identified in https://bugs.webkit.org/show_bug.cgi?id=132948)
1356     CGContextStateSaver stateSaver(context);
1357     setStrokeThickness(lineWidth);
1358
1359     CGContextAddRect(context, rect);
1360     CGContextStrokePath(context);
1361 }
1362
1363 void GraphicsContext::setLineCap(LineCap cap)
1364 {
1365     if (paintingDisabled())
1366         return;
1367
1368     if (m_impl) {
1369         m_impl->setLineCap(cap);
1370         return;
1371     }
1372
1373     switch (cap) {
1374     case ButtCap:
1375         CGContextSetLineCap(platformContext(), kCGLineCapButt);
1376         break;
1377     case RoundCap:
1378         CGContextSetLineCap(platformContext(), kCGLineCapRound);
1379         break;
1380     case SquareCap:
1381         CGContextSetLineCap(platformContext(), kCGLineCapSquare);
1382         break;
1383     }
1384 }
1385
1386 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1387 {
1388     if (paintingDisabled())
1389         return;
1390
1391     if (m_impl) {
1392         m_impl->setLineDash(dashes, dashOffset);
1393         return;
1394     }
1395
1396     if (dashOffset < 0) {
1397         float length = 0;
1398         for (size_t i = 0; i < dashes.size(); ++i)
1399             length += static_cast<float>(dashes[i]);
1400         if (length)
1401             dashOffset = fmod(dashOffset, length) + length;
1402     }
1403     CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
1404 }
1405
1406 void GraphicsContext::setLineJoin(LineJoin join)
1407 {
1408     if (paintingDisabled())
1409         return;
1410
1411     if (m_impl) {
1412         m_impl->setLineJoin(join);
1413         return;
1414     }
1415
1416     switch (join) {
1417     case MiterJoin:
1418         CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
1419         break;
1420     case RoundJoin:
1421         CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
1422         break;
1423     case BevelJoin:
1424         CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
1425         break;
1426     }
1427 }
1428
1429 void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
1430 {
1431     clipPath(path, fillRule);
1432 }
1433
1434 void GraphicsContext::scale(const FloatSize& size)
1435 {
1436     if (paintingDisabled())
1437         return;
1438
1439     if (m_impl) {
1440         m_impl->scale(size);
1441         return;
1442     }
1443
1444     CGContextScaleCTM(platformContext(), size.width(), size.height());
1445     m_data->scale(size);
1446     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1447 }
1448
1449 void GraphicsContext::rotate(float angle)
1450 {
1451     if (paintingDisabled())
1452         return;
1453
1454     if (m_impl) {
1455         m_impl->rotate(angle);
1456         return;
1457     }
1458
1459     CGContextRotateCTM(platformContext(), angle);
1460     m_data->rotate(angle);
1461     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1462 }
1463
1464 void GraphicsContext::translate(float x, float y)
1465 {
1466     if (paintingDisabled())
1467         return;
1468
1469     if (m_impl) {
1470         m_impl->translate(x, y);
1471         return;
1472     }
1473
1474     CGContextTranslateCTM(platformContext(), x, y);
1475     m_data->translate(x, y);
1476     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1477 }
1478
1479 void GraphicsContext::concatCTM(const AffineTransform& transform)
1480 {
1481     if (paintingDisabled())
1482         return;
1483
1484     if (m_impl) {
1485         m_impl->concatCTM(transform);
1486         return;
1487     }
1488
1489     CGContextConcatCTM(platformContext(), transform);
1490     m_data->concatCTM(transform);
1491     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1492 }
1493
1494 void GraphicsContext::setCTM(const AffineTransform& transform)
1495 {
1496     if (paintingDisabled())
1497         return;
1498
1499     if (m_impl) {
1500         WTFLogAlways("GraphicsContext::setCTM() is not compatible with recording contexts.");
1501         return;
1502     }
1503
1504     CGContextSetCTM(platformContext(), transform);
1505     m_data->setCTM(transform);
1506     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
1507 }
1508
1509 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale includeScale) const
1510 {
1511     if (paintingDisabled())
1512         return AffineTransform();
1513
1514     if (m_impl) {
1515         WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts.");
1516         return AffineTransform();
1517     }
1518
1519     // The CTM usually includes the deviceScaleFactor except in WebKit 1 when the
1520     // content is non-composited, since the scale factor is integrated at a lower
1521     // level. To guarantee the deviceScale is included, we can use this CG API.
1522     if (includeScale == DefinitelyIncludeDeviceScale)
1523         return CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1524
1525     return CGContextGetCTM(platformContext());
1526 }
1527
1528 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode roundingMode)
1529 {
1530     if (paintingDisabled())
1531         return rect;
1532
1533     if (m_impl) {
1534         WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts.");
1535         return rect;
1536     }
1537
1538     // It is not enough just to round to pixels in device space. The rotation part of the
1539     // affine transform matrix to device space can mess with this conversion if we have a
1540     // rotating image like the hands of the world clock widget. We just need the scale, so
1541     // we get the affine transform matrix and extract the scale.
1542
1543     if (m_data->m_userToDeviceTransformKnownToBeIdentity)
1544         return roundedIntRect(rect);
1545
1546     CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
1547     if (CGAffineTransformIsIdentity(deviceMatrix)) {
1548         m_data->m_userToDeviceTransformKnownToBeIdentity = true;
1549         return roundedIntRect(rect);
1550     }
1551
1552     float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
1553     float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
1554
1555     CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
1556     CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
1557         (rect.y() + rect.height()) * deviceScaleY);
1558
1559     deviceOrigin.x = roundf(deviceOrigin.x);
1560     deviceOrigin.y = roundf(deviceOrigin.y);
1561     if (roundingMode == RoundAllSides) {
1562         deviceLowerRight.x = roundf(deviceLowerRight.x);
1563         deviceLowerRight.y = roundf(deviceLowerRight.y);
1564     } else {
1565         deviceLowerRight.x = deviceOrigin.x + roundf(rect.width() * deviceScaleX);
1566         deviceLowerRight.y = deviceOrigin.y + roundf(rect.height() * deviceScaleY);
1567     }
1568
1569     // Don't let the height or width round to 0 unless either was originally 0
1570     if (deviceOrigin.y == deviceLowerRight.y && rect.height())
1571         deviceLowerRight.y += 1;
1572     if (deviceOrigin.x == deviceLowerRight.x && rect.width())
1573         deviceLowerRight.x += 1;
1574
1575     FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
1576     FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
1577     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
1578 }
1579
1580 void GraphicsContext::drawLineForText(const FloatPoint& point, float width, bool printing, bool doubleLines, StrokeStyle strokeStyle)
1581 {
1582     DashArray widths;
1583     widths.append(width);
1584     widths.append(0);
1585     drawLinesForText(point, widths, printing, doubleLines, strokeStyle);
1586 }
1587
1588 void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleLines, StrokeStyle strokeStyle)
1589 {
1590     if (paintingDisabled())
1591         return;
1592
1593     if (!widths.size())
1594         return;
1595
1596     if (m_impl) {
1597         m_impl->drawLinesForText(point, widths, printing, doubleLines, strokeThickness());
1598         return;
1599     }
1600
1601     Color localStrokeColor(strokeColor());
1602
1603     FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, localStrokeColor);
1604     bool fillColorIsNotEqualToStrokeColor = fillColor() != localStrokeColor;
1605     
1606     Vector<CGRect, 4> dashBounds;
1607     ASSERT(!(widths.size() % 2));
1608     dashBounds.reserveInitialCapacity(dashBounds.size() / 2);
1609
1610     float dashWidth = 0;
1611     switch (strokeStyle) {
1612     case DottedStroke:
1613         dashWidth = bounds.height();
1614         break;
1615     case DashedStroke:
1616         dashWidth = 2 * bounds.height();
1617         break;
1618     case SolidStroke:
1619     default:
1620         break;
1621     }
1622
1623     for (size_t i = 0; i < widths.size(); i += 2) {
1624         auto left = widths[i];
1625         auto width = widths[i+1] - widths[i];
1626         if (!dashWidth)
1627             dashBounds.append(CGRectMake(bounds.x() + left, bounds.y(), width, bounds.height()));
1628         else {
1629             auto startParticle = static_cast<unsigned>(std::ceil(left / (2 * dashWidth)));
1630             auto endParticle = static_cast<unsigned>((left + width) / (2 * dashWidth));
1631             for (unsigned j = startParticle; j < endParticle; ++j)
1632                 dashBounds.append(CGRectMake(bounds.x() + j * 2 * dashWidth, bounds.y(), dashWidth, bounds.height()));
1633         }
1634     }
1635
1636     if (doubleLines) {
1637         // The space between double underlines is equal to the height of the underline
1638         for (size_t i = 0; i < widths.size(); i += 2)
1639             dashBounds.append(CGRectMake(bounds.x() + widths[i], bounds.y() + 2 * bounds.height(), widths[i+1] - widths[i], bounds.height()));
1640     }
1641
1642     if (fillColorIsNotEqualToStrokeColor)
1643         setCGFillColor(platformContext(), localStrokeColor);
1644
1645     CGContextFillRects(platformContext(), dashBounds.data(), dashBounds.size());
1646
1647     if (fillColorIsNotEqualToStrokeColor)
1648         setCGFillColor(platformContext(), fillColor());
1649 }
1650
1651 void GraphicsContext::setURLForRect(const URL& link, const FloatRect& destRect)
1652 {
1653 #if !PLATFORM(IOS)
1654     if (paintingDisabled())
1655         return;
1656
1657     if (m_impl) {
1658         WTFLogAlways("GraphicsContext::setURLForRect() is not yet compatible with recording contexts.");
1659         return; // FIXME for display lists.
1660     }
1661
1662     RetainPtr<CFURLRef> urlRef = link.createCFURL();
1663     if (!urlRef)
1664         return;
1665
1666     CGContextRef context = platformContext();
1667
1668     FloatRect rect = destRect;
1669     // Get the bounding box to handle clipping.
1670     rect.intersect(CGContextGetClipBoundingBox(context));
1671
1672     CGPDFContextSetURLForRect(context, urlRef.get(), CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
1673 #else
1674     UNUSED_PARAM(link);
1675     UNUSED_PARAM(destRect);
1676 #endif
1677 }
1678
1679 void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality mode)
1680 {
1681     ASSERT(!paintingDisabled());
1682
1683     CGInterpolationQuality quality = kCGInterpolationDefault;
1684     switch (mode) {
1685     case InterpolationDefault:
1686         quality = kCGInterpolationDefault;
1687         break;
1688     case InterpolationNone:
1689         quality = kCGInterpolationNone;
1690         break;
1691     case InterpolationLow:
1692         quality = kCGInterpolationLow;
1693         break;
1694     case InterpolationMedium:
1695         quality = kCGInterpolationMedium;
1696         break;
1697     case InterpolationHigh:
1698         quality = kCGInterpolationHigh;
1699         break;
1700     }
1701     CGContextSetInterpolationQuality(platformContext(), quality);
1702 }
1703
1704 void GraphicsContext::setIsCALayerContext(bool isLayerContext)
1705 {
1706     if (paintingDisabled())
1707         return;
1708
1709     // FIXME
1710     if (m_impl)
1711         return;
1712
1713     if (isLayerContext)
1714         m_data->m_contextFlags |= IsLayerCGContext;
1715     else
1716         m_data->m_contextFlags &= ~IsLayerCGContext;
1717 }
1718
1719 bool GraphicsContext::isCALayerContext() const
1720 {
1721     if (paintingDisabled())
1722         return false;
1723
1724     // FIXME
1725     if (m_impl)
1726         return false;
1727
1728     return m_data->m_contextFlags & IsLayerCGContext;
1729 }
1730
1731 void GraphicsContext::setIsAcceleratedContext(bool isAccelerated)
1732 {
1733     if (paintingDisabled())
1734         return;
1735
1736     // FIXME
1737     if (m_impl)
1738         return;
1739
1740     if (isAccelerated)
1741         m_data->m_contextFlags |= IsAcceleratedCGContext;
1742     else
1743         m_data->m_contextFlags &= ~IsAcceleratedCGContext;
1744 }
1745
1746 bool GraphicsContext::isAcceleratedContext() const
1747 {
1748     if (paintingDisabled())
1749         return false;
1750
1751     // FIXME
1752     if (m_impl)
1753         return false;
1754
1755     return m_data->m_contextFlags & IsAcceleratedCGContext;
1756 }
1757
1758 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1759 {
1760     if (paintingDisabled())
1761         return;
1762
1763     ASSERT(hasPlatformContext());
1764
1765     CGContextRef context = platformContext();
1766     switch (mode) {
1767     case TextModeFill:
1768         CGContextSetTextDrawingMode(context, kCGTextFill);
1769         break;
1770     case TextModeStroke:
1771         CGContextSetTextDrawingMode(context, kCGTextStroke);
1772         break;
1773     case TextModeFill | TextModeStroke:
1774         CGContextSetTextDrawingMode(context, kCGTextFillStroke);
1775         break;
1776     default:
1777         break;
1778     }
1779 }
1780
1781 void GraphicsContext::setPlatformStrokeColor(const Color& color)
1782 {
1783     if (paintingDisabled())
1784         return;
1785
1786     ASSERT(hasPlatformContext());
1787
1788     setCGStrokeColor(platformContext(), color);
1789 }
1790
1791 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1792 {
1793     if (paintingDisabled())
1794         return;
1795
1796     ASSERT(hasPlatformContext());
1797
1798     CGContextSetLineWidth(platformContext(), std::max(thickness, 0.f));
1799 }
1800
1801 void GraphicsContext::setPlatformFillColor(const Color& color)
1802 {
1803     if (paintingDisabled())
1804         return;
1805
1806     ASSERT(hasPlatformContext());
1807
1808     setCGFillColor(platformContext(), color);
1809 }
1810
1811 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1812 {
1813     if (paintingDisabled())
1814         return;
1815
1816     ASSERT(hasPlatformContext());
1817
1818     CGContextSetShouldAntialias(platformContext(), enable);
1819 }
1820
1821 void GraphicsContext::setPlatformShouldSmoothFonts(bool enable)
1822 {
1823     if (paintingDisabled())
1824         return;
1825
1826     ASSERT(hasPlatformContext());
1827
1828     CGContextSetShouldSmoothFonts(platformContext(), enable);
1829 }
1830
1831 void GraphicsContext::setPlatformAlpha(float alpha)
1832 {
1833     if (paintingDisabled())
1834         return;
1835
1836     ASSERT(hasPlatformContext());
1837
1838     CGContextSetAlpha(platformContext(), alpha);
1839 }
1840
1841 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator compositeOperator, BlendMode blendMode)
1842 {
1843     if (paintingDisabled())
1844         return;
1845
1846     ASSERT(hasPlatformContext());
1847     CGContextSetBlendMode(platformContext(), selectCGBlendMode(compositeOperator, blendMode));
1848 }
1849
1850 void GraphicsContext::platformApplyDeviceScaleFactor(float deviceScaleFactor)
1851 {
1852     if (paintingDisabled())
1853         return;
1854
1855     ASSERT(hasPlatformContext());
1856
1857     // CoreGraphics expects the base CTM of a HiDPI context to have the scale factor applied to it.
1858     // Failing to change the base level CTM will cause certain CG features, such as focus rings,
1859     // to draw with a scale factor of 1 rather than the actual scale factor.
1860     CGContextSetBaseCTM(platformContext(), CGAffineTransformScale(CGContextGetBaseCTM(platformContext()), deviceScaleFactor, deviceScaleFactor));
1861 }
1862
1863 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1864 {
1865     if (paintingDisabled())
1866         return;
1867
1868     ASSERT(hasPlatformContext());
1869
1870     // CGContextFillEllipseInRect only supports solid colors.
1871     if (m_state.fillGradient || m_state.fillPattern) {
1872         fillEllipseAsPath(ellipse);
1873         return;
1874     }
1875
1876     CGContextRef context = platformContext();
1877     CGContextFillEllipseInRect(context, ellipse);
1878 }
1879
1880 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1881 {
1882     if (paintingDisabled())
1883         return;
1884
1885     ASSERT(hasPlatformContext());
1886
1887     // CGContextStrokeEllipseInRect only supports solid colors.
1888     if (m_state.strokeGradient || m_state.strokePattern) {
1889         strokeEllipseAsPath(ellipse);
1890         return;
1891     }
1892
1893     CGContextRef context = platformContext();
1894     CGContextStrokeEllipseInRect(context, ellipse);
1895 }
1896
1897 bool GraphicsContext::supportsInternalLinks() const
1898 {
1899     return true;
1900 }
1901
1902 void GraphicsContext::setDestinationForRect(const String& name, const FloatRect& destRect)
1903 {
1904     if (paintingDisabled())
1905         return;
1906
1907     ASSERT(hasPlatformContext());
1908
1909     CGContextRef context = platformContext();
1910
1911     FloatRect rect = destRect;
1912     rect.intersect(CGContextGetClipBoundingBox(context));
1913
1914     CGRect transformedRect = CGRectApplyAffineTransform(rect, CGContextGetCTM(context));
1915     CGPDFContextSetDestinationForRect(context, name.createCFString().get(), transformedRect);
1916 }
1917
1918 void GraphicsContext::addDestinationAtPoint(const String& name, const FloatPoint& position)
1919 {
1920     if (paintingDisabled())
1921         return;
1922
1923     ASSERT(hasPlatformContext());
1924
1925     CGContextRef context = platformContext();
1926
1927     CGPoint transformedPoint = CGPointApplyAffineTransform(position, CGContextGetCTM(context));
1928     CGPDFContextAddDestinationAtPoint(context, name.createCFString().get(), transformedPoint);
1929 }
1930
1931 }
1932
1933 #endif