71ebdb317701cda9f95eb9f0f25a24c7f26a9822
[WebKit-https.git] / Source / WebCore / platform / graphics / cairo / GraphicsContextCairo.cpp
1 /*
2  * Copyright (C) 2006 Apple 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 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 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 "FloatRoundedRect.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 FloatRect& rect, float)
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 FloatPoint& point1, const FloatPoint& 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 FloatRect& 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, FloatRoundedRect(rect));
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 #else
515     UNUSED_PARAM(color);
516 #endif
517 }
518
519 static inline void adjustFocusRingLineWidth(int& width)
520 {
521 #if PLATFORM(GTK)
522     width = 2;
523 #else
524     UNUSED_PARAM(width);
525 #endif
526 }
527
528 static inline StrokeStyle focusRingStrokeStyle()
529 {
530 #if PLATFORM(GTK)
531     return DottedStroke;
532 #else
533     return SolidStroke;
534 #endif
535 }
536
537 void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color)
538 {
539     // FIXME: We should draw paths that describe a rectangle with rounded corners
540     // so as to be consistent with how we draw rectangular focus rings.
541     Color ringColor = color;
542     adjustFocusRingColor(ringColor);
543     adjustFocusRingLineWidth(width);
544
545     cairo_t* cr = platformContext()->cr();
546     cairo_save(cr);
547     appendWebCorePathToCairoContext(cr, path);
548     setSourceRGBAFromColor(cr, ringColor);
549     cairo_set_line_width(cr, width);
550     setPlatformStrokeStyle(focusRingStrokeStyle());
551     cairo_stroke(cr);
552     cairo_restore(cr);
553 }
554
555 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
556 {
557     if (paintingDisabled())
558         return;
559
560     unsigned rectCount = rects.size();
561
562     cairo_t* cr = platformContext()->cr();
563     cairo_save(cr);
564     cairo_push_group(cr);
565     cairo_new_path(cr);
566
567 #if PLATFORM(GTK)
568 #ifdef GTK_API_VERSION_2
569     GdkRegion* reg = gdk_region_new();
570 #else
571     cairo_region_t* reg = cairo_region_create();
572 #endif
573
574     for (unsigned i = 0; i < rectCount; i++) {
575 #ifdef GTK_API_VERSION_2
576         GdkRectangle rect = rects[i];
577         gdk_region_union_with_rect(reg, &rect);
578 #else
579         cairo_rectangle_int_t rect = rects[i];
580         cairo_region_union_rectangle(reg, &rect);
581 #endif
582     }
583     gdk_cairo_region(cr, reg);
584 #ifdef GTK_API_VERSION_2
585     gdk_region_destroy(reg);
586 #else
587     cairo_region_destroy(reg);
588 #endif
589 #else
590     int radius = (width - 1) / 2;
591     Path path;
592     for (unsigned i = 0; i < rectCount; ++i) {
593         if (i > 0)
594             path.clear();
595         path.addRoundedRect(rects[i], FloatSize(radius, radius));
596         appendWebCorePathToCairoContext(cr, path);
597     }
598 #endif
599     Color ringColor = color;
600     adjustFocusRingColor(ringColor);
601     adjustFocusRingLineWidth(width);
602     setSourceRGBAFromColor(cr, ringColor);
603     cairo_set_line_width(cr, width);
604     setPlatformStrokeStyle(focusRingStrokeStyle());
605
606     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
607     cairo_stroke_preserve(cr);
608
609     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
610     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
611     cairo_fill(cr);
612
613     cairo_pop_group_to_source(cr);
614     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
615     cairo_paint(cr);
616     cairo_restore(cr);
617 }
618
619 FloatRect GraphicsContext::computeLineBoundsForText(const FloatPoint& origin, float width, bool printing)
620 {
621     bool dummyBool;
622     Color dummyColor;
623     return computeLineBoundsAndAntialiasingModeForText(origin, width, printing, dummyBool, dummyColor);
624 }
625
626 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines)
627 {
628     DashArray widths;
629     widths.append(width);
630     widths.append(0);
631     drawLinesForText(origin, widths, printing, doubleUnderlines);
632 }
633
634 void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleUnderlines)
635 {
636     if (paintingDisabled())
637         return;
638
639     if (widths.size() <= 0)
640         return;
641
642     Color localStrokeColor(strokeColor());
643
644     bool shouldAntialiasLine;
645     FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, shouldAntialiasLine, localStrokeColor);
646
647     Vector<FloatRect, 4> dashBounds;
648     ASSERT(!(widths.size() % 2));
649     dashBounds.reserveInitialCapacity(dashBounds.size() / 2);
650     for (size_t i = 0; i < widths.size(); i += 2)
651         dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y()), FloatSize(widths[i+1] - widths[i], bounds.height())));
652
653     if (doubleUnderlines) {
654         // The space between double underlines is equal to the height of the underline
655         for (size_t i = 0; i < widths.size(); i += 2)
656             dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y() + 2 * bounds.height()), FloatSize(widths[i+1] - widths[i], bounds.height())));
657     }
658
659     cairo_t* cr = platformContext()->cr();
660     cairo_save(cr);
661
662     for (auto& dash : dashBounds)
663         fillRectWithColor(cr, dash, localStrokeColor);
664
665     cairo_restore(cr);
666 }
667
668 void GraphicsContext::updateDocumentMarkerResources()
669 {
670     // Unnecessary, since our document markers don't use resources.
671 }
672
673 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
674 {
675     if (paintingDisabled())
676         return;
677
678     cairo_t* cr = platformContext()->cr();
679     cairo_save(cr);
680
681     switch (style) {
682     case DocumentMarkerSpellingLineStyle:
683         cairo_set_source_rgb(cr, 1, 0, 0);
684         break;
685     case DocumentMarkerGrammarLineStyle:
686         cairo_set_source_rgb(cr, 0, 1, 0);
687         break;
688     default:
689         cairo_restore(cr);
690         return;
691     }
692
693     drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
694
695     cairo_restore(cr);
696 }
697
698 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
699 {
700     FloatRect result;
701     double x = frect.x();
702     double y = frect.y();
703     cairo_t* cr = platformContext()->cr();
704     cairo_user_to_device(cr, &x, &y);
705     x = round(x);
706     y = round(y);
707     cairo_device_to_user(cr, &x, &y);
708     result.setX(narrowPrecisionToFloat(x));
709     result.setY(narrowPrecisionToFloat(y));
710
711     // We must ensure width and height are at least 1 (or -1) when
712     // we're given float values in the range between 0 and 1 (or -1 and 0).
713     double width = frect.width();
714     double height = frect.height();
715     cairo_user_to_device_distance(cr, &width, &height);
716     if (width > -1 && width < 0)
717         width = -1;
718     else if (width > 0 && width < 1)
719         width = 1;
720     else
721         width = round(width);
722     if (height > -1 && width < 0)
723         height = -1;
724     else if (height > 0 && height < 1)
725         height = 1;
726     else
727         height = round(height);
728     cairo_device_to_user_distance(cr, &width, &height);
729     result.setWidth(narrowPrecisionToFloat(width));
730     result.setHeight(narrowPrecisionToFloat(height));
731
732     return result;
733 }
734
735 void GraphicsContext::translate(float x, float y)
736 {
737     if (paintingDisabled())
738         return;
739
740     cairo_t* cr = platformContext()->cr();
741     cairo_translate(cr, x, y);
742     m_data->translate(x, y);
743 }
744
745 void GraphicsContext::setPlatformFillColor(const Color&, ColorSpace)
746 {
747     // Cairo contexts can't hold separate fill and stroke colors
748     // so we set them just before we actually fill or stroke
749 }
750
751 void GraphicsContext::setPlatformStrokeColor(const Color&, ColorSpace)
752 {
753     // Cairo contexts can't hold separate fill and stroke colors
754     // so we set them just before we actually fill or stroke
755 }
756
757 void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
758 {
759     if (paintingDisabled())
760         return;
761
762     cairo_set_line_width(platformContext()->cr(), strokeThickness);
763 }
764
765 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
766 {
767     static double dashPattern[] = {5.0, 5.0};
768     static double dotPattern[] = {1.0, 1.0};
769
770     if (paintingDisabled())
771         return;
772
773     switch (strokeStyle) {
774     case NoStroke:
775         // FIXME: is it the right way to emulate NoStroke?
776         cairo_set_line_width(platformContext()->cr(), 0);
777         break;
778     case SolidStroke:
779     case DoubleStroke:
780     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=94110 - Needs platform support.
781         cairo_set_dash(platformContext()->cr(), 0, 0, 0);
782         break;
783     case DottedStroke:
784         cairo_set_dash(platformContext()->cr(), dotPattern, 2, 0);
785         break;
786     case DashedStroke:
787         cairo_set_dash(platformContext()->cr(), dashPattern, 2, 0);
788         break;
789     }
790 }
791
792 void GraphicsContext::setURLForRect(const URL&, const IntRect&)
793 {
794     notImplemented();
795 }
796
797 void GraphicsContext::concatCTM(const AffineTransform& transform)
798 {
799     if (paintingDisabled())
800         return;
801
802     cairo_t* cr = platformContext()->cr();
803     const cairo_matrix_t matrix = cairo_matrix_t(transform);
804     cairo_transform(cr, &matrix);
805     m_data->concatCTM(transform);
806 }
807
808 void GraphicsContext::setCTM(const AffineTransform& transform)
809 {
810     if (paintingDisabled())
811         return;
812
813     cairo_t* cr = platformContext()->cr();
814     const cairo_matrix_t matrix = cairo_matrix_t(transform);
815     cairo_set_matrix(cr, &matrix);
816     m_data->setCTM(transform);
817 }
818
819 void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&, ColorSpace)
820 {
821     if (paintingDisabled())
822         return;
823
824     if (m_state.shadowsIgnoreTransforms) {
825         // Meaning that this graphics context is associated with a CanvasRenderingContext
826         // We flip the height since CG and HTML5 Canvas have opposite Y axis
827         m_state.shadowOffset = FloatSize(size.width(), -size.height());
828     }
829
830     // Cairo doesn't support shadows natively, they are drawn manually in the draw* functions using ShadowBlur.
831     platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
832                                                     m_state.shadowOffset,
833                                                     m_state.shadowColor,
834                                                     m_state.shadowColorSpace,
835                                                     m_state.shadowsIgnoreTransforms);
836 }
837
838 void GraphicsContext::clearPlatformShadow()
839 {
840     if (paintingDisabled())
841         return;
842
843     platformContext()->shadowBlur().clear();
844 }
845
846 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
847 {
848     if (paintingDisabled())
849         return;
850
851     cairo_t* cr = platformContext()->cr();
852     cairo_push_group(cr);
853     m_data->layers.append(opacity);
854 }
855
856 void GraphicsContext::endPlatformTransparencyLayer()
857 {
858     if (paintingDisabled())
859         return;
860
861     cairo_t* cr = platformContext()->cr();
862
863     cairo_pop_group_to_source(cr);
864     cairo_paint_with_alpha(cr, m_data->layers.last());
865     m_data->layers.removeLast();
866 }
867
868 bool GraphicsContext::supportsTransparencyLayers()
869 {
870     return true;
871 }
872
873 void GraphicsContext::clearRect(const FloatRect& rect)
874 {
875     if (paintingDisabled())
876         return;
877
878     cairo_t* cr = platformContext()->cr();
879
880     cairo_save(cr);
881     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
882     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
883     cairo_fill(cr);
884     cairo_restore(cr);
885 }
886
887 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
888 {
889     if (paintingDisabled())
890         return;
891
892     cairo_t* cr = platformContext()->cr();
893     cairo_save(cr);
894     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
895     cairo_set_line_width(cr, width);
896     shadowAndStrokeCurrentCairoPath(this);
897     cairo_restore(cr);
898 }
899
900 void GraphicsContext::setLineCap(LineCap lineCap)
901 {
902     if (paintingDisabled())
903         return;
904
905     cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
906     switch (lineCap) {
907     case ButtCap:
908         // no-op
909         break;
910     case RoundCap:
911         cairoCap = CAIRO_LINE_CAP_ROUND;
912         break;
913     case SquareCap:
914         cairoCap = CAIRO_LINE_CAP_SQUARE;
915         break;
916     }
917     cairo_set_line_cap(platformContext()->cr(), cairoCap);
918 }
919
920 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
921 {
922     cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
923 }
924
925 void GraphicsContext::setLineJoin(LineJoin lineJoin)
926 {
927     if (paintingDisabled())
928         return;
929
930     cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
931     switch (lineJoin) {
932     case MiterJoin:
933         // no-op
934         break;
935     case RoundJoin:
936         cairoJoin = CAIRO_LINE_JOIN_ROUND;
937         break;
938     case BevelJoin:
939         cairoJoin = CAIRO_LINE_JOIN_BEVEL;
940         break;
941     }
942     cairo_set_line_join(platformContext()->cr(), cairoJoin);
943 }
944
945 void GraphicsContext::setMiterLimit(float miter)
946 {
947     if (paintingDisabled())
948         return;
949
950     cairo_set_miter_limit(platformContext()->cr(), miter);
951 }
952
953 void GraphicsContext::setAlpha(float alpha)
954 {
955     platformContext()->setGlobalAlpha(alpha);
956 }
957
958 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode blendOp)
959 {
960     if (paintingDisabled())
961         return;
962
963     cairo_operator_t cairo_op;
964     if (blendOp == BlendModeNormal)
965         cairo_op = toCairoOperator(op);
966     else
967         cairo_op = toCairoOperator(blendOp);
968
969     cairo_set_operator(platformContext()->cr(), cairo_op);
970 }
971
972 void GraphicsContext::clip(const Path& path, WindRule windRule)
973 {
974     if (paintingDisabled())
975         return;
976
977     cairo_t* cr = platformContext()->cr();
978     OwnPtr<cairo_path_t> pathCopy;
979     if (!path.isNull()) {
980         pathCopy = adoptPtr(cairo_copy_path(path.platformPath()->context()));
981         cairo_append_path(cr, pathCopy.get());
982     }
983     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
984     if (windRule == RULE_NONZERO)
985         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
986     else
987         cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
988     cairo_clip(cr);
989     cairo_set_fill_rule(cr, savedFillRule);
990     m_data->clip(path);
991 }
992
993 void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
994 {
995     clip(path, windRule);
996 }
997
998 void GraphicsContext::clipOut(const Path& path)
999 {
1000     if (paintingDisabled())
1001         return;
1002
1003     cairo_t* cr = platformContext()->cr();
1004     double x1, y1, x2, y2;
1005     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1006     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1007     appendWebCorePathToCairoContext(cr, path);
1008
1009     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1010     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1011     cairo_clip(cr);
1012     cairo_set_fill_rule(cr, savedFillRule);
1013 }
1014
1015 void GraphicsContext::rotate(float radians)
1016 {
1017     if (paintingDisabled())
1018         return;
1019
1020     cairo_rotate(platformContext()->cr(), radians);
1021     m_data->rotate(radians);
1022 }
1023
1024 void GraphicsContext::scale(const FloatSize& size)
1025 {
1026     if (paintingDisabled())
1027         return;
1028
1029     cairo_scale(platformContext()->cr(), size.width(), size.height());
1030     m_data->scale(size);
1031 }
1032
1033 void GraphicsContext::clipOut(const FloatRect& r)
1034 {
1035     if (paintingDisabled())
1036         return;
1037
1038     cairo_t* cr = platformContext()->cr();
1039     double x1, y1, x2, y2;
1040     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1041     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1042     cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
1043     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1044     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1045     cairo_clip(cr);
1046     cairo_set_fill_rule(cr, savedFillRule);
1047 }
1048
1049 void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace)
1050 {
1051     if (paintingDisabled())
1052         return;
1053
1054     if (hasShadow())
1055         platformContext()->shadowBlur().drawRectShadow(this, rect);
1056
1057     cairo_t* cr = platformContext()->cr();
1058     cairo_save(cr);
1059     Path path;
1060     path.addRoundedRect(rect);
1061     appendWebCorePathToCairoContext(cr, path);
1062     setSourceRGBAFromColor(cr, color);
1063     cairo_fill(cr);
1064     cairo_restore(cr);
1065 }
1066
1067 void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color, ColorSpace)
1068 {
1069     if (paintingDisabled() || !color.isValid())
1070         return;
1071
1072     if (this->mustUseShadowBlur())
1073         platformContext()->shadowBlur().drawInsetShadow(this, rect, roundedHoleRect);
1074
1075     Path path;
1076     path.addRect(rect);
1077     if (!roundedHoleRect.radii().isZero())
1078         path.addRoundedRect(roundedHoleRect);
1079     else
1080         path.addRect(roundedHoleRect.rect());
1081
1082     cairo_t* cr = platformContext()->cr();
1083     cairo_save(cr);
1084     setPathOnCairoContext(platformContext()->cr(), path.platformPath()->context());
1085     fillCurrentCairoPath(this);
1086     cairo_restore(cr);
1087 }
1088
1089 #if PLATFORM(GTK)
1090 void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
1091 {
1092     m_data->expose = expose;
1093 }
1094
1095 GdkEventExpose* GraphicsContext::gdkExposeEvent() const
1096 {
1097     return m_data->expose;
1098 }
1099
1100 GdkWindow* GraphicsContext::gdkWindow() const
1101 {
1102     if (!m_data->expose)
1103         return 0;
1104
1105     return m_data->expose->window;
1106 }
1107 #endif
1108
1109 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1110 {
1111     if (paintingDisabled())
1112         return;
1113
1114     // When true, use the default Cairo backend antialias mode (usually this
1115     // enables standard 'grayscale' antialiasing); false to explicitly disable
1116     // antialiasing. This is the same strategy as used in drawConvexPolygon().
1117     cairo_set_antialias(platformContext()->cr(), enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
1118 }
1119
1120 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1121 {
1122     platformContext()->setImageInterpolationQuality(quality);
1123 }
1124
1125 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1126 {
1127     return platformContext()->imageInterpolationQuality();
1128 }
1129
1130 bool GraphicsContext::isAcceleratedContext() const
1131 {
1132     return cairo_surface_get_type(cairo_get_target(platformContext()->cr())) == CAIRO_SURFACE_TYPE_GL;
1133 }
1134
1135 #if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER)
1136 TransformationMatrix GraphicsContext::get3DTransform() const
1137 {
1138     // FIXME: Can we approximate the transformation better than this?
1139     return getCTM().toTransformationMatrix();
1140 }
1141
1142 void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
1143 {
1144     concatCTM(transform.toAffineTransform());
1145 }
1146
1147 void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
1148 {
1149     setCTM(transform.toAffineTransform());
1150 }
1151 #endif
1152
1153 } // namespace WebCore
1154
1155 #endif // USE(CAIRO)