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