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