• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #define _USE_MATH_DEFINES 1
28 #include "config.h"
29 #include "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 
createCGColorWithColorSpace(const Color & color,ColorSpace colorSpace)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 
setCGFillColor(CGContextRef context,const Color & color,ColorSpace colorSpace)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 
setCGStrokeColor(CGContextRef context,const Color & color,ColorSpace colorSpace)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 
setCGFillColorSpace(CGContextRef context,ColorSpace colorSpace)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 
setCGStrokeColorSpace(CGContextRef context,ColorSpace colorSpace)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 
deviceRGBColorSpaceRef()128 CGColorSpaceRef deviceRGBColorSpaceRef()
129 {
130     static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
131     return deviceSpace;
132 }
133 
sRGBColorSpaceRef()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 
GraphicsContext(CGContextRef cgContext)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 
~GraphicsContext()157 GraphicsContext::~GraphicsContext()
158 {
159     destroyGraphicsContextPrivate(m_common);
160     delete m_data;
161 }
162 
platformContext() const163 CGContextRef GraphicsContext::platformContext() const
164 {
165     ASSERT(!paintingDisabled());
166     ASSERT(m_data->m_cgContext);
167     return m_data->m_cgContext.get();
168 }
169 
savePlatformState()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 
restorePlatformState()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.
drawRect(const IntRect & rect)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.
drawLine(const IntPoint & point1,const IntPoint & point2)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.
drawEllipse(const IntRect & rect)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 
strokeArc(const IntRect & rect,int startAngle,int angleSpan)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 
drawConvexPolygon(size_t npoints,const FloatPoint * points,bool antialiased)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 
applyStrokePattern()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 
applyFillPattern()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 
calculateDrawingMode(const GraphicsContextState & state,CGPathDrawingMode & mode)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 
drawPath()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 
fillPathWithFillRule(CGContextRef context,WindRule fillRule)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 
fillPath()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 
strokePath()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 
fillRect(const FloatRect & rect)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 
fillRect(const FloatRect & rect,const Color & color,ColorSpace colorSpace)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 
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color,ColorSpace colorSpace)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 
clip(const FloatRect & rect)670 void GraphicsContext::clip(const FloatRect& rect)
671 {
672     if (paintingDisabled())
673         return;
674     CGContextClipToRect(platformContext(), rect);
675     m_data->clip(rect);
676 }
677 
clipOut(const IntRect & rect)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 
clipOutEllipseInRect(const IntRect & rect)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 
clipPath(WindRule clipRule)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 
addInnerRoundedRectClip(const IntRect & rect,int thickness)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 
clipToImageBuffer(const FloatRect & rect,const ImageBuffer * imageBuffer)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 
beginTransparencyLayer(float opacity)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 
endTransparencyLayer()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 
setPlatformShadow(const IntSize & offset,int blur,const Color & color,ColorSpace colorSpace)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 
clearPlatformShadow()821 void GraphicsContext::clearPlatformShadow()
822 {
823     if (paintingDisabled())
824         return;
825     CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
826 }
827 
setMiterLimit(float limit)828 void GraphicsContext::setMiterLimit(float limit)
829 {
830     if (paintingDisabled())
831         return;
832     CGContextSetMiterLimit(platformContext(), limit);
833 }
834 
setAlpha(float alpha)835 void GraphicsContext::setAlpha(float alpha)
836 {
837     if (paintingDisabled())
838         return;
839     CGContextSetAlpha(platformContext(), alpha);
840 }
841 
clearRect(const FloatRect & r)842 void GraphicsContext::clearRect(const FloatRect& r)
843 {
844     if (paintingDisabled())
845         return;
846     CGContextClearRect(platformContext(), r);
847 }
848 
strokeRect(const FloatRect & r,float lineWidth)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 
setLineCap(LineCap cap)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 
setLineDash(const DashArray & dashes,float dashOffset)892 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
893 {
894     CGContextSetLineDash(platformContext(), dashOffset, dashes.data(), dashes.size());
895 }
896 
setLineJoin(LineJoin join)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 
beginPath()914 void GraphicsContext::beginPath()
915 {
916     CGContextBeginPath(platformContext());
917 }
918 
addPath(const Path & path)919 void GraphicsContext::addPath(const Path& path)
920 {
921     CGContextAddPath(platformContext(), path.platformPath());
922 }
923 
clip(const Path & path)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 
canvasClip(const Path & path)935 void GraphicsContext::canvasClip(const Path& path)
936 {
937     clip(path);
938 }
939 
clipOut(const Path & path)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 
scale(const FloatSize & size)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 
rotate(float angle)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 
translate(float x,float y)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 
concatCTM(const AffineTransform & transform)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 
getCTM() const987 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 
roundToDevicePixels(const FloatRect & rect)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 
drawLineForText(const IntPoint & point,int width,bool printing)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 
setURLForRect(const KURL & link,const IntRect & destRect)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 
setImageInterpolationQuality(InterpolationQuality mode)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 
imageInterpolationQuality() const1133 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 
setPlatformTextDrawingMode(int mode)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 
setPlatformStrokeColor(const Color & color,ColorSpace colorSpace)1199 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1200 {
1201     if (paintingDisabled())
1202         return;
1203     setCGStrokeColor(platformContext(), color, colorSpace);
1204 }
1205 
setPlatformStrokeThickness(float thickness)1206 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1207 {
1208     if (paintingDisabled())
1209         return;
1210     CGContextSetLineWidth(platformContext(), thickness);
1211 }
1212 
setPlatformFillColor(const Color & color,ColorSpace colorSpace)1213 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1214 {
1215     if (paintingDisabled())
1216         return;
1217     setCGFillColor(platformContext(), color, colorSpace);
1218 }
1219 
setPlatformShouldAntialias(bool enable)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.
setCompositeOperation(CompositeOperator mode)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