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