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