09998c91f3b8cf6e0bc74b88fc806089309287ac
[WebKit-https.git] / Source / 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  * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
5  * Copyright (C) 2008 Nuanti Ltd.
6  * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
7  * Copyright (C) 2010, 2011 Igalia S.L.
8  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9  * Copyright (C) 2012, Intel Corporation
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "GraphicsContext.h"
35
36 #if USE(CAIRO)
37
38 #include "AffineTransform.h"
39 #include "CairoUtilities.h"
40 #include "FloatConversion.h"
41 #include "FloatRect.h"
42 #include "Font.h"
43 #include "GraphicsContextPlatformPrivateCairo.h"
44 #include "IntRect.h"
45 #include "NotImplemented.h"
46 #include "OwnPtrCairo.h"
47 #include "Path.h"
48 #include "Pattern.h"
49 #include "PlatformContextCairo.h"
50 #include "PlatformPathCairo.h"
51 #include "RefPtrCairo.h"
52 #include "ShadowBlur.h"
53 #include "SimpleFontData.h"
54 #include "TransformationMatrix.h"
55 #include <cairo.h>
56 #include <math.h>
57 #include <stdio.h>
58 #include <wtf/MathExtras.h>
59
60 #if PLATFORM(GTK)
61 #include <gdk/gdk.h>
62 #include <pango/pango.h>
63 #elif PLATFORM(WIN)
64 #include <cairo-win32.h>
65 #endif
66
67 using namespace std;
68
69 namespace WebCore {
70
71 // A helper which quickly fills a rectangle with a simple color fill.
72 static inline void fillRectWithColor(cairo_t* cr, const FloatRect& rect, const Color& color)
73 {
74     if (!color.alpha() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER)
75         return;
76     setSourceRGBAFromColor(cr, color);
77     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
78     cairo_fill(cr);
79 }
80
81 static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points)
82 {
83     cairo_move_to(context, points[0].x(), points[0].y());
84     for (size_t i = 1; i < numPoints; i++)
85         cairo_line_to(context, points[i].x(), points[i].y());
86     cairo_close_path(context);
87 }
88
89 enum PathDrawingStyle { 
90     Fill = 1,
91     Stroke = 2,
92     FillAndStroke = Fill + Stroke
93 };
94
95 static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle drawingStyle)
96 {
97     ShadowBlur& shadow = context->platformContext()->shadowBlur();
98     if (shadow.type() == ShadowBlur::NoShadow)
99         return;
100
101     // Calculate the extents of the rendered solid paths.
102     cairo_t* cairoContext = context->platformContext()->cr();
103     OwnPtr<cairo_path_t> path = adoptPtr(cairo_copy_path(cairoContext));
104
105     FloatRect solidFigureExtents;
106     double x0 = 0;
107     double x1 = 0;
108     double y0 = 0;
109     double y1 = 0;
110     if (drawingStyle & Stroke) {
111         cairo_stroke_extents(cairoContext, &x0, &y0, &x1, &y1);
112         solidFigureExtents = FloatRect(x0, y0, x1 - x0, y1 - y0);
113     }
114     if (drawingStyle & Fill) {
115         cairo_fill_extents(cairoContext, &x0, &y0, &x1, &y1);
116         FloatRect fillExtents(x0, y0, x1 - x0, y1 - y0);
117         solidFigureExtents.unite(fillExtents);
118     }
119
120     GraphicsContext* shadowContext = shadow.beginShadowLayer(context, solidFigureExtents);
121     if (!shadowContext)
122         return;
123
124     cairo_t* cairoShadowContext = shadowContext->platformContext()->cr();
125
126     // It's important to copy the context properties to the new shadow
127     // context to preserve things such as the fill rule and stroke width.
128     copyContextProperties(cairoContext, cairoShadowContext);
129
130     if (drawingStyle & Fill) {
131         cairo_save(cairoShadowContext);
132         cairo_append_path(cairoShadowContext, path.get());
133         shadowContext->platformContext()->prepareForFilling(context->state(), PlatformContextCairo::NoAdjustment);
134         cairo_fill(cairoShadowContext);
135         cairo_restore(cairoShadowContext);
136     }
137
138     if (drawingStyle & Stroke) {
139         cairo_append_path(cairoShadowContext, path.get());
140         shadowContext->platformContext()->prepareForStroking(context->state(), PlatformContextCairo::DoNotPreserveAlpha);
141         cairo_stroke(cairoShadowContext);
142     }
143
144     shadow.endShadowLayer(context);
145
146     // ShadowBlur::endShadowLayer destroys the current path on the Cairo context. We restore it here.
147     cairo_new_path(cairoContext);
148     cairo_append_path(cairoContext, path.get());
149 }
150
151 static inline void shadowAndFillCurrentCairoPath(GraphicsContext* context)
152 {
153     cairo_t* cr = context->platformContext()->cr();
154     cairo_save(cr);
155
156     drawPathShadow(context, Fill);
157
158     context->platformContext()->prepareForFilling(context->state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
159     cairo_fill(cr);
160
161     cairo_restore(cr);
162 }
163
164 static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext* context)
165 {
166     drawPathShadow(context, Stroke);
167     context->platformContext()->prepareForStroking(context->state());
168     cairo_stroke(context->platformContext()->cr());
169 }
170
171 GraphicsContext::GraphicsContext(cairo_t* cr)
172     : m_updatingControlTints(false),
173       m_transparencyCount(0)
174 {
175     m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
176 }
177
178 void GraphicsContext::platformInit(PlatformContextCairo* platformContext)
179 {
180     m_data = new GraphicsContextPlatformPrivate(platformContext);
181     if (platformContext)
182         m_data->syncContext(platformContext->cr());
183     else
184         setPaintingDisabled(true);
185 }
186
187 void GraphicsContext::platformDestroy()
188 {
189     delete m_data;
190 }
191
192 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
193 {
194     if (paintingDisabled())
195         return AffineTransform();
196
197     cairo_t* cr = platformContext()->cr();
198     cairo_matrix_t m;
199     cairo_get_matrix(cr, &m);
200     return AffineTransform(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
201 }
202
203 PlatformContextCairo* GraphicsContext::platformContext() const
204 {
205     return m_data->platformContext;
206 }
207
208 void GraphicsContext::savePlatformState()
209 {
210     platformContext()->save();
211     m_data->save();
212 }
213
214 void GraphicsContext::restorePlatformState()
215 {
216     platformContext()->restore();
217     m_data->restore();
218
219     platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
220                                                     m_state.shadowOffset,
221                                                     m_state.shadowColor,
222                                                     m_state.shadowColorSpace,
223                                                     m_state.shadowsIgnoreTransforms);
224 }
225
226 // Draws a filled rectangle with a stroked border.
227 void GraphicsContext::drawRect(const IntRect& rect)
228 {
229     if (paintingDisabled())
230         return;
231
232     ASSERT(!rect.isEmpty());
233
234     cairo_t* cr = platformContext()->cr();
235     cairo_save(cr);
236
237     fillRectWithColor(cr, rect, fillColor());
238
239     if (strokeStyle() != NoStroke) {
240         setSourceRGBAFromColor(cr, strokeColor());
241         FloatRect r(rect);
242         r.inflate(-.5f);
243         cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
244         cairo_set_line_width(cr, 1.0);
245         cairo_stroke(cr);
246     }
247
248     cairo_restore(cr);
249 }
250
251 static double calculateStrokePatternOffset(int distance, int patternWidth)
252 {
253     // Example: 80 pixels with a width of 30 pixels. Remainder is 20.
254     // The maximum pixels of line we could paint will be 50 pixels.
255     int remainder = distance % patternWidth;
256     int numSegments = (distance - remainder) / patternWidth;
257
258     // Special case 1px dotted borders for speed.
259     if (patternWidth == 1)
260         return 1;
261
262     bool evenNumberOfSegments = !(numSegments % 2);
263     if (remainder)
264         evenNumberOfSegments = !evenNumberOfSegments;
265
266     if (evenNumberOfSegments) {
267         if (remainder)
268             return (patternWidth - remainder) + (remainder / 2);
269         return patternWidth / 2;
270     }
271
272     // Odd number of segments.
273     if (remainder)
274         return (patternWidth - remainder) / 2.f;
275     return 0;
276 }
277
278 static void drawLineOnCairoContext(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point1, const FloatPoint& point2)
279 {
280     StrokeStyle style = graphicsContext->strokeStyle();
281     if (style == NoStroke)
282         return;
283
284     const Color& strokeColor = graphicsContext->strokeColor();
285     int strokeThickness = floorf(graphicsContext->strokeThickness());
286     if (graphicsContext->strokeThickness() < 1)
287         strokeThickness = 1;
288
289     int patternWidth = 0;
290     if (style == DottedStroke)
291         patternWidth = strokeThickness;
292     else if (style == DashedStroke)
293         patternWidth = 3 * strokeThickness;
294
295     bool isVerticalLine = point1.x() == point2.x();
296     FloatPoint point1OnPixelBoundaries = point1;
297     FloatPoint point2OnPixelBoundaries = point2;
298     GraphicsContext::adjustLineToPixelBoundaries(point1OnPixelBoundaries, point2OnPixelBoundaries, strokeThickness, style);
299
300     cairo_set_antialias(context, CAIRO_ANTIALIAS_NONE);
301     if (patternWidth) {
302         // Do a rect fill of our endpoints.  This ensures we always have the
303         // appearance of being a border.  We then draw the actual dotted/dashed line.
304         FloatRect firstRect(point1OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness));
305         FloatRect secondRect(point2OnPixelBoundaries, FloatSize(strokeThickness, strokeThickness));
306         if (isVerticalLine) {
307             firstRect.move(-strokeThickness / 2, -strokeThickness);
308             secondRect.move(-strokeThickness / 2, 0);
309         } else {
310             firstRect.move(-strokeThickness, -strokeThickness / 2);
311             secondRect.move(0, -strokeThickness / 2);
312         }
313         fillRectWithColor(context, firstRect, strokeColor);
314         fillRectWithColor(context, secondRect, strokeColor);
315
316         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2 * strokeThickness;
317         double patternOffset = calculateStrokePatternOffset(distance, patternWidth);
318         double patternWidthAsDouble = patternWidth;
319         cairo_set_dash(context, &patternWidthAsDouble, 1, patternOffset);
320     }
321
322     setSourceRGBAFromColor(context, strokeColor);
323     cairo_set_line_width(context, strokeThickness);
324     cairo_move_to(context, point1OnPixelBoundaries.x(), point1OnPixelBoundaries.y());
325     cairo_line_to(context, point2OnPixelBoundaries.x(), point2OnPixelBoundaries.y());
326     cairo_stroke(context);
327 }
328
329 // This is only used to draw borders, so we should not draw shadows.
330 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
331 {
332     if (paintingDisabled())
333         return;
334
335     cairo_t* cairoContext = platformContext()->cr();
336     cairo_save(cairoContext);
337     drawLineOnCairoContext(this, cairoContext, point1, point2);
338     cairo_restore(cairoContext);
339 }
340
341 // This method is only used to draw the little circles used in lists.
342 void GraphicsContext::drawEllipse(const IntRect& rect)
343 {
344     if (paintingDisabled())
345         return;
346
347     cairo_t* cr = platformContext()->cr();
348     cairo_save(cr);
349     float yRadius = .5 * rect.height();
350     float xRadius = .5 * rect.width();
351     cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
352     cairo_scale(cr, xRadius, yRadius);
353     cairo_arc(cr, 0., 0., 1., 0., 2 * piFloat);
354     cairo_restore(cr);
355
356     if (fillColor().alpha()) {
357         setSourceRGBAFromColor(cr, fillColor());
358         cairo_fill_preserve(cr);
359     }
360
361     if (strokeStyle() != NoStroke) {
362         setSourceRGBAFromColor(cr, strokeColor());
363         cairo_set_line_width(cr, strokeThickness());
364         cairo_stroke(cr);
365     } else
366         cairo_new_path(cr);
367 }
368
369 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
370 {
371     if (paintingDisabled() || strokeStyle() == NoStroke)
372         return;
373
374     int x = rect.x();
375     int y = rect.y();
376     float w = rect.width();
377     float h = rect.height();
378     float scaleFactor = h / w;
379     float reverseScaleFactor = w / h;
380
381     float hRadius = w / 2;
382     float vRadius = h / 2;
383     float fa = startAngle;
384     float falen =  fa + angleSpan;
385
386     cairo_t* cr = platformContext()->cr();
387     cairo_save(cr);
388
389     if (w != h)
390         cairo_scale(cr, 1., scaleFactor);
391
392     cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, deg2rad(-fa), deg2rad(-falen));
393
394     if (w != h)
395         cairo_scale(cr, 1., reverseScaleFactor);
396
397     int patternWidth = 0;
398     switch (strokeStyle()) {
399     case DottedStroke:
400         patternWidth = floorf(strokeThickness() / 2.f);
401         break;
402     case DashedStroke:
403         patternWidth = 3 * floorf(strokeThickness() / 2.f);
404         break;
405     default:
406         break;
407     }
408
409     setSourceRGBAFromColor(cr, strokeColor());
410
411     if (patternWidth) {
412         float distance = 0;
413         if (hRadius == vRadius)
414             distance = (piFloat * hRadius) / 2.f;
415         else // We are elliptical and will have to estimate the distance
416             distance = (piFloat * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.f)) / 2.f;
417         double patternOffset = calculateStrokePatternOffset(floorf(distance), patternWidth);
418         double patternWidthAsDouble = patternWidth;
419         cairo_set_dash(cr, &patternWidthAsDouble, 1, patternOffset);
420     }
421
422     cairo_stroke(cr);
423     cairo_restore(cr);
424 }
425
426 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
427 {
428     if (paintingDisabled())
429         return;
430
431     if (npoints <= 1)
432         return;
433
434     cairo_t* cr = platformContext()->cr();
435
436     cairo_save(cr);
437     cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
438     addConvexPolygonToContext(cr, npoints, points);
439
440     if (fillColor().alpha()) {
441         setSourceRGBAFromColor(cr, fillColor());
442         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
443         cairo_fill_preserve(cr);
444     }
445
446     if (strokeStyle() != NoStroke) {
447         setSourceRGBAFromColor(cr, strokeColor());
448         cairo_set_line_width(cr, strokeThickness());
449         cairo_stroke(cr);
450     } else
451         cairo_new_path(cr);
452
453     cairo_restore(cr);
454 }
455
456 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
457 {
458     if (paintingDisabled())
459         return;
460
461     if (numPoints <= 1)
462         return;
463
464     cairo_t* cr = platformContext()->cr();
465
466     cairo_new_path(cr);
467     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
468     cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr);
469
470     cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
471     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
472     addConvexPolygonToContext(cr, numPoints, points);
473     cairo_clip(cr);
474
475     cairo_set_antialias(cr, savedAntialiasRule);
476     cairo_set_fill_rule(cr, savedFillRule);
477 }
478
479 void GraphicsContext::fillPath(const Path& path)
480 {
481     if (paintingDisabled() || path.isEmpty())
482         return;
483
484     cairo_t* cr = platformContext()->cr();
485     setPathOnCairoContext(cr, path.platformPath()->context());
486     shadowAndFillCurrentCairoPath(this);
487 }
488
489 void GraphicsContext::strokePath(const Path& path)
490 {
491     if (paintingDisabled() || path.isEmpty())
492         return;
493
494     cairo_t* cr = platformContext()->cr();
495     setPathOnCairoContext(cr, path.platformPath()->context());
496     shadowAndStrokeCurrentCairoPath(this);
497 }
498
499 void GraphicsContext::fillRect(const FloatRect& rect)
500 {
501     if (paintingDisabled())
502         return;
503
504     cairo_t* cr = platformContext()->cr();
505     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
506     shadowAndFillCurrentCairoPath(this);
507 }
508
509 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
510 {
511     if (paintingDisabled())
512         return;
513
514     if (hasShadow())
515         platformContext()->shadowBlur().drawRectShadow(this, rect, RoundedRect::Radii());
516
517     fillRectWithColor(platformContext()->cr(), rect, color);
518 }
519
520 void GraphicsContext::clip(const FloatRect& rect)
521 {
522     if (paintingDisabled())
523         return;
524
525     cairo_t* cr = platformContext()->cr();
526     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
527     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
528     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
529     // The rectangular clip function is traditionally not expected to
530     // antialias. If we don't force antialiased clipping here,
531     // edge fringe artifacts may occur at the layer edges
532     // when a transformation is applied to the GraphicsContext
533     // while drawing the transformed layer.
534     cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr);
535     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
536     cairo_clip(cr);
537     cairo_set_fill_rule(cr, savedFillRule);
538     cairo_set_antialias(cr, savedAntialiasRule);
539     m_data->clip(rect);
540 }
541
542 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
543 {
544     if (paintingDisabled())
545         return;
546
547     cairo_t* cr = platformContext()->cr();
548     if (!path.isNull())
549         setPathOnCairoContext(cr, path.platformPath()->context());
550     cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
551     cairo_clip(cr);
552 }
553
554 IntRect GraphicsContext::clipBounds() const
555 {
556     double x1, x2, y1, y2;
557     cairo_clip_extents(platformContext()->cr(), &x1, &y1, &x2, &y2);
558     return enclosingIntRect(FloatRect(x1, y1, x2 - x1, y2 - y1));
559 }
560
561 static inline void adjustFocusRingColor(Color& color)
562 {
563 #if !PLATFORM(GTK)
564     // Force the alpha to 50%.  This matches what the Mac does with outline rings.
565     color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127));
566 #endif
567 }
568
569 static inline void adjustFocusRingLineWidth(int& width)
570 {
571 #if PLATFORM(GTK)
572     width = 2;
573 #else
574     UNUSED_PARAM(width);
575 #endif
576 }
577
578 static inline StrokeStyle focusRingStrokeStyle()
579 {
580 #if PLATFORM(GTK)
581     return DottedStroke;
582 #else
583     return SolidStroke;
584 #endif
585 }
586
587 void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color)
588 {
589     // FIXME: We should draw paths that describe a rectangle with rounded corners
590     // so as to be consistent with how we draw rectangular focus rings.
591     Color ringColor = color;
592     adjustFocusRingColor(ringColor);
593     adjustFocusRingLineWidth(width);
594
595     cairo_t* cr = platformContext()->cr();
596     cairo_save(cr);
597     appendWebCorePathToCairoContext(cr, path);
598     setSourceRGBAFromColor(cr, ringColor);
599     cairo_set_line_width(cr, width);
600     setPlatformStrokeStyle(focusRingStrokeStyle());
601     cairo_stroke(cr);
602     cairo_restore(cr);
603 }
604
605 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
606 {
607     if (paintingDisabled())
608         return;
609
610     unsigned rectCount = rects.size();
611
612     cairo_t* cr = platformContext()->cr();
613     cairo_save(cr);
614     cairo_push_group(cr);
615     cairo_new_path(cr);
616
617 #if PLATFORM(GTK)
618 #ifdef GTK_API_VERSION_2
619     GdkRegion* reg = gdk_region_new();
620 #else
621     cairo_region_t* reg = cairo_region_create();
622 #endif
623
624     for (unsigned i = 0; i < rectCount; i++) {
625 #ifdef GTK_API_VERSION_2
626         GdkRectangle rect = rects[i];
627         gdk_region_union_with_rect(reg, &rect);
628 #else
629         cairo_rectangle_int_t rect = rects[i];
630         cairo_region_union_rectangle(reg, &rect);
631 #endif
632     }
633     gdk_cairo_region(cr, reg);
634 #ifdef GTK_API_VERSION_2
635     gdk_region_destroy(reg);
636 #else
637     cairo_region_destroy(reg);
638 #endif
639 #else
640     int radius = (width - 1) / 2;
641     Path path;
642     for (unsigned i = 0; i < rectCount; ++i) {
643         if (i > 0)
644             path.clear();
645         path.addRoundedRect(rects[i], FloatSize(radius, radius));
646         appendWebCorePathToCairoContext(cr, path);
647     }
648 #endif
649     Color ringColor = color;
650     adjustFocusRingColor(ringColor);
651     adjustFocusRingLineWidth(width);
652     setSourceRGBAFromColor(cr, ringColor);
653     cairo_set_line_width(cr, width);
654     setPlatformStrokeStyle(focusRingStrokeStyle());
655
656     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
657     cairo_stroke_preserve(cr);
658
659     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
660     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
661     cairo_fill(cr);
662
663     cairo_pop_group_to_source(cr);
664     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
665     cairo_paint(cr);
666     cairo_restore(cr);
667 }
668
669 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool)
670 {
671     if (paintingDisabled())
672         return;
673
674     cairo_t* cairoContext = platformContext()->cr();
675     cairo_save(cairoContext);
676
677     // This bumping of <1 stroke thicknesses matches the one in drawLineOnCairoContext.
678     FloatPoint endPoint(origin + IntSize(width, 0));
679     FloatRect lineExtents(origin, FloatSize(width, strokeThickness()));
680
681     ShadowBlur& shadow = platformContext()->shadowBlur();
682     if (GraphicsContext* shadowContext = shadow.beginShadowLayer(this, lineExtents)) {
683         drawLineOnCairoContext(this, shadowContext->platformContext()->cr(), origin, endPoint);
684         shadow.endShadowLayer(this);
685     }
686
687     drawLineOnCairoContext(this, cairoContext, origin, endPoint);
688     cairo_restore(cairoContext);
689 }
690
691 #if !PLATFORM(GTK)
692 #include "DrawErrorUnderline.h"
693 #endif
694
695 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
696 {
697     if (paintingDisabled())
698         return;
699
700     cairo_t* cr = platformContext()->cr();
701     cairo_save(cr);
702
703     switch (style) {
704     case DocumentMarkerSpellingLineStyle:
705         cairo_set_source_rgb(cr, 1, 0, 0);
706         break;
707     case DocumentMarkerGrammarLineStyle:
708         cairo_set_source_rgb(cr, 0, 1, 0);
709         break;
710     default:
711         cairo_restore(cr);
712         return;
713     }
714
715 #if PLATFORM(GTK)
716     // We ignore most of the provided constants in favour of the platform style
717     pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
718 #else
719     drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
720 #endif
721
722     cairo_restore(cr);
723 }
724
725 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
726 {
727     FloatRect result;
728     double x = frect.x();
729     double y = frect.y();
730     cairo_t* cr = platformContext()->cr();
731     cairo_user_to_device(cr, &x, &y);
732     x = round(x);
733     y = round(y);
734     cairo_device_to_user(cr, &x, &y);
735     result.setX(narrowPrecisionToFloat(x));
736     result.setY(narrowPrecisionToFloat(y));
737
738     // We must ensure width and height are at least 1 (or -1) when
739     // we're given float values in the range between 0 and 1 (or -1 and 0).
740     double width = frect.width();
741     double height = frect.height();
742     cairo_user_to_device_distance(cr, &width, &height);
743     if (width > -1 && width < 0)
744         width = -1;
745     else if (width > 0 && width < 1)
746         width = 1;
747     else
748         width = round(width);
749     if (height > -1 && width < 0)
750         height = -1;
751     else if (height > 0 && height < 1)
752         height = 1;
753     else
754         height = round(height);
755     cairo_device_to_user_distance(cr, &width, &height);
756     result.setWidth(narrowPrecisionToFloat(width));
757     result.setHeight(narrowPrecisionToFloat(height));
758
759     return result;
760 }
761
762 void GraphicsContext::translate(float x, float y)
763 {
764     if (paintingDisabled())
765         return;
766
767     cairo_t* cr = platformContext()->cr();
768     cairo_translate(cr, x, y);
769     m_data->translate(x, y);
770 }
771
772 void GraphicsContext::setPlatformFillColor(const Color&, ColorSpace)
773 {
774     // Cairo contexts can't hold separate fill and stroke colors
775     // so we set them just before we actually fill or stroke
776 }
777
778 void GraphicsContext::setPlatformStrokeColor(const Color&, ColorSpace)
779 {
780     // Cairo contexts can't hold separate fill and stroke colors
781     // so we set them just before we actually fill or stroke
782 }
783
784 void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
785 {
786     if (paintingDisabled())
787         return;
788
789     cairo_set_line_width(platformContext()->cr(), strokeThickness);
790 }
791
792 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
793 {
794     static double dashPattern[] = {5.0, 5.0};
795     static double dotPattern[] = {1.0, 1.0};
796
797     if (paintingDisabled())
798         return;
799
800     switch (strokeStyle) {
801     case NoStroke:
802         // FIXME: is it the right way to emulate NoStroke?
803         cairo_set_line_width(platformContext()->cr(), 0);
804         break;
805     case SolidStroke:
806 #if ENABLE(CSS3_TEXT)
807     case DoubleStroke:
808     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=94110 - Needs platform support.
809 #endif // CSS3_TEXT
810         cairo_set_dash(platformContext()->cr(), 0, 0, 0);
811         break;
812     case DottedStroke:
813         cairo_set_dash(platformContext()->cr(), dotPattern, 2, 0);
814         break;
815     case DashedStroke:
816         cairo_set_dash(platformContext()->cr(), dashPattern, 2, 0);
817         break;
818     }
819 }
820
821 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
822 {
823     notImplemented();
824 }
825
826 void GraphicsContext::concatCTM(const AffineTransform& transform)
827 {
828     if (paintingDisabled())
829         return;
830
831     cairo_t* cr = platformContext()->cr();
832     const cairo_matrix_t matrix = cairo_matrix_t(transform);
833     cairo_transform(cr, &matrix);
834     m_data->concatCTM(transform);
835 }
836
837 void GraphicsContext::setCTM(const AffineTransform& transform)
838 {
839     if (paintingDisabled())
840         return;
841
842     cairo_t* cr = platformContext()->cr();
843     const cairo_matrix_t matrix = cairo_matrix_t(transform);
844     cairo_set_matrix(cr, &matrix);
845     m_data->setCTM(transform);
846 }
847
848 void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&, ColorSpace)
849 {
850     if (paintingDisabled())
851         return;
852
853     if (m_state.shadowsIgnoreTransforms) {
854         // Meaning that this graphics context is associated with a CanvasRenderingContext
855         // We flip the height since CG and HTML5 Canvas have opposite Y axis
856         m_state.shadowOffset = FloatSize(size.width(), -size.height());
857     }
858
859     // Cairo doesn't support shadows natively, they are drawn manually in the draw* functions using ShadowBlur.
860     platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
861                                                     m_state.shadowOffset,
862                                                     m_state.shadowColor,
863                                                     m_state.shadowColorSpace,
864                                                     m_state.shadowsIgnoreTransforms);
865 }
866
867 void GraphicsContext::clearPlatformShadow()
868 {
869     if (paintingDisabled())
870         return;
871
872     platformContext()->shadowBlur().clear();
873 }
874
875 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
876 {
877     if (paintingDisabled())
878         return;
879
880     cairo_t* cr = platformContext()->cr();
881     cairo_push_group(cr);
882     m_data->layers.append(opacity);
883 }
884
885 void GraphicsContext::endPlatformTransparencyLayer()
886 {
887     if (paintingDisabled())
888         return;
889
890     cairo_t* cr = platformContext()->cr();
891
892     cairo_pop_group_to_source(cr);
893     cairo_paint_with_alpha(cr, m_data->layers.last());
894     m_data->layers.removeLast();
895 }
896
897 bool GraphicsContext::supportsTransparencyLayers()
898 {
899     return true;
900 }
901
902 void GraphicsContext::clearRect(const FloatRect& rect)
903 {
904     if (paintingDisabled())
905         return;
906
907     cairo_t* cr = platformContext()->cr();
908
909     cairo_save(cr);
910     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
911     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
912     cairo_fill(cr);
913     cairo_restore(cr);
914 }
915
916 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
917 {
918     if (paintingDisabled())
919         return;
920
921     cairo_t* cr = platformContext()->cr();
922     cairo_save(cr);
923     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
924     cairo_set_line_width(cr, width);
925     shadowAndStrokeCurrentCairoPath(this);
926     cairo_restore(cr);
927 }
928
929 void GraphicsContext::setLineCap(LineCap lineCap)
930 {
931     if (paintingDisabled())
932         return;
933
934     cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
935     switch (lineCap) {
936     case ButtCap:
937         // no-op
938         break;
939     case RoundCap:
940         cairoCap = CAIRO_LINE_CAP_ROUND;
941         break;
942     case SquareCap:
943         cairoCap = CAIRO_LINE_CAP_SQUARE;
944         break;
945     }
946     cairo_set_line_cap(platformContext()->cr(), cairoCap);
947 }
948
949 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
950 {
951     cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
952 }
953
954 void GraphicsContext::setLineJoin(LineJoin lineJoin)
955 {
956     if (paintingDisabled())
957         return;
958
959     cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
960     switch (lineJoin) {
961     case MiterJoin:
962         // no-op
963         break;
964     case RoundJoin:
965         cairoJoin = CAIRO_LINE_JOIN_ROUND;
966         break;
967     case BevelJoin:
968         cairoJoin = CAIRO_LINE_JOIN_BEVEL;
969         break;
970     }
971     cairo_set_line_join(platformContext()->cr(), cairoJoin);
972 }
973
974 void GraphicsContext::setMiterLimit(float miter)
975 {
976     if (paintingDisabled())
977         return;
978
979     cairo_set_miter_limit(platformContext()->cr(), miter);
980 }
981
982 void GraphicsContext::setAlpha(float alpha)
983 {
984     platformContext()->setGlobalAlpha(alpha);
985 }
986
987 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode blendOp)
988 {
989     if (paintingDisabled())
990         return;
991
992     cairo_operator_t cairo_op;
993     if (blendOp == BlendModeNormal)
994         cairo_op = toCairoOperator(op);
995     else
996         cairo_op = toCairoOperator(blendOp);
997
998     cairo_set_operator(platformContext()->cr(), cairo_op);
999 }
1000
1001 void GraphicsContext::clip(const Path& path, WindRule windRule)
1002 {
1003     if (paintingDisabled())
1004         return;
1005
1006     cairo_t* cr = platformContext()->cr();
1007     OwnPtr<cairo_path_t> pathCopy;
1008     if (!path.isNull()) {
1009         pathCopy = adoptPtr(cairo_copy_path(path.platformPath()->context()));
1010         cairo_append_path(cr, pathCopy.get());
1011     }
1012     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1013     if (windRule == RULE_NONZERO)
1014         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
1015     else
1016         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1017     cairo_clip(cr);
1018     cairo_set_fill_rule(cr, savedFillRule);
1019     m_data->clip(path);
1020 }
1021
1022 void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
1023 {
1024     clip(path, windRule);
1025 }
1026
1027 void GraphicsContext::clipOut(const Path& path)
1028 {
1029     if (paintingDisabled())
1030         return;
1031
1032     cairo_t* cr = platformContext()->cr();
1033     double x1, y1, x2, y2;
1034     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1035     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1036     appendWebCorePathToCairoContext(cr, path);
1037
1038     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1039     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1040     cairo_clip(cr);
1041     cairo_set_fill_rule(cr, savedFillRule);
1042 }
1043
1044 void GraphicsContext::rotate(float radians)
1045 {
1046     if (paintingDisabled())
1047         return;
1048
1049     cairo_rotate(platformContext()->cr(), radians);
1050     m_data->rotate(radians);
1051 }
1052
1053 void GraphicsContext::scale(const FloatSize& size)
1054 {
1055     if (paintingDisabled())
1056         return;
1057
1058     cairo_scale(platformContext()->cr(), size.width(), size.height());
1059     m_data->scale(size);
1060 }
1061
1062 void GraphicsContext::clipOut(const IntRect& r)
1063 {
1064     if (paintingDisabled())
1065         return;
1066
1067     cairo_t* cr = platformContext()->cr();
1068     double x1, y1, x2, y2;
1069     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1070     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1071     cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
1072     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1073     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1074     cairo_clip(cr);
1075     cairo_set_fill_rule(cr, savedFillRule);
1076 }
1077
1078 static inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile)
1079 {
1080     FloatPoint phase = dest.location();
1081     phase.move(-tile.x(), -tile.y());
1082
1083     return phase;
1084 }
1085
1086 void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
1087 {
1088     if (paintingDisabled())
1089         return;
1090
1091     if (hasShadow())
1092         platformContext()->shadowBlur().drawRectShadow(this, r, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight));
1093
1094     cairo_t* cr = platformContext()->cr();
1095     cairo_save(cr);
1096     Path path;
1097     path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight);
1098     appendWebCorePathToCairoContext(cr, path);
1099     setSourceRGBAFromColor(cr, color);
1100     cairo_fill(cr);
1101     cairo_restore(cr);
1102 }
1103
1104 #if PLATFORM(GTK)
1105 void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
1106 {
1107     m_data->expose = expose;
1108 }
1109
1110 GdkEventExpose* GraphicsContext::gdkExposeEvent() const
1111 {
1112     return m_data->expose;
1113 }
1114
1115 GdkWindow* GraphicsContext::gdkWindow() const
1116 {
1117     if (!m_data->expose)
1118         return 0;
1119
1120     return m_data->expose->window;
1121 }
1122 #endif
1123
1124 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1125 {
1126     if (paintingDisabled())
1127         return;
1128
1129     // When true, use the default Cairo backend antialias mode (usually this
1130     // enables standard 'grayscale' antialiasing); false to explicitly disable
1131     // antialiasing. This is the same strategy as used in drawConvexPolygon().
1132     cairo_set_antialias(platformContext()->cr(), enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
1133 }
1134
1135 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1136 {
1137     platformContext()->setImageInterpolationQuality(quality);
1138 }
1139
1140 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1141 {
1142     return platformContext()->imageInterpolationQuality();
1143 }
1144
1145 #if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER)
1146 TransformationMatrix GraphicsContext::get3DTransform() const
1147 {
1148     // FIXME: Can we approximate the transformation better than this?
1149     return getCTM().toTransformationMatrix();
1150 }
1151
1152 void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
1153 {
1154     concatCTM(transform.toAffineTransform());
1155 }
1156
1157 void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
1158 {
1159     setCTM(transform.toAffineTransform());
1160 }
1161 #endif
1162
1163 } // namespace WebCore
1164
1165 #endif // USE(CAIRO)