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