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