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