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