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 "GraphicsContext.h"
     30 
     31 #include "AffineTransform.h"
     32 #include "FloatConversion.h"
     33 #include "GraphicsContextPlatformPrivateCG.h"
     34 #include "GraphicsContextPrivate.h"
     35 #include "ImageBuffer.h"
     36 #include "KURL.h"
     37 #include "Path.h"
     38 #include "Pattern.h"
     39 
     40 #include <CoreGraphics/CGBitmapContext.h>
     41 #include <CoreGraphics/CGPDFContext.h>
     42 #include <wtf/MathExtras.h>
     43 #include <wtf/OwnArrayPtr.h>
     44 #include <wtf/RetainPtr.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 using namespace std;
     69 
     70 namespace WebCore {
     71 
     72 static CGColorRef createCGColorWithColorSpace(const Color& color, ColorSpace colorSpace)
     73 {
     74     CGFloat components[4];
     75     color.getRGBA(components[0], components[1], components[2], components[3]);
     76 
     77     CGColorRef cgColor = 0;
     78     if (colorSpace == sRGBColorSpace)
     79         cgColor = CGColorCreate(sRGBColorSpaceRef(), components);
     80     else
     81         cgColor = CGColorCreate(deviceRGBColorSpaceRef(), components);
     82 
     83     return cgColor;
     84 }
     85 
     86 static void setCGFillColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
     87 {
     88     CGColorRef cgColor = createCGColorWithColorSpace(color, colorSpace);
     89     CGContextSetFillColorWithColor(context, cgColor);
     90     CFRelease(cgColor);
     91 }
     92 
     93 static void setCGStrokeColor(CGContextRef context, const Color& color, ColorSpace colorSpace)
     94 {
     95     CGColorRef cgColor = createCGColorWithColorSpace(color, colorSpace);
     96     CGContextSetStrokeColorWithColor(context, cgColor);
     97     CFRelease(cgColor);
     98 }
     99 
    100 static void setCGFillColorSpace(CGContextRef context, ColorSpace colorSpace)
    101 {
    102     switch (colorSpace) {
    103     case DeviceColorSpace:
    104         break;
    105     case sRGBColorSpace:
    106         CGContextSetFillColorSpace(context, sRGBColorSpaceRef());
    107         break;
    108     default:
    109         ASSERT_NOT_REACHED();
    110         break;
    111     }
    112 }
    113 
    114 static void setCGStrokeColorSpace(CGContextRef context, ColorSpace colorSpace)
    115 {
    116     switch (colorSpace) {
    117     case DeviceColorSpace:
    118         break;
    119     case sRGBColorSpace:
    120         CGContextSetStrokeColorSpace(context, sRGBColorSpaceRef());
    121         break;
    122     default:
    123         ASSERT_NOT_REACHED();
    124         break;
    125     }
    126 }
    127 
    128 CGColorSpaceRef deviceRGBColorSpaceRef()
    129 {
    130     static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
    131     return deviceSpace;
    132 }
    133 
    134 CGColorSpaceRef sRGBColorSpaceRef()
    135 {
    136     // FIXME: Windows should be able to use kCGColorSpaceSRGB, this is tracked by http://webkit.org/b/31363.
    137 #if PLATFORM(WIN) || defined(BUILDING_ON_TIGER)
    138     return deviceRGBColorSpaceRef();
    139 #else
    140     static CGColorSpaceRef sRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
    141     return sRGBSpace;
    142 #endif
    143 }
    144 
    145 GraphicsContext::GraphicsContext(CGContextRef cgContext)
    146     : m_common(createGraphicsContextPrivate())
    147     , m_data(new GraphicsContextPlatformPrivate(cgContext))
    148 {
    149     setPaintingDisabled(!cgContext);
    150     if (cgContext) {
    151         // Make sure the context starts in sync with our state.
    152         setPlatformFillColor(fillColor(), fillColorSpace());
    153         setPlatformStrokeColor(strokeColor(), strokeColorSpace());
    154     }
    155 }
    156 
    157 GraphicsContext::~GraphicsContext()
    158 {
    159     destroyGraphicsContextPrivate(m_common);
    160     delete m_data;
    161 }
    162 
    163 CGContextRef GraphicsContext::platformContext() const
    164 {
    165     ASSERT(!paintingDisabled());
    166     ASSERT(m_data->m_cgContext);
    167     return m_data->m_cgContext.get();
    168 }
    169 
    170 void GraphicsContext::savePlatformState()
    171 {
    172     // Note: Do not use this function within this class implementation, since we want to avoid the extra
    173     // save of the secondary context (in GraphicsContextPlatformPrivateCG.h).
    174     CGContextSaveGState(platformContext());
    175     m_data->save();
    176 }
    177 
    178 void GraphicsContext::restorePlatformState()
    179 {
    180     // Note: Do not use this function within this class implementation, since we want to avoid the extra
    181     // restore of the secondary context (in GraphicsContextPlatformPrivateCG.h).
    182     CGContextRestoreGState(platformContext());
    183     m_data->restore();
    184     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    185 }
    186 
    187 // Draws a filled rectangle with a stroked border.
    188 void GraphicsContext::drawRect(const IntRect& rect)
    189 {
    190     // FIXME: this function does not handle patterns and gradients
    191     // like drawPath does, it probably should.
    192     if (paintingDisabled())
    193         return;
    194 
    195     CGContextRef context = platformContext();
    196 
    197     CGContextFillRect(context, rect);
    198 
    199     if (strokeStyle() != NoStroke) {
    200         // We do a fill of four rects to simulate the stroke of a border.
    201         Color oldFillColor = fillColor();
    202         if (oldFillColor != strokeColor())
    203             setCGFillColor(context, strokeColor(), strokeColorSpace());
    204         CGRect rects[4] = {
    205             FloatRect(rect.x(), rect.y(), rect.width(), 1),
    206             FloatRect(rect.x(), rect.bottom() - 1, rect.width(), 1),
    207             FloatRect(rect.x(), rect.y() + 1, 1, rect.height() - 2),
    208             FloatRect(rect.right() - 1, rect.y() + 1, 1, rect.height() - 2)
    209         };
    210         CGContextFillRects(context, rects, 4);
    211         if (oldFillColor != strokeColor())
    212             setCGFillColor(context, oldFillColor, fillColorSpace());
    213     }
    214 }
    215 
    216 // This is only used to draw borders.
    217 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
    218 {
    219     if (paintingDisabled())
    220         return;
    221 
    222     if (strokeStyle() == NoStroke)
    223         return;
    224 
    225     float width = strokeThickness();
    226 
    227     FloatPoint p1 = point1;
    228     FloatPoint p2 = point2;
    229     bool isVerticalLine = (p1.x() == p2.x());
    230 
    231     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
    232     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
    233     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
    234     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
    235     if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
    236         if (isVerticalLine) {
    237             p1.move(0, width);
    238             p2.move(0, -width);
    239         } else {
    240             p1.move(width, 0);
    241             p2.move(-width, 0);
    242         }
    243     }
    244 
    245     if (((int)width) % 2) {
    246         if (isVerticalLine) {
    247             // We're a vertical line.  Adjust our x.
    248             p1.move(0.5f, 0.0f);
    249             p2.move(0.5f, 0.0f);
    250         } else {
    251             // We're a horizontal line. Adjust our y.
    252             p1.move(0.0f, 0.5f);
    253             p2.move(0.0f, 0.5f);
    254         }
    255     }
    256 
    257     int patWidth = 0;
    258     switch (strokeStyle()) {
    259     case NoStroke:
    260     case SolidStroke:
    261         break;
    262     case DottedStroke:
    263         patWidth = (int)width;
    264         break;
    265     case DashedStroke:
    266         patWidth = 3 * (int)width;
    267         break;
    268     }
    269 
    270     CGContextRef context = platformContext();
    271 
    272     if (shouldAntialias())
    273         CGContextSetShouldAntialias(context, false);
    274 
    275     if (patWidth) {
    276         CGContextSaveGState(context);
    277 
    278         // Do a rect fill of our endpoints.  This ensures we always have the
    279         // appearance of being a border.  We then draw the actual dotted/dashed line.
    280         setCGFillColor(context, strokeColor(), strokeColorSpace());  // The save/restore make it safe to mutate the fill color here without setting it back to the old color.
    281         if (isVerticalLine) {
    282             CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width));
    283             CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width));
    284         } else {
    285             CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width));
    286             CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width));
    287         }
    288 
    289         // Example: 80 pixels with a width of 30 pixels.
    290         // Remainder is 20.  The maximum pixels of line we could paint
    291         // will be 50 pixels.
    292         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
    293         int remainder = distance % patWidth;
    294         int coverage = distance - remainder;
    295         int numSegments = coverage / patWidth;
    296 
    297         float patternOffset = 0.0f;
    298         // Special case 1px dotted borders for speed.
    299         if (patWidth == 1)
    300             patternOffset = 1.0f;
    301         else {
    302             bool evenNumberOfSegments = !(numSegments % 2);
    303             if (remainder)
    304                 evenNumberOfSegments = !evenNumberOfSegments;
    305             if (evenNumberOfSegments) {
    306                 if (remainder) {
    307                     patternOffset += patWidth - remainder;
    308                     patternOffset += remainder / 2;
    309                 } else
    310                     patternOffset = patWidth / 2;
    311             } else {
    312                 if (remainder)
    313                     patternOffset = (patWidth - remainder)/2;
    314             }
    315         }
    316 
    317         const CGFloat dottedLine[2] = { patWidth, patWidth };
    318         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
    319     }
    320 
    321     CGContextBeginPath(context);
    322     CGContextMoveToPoint(context, p1.x(), p1.y());
    323     CGContextAddLineToPoint(context, p2.x(), p2.y());
    324 
    325     CGContextStrokePath(context);
    326 
    327     if (patWidth)
    328         CGContextRestoreGState(context);
    329 
    330     if (shouldAntialias())
    331         CGContextSetShouldAntialias(context, true);
    332 }
    333 
    334 // This method is only used to draw the little circles used in lists.
    335 void GraphicsContext::drawEllipse(const IntRect& rect)
    336 {
    337     // FIXME: CG added CGContextAddEllipseinRect in Tiger, so we should be able to quite easily draw an ellipse.
    338     // This code can only handle circles, not ellipses. But khtml only
    339     // uses it for circles.
    340     ASSERT(rect.width() == rect.height());
    341 
    342     if (paintingDisabled())
    343         return;
    344 
    345     CGContextRef context = platformContext();
    346     CGContextBeginPath(context);
    347     float r = (float)rect.width() / 2;
    348     CGContextAddArc(context, rect.x() + r, rect.y() + r, r, 0.0f, 2.0f * piFloat, 0);
    349     CGContextClosePath(context);
    350 
    351     drawPath();
    352 }
    353 
    354 
    355 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
    356 {
    357     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f)
    358         return;
    359 
    360     CGContextRef context = platformContext();
    361     CGContextSaveGState(context);
    362     CGContextBeginPath(context);
    363     CGContextSetShouldAntialias(context, false);
    364 
    365     int x = rect.x();
    366     int y = rect.y();
    367     float w = (float)rect.width();
    368     float h = (float)rect.height();
    369     float scaleFactor = h / w;
    370     float reverseScaleFactor = w / h;
    371 
    372     if (w != h)
    373         scale(FloatSize(1, scaleFactor));
    374 
    375     float hRadius = w / 2;
    376     float vRadius = h / 2;
    377     float fa = startAngle;
    378     float falen =  fa + angleSpan;
    379     float start = -fa * piFloat / 180.0f;
    380     float end = -falen * piFloat / 180.0f;
    381     CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true);
    382 
    383     if (w != h)
    384         scale(FloatSize(1, reverseScaleFactor));
    385 
    386     float width = strokeThickness();
    387     int patWidth = 0;
    388 
    389     switch (strokeStyle()) {
    390     case DottedStroke:
    391         patWidth = (int)(width / 2);
    392         break;
    393     case DashedStroke:
    394         patWidth = 3 * (int)(width / 2);
    395         break;
    396     default:
    397         break;
    398     }
    399 
    400     if (patWidth) {
    401         // Example: 80 pixels with a width of 30 pixels.
    402         // Remainder is 20.  The maximum pixels of line we could paint
    403         // will be 50 pixels.
    404         int distance;
    405         if (hRadius == vRadius)
    406             distance = static_cast<int>((piFloat * hRadius) / 2.0f);
    407         else // We are elliptical and will have to estimate the distance
    408             distance = static_cast<int>((piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0f)) / 2.0f);
    409 
    410         int remainder = distance % patWidth;
    411         int coverage = distance - remainder;
    412         int numSegments = coverage / patWidth;
    413 
    414         float patternOffset = 0.0f;
    415         // Special case 1px dotted borders for speed.
    416         if (patWidth == 1)
    417             patternOffset = 1.0f;
    418         else {
    419             bool evenNumberOfSegments = !(numSegments % 2);
    420             if (remainder)
    421                 evenNumberOfSegments = !evenNumberOfSegments;
    422             if (evenNumberOfSegments) {
    423                 if (remainder) {
    424                     patternOffset += patWidth - remainder;
    425                     patternOffset += remainder / 2.0f;
    426                 } else
    427                     patternOffset = patWidth / 2.0f;
    428             } else {
    429                 if (remainder)
    430                     patternOffset = (patWidth - remainder) / 2.0f;
    431             }
    432         }
    433 
    434         const CGFloat dottedLine[2] = { patWidth, patWidth };
    435         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
    436     }
    437 
    438     CGContextStrokePath(context);
    439 
    440     CGContextRestoreGState(context);
    441 }
    442 
    443 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool antialiased)
    444 {
    445     if (paintingDisabled())
    446         return;
    447 
    448     if (npoints <= 1)
    449         return;
    450 
    451     CGContextRef context = platformContext();
    452 
    453     if (antialiased != shouldAntialias())
    454         CGContextSetShouldAntialias(context, antialiased);
    455 
    456     CGContextBeginPath(context);
    457     CGContextMoveToPoint(context, points[0].x(), points[0].y());
    458     for (size_t i = 1; i < npoints; i++)
    459         CGContextAddLineToPoint(context, points[i].x(), points[i].y());
    460     CGContextClosePath(context);
    461 
    462     drawPath();
    463 
    464     if (antialiased != shouldAntialias())
    465         CGContextSetShouldAntialias(context, shouldAntialias());
    466 }
    467 
    468 void GraphicsContext::applyStrokePattern()
    469 {
    470     CGContextRef cgContext = platformContext();
    471 
    472     RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_common->state.strokePattern->createPlatformPattern(getCTM()));
    473     if (!platformPattern)
    474         return;
    475 
    476     RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0));
    477     CGContextSetStrokeColorSpace(cgContext, patternSpace.get());
    478 
    479     const CGFloat patternAlpha = 1;
    480     CGContextSetStrokePattern(cgContext, platformPattern.get(), &patternAlpha);
    481 }
    482 
    483 void GraphicsContext::applyFillPattern()
    484 {
    485     CGContextRef cgContext = platformContext();
    486 
    487     RetainPtr<CGPatternRef> platformPattern(AdoptCF, m_common->state.fillPattern->createPlatformPattern(getCTM()));
    488     if (!platformPattern)
    489         return;
    490 
    491     RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0));
    492     CGContextSetFillColorSpace(cgContext, patternSpace.get());
    493 
    494     const CGFloat patternAlpha = 1;
    495     CGContextSetFillPattern(cgContext, platformPattern.get(), &patternAlpha);
    496 }
    497 
    498 static inline bool calculateDrawingMode(const GraphicsContextState& state, CGPathDrawingMode& mode)
    499 {
    500     bool shouldFill = state.fillPattern || state.fillColor.alpha();
    501     bool shouldStroke = state.strokePattern || (state.strokeStyle != NoStroke && state.strokeColor.alpha());
    502     bool useEOFill = state.fillRule == RULE_EVENODD;
    503 
    504     if (shouldFill) {
    505         if (shouldStroke) {
    506             if (useEOFill)
    507                 mode = kCGPathEOFillStroke;
    508             else
    509                 mode = kCGPathFillStroke;
    510         } else { // fill, no stroke
    511             if (useEOFill)
    512                 mode = kCGPathEOFill;
    513             else
    514                 mode = kCGPathFill;
    515         }
    516     } else {
    517         // Setting mode to kCGPathStroke even if shouldStroke is false. In that case, we return false and mode will not be used,
    518         // but the compiler will not complain about an uninitialized variable.
    519         mode = kCGPathStroke;
    520     }
    521 
    522     return shouldFill || shouldStroke;
    523 }
    524 
    525 void GraphicsContext::drawPath()
    526 {
    527     if (paintingDisabled())
    528         return;
    529 
    530     CGContextRef context = platformContext();
    531     const GraphicsContextState& state = m_common->state;
    532 
    533     if (state.fillGradient || state.strokeGradient) {
    534         // We don't have any optimized way to fill & stroke a path using gradients
    535         fillPath();
    536         strokePath();
    537         return;
    538     }
    539 
    540     if (state.fillPattern)
    541         applyFillPattern();
    542     if (state.strokePattern)
    543         applyStrokePattern();
    544 
    545     CGPathDrawingMode drawingMode;
    546     if (calculateDrawingMode(state, drawingMode))
    547         CGContextDrawPath(context, drawingMode);
    548 }
    549 
    550 static inline void fillPathWithFillRule(CGContextRef context, WindRule fillRule)
    551 {
    552     if (fillRule == RULE_EVENODD)
    553         CGContextEOFillPath(context);
    554     else
    555         CGContextFillPath(context);
    556 }
    557 
    558 void GraphicsContext::fillPath()
    559 {
    560     if (paintingDisabled())
    561         return;
    562 
    563     CGContextRef context = platformContext();
    564 
    565     // FIXME: Is this helpful and correct in the fillPattern and fillGradient cases?
    566     setCGFillColorSpace(context, m_common->state.fillColorSpace);
    567 
    568     if (m_common->state.fillGradient) {
    569         CGContextSaveGState(context);
    570         if (fillRule() == RULE_EVENODD)
    571             CGContextEOClip(context);
    572         else
    573             CGContextClip(context);
    574         CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform());
    575         m_common->state.fillGradient->paint(this);
    576         CGContextRestoreGState(context);
    577         return;
    578     }
    579 
    580     if (m_common->state.fillPattern)
    581         applyFillPattern();
    582     fillPathWithFillRule(context, fillRule());
    583 }
    584 
    585 void GraphicsContext::strokePath()
    586 {
    587     if (paintingDisabled())
    588         return;
    589 
    590     CGContextRef context = platformContext();
    591 
    592     // FIXME: Is this helpful and correct in the strokePattern and strokeGradient cases?
    593     setCGStrokeColorSpace(context, m_common->state.strokeColorSpace);
    594 
    595     if (m_common->state.strokeGradient) {
    596         CGContextSaveGState(context);
    597         CGContextReplacePathWithStrokedPath(context);
    598         CGContextClip(context);
    599         CGContextConcatCTM(context, m_common->state.strokeGradient->gradientSpaceTransform());
    600         m_common->state.strokeGradient->paint(this);
    601         CGContextRestoreGState(context);
    602         return;
    603     }
    604 
    605     if (m_common->state.strokePattern)
    606         applyStrokePattern();
    607     CGContextStrokePath(context);
    608 }
    609 
    610 void GraphicsContext::fillRect(const FloatRect& rect)
    611 {
    612     if (paintingDisabled())
    613         return;
    614 
    615     CGContextRef context = platformContext();
    616 
    617     // FIXME: Is this helpful and correct in the fillPattern and fillGradient cases?
    618     setCGFillColorSpace(context, m_common->state.fillColorSpace);
    619 
    620     if (m_common->state.fillGradient) {
    621         CGContextSaveGState(context);
    622         CGContextClipToRect(context, rect);
    623         CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform());
    624         m_common->state.fillGradient->paint(this);
    625         CGContextRestoreGState(context);
    626         return;
    627     }
    628 
    629     if (m_common->state.fillPattern)
    630         applyFillPattern();
    631     CGContextFillRect(context, rect);
    632 }
    633 
    634 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
    635 {
    636     if (paintingDisabled())
    637         return;
    638     CGContextRef context = platformContext();
    639     Color oldFillColor = fillColor();
    640     ColorSpace oldColorSpace = fillColorSpace();
    641 
    642     if (oldFillColor != color || oldColorSpace != colorSpace)
    643       setCGFillColor(context, color, colorSpace);
    644 
    645     CGContextFillRect(context, rect);
    646 
    647     if (oldFillColor != color || oldColorSpace != colorSpace)
    648       setCGFillColor(context, oldFillColor, oldColorSpace);
    649 }
    650 
    651 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
    652 {
    653     if (paintingDisabled())
    654         return;
    655 
    656     CGContextRef context = platformContext();
    657     Color oldFillColor = fillColor();
    658     ColorSpace oldColorSpace = fillColorSpace();
    659 
    660     if (oldFillColor != color || oldColorSpace != colorSpace)
    661         setCGFillColor(context, color, colorSpace);
    662 
    663     addPath(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
    664     fillPath();
    665 
    666     if (oldFillColor != color || oldColorSpace != colorSpace)
    667         setCGFillColor(context, oldFillColor, oldColorSpace);
    668 }
    669 
    670 void GraphicsContext::clip(const FloatRect& rect)
    671 {
    672     if (paintingDisabled())
    673         return;
    674     CGContextClipToRect(platformContext(), rect);
    675     m_data->clip(rect);
    676 }
    677 
    678 void GraphicsContext::clipOut(const IntRect& rect)
    679 {
    680     if (paintingDisabled())
    681         return;
    682 
    683     CGRect rects[2] = { CGContextGetClipBoundingBox(platformContext()), rect };
    684     CGContextBeginPath(platformContext());
    685     CGContextAddRects(platformContext(), rects, 2);
    686     CGContextEOClip(platformContext());
    687 }
    688 
    689 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
    690 {
    691     if (paintingDisabled())
    692         return;
    693 
    694     CGContextBeginPath(platformContext());
    695     CGContextAddRect(platformContext(), CGContextGetClipBoundingBox(platformContext()));
    696     CGContextAddEllipseInRect(platformContext(), rect);
    697     CGContextEOClip(platformContext());
    698 }
    699 
    700 void GraphicsContext::clipPath(WindRule clipRule)
    701 {
    702     if (paintingDisabled())
    703         return;
    704 
    705     CGContextRef context = platformContext();
    706 
    707     if (!CGContextIsPathEmpty(context)) {
    708         if (clipRule == RULE_EVENODD)
    709             CGContextEOClip(context);
    710         else
    711             CGContextClip(context);
    712     }
    713 }
    714 
    715 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
    716 {
    717     if (paintingDisabled())
    718         return;
    719 
    720     clip(rect);
    721     CGContextRef context = platformContext();
    722 
    723     // Add outer ellipse
    724     CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
    725     // Add inner ellipse.
    726     CGContextAddEllipseInRect(context, CGRectMake(rect.x() + thickness, rect.y() + thickness,
    727         rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
    728 
    729     CGContextEOClip(context);
    730 }
    731 
    732 void GraphicsContext::clipToImageBuffer(const FloatRect& rect, const ImageBuffer* imageBuffer)
    733 {
    734     if (paintingDisabled())
    735         return;
    736 
    737     CGContextTranslateCTM(platformContext(), rect.x(), rect.y() + rect.height());
    738     CGContextScaleCTM(platformContext(), 1, -1);
    739     CGContextClipToMask(platformContext(), FloatRect(FloatPoint(), rect.size()), imageBuffer->image()->getCGImageRef());
    740     CGContextScaleCTM(platformContext(), 1, -1);
    741     CGContextTranslateCTM(platformContext(), -rect.x(), -rect.y() - rect.height());
    742 }
    743 
    744 void GraphicsContext::beginTransparencyLayer(float opacity)
    745 {
    746     if (paintingDisabled())
    747         return;
    748     CGContextRef context = platformContext();
    749     CGContextSaveGState(context);
    750     CGContextSetAlpha(context, opacity);
    751     CGContextBeginTransparencyLayer(context, 0);
    752     m_data->beginTransparencyLayer();
    753     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    754 }
    755 
    756 void GraphicsContext::endTransparencyLayer()
    757 {
    758     if (paintingDisabled())
    759         return;
    760     CGContextRef context = platformContext();
    761     CGContextEndTransparencyLayer(context);
    762     CGContextRestoreGState(context);
    763     m_data->endTransparencyLayer();
    764     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    765 }
    766 
    767 void GraphicsContext::setPlatformShadow(const IntSize& offset, int blur, const Color& color, ColorSpace colorSpace)
    768 {
    769     if (paintingDisabled())
    770         return;
    771     CGFloat xOffset = offset.width();
    772     CGFloat yOffset = offset.height();
    773     CGFloat blurRadius = blur;
    774     CGContextRef context = platformContext();
    775 
    776     if (!m_common->state.shadowsIgnoreTransforms) {
    777         CGAffineTransform userToBaseCTM = wkGetUserToBaseCTM(context);
    778 
    779         CGFloat A = userToBaseCTM.a * userToBaseCTM.a + userToBaseCTM.b * userToBaseCTM.b;
    780         CGFloat B = userToBaseCTM.a * userToBaseCTM.c + userToBaseCTM.b * userToBaseCTM.d;
    781         CGFloat C = B;
    782         CGFloat D = userToBaseCTM.c * userToBaseCTM.c + userToBaseCTM.d * userToBaseCTM.d;
    783 
    784         CGFloat smallEigenvalue = narrowPrecisionToCGFloat(sqrt(0.5 * ((A + D) - sqrt(4 * B * C + (A - D) * (A - D)))));
    785 
    786         // Extreme "blur" values can make text drawing crash or take crazy long times, so clamp
    787         blurRadius = min(blur * smallEigenvalue, narrowPrecisionToCGFloat(1000.0));
    788 
    789         CGSize offsetInBaseSpace = CGSizeApplyAffineTransform(offset, userToBaseCTM);
    790 
    791         xOffset = offsetInBaseSpace.width;
    792         yOffset = offsetInBaseSpace.height;
    793     }
    794 
    795     // Work around <rdar://problem/5539388> by ensuring that the offsets will get truncated
    796     // to the desired integer.
    797     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
    798     if (xOffset > 0)
    799         xOffset += extraShadowOffset;
    800     else if (xOffset < 0)
    801         xOffset -= extraShadowOffset;
    802 
    803     if (yOffset > 0)
    804         yOffset += extraShadowOffset;
    805     else if (yOffset < 0)
    806         yOffset -= extraShadowOffset;
    807 
    808     // Check for an invalid color, as this means that the color was not set for the shadow
    809     // and we should therefore just use the default shadow color.
    810     if (!color.isValid())
    811         CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius);
    812     else {
    813         RetainPtr<CGColorRef> colorCG(AdoptCF, createCGColorWithColorSpace(color, colorSpace));
    814         CGContextSetShadowWithColor(context,
    815                                     CGSizeMake(xOffset, yOffset),
    816                                     blurRadius,
    817                                     colorCG.get());
    818     }
    819 }
    820 
    821 void GraphicsContext::clearPlatformShadow()
    822 {
    823     if (paintingDisabled())
    824         return;
    825     CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
    826 }
    827 
    828 void GraphicsContext::setMiterLimit(float limit)
    829 {
    830     if (paintingDisabled())
    831         return;
    832     CGContextSetMiterLimit(platformContext(), limit);
    833 }
    834 
    835 void GraphicsContext::setAlpha(float alpha)
    836 {
    837     if (paintingDisabled())
    838         return;
    839     CGContextSetAlpha(platformContext(), alpha);
    840 }
    841 
    842 void GraphicsContext::clearRect(const FloatRect& r)
    843 {
    844     if (paintingDisabled())
    845         return;
    846     CGContextClearRect(platformContext(), r);
    847 }
    848 
    849 void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth)
    850 {
    851     if (paintingDisabled())
    852         return;
    853 
    854     CGContextRef context = platformContext();
    855 
    856     // FIXME: Is this helpful and correct in the strokePattern and strokeGradient cases?
    857     setCGStrokeColorSpace(context, m_common->state.strokeColorSpace);
    858 
    859     if (m_common->state.strokeGradient) {
    860         CGContextSaveGState(context);
    861         setStrokeThickness(lineWidth);
    862         CGContextAddRect(context, r);
    863         CGContextReplacePathWithStrokedPath(context);
    864         CGContextClip(context);
    865         m_common->state.strokeGradient->paint(this);
    866         CGContextRestoreGState(context);
    867         return;
    868     }
    869 
    870     if (m_common->state.strokePattern)
    871         applyStrokePattern();
    872     CGContextStrokeRectWithWidth(context, r, lineWidth);
    873 }
    874 
    875 void GraphicsContext::setLineCap(LineCap cap)
    876 {
    877     if (paintingDisabled())
    878         return;
    879     switch (cap) {
    880     case ButtCap:
    881         CGContextSetLineCap(platformContext(), kCGLineCapButt);
    882         break;
    883     case RoundCap:
    884         CGContextSetLineCap(platformContext(), kCGLineCapRound);
    885         break;
    886     case SquareCap:
    887         CGContextSetLineCap(platformContext(), kCGLineCapSquare);
    888         break;
    889     }
    890 }
    891 
    892 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
    893 {
    894     CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
    895 }
    896 
    897 void GraphicsContext::setLineJoin(LineJoin join)
    898 {
    899     if (paintingDisabled())
    900         return;
    901     switch (join) {
    902     case MiterJoin:
    903         CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
    904         break;
    905     case RoundJoin:
    906         CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
    907         break;
    908     case BevelJoin:
    909         CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
    910         break;
    911     }
    912 }
    913 
    914 void GraphicsContext::beginPath()
    915 {
    916     CGContextBeginPath(platformContext());
    917 }
    918 
    919 void GraphicsContext::addPath(const Path& path)
    920 {
    921     CGContextAddPath(platformContext(), path.platformPath());
    922 }
    923 
    924 void GraphicsContext::clip(const Path& path)
    925 {
    926     if (paintingDisabled())
    927         return;
    928     CGContextRef context = platformContext();
    929     CGContextBeginPath(context);
    930     CGContextAddPath(context, path.platformPath());
    931     CGContextClip(context);
    932     m_data->clip(path);
    933 }
    934 
    935 void GraphicsContext::canvasClip(const Path& path)
    936 {
    937     clip(path);
    938 }
    939 
    940 void GraphicsContext::clipOut(const Path& path)
    941 {
    942     if (paintingDisabled())
    943         return;
    944 
    945     CGContextBeginPath(platformContext());
    946     CGContextAddRect(platformContext(), CGContextGetClipBoundingBox(platformContext()));
    947     CGContextAddPath(platformContext(), path.platformPath());
    948     CGContextEOClip(platformContext());
    949 }
    950 
    951 void GraphicsContext::scale(const FloatSize& size)
    952 {
    953     if (paintingDisabled())
    954         return;
    955     CGContextScaleCTM(platformContext(), size.width(), size.height());
    956     m_data->scale(size);
    957     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    958 }
    959 
    960 void GraphicsContext::rotate(float angle)
    961 {
    962     if (paintingDisabled())
    963         return;
    964     CGContextRotateCTM(platformContext(), angle);
    965     m_data->rotate(angle);
    966     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    967 }
    968 
    969 void GraphicsContext::translate(float x, float y)
    970 {
    971     if (paintingDisabled())
    972         return;
    973     CGContextTranslateCTM(platformContext(), x, y);
    974     m_data->translate(x, y);
    975     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    976 }
    977 
    978 void GraphicsContext::concatCTM(const AffineTransform& transform)
    979 {
    980     if (paintingDisabled())
    981         return;
    982     CGContextConcatCTM(platformContext(), transform);
    983     m_data->concatCTM(transform);
    984     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
    985 }
    986 
    987 AffineTransform GraphicsContext::getCTM() const
    988 {
    989     CGAffineTransform t = CGContextGetCTM(platformContext());
    990     return AffineTransform(t.a, t.b, t.c, t.d, t.tx, t.ty);
    991 }
    992 
    993 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
    994 {
    995     // It is not enough just to round to pixels in device space. The rotation part of the
    996     // affine transform matrix to device space can mess with this conversion if we have a
    997     // rotating image like the hands of the world clock widget. We just need the scale, so
    998     // we get the affine transform matrix and extract the scale.
    999 
   1000     if (m_data->m_userToDeviceTransformKnownToBeIdentity)
   1001         return rect;
   1002 
   1003     CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
   1004     if (CGAffineTransformIsIdentity(deviceMatrix)) {
   1005         m_data->m_userToDeviceTransformKnownToBeIdentity = true;
   1006         return rect;
   1007     }
   1008 
   1009     float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
   1010     float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
   1011 
   1012     CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
   1013     CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
   1014         (rect.y() + rect.height()) * deviceScaleY);
   1015 
   1016     deviceOrigin.x = roundf(deviceOrigin.x);
   1017     deviceOrigin.y = roundf(deviceOrigin.y);
   1018     deviceLowerRight.x = roundf(deviceLowerRight.x);
   1019     deviceLowerRight.y = roundf(deviceLowerRight.y);
   1020 
   1021     // Don't let the height or width round to 0 unless either was originally 0
   1022     if (deviceOrigin.y == deviceLowerRight.y && rect.height())
   1023         deviceLowerRight.y += 1;
   1024     if (deviceOrigin.x == deviceLowerRight.x && rect.width())
   1025         deviceLowerRight.x += 1;
   1026 
   1027     FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
   1028     FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
   1029     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
   1030 }
   1031 
   1032 void GraphicsContext::drawLineForText(const IntPoint& point, int width, bool printing)
   1033 {
   1034     if (paintingDisabled())
   1035         return;
   1036 
   1037     if (width <= 0)
   1038         return;
   1039 
   1040     float x = point.x();
   1041     float y = point.y();
   1042     float lineLength = width;
   1043 
   1044     // Use a minimum thickness of 0.5 in user space.
   1045     // See http://bugs.webkit.org/show_bug.cgi?id=4255 for details of why 0.5 is the right minimum thickness to use.
   1046     float thickness = max(strokeThickness(), 0.5f);
   1047 
   1048     bool restoreAntialiasMode = false;
   1049 
   1050     if (!printing) {
   1051         // On screen, use a minimum thickness of 1.0 in user space (later rounded to an integral number in device space).
   1052         float adjustedThickness = max(thickness, 1.0f);
   1053 
   1054         // FIXME: This should be done a better way.
   1055         // We try to round all parameters to integer boundaries in device space. If rounding pixels in device space
   1056         // makes our thickness more than double, then there must be a shrinking-scale factor and rounding to pixels
   1057         // in device space will make the underlines too thick.
   1058         CGRect lineRect = roundToDevicePixels(FloatRect(x, y, lineLength, adjustedThickness));
   1059         if (lineRect.size.height < thickness * 2.0) {
   1060             x = lineRect.origin.x;
   1061             y = lineRect.origin.y;
   1062             lineLength = lineRect.size.width;
   1063             thickness = lineRect.size.height;
   1064             if (shouldAntialias()) {
   1065                 CGContextSetShouldAntialias(platformContext(), false);
   1066                 restoreAntialiasMode = true;
   1067             }
   1068         }
   1069     }
   1070 
   1071     if (fillColor() != strokeColor())
   1072         setCGFillColor(platformContext(), strokeColor(), strokeColorSpace());
   1073     CGContextFillRect(platformContext(), CGRectMake(x, y, lineLength, thickness));
   1074     if (fillColor() != strokeColor())
   1075         setCGFillColor(platformContext(), fillColor(), fillColorSpace());
   1076 
   1077     if (restoreAntialiasMode)
   1078         CGContextSetShouldAntialias(platformContext(), true);
   1079 }
   1080 
   1081 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
   1082 {
   1083     if (paintingDisabled())
   1084         return;
   1085 
   1086     RetainPtr<CFURLRef> urlRef(AdoptCF, link.createCFURL());
   1087     if (!urlRef)
   1088         return;
   1089 
   1090     CGContextRef context = platformContext();
   1091 
   1092     // Get the bounding box to handle clipping.
   1093     CGRect box = CGContextGetClipBoundingBox(context);
   1094 
   1095     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
   1096     IntRect rect = destRect;
   1097     rect.intersect(intBox);
   1098 
   1099     CGPDFContextSetURLForRect(context, urlRef.get(),
   1100         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
   1101 }
   1102 
   1103 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
   1104 {
   1105     if (paintingDisabled())
   1106         return;
   1107 
   1108     CGInterpolationQuality quality = kCGInterpolationDefault;
   1109     switch (mode) {
   1110     case InterpolationDefault:
   1111         quality = kCGInterpolationDefault;
   1112         break;
   1113     case InterpolationNone:
   1114         quality = kCGInterpolationNone;
   1115         break;
   1116     case InterpolationLow:
   1117         quality = kCGInterpolationLow;
   1118         break;
   1119 
   1120     // Fall through to InterpolationHigh if kCGInterpolationMedium is not usable.
   1121     case InterpolationMedium:
   1122 #if USE(CG_INTERPOLATION_MEDIUM)
   1123         quality = kCGInterpolationMedium;
   1124         break;
   1125 #endif
   1126     case InterpolationHigh:
   1127         quality = kCGInterpolationHigh;
   1128         break;
   1129     }
   1130     CGContextSetInterpolationQuality(platformContext(), quality);
   1131 }
   1132 
   1133 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
   1134 {
   1135     if (paintingDisabled())
   1136         return InterpolationDefault;
   1137 
   1138     CGInterpolationQuality quality = CGContextGetInterpolationQuality(platformContext());
   1139     switch (quality) {
   1140     case kCGInterpolationDefault:
   1141         return InterpolationDefault;
   1142     case kCGInterpolationNone:
   1143         return InterpolationNone;
   1144     case kCGInterpolationLow:
   1145         return InterpolationLow;
   1146 #if HAVE(CG_INTERPOLATION_MEDIUM)
   1147     // kCGInterpolationMedium is known to be present in the CGInterpolationQuality enum.
   1148     case kCGInterpolationMedium:
   1149 #if USE(CG_INTERPOLATION_MEDIUM)
   1150         // Only map to InterpolationMedium if targeting a system that understands it.
   1151         return InterpolationMedium;
   1152 #else
   1153         return InterpolationDefault;
   1154 #endif  // USE(CG_INTERPOLATION_MEDIUM)
   1155 #endif  // HAVE(CG_INTERPOLATION_MEDIUM)
   1156     case kCGInterpolationHigh:
   1157         return InterpolationHigh;
   1158     }
   1159     return InterpolationDefault;
   1160 }
   1161 
   1162 void GraphicsContext::setPlatformTextDrawingMode(int mode)
   1163 {
   1164     if (paintingDisabled())
   1165         return;
   1166 
   1167     // Wow, wish CG had used bits here.
   1168     CGContextRef context = platformContext();
   1169     switch (mode) {
   1170     case cTextInvisible: // Invisible
   1171         CGContextSetTextDrawingMode(context, kCGTextInvisible);
   1172         break;
   1173     case cTextFill: // Fill
   1174         CGContextSetTextDrawingMode(context, kCGTextFill);
   1175         break;
   1176     case cTextStroke: // Stroke
   1177         CGContextSetTextDrawingMode(context, kCGTextStroke);
   1178         break;
   1179     case 3: // Fill | Stroke
   1180         CGContextSetTextDrawingMode(context, kCGTextFillStroke);
   1181         break;
   1182     case cTextClip: // Clip
   1183         CGContextSetTextDrawingMode(context, kCGTextClip);
   1184         break;
   1185     case 5: // Fill | Clip
   1186         CGContextSetTextDrawingMode(context, kCGTextFillClip);
   1187         break;
   1188     case 6: // Stroke | Clip
   1189         CGContextSetTextDrawingMode(context, kCGTextStrokeClip);
   1190         break;
   1191     case 7: // Fill | Stroke | Clip
   1192         CGContextSetTextDrawingMode(context, kCGTextFillStrokeClip);
   1193         break;
   1194     default:
   1195         break;
   1196     }
   1197 }
   1198 
   1199 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
   1200 {
   1201     if (paintingDisabled())
   1202         return;
   1203     setCGStrokeColor(platformContext(), color, colorSpace);
   1204 }
   1205 
   1206 void GraphicsContext::setPlatformStrokeThickness(float thickness)
   1207 {
   1208     if (paintingDisabled())
   1209         return;
   1210     CGContextSetLineWidth(platformContext(), thickness);
   1211 }
   1212 
   1213 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
   1214 {
   1215     if (paintingDisabled())
   1216         return;
   1217     setCGFillColor(platformContext(), color, colorSpace);
   1218 }
   1219 
   1220 void GraphicsContext::setPlatformShouldAntialias(bool enable)
   1221 {
   1222     if (paintingDisabled())
   1223         return;
   1224     CGContextSetShouldAntialias(platformContext(), enable);
   1225 }
   1226 
   1227 #ifndef BUILDING_ON_TIGER // Tiger's setCompositeOperation() is defined in GraphicsContextMac.mm.
   1228 void GraphicsContext::setCompositeOperation(CompositeOperator mode)
   1229 {
   1230     if (paintingDisabled())
   1231         return;
   1232 
   1233     CGBlendMode target = kCGBlendModeNormal;
   1234     switch (mode) {
   1235     case CompositeClear:
   1236         target = kCGBlendModeClear;
   1237         break;
   1238     case CompositeCopy:
   1239         target = kCGBlendModeCopy;
   1240         break;
   1241     case CompositeSourceOver:
   1242         //kCGBlendModeNormal
   1243         break;
   1244     case CompositeSourceIn:
   1245         target = kCGBlendModeSourceIn;
   1246         break;
   1247     case CompositeSourceOut:
   1248         target = kCGBlendModeSourceOut;
   1249         break;
   1250     case CompositeSourceAtop:
   1251         target = kCGBlendModeSourceAtop;
   1252         break;
   1253     case CompositeDestinationOver:
   1254         target = kCGBlendModeDestinationOver;
   1255         break;
   1256     case CompositeDestinationIn:
   1257         target = kCGBlendModeDestinationIn;
   1258         break;
   1259     case CompositeDestinationOut:
   1260         target = kCGBlendModeDestinationOut;
   1261         break;
   1262     case CompositeDestinationAtop:
   1263         target = kCGBlendModeDestinationAtop;
   1264         break;
   1265     case CompositeXOR:
   1266         target = kCGBlendModeXOR;
   1267         break;
   1268     case CompositePlusDarker:
   1269         target = kCGBlendModePlusDarker;
   1270         break;
   1271     case CompositeHighlight:
   1272         // currently unsupported
   1273         break;
   1274     case CompositePlusLighter:
   1275         target = kCGBlendModePlusLighter;
   1276         break;
   1277     }
   1278     CGContextSetBlendMode(platformContext(), target);
   1279 }
   1280 #endif
   1281 
   1282 }
   1283