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