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