9baf7801c0322c7240a8465fffa48ed968ebee29
[WebKit-https.git] / WebCore / platform / graphics / cairo / GraphicsContextCairo.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
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 #include "config.h"
28 #include "GraphicsContext.h"
29
30 #if PLATFORM(CAIRO)
31
32 #include "AffineTransform.h"
33 #include "CairoPath.h"
34 #include "FloatRect.h"
35 #include "Font.h"
36 #include "ImageBuffer.h"
37 #include "IntRect.h"
38 #include "NotImplemented.h"
39 #include "Path.h"
40 #include "SimpleFontData.h"
41 #include <cairo.h>
42 #include <math.h>
43 #include <stdio.h>
44 #include <wtf/MathExtras.h>
45
46 #if PLATFORM(GTK)
47 #include <gdk/gdk.h>
48 #include <pango/pango.h>
49 #elif PLATFORM(WIN)
50 #include <cairo-win32.h>
51 #endif
52 #include "GraphicsContextPlatformPrivateCairo.h"
53
54 #ifndef M_PI
55 #define M_PI 3.14159265358979323846
56 #endif
57
58 namespace WebCore {
59
60 static inline void setColor(cairo_t* cr, const Color& col)
61 {
62     float red, green, blue, alpha;
63     col.getRGBA(red, green, blue, alpha);
64     cairo_set_source_rgba(cr, red, green, blue, alpha);
65 }
66
67 // A fillRect helper
68 static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col)
69 {
70     setColor(cr, col);
71     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
72     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
73     cairo_fill(cr);
74 }
75
76 GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr)
77     : m_common(createGraphicsContextPrivate())
78     , m_data(new GraphicsContextPlatformPrivate)
79 {
80     m_data->cr = cairo_reference(cr);
81     setPaintingDisabled(!cr);
82 }
83
84 GraphicsContext::~GraphicsContext()
85 {
86     destroyGraphicsContextPrivate(m_common);
87     delete m_data;
88 }
89
90 AffineTransform GraphicsContext::getCTM() const
91 {
92     cairo_t* cr = platformContext();
93     cairo_matrix_t m;
94     cairo_get_matrix(cr, &m);
95     return m;
96 }
97
98 cairo_t* GraphicsContext::platformContext() const
99 {
100     return m_data->cr;
101 }
102
103 void GraphicsContext::savePlatformState()
104 {
105     cairo_save(m_data->cr);
106 }
107
108 void GraphicsContext::restorePlatformState()
109 {
110     cairo_restore(m_data->cr);
111 }
112
113 // Draws a filled rectangle with a stroked border.
114 void GraphicsContext::drawRect(const IntRect& rect)
115 {
116     if (paintingDisabled())
117         return;
118
119     cairo_t* cr = m_data->cr;
120     cairo_save(cr);
121
122     if (fillColor().alpha())
123         fillRectSourceOver(cr, rect, fillColor());
124
125     if (strokeStyle() != NoStroke) {
126         setColor(cr, strokeColor());
127         FloatRect r(rect);
128         r.inflate(-.5f);
129         cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
130         cairo_set_line_width(cr, 1.0);
131         cairo_stroke(cr);
132     }
133
134     cairo_restore(cr);
135 }
136
137 // FIXME: Now that this is refactored, it should be shared by all contexts.
138 static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle style)
139 {
140     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
141     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
142     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
143     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
144     if (style == DottedStroke || style == DashedStroke) {
145         if (p1.x() == p2.x()) {
146             p1.setY(p1.y() + strokeWidth);
147             p2.setY(p2.y() - strokeWidth);
148         }
149         else {
150             p1.setX(p1.x() + strokeWidth);
151             p2.setX(p2.x() - strokeWidth);
152         }
153     }
154
155     if (static_cast<int>(strokeWidth) % 2) {
156         if (p1.x() == p2.x()) {
157             // We're a vertical line.  Adjust our x.
158             p1.setX(p1.x() + 0.5);
159             p2.setX(p2.x() + 0.5);
160         }
161         else {
162             // We're a horizontal line. Adjust our y.
163             p1.setY(p1.y() + 0.5);
164             p2.setY(p2.y() + 0.5);
165         }
166     }
167 }
168
169 // This is only used to draw borders.
170 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
171 {
172     if (paintingDisabled())
173         return;
174
175     StrokeStyle style = strokeStyle();
176     if (style == NoStroke)
177         return;
178
179     cairo_t* cr = m_data->cr;
180     cairo_save(cr);
181
182     float width = strokeThickness();
183     if (width < 1)
184         width = 1;
185
186     FloatPoint p1 = point1;
187     FloatPoint p2 = point2;
188     bool isVerticalLine = (p1.x() == p2.x());
189
190     adjustLineToPixelBoundaries(p1, p2, width, style);
191     cairo_set_line_width(cr, width);
192
193     int patWidth = 0;
194     switch (style) {
195     case NoStroke:
196     case SolidStroke:
197         break;
198     case DottedStroke:
199         patWidth = static_cast<int>(width);
200         break;
201     case DashedStroke:
202         patWidth = 3*static_cast<int>(width);
203         break;
204     }
205
206     setColor(cr, strokeColor());
207
208     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
209
210     if (patWidth) {
211         // Do a rect fill of our endpoints.  This ensures we always have the
212         // appearance of being a border.  We then draw the actual dotted/dashed line.
213         if (isVerticalLine) {
214             fillRectSourceOver(cr, FloatRect(p1.x() - width/2, p1.y() - width, width, width), strokeColor());
215             fillRectSourceOver(cr, FloatRect(p2.x() - width/2, p2.y(), width, width), strokeColor());
216         } else {
217             fillRectSourceOver(cr, FloatRect(p1.x() - width, p1.y() - width/2, width, width), strokeColor());
218             fillRectSourceOver(cr, FloatRect(p2.x(), p2.y() - width/2, width, width), strokeColor());
219         }
220
221         // Example: 80 pixels with a width of 30 pixels.
222         // Remainder is 20.  The maximum pixels of line we could paint
223         // will be 50 pixels.
224         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*static_cast<int>(width);
225         int remainder = distance%patWidth;
226         int coverage = distance-remainder;
227         int numSegments = coverage/patWidth;
228
229         float patternOffset = 0;
230         // Special case 1px dotted borders for speed.
231         if (patWidth == 1)
232             patternOffset = 1.0;
233         else {
234             bool evenNumberOfSegments = numSegments%2 == 0;
235             if (remainder)
236                 evenNumberOfSegments = !evenNumberOfSegments;
237             if (evenNumberOfSegments) {
238                 if (remainder) {
239                     patternOffset += patWidth - remainder;
240                     patternOffset += remainder/2;
241                 }
242                 else
243                     patternOffset = patWidth/2;
244             }
245             else if (!evenNumberOfSegments) {
246                 if (remainder)
247                     patternOffset = (patWidth - remainder)/2;
248             }
249         }
250
251         double dash = patWidth;
252         cairo_set_dash(cr, &dash, 1, patternOffset);
253     }
254
255     cairo_move_to(cr, p1.x(), p1.y());
256     cairo_line_to(cr, p2.x(), p2.y());
257
258     cairo_stroke(cr);
259     cairo_restore(cr);
260 }
261
262 // This method is only used to draw the little circles used in lists.
263 void GraphicsContext::drawEllipse(const IntRect& rect)
264 {
265     if (paintingDisabled())
266         return;
267
268     cairo_t* cr = m_data->cr;
269     cairo_save(cr);
270     float yRadius = .5 * rect.height();
271     float xRadius = .5 * rect.width();
272     cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
273     cairo_scale(cr, xRadius, yRadius);
274     cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
275     cairo_restore(cr);
276
277     if (fillColor().alpha()) {
278         setColor(cr, fillColor());
279         cairo_fill_preserve(cr);
280     }
281
282     if (strokeStyle() != NoStroke) {
283         setColor(cr, strokeColor());
284         cairo_set_line_width(cr, strokeThickness());
285         cairo_stroke(cr);
286     }
287
288     cairo_new_path(cr);
289 }
290
291 // FIXME: This function needs to be adjusted to match the functionality on the Mac side.
292 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
293 {
294     if (paintingDisabled())
295         return;
296
297     if (strokeStyle() == NoStroke)
298         return;
299
300     int x = rect.x();
301     int y = rect.y();
302     float w = rect.width();
303 #if 0 // FIXME: unused so far
304     float h = rect.height();
305     float scaleFactor = h / w;
306     float reverseScaleFactor = w / h;
307 #endif
308     float r = w / 2;
309     float fa = startAngle;
310     float falen =  fa + angleSpan;
311
312     cairo_t* cr = m_data->cr;
313     cairo_save(cr);
314     cairo_arc_negative(cr, x + r, y + r, r, -fa * M_PI/180, -falen * M_PI/180);
315     setColor(cr, strokeColor());
316     cairo_set_line_width(cr, strokeThickness());
317     cairo_stroke(cr);
318     cairo_restore(cr);
319 }
320
321 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
322 {
323     if (paintingDisabled())
324         return;
325
326     if (npoints <= 1)
327         return;
328
329     cairo_t* cr = m_data->cr;
330
331     cairo_save(cr);
332     cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
333     cairo_move_to(cr, points[0].x(), points[0].y());
334     for (size_t i = 1; i < npoints; i++)
335         cairo_line_to(cr, points[i].x(), points[i].y());
336     cairo_close_path(cr);
337
338     if (fillColor().alpha()) {
339         setColor(cr, fillColor());
340         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
341         cairo_fill_preserve(cr);
342     }
343
344     if (strokeStyle() != NoStroke) {
345         setColor(cr, strokeColor());
346         cairo_set_line_width(cr, strokeThickness());
347         cairo_stroke(cr);
348     }
349
350     cairo_new_path(cr);
351     cairo_restore(cr);
352 }
353
354 void GraphicsContext::fillRect(const IntRect& rect, const Color& color)
355 {
356     if (paintingDisabled())
357         return;
358
359     if (color.alpha())
360         fillRectSourceOver(m_data->cr, rect, color);
361 }
362
363 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
364 {
365     if (paintingDisabled())
366         return;
367
368     if (color.alpha())
369         fillRectSourceOver(m_data->cr, rect, color);
370 }
371
372 void GraphicsContext::clip(const IntRect& rect)
373 {
374     if (paintingDisabled())
375         return;
376
377     cairo_t* cr = m_data->cr;
378     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
379     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
380     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
381     cairo_clip(cr);
382     cairo_set_fill_rule(cr, savedFillRule);
383 }
384
385 void GraphicsContext::drawFocusRing(const Color& color)
386 {
387     if (paintingDisabled())
388         return;
389
390     int radius = (focusRingWidth() - 1) / 2;
391     int offset = radius + focusRingOffset();
392
393     const Vector<IntRect>& rects = focusRingRects();
394     unsigned rectCount = rects.size();
395     IntRect finalFocusRect;
396     for (unsigned i = 0; i < rectCount; i++) {
397         IntRect focusRect = rects[i];
398         focusRect.inflate(offset);
399         finalFocusRect.unite(focusRect);
400     }
401
402     cairo_t* cr = m_data->cr;
403     cairo_save(cr);
404     // FIXME: These rects should be rounded
405     cairo_rectangle(cr, finalFocusRect.x(), finalFocusRect.y(), finalFocusRect.width(), finalFocusRect.height());
406
407     // Force the alpha to 50%.  This matches what the Mac does with outline rings.
408     Color ringColor(color.red(), color.green(), color.blue(), 127);
409     setColor(cr, ringColor);
410     cairo_stroke(cr);
411     cairo_restore(cr);
412 }
413
414 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
415 {
416     if (paintingDisabled())
417         return;
418
419     // This is a workaround for http://bugs.webkit.org/show_bug.cgi?id=15659
420     StrokeStyle savedStrokeStyle = strokeStyle();
421     setStrokeStyle(SolidStroke);
422
423     IntPoint endPoint = origin + IntSize(width, 0);
424     drawLine(origin, endPoint);
425
426     setStrokeStyle(savedStrokeStyle);
427 }
428
429 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, int width, bool grammar)
430 {
431     if (paintingDisabled())
432         return;
433
434 #if PLATFORM(GTK)
435     cairo_t* cr = m_data->cr;
436     cairo_save(cr);
437
438     // Convention is green for grammar, red for spelling
439     // These need to become configurable
440     if (grammar)
441         cairo_set_source_rgb(cr, 0, 1, 0);
442     else
443         cairo_set_source_rgb(cr, 1, 0, 0);
444
445     // We ignore most of the provided constants in favour of the platform style
446     pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
447
448     cairo_restore(cr);
449 #else
450     notImplemented();
451 #endif
452 }
453
454 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
455 {
456     FloatRect result;
457     double x = frect.x();
458     double y = frect.y();
459     cairo_t* cr = m_data->cr;
460     cairo_user_to_device(cr, &x, &y);
461     x = round(x);
462     y = round(y);
463     cairo_device_to_user(cr, &x, &y);
464     result.setX(static_cast<float>(x));
465     result.setY(static_cast<float>(y));
466     x = frect.width();
467     y = frect.height();
468     cairo_user_to_device_distance(cr, &x, &y);
469     x = round(x);
470     y = round(y);
471     cairo_device_to_user_distance(cr, &x, &y);
472     result.setWidth(static_cast<float>(x));
473     result.setHeight(static_cast<float>(y));
474     return result;
475 }
476
477 void GraphicsContext::translate(float x, float y)
478 {
479     if (paintingDisabled())
480         return;
481
482     cairo_t* cr = m_data->cr;
483     cairo_translate(cr, x, y);
484 }
485
486 IntPoint GraphicsContext::origin()
487 {
488     cairo_matrix_t matrix;
489     cairo_t* cr = m_data->cr;
490     cairo_get_matrix(cr, &matrix);
491     return IntPoint(static_cast<int>(matrix.x0), static_cast<int>(matrix.y0));
492 }
493
494 void GraphicsContext::setPlatformFillColor(const Color& col)
495 {
496     // FIXME: this is probably a no-op but I'm not sure
497     // notImplemented(); // commented-out because it's chatty and clutters output
498 }
499
500 void GraphicsContext::setPlatformStrokeColor(const Color& col)
501 {
502     // FIXME: this is probably a no-op but I'm not sure
503     //notImplemented(); // commented-out because it's chatty and clutters output
504 }
505
506 void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
507 {
508     if (paintingDisabled())
509         return;
510
511     cairo_set_line_width(m_data->cr, strokeThickness);
512 }
513
514 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
515 {
516     static double dashPattern[] = {5.0, 5.0};
517     static double dotPattern[] = {1.0, 1.0};
518
519     if (paintingDisabled())
520         return;
521
522     switch (strokeStyle) {
523     case NoStroke:
524         // FIXME: is it the right way to emulate NoStroke?
525         cairo_set_line_width(m_data->cr, 0);
526         break;
527     case SolidStroke:
528         cairo_set_dash(m_data->cr, 0, 0, 0);
529         break;
530     case DottedStroke:
531         cairo_set_dash(m_data->cr, dotPattern, 2, 0);
532         break;
533     case DashedStroke:
534         cairo_set_dash(m_data->cr, dashPattern, 2, 0);
535         break;
536     default:
537         notImplemented();
538         break;
539     }
540 }
541
542 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
543 {
544     notImplemented();
545 }
546
547 #if PLATFORM(GTK)
548 // FIXME:  This should be moved to something like GraphicsContextCairoGTK.cpp,
549 // as there is a Windows implementation in platform/graphics/win/GraphicsContextCairoWin.cpp
550 void GraphicsContext::concatCTM(const AffineTransform& transform)
551 {
552     if (paintingDisabled())
553         return;
554
555     cairo_t* cr = m_data->cr;
556     const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform);
557     cairo_transform(cr, matrix);
558 }
559 #endif
560
561 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
562 {
563     if (paintingDisabled())
564         return;
565
566     clip(rect);
567
568     Path p;
569     FloatRect r(rect);
570     // Add outer ellipse
571     p.addEllipse(r);
572     // Add inner ellipse
573     r.inflate(-thickness);
574     p.addEllipse(r);
575     addPath(p);
576
577     cairo_t* cr = m_data->cr;
578     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
579     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
580     cairo_clip(cr);
581     cairo_set_fill_rule(cr, savedFillRule);
582 }
583
584
585 void GraphicsContext::setShadow(IntSize const&, int, Color const&)
586 {
587     notImplemented();
588 }
589
590 void GraphicsContext::clearShadow()
591 {
592     notImplemented();
593 }
594
595 void GraphicsContext::beginTransparencyLayer(float opacity)
596 {
597     if (paintingDisabled())
598         return;
599
600     cairo_t* cr = m_data->cr;
601     cairo_push_group(cr);
602     m_data->layers.append(opacity);
603 }
604
605 void GraphicsContext::endTransparencyLayer()
606 {
607     if (paintingDisabled())
608         return;
609
610     cairo_t* cr = m_data->cr;
611
612     cairo_pop_group_to_source(cr);
613     cairo_paint_with_alpha(cr, m_data->layers.last());
614     m_data->layers.removeLast();
615 }
616
617 void GraphicsContext::clearRect(const FloatRect& rect)
618 {
619     if (paintingDisabled())
620         return;
621
622     cairo_t* cr = m_data->cr;
623
624     cairo_save(cr);
625     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
626     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
627     cairo_fill(cr);
628     cairo_restore(cr);
629 }
630
631 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
632 {
633     if (paintingDisabled())
634         return;
635
636     cairo_t* cr = m_data->cr;
637     cairo_save(cr);
638     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
639     setColor(cr, strokeColor());
640     cairo_set_line_width(cr, width);
641     cairo_stroke(cr);
642     cairo_restore(cr);
643 }
644
645 void GraphicsContext::setLineCap(LineCap lineCap)
646 {
647     if (paintingDisabled())
648         return;
649
650     cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
651     switch (lineCap) {
652         case ButtCap:
653             // no-op
654             break;
655         case RoundCap:
656             cairoCap = CAIRO_LINE_CAP_ROUND;
657             break;
658         case SquareCap:
659             cairoCap = CAIRO_LINE_CAP_SQUARE;
660             break;
661     }
662     cairo_set_line_cap(m_data->cr, cairoCap);
663 }
664
665 void GraphicsContext::setLineJoin(LineJoin lineJoin)
666 {
667     if (paintingDisabled())
668         return;
669
670     cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
671     switch (lineJoin) {
672         case MiterJoin:
673             // no-op
674             break;
675         case RoundJoin:
676             cairoJoin = CAIRO_LINE_JOIN_ROUND;
677             break;
678         case BevelJoin:
679             cairoJoin = CAIRO_LINE_JOIN_BEVEL;
680             break;
681     }
682     cairo_set_line_join(m_data->cr, cairoJoin);
683 }
684
685 void GraphicsContext::setMiterLimit(float miter)
686 {
687     if (paintingDisabled())
688         return;
689
690     cairo_set_miter_limit(m_data->cr, miter);
691 }
692
693 void GraphicsContext::setAlpha(float)
694 {
695     notImplemented();
696 }
697
698 static inline cairo_operator_t toCairoOperator(CompositeOperator op)
699 {
700     switch (op) {
701         case CompositeClear:
702             return CAIRO_OPERATOR_CLEAR;
703         case CompositeCopy:
704             return CAIRO_OPERATOR_SOURCE;
705         case CompositeSourceOver:
706             return CAIRO_OPERATOR_OVER;
707         case CompositeSourceIn:
708             return CAIRO_OPERATOR_IN;
709         case CompositeSourceOut:
710             return CAIRO_OPERATOR_OUT;
711         case CompositeSourceAtop:
712             return CAIRO_OPERATOR_ATOP;
713         case CompositeDestinationOver:
714             return CAIRO_OPERATOR_DEST_OVER;
715         case CompositeDestinationIn:
716             return CAIRO_OPERATOR_DEST_IN;
717         case CompositeDestinationOut:
718             return CAIRO_OPERATOR_DEST_OUT;
719         case CompositeDestinationAtop:
720             return CAIRO_OPERATOR_DEST_ATOP;
721         case CompositeXOR:
722             return CAIRO_OPERATOR_XOR;
723         case CompositePlusDarker:
724             return CAIRO_OPERATOR_SATURATE;
725         case CompositeHighlight:
726             // There is no Cairo equivalent for CompositeHighlight.
727             return CAIRO_OPERATOR_OVER;
728         case CompositePlusLighter:
729             return CAIRO_OPERATOR_ADD;
730         default:
731             return CAIRO_OPERATOR_SOURCE;
732     }
733 }
734
735 void GraphicsContext::setCompositeOperation(CompositeOperator op)
736 {
737     if (paintingDisabled())
738         return;
739
740     cairo_set_operator(m_data->cr, toCairoOperator(op));
741 }
742
743 void GraphicsContext::beginPath()
744 {
745     if (paintingDisabled())
746         return;
747
748     cairo_t* cr = m_data->cr;
749     cairo_new_path(cr);
750 }
751
752 void GraphicsContext::addPath(const Path& path)
753 {
754     if (paintingDisabled())
755         return;
756
757     cairo_t* cr = m_data->cr;
758     cairo_path_t* p = cairo_copy_path(path.platformPath()->m_cr);
759     cairo_append_path(cr, p);
760     cairo_path_destroy(p);
761 }
762
763 void GraphicsContext::clip(const Path& path)
764 {
765     if (paintingDisabled())
766         return;
767
768     cairo_t* cr = m_data->cr;
769     cairo_path_t* p = cairo_copy_path(path.platformPath()->m_cr);
770     cairo_append_path(cr, p);
771     cairo_path_destroy(p);
772     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
773     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
774     cairo_clip(cr);
775     cairo_set_fill_rule(cr, savedFillRule);
776 }
777
778 void GraphicsContext::clipOut(const Path& path)
779 {
780     if (paintingDisabled())
781         return;
782
783     cairo_t* cr = m_data->cr;
784     double x1, y1, x2, y2;
785     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
786     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
787     addPath(path);
788
789     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
790     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
791     cairo_clip(cr);
792     cairo_set_fill_rule(cr, savedFillRule);
793 }
794
795 void GraphicsContext::rotate(float radians)
796 {
797     if (paintingDisabled())
798         return;
799
800     cairo_rotate(m_data->cr, radians);
801 }
802
803 void GraphicsContext::scale(const FloatSize& size)
804 {
805     if (paintingDisabled())
806         return;
807
808     cairo_scale(m_data->cr, size.width(), size.height());
809 }
810
811 void GraphicsContext::clipOut(const IntRect& r)
812 {
813     if (paintingDisabled())
814         return;
815
816     cairo_t* cr = m_data->cr;
817     double x1, y1, x2, y2;
818     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
819     cairo_rectangle(cr, x1, x2, x2 - x1, y2 - y1);
820     cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
821     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
822     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
823     cairo_clip(cr);
824     cairo_set_fill_rule(cr, savedFillRule);
825 }
826
827 void GraphicsContext::clipOutEllipseInRect(const IntRect& r)
828 {
829     if (paintingDisabled())
830         return;
831
832     Path p;
833     p.addEllipse(r);
834     clipOut(p);
835 }
836
837 void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
838 {
839     if (paintingDisabled())
840         return;
841
842     cairo_t* cr = m_data->cr;
843     cairo_save(cr);
844     beginPath();
845     addPath(Path::createRoundedRectangle(r, topLeft, topRight, bottomLeft, bottomRight));
846     setColor(cr, color);
847     cairo_fill(cr);
848     cairo_restore(cr);
849 }
850
851 #if PLATFORM(GTK)
852 void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
853 {
854     m_data->expose = expose;
855 }
856
857 GdkEventExpose* GraphicsContext::gdkExposeEvent() const
858 {
859     return m_data->expose;
860 }
861
862 GdkDrawable* GraphicsContext::gdkDrawable() const
863 {
864     if (!m_data->expose)
865         return 0;
866
867     return GDK_DRAWABLE(m_data->expose->window);
868 }
869
870 IntPoint GraphicsContext::translatePoint(const IntPoint& point) const
871 {
872     cairo_matrix_t tm;
873     cairo_get_matrix(m_data->cr, &tm);
874     double x = point.x();
875     double y = point.y();
876
877     cairo_matrix_transform_point(&tm, &x, &y);
878     return IntPoint(x, y);
879 }
880 #endif
881
882 void GraphicsContext::setUseAntialiasing(bool enable)
883 {
884     if (paintingDisabled())
885         return;
886
887     // When true, use the default Cairo backend antialias mode (usually this
888     // enables standard 'grayscale' antialiasing); false to explicitly disable
889     // antialiasing. This is the same strategy as used in drawConvexPolygon().
890     cairo_set_antialias(m_data->cr, enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
891 }
892
893 void GraphicsContext::paintBuffer(ImageBuffer* buffer, const IntRect& r)
894 {
895     if (paintingDisabled())
896         return;
897     cairo_surface_t* image = buffer->surface();
898     if (!image)
899         return;
900     cairo_surface_flush(image);
901     cairo_surface_reference(image);
902     cairo_t* cr = platformContext();
903     cairo_save(cr);
904     cairo_translate(cr, r.x(), r.y());
905     cairo_set_source_surface(cr, image, 0, 0);
906     cairo_surface_destroy(image);
907     cairo_rectangle(cr, 0, 0, r.width(), r.height());
908     cairo_fill(cr);
909     cairo_restore(cr);
910 }
911
912 void GraphicsContext::drawImage(ImageBuffer* buffer, const FloatRect& srcRect, const FloatRect& destRect)
913 {
914     cairo_surface_flush(m_data->surface());    
915     cairo_save(platformContext());
916     cairo_set_source_surface(platformContext(), m_data->surface(), srcRect.x(), srcRect.y());
917     cairo_rectangle(platformContext(), dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height());
918     cairo_fill(platformContext());
919     cairo_restore(platformContext());
920 }
921
922 } // namespace WebCore
923
924 #endif // PLATFORM(CAIRO)