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