Remove GraphicsContext::drawConvexPolygon() and GraphicsContext::clipConvexPolygon()
[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 "DisplayListRecorder.h"
41 #include "DrawErrorUnderline.h"
42 #include "FloatConversion.h"
43 #include "FloatRect.h"
44 #include "FloatRoundedRect.h"
45 #include "Font.h"
46 #include "GraphicsContextPlatformPrivateCairo.h"
47 #include "ImageBuffer.h"
48 #include "IntRect.h"
49 #include "NotImplemented.h"
50 #include "Path.h"
51 #include "Pattern.h"
52 #include "PlatformContextCairo.h"
53 #include "PlatformPathCairo.h"
54 #include "RefPtrCairo.h"
55 #include "ShadowBlur.h"
56 #include "TransformationMatrix.h"
57 #include <cairo.h>
58 #include <math.h>
59 #include <stdio.h>
60 #include <wtf/MathExtras.h>
61
62 #if PLATFORM(WIN)
63 #include <cairo-win32.h>
64 #endif
65
66 using namespace std;
67
68 namespace WebCore {
69
70 // A helper which quickly fills a rectangle with a simple color fill.
71 static inline void fillRectWithColor(cairo_t* cr, const FloatRect& rect, const Color& color)
72 {
73     if (!color.alpha() && cairo_get_operator(cr) == CAIRO_OPERATOR_OVER)
74         return;
75     setSourceRGBAFromColor(cr, color);
76     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
77     cairo_fill(cr);
78 }
79
80 enum PathDrawingStyle { 
81     Fill = 1,
82     Stroke = 2,
83     FillAndStroke = Fill + Stroke
84 };
85
86 static inline void drawPathShadow(GraphicsContext& context, PathDrawingStyle drawingStyle)
87 {
88     ShadowBlur& shadow = context.platformContext()->shadowBlur();
89     if (shadow.type() == ShadowBlur::NoShadow)
90         return;
91
92     // Calculate the extents of the rendered solid paths.
93     cairo_t* cairoContext = context.platformContext()->cr();
94     std::unique_ptr<cairo_path_t, void(*)(cairo_path_t*)> path(cairo_copy_path(cairoContext), [](cairo_path_t* path) {
95         cairo_path_destroy(path);
96     });
97
98     FloatRect solidFigureExtents;
99     double x0 = 0;
100     double x1 = 0;
101     double y0 = 0;
102     double y1 = 0;
103     if (drawingStyle & Stroke) {
104         cairo_stroke_extents(cairoContext, &x0, &y0, &x1, &y1);
105         solidFigureExtents = FloatRect(x0, y0, x1 - x0, y1 - y0);
106     }
107     if (drawingStyle & Fill) {
108         cairo_fill_extents(cairoContext, &x0, &y0, &x1, &y1);
109         FloatRect fillExtents(x0, y0, x1 - x0, y1 - y0);
110         solidFigureExtents.unite(fillExtents);
111     }
112
113     GraphicsContext* shadowContext = shadow.beginShadowLayer(context, solidFigureExtents);
114     if (!shadowContext)
115         return;
116
117     cairo_t* cairoShadowContext = shadowContext->platformContext()->cr();
118
119     // It's important to copy the context properties to the new shadow
120     // context to preserve things such as the fill rule and stroke width.
121     copyContextProperties(cairoContext, cairoShadowContext);
122
123     if (drawingStyle & Fill) {
124         cairo_save(cairoShadowContext);
125         cairo_append_path(cairoShadowContext, path.get());
126         shadowContext->platformContext()->prepareForFilling(context.state(), PlatformContextCairo::NoAdjustment);
127         cairo_fill(cairoShadowContext);
128         cairo_restore(cairoShadowContext);
129     }
130
131     if (drawingStyle & Stroke) {
132         cairo_append_path(cairoShadowContext, path.get());
133         shadowContext->platformContext()->prepareForStroking(context.state(), PlatformContextCairo::DoNotPreserveAlpha);
134         cairo_stroke(cairoShadowContext);
135     }
136
137     // The original path may still be hanging around on the context and endShadowLayer
138     // will take care of properly creating a path to draw the result shadow. We remove the path
139     // temporarily and then restore it.
140     // See: https://bugs.webkit.org/show_bug.cgi?id=108897
141     cairo_new_path(cairoContext);
142     shadow.endShadowLayer(context);
143     cairo_append_path(cairoContext, path.get());
144 }
145
146 static inline void fillCurrentCairoPath(GraphicsContext& context)
147 {
148     cairo_t* cr = context.platformContext()->cr();
149     cairo_save(cr);
150
151     context.platformContext()->prepareForFilling(context.state(), PlatformContextCairo::AdjustPatternForGlobalAlpha);
152     cairo_fill(cr);
153
154     cairo_restore(cr);
155 }
156
157 static inline void shadowAndFillCurrentCairoPath(GraphicsContext& context)
158 {
159     drawPathShadow(context, Fill);
160     fillCurrentCairoPath(context);
161 }
162
163 static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext& context)
164 {
165     drawPathShadow(context, Stroke);
166     context.platformContext()->prepareForStroking(context.state());
167     cairo_stroke(context.platformContext()->cr());
168 }
169
170 GraphicsContext::GraphicsContext(cairo_t* cr)
171 {
172     if (!cr)
173         return;
174
175     m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
176 }
177
178 void GraphicsContext::platformInit(PlatformContextCairo* platformContext)
179 {
180     if (!platformContext)
181         return;
182
183     m_data = new GraphicsContextPlatformPrivate(platformContext);
184     m_data->syncContext(platformContext->cr());
185 }
186
187 void GraphicsContext::platformDestroy()
188 {
189     delete m_data;
190 }
191
192 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
193 {
194     if (paintingDisabled())
195         return AffineTransform();
196
197     if (isRecording()) {
198         WTFLogAlways("GraphicsContext::getCTM() is not yet compatible with recording contexts.");
199         return AffineTransform();
200     }
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     ASSERT(!isRecording());
216     platformContext()->save();
217     m_data->save();
218 }
219
220 void GraphicsContext::restorePlatformState()
221 {
222     ASSERT(!isRecording());
223     platformContext()->restore();
224     m_data->restore();
225
226     platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
227                                                     m_state.shadowOffset,
228                                                     m_state.shadowColor,
229                                                     m_state.shadowsIgnoreTransforms);
230 }
231
232 // Draws a filled rectangle with a stroked border.
233 void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness)
234 {
235     if (paintingDisabled())
236         return;
237
238     if (isRecording()) {
239         m_displayListRecorder->drawRect(rect, borderThickness);
240         return;
241     }
242
243     ASSERT(!rect.isEmpty());
244
245     cairo_t* cr = platformContext()->cr();
246     cairo_save(cr);
247
248     fillRectWithColor(cr, rect, fillColor());
249
250     if (strokeStyle() != NoStroke) {
251         setSourceRGBAFromColor(cr, strokeColor());
252         FloatRect r(rect);
253         r.inflate(-.5f);
254         cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
255         cairo_set_line_width(cr, 1.0); // borderThickness?
256         cairo_stroke(cr);
257     }
258
259     cairo_restore(cr);
260 }
261
262 void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
263 {
264     if (paintingDisabled())
265         return;
266
267     if (isRecording()) {
268         m_displayListRecorder->drawNativeImage(imagePtr, imageSize, destRect, srcRect, op, blendMode, orientation);
269         return;
270     }
271
272     NativeImagePtr image = imagePtr;
273
274     platformContext()->save();
275
276     // Set the compositing operation.
277     if (op == CompositeSourceOver && blendMode == BlendModeNormal)
278         setCompositeOperation(CompositeCopy);
279     else
280         setCompositeOperation(op, blendMode);
281
282     FloatRect dst = destRect;
283
284     if (orientation != DefaultImageOrientation) {
285         // ImageOrientation expects the origin to be at (0, 0).
286         translate(dst.x(), dst.y());
287         dst.setLocation(FloatPoint());
288         concatCTM(orientation.transformFromDefault(dst.size()));
289         if (orientation.usesWidthAsHeight()) {
290             // The destination rectangle will have its width and height already reversed for the orientation of
291             // the image, as it was needed for page layout, so we need to reverse it back here.
292             dst = FloatRect(dst.x(), dst.y(), dst.height(), dst.width());
293         }
294     }
295
296     platformContext()->drawSurfaceToContext(image.get(), dst, srcRect, *this);
297     platformContext()->restore();
298 }
299
300 // This is only used to draw borders, so we should not draw shadows.
301 void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2)
302 {
303     if (paintingDisabled())
304         return;
305
306     if (strokeStyle() == NoStroke)
307         return;
308
309     if (isRecording()) {
310         m_displayListRecorder->drawLine(point1, point2);
311         return;
312     }
313
314     const Color& strokeColor = this->strokeColor();
315     float thickness = strokeThickness();
316     bool isVerticalLine = (point1.x() + thickness == point2.x());
317     float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x();
318     if (!thickness || !strokeWidth)
319         return;
320
321     cairo_t* cairoContext = platformContext()->cr();
322     StrokeStyle strokeStyle = this->strokeStyle();
323     float cornerWidth = 0;
324     bool drawsDashedLine = strokeStyle == DottedStroke || strokeStyle == DashedStroke;
325
326     if (drawsDashedLine) {
327         cairo_save(cairoContext);
328         // Figure out end points to ensure we always paint corners.
329         cornerWidth = strokeStyle == DottedStroke ? thickness : std::min(2 * thickness, std::max(thickness, strokeWidth / 3));
330         if (isVerticalLine) {
331             fillRectWithColor(cairoContext, FloatRect(point1.x(), point1.y(), thickness, cornerWidth), strokeColor);
332             fillRectWithColor(cairoContext, FloatRect(point1.x(), point2.y() - cornerWidth, thickness, cornerWidth), strokeColor);
333         } else {
334             fillRectWithColor(cairoContext, FloatRect(point1.x(), point1.y(), cornerWidth, thickness), strokeColor);
335             fillRectWithColor(cairoContext, FloatRect(point2.x() - cornerWidth, point1.y(), cornerWidth, thickness), strokeColor);
336         }
337         strokeWidth -= 2 * cornerWidth;
338         float patternWidth = strokeStyle == DottedStroke ? thickness : std::min(3 * thickness, std::max(thickness, strokeWidth / 3));
339         // Check if corner drawing sufficiently covers the line.
340         if (strokeWidth <= patternWidth + 1) {
341             cairo_restore(cairoContext);
342             return;
343         }
344
345         // Pattern starts with full fill and ends with the empty fill.
346         // 1. Let's start with the empty phase after the corner.
347         // 2. Check if we've got odd or even number of patterns and whether they fully cover the line.
348         // 3. In case of even number of patterns and/or remainder, move the pattern start position
349         // so that the pattern is balanced between the corners.
350         float patternOffset = patternWidth;
351         int numberOfSegments = std::floor(strokeWidth / patternWidth);
352         bool oddNumberOfSegments = numberOfSegments % 2;
353         float remainder = strokeWidth - (numberOfSegments * patternWidth);
354         if (oddNumberOfSegments && remainder)
355             patternOffset -= remainder / 2.f;
356         else if (!oddNumberOfSegments) {
357             if (remainder)
358                 patternOffset += patternOffset - (patternWidth + remainder) / 2.f;
359             else
360                 patternOffset += patternWidth / 2.f;
361         }
362         const double dashedLine[2] = { static_cast<double>(patternWidth), static_cast<double>(patternWidth) };
363         cairo_set_dash(cairoContext, dashedLine, 2, patternOffset);
364     } else {
365         setSourceRGBAFromColor(cairoContext, strokeColor);
366         if (thickness < 1)
367             cairo_set_line_width(cairoContext, 1);
368     }
369
370
371     FloatPoint p1 = point1;
372     FloatPoint p2 = point2;
373     // Center line and cut off corners for pattern patining.
374     if (isVerticalLine) {
375         float centerOffset = (p2.x() - p1.x()) / 2;
376         p1.move(centerOffset, cornerWidth);
377         p2.move(-centerOffset, -cornerWidth);
378     } else {
379         float centerOffset = (p2.y() - p1.y()) / 2;
380         p1.move(cornerWidth, centerOffset);
381         p2.move(-cornerWidth, -centerOffset);
382     }
383
384     if (shouldAntialias())
385         cairo_set_antialias(cairoContext, CAIRO_ANTIALIAS_NONE);
386
387     cairo_new_path(cairoContext);
388     cairo_move_to(cairoContext, p1.x(), p1.y());
389     cairo_line_to(cairoContext, p2.x(), p2.y());
390     cairo_stroke(cairoContext);
391     if (drawsDashedLine)
392         cairo_restore(cairoContext);
393     if (shouldAntialias())
394         cairo_set_antialias(cairoContext, CAIRO_ANTIALIAS_DEFAULT);
395 }
396
397 // This method is only used to draw the little circles used in lists.
398 void GraphicsContext::drawEllipse(const FloatRect& rect)
399 {
400     if (paintingDisabled())
401         return;
402
403     if (isRecording()) {
404         m_displayListRecorder->drawEllipse(rect);
405         return;
406     }
407
408     cairo_t* cr = platformContext()->cr();
409     cairo_save(cr);
410     float yRadius = .5 * rect.height();
411     float xRadius = .5 * rect.width();
412     cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
413     cairo_scale(cr, xRadius, yRadius);
414     cairo_arc(cr, 0., 0., 1., 0., 2 * piFloat);
415     cairo_restore(cr);
416
417     if (fillColor().alpha()) {
418         setSourceRGBAFromColor(cr, fillColor());
419         cairo_fill_preserve(cr);
420     }
421
422     if (strokeStyle() != NoStroke) {
423         setSourceRGBAFromColor(cr, strokeColor());
424         cairo_set_line_width(cr, strokeThickness());
425         cairo_stroke(cr);
426     } else
427         cairo_new_path(cr);
428 }
429
430 void GraphicsContext::fillPath(const Path& path)
431 {
432     if (paintingDisabled() || path.isEmpty())
433         return;
434
435     if (isRecording()) {
436         m_displayListRecorder->fillPath(path);
437         return;
438     }
439
440     cairo_t* cr = platformContext()->cr();
441     setPathOnCairoContext(cr, path.platformPath()->context());
442     shadowAndFillCurrentCairoPath(*this);
443 }
444
445 void GraphicsContext::strokePath(const Path& path)
446 {
447     if (paintingDisabled() || path.isEmpty())
448         return;
449
450     if (isRecording()) {
451         m_displayListRecorder->strokePath(path);
452         return;
453     }
454
455     cairo_t* cr = platformContext()->cr();
456     setPathOnCairoContext(cr, path.platformPath()->context());
457     shadowAndStrokeCurrentCairoPath(*this);
458 }
459
460 void GraphicsContext::fillRect(const FloatRect& rect)
461 {
462     if (paintingDisabled())
463         return;
464
465     if (isRecording()) {
466         m_displayListRecorder->fillRect(rect);
467         return;
468     }
469
470     cairo_t* cr = platformContext()->cr();
471     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
472     shadowAndFillCurrentCairoPath(*this);
473 }
474
475 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
476 {
477     if (paintingDisabled())
478         return;
479
480     if (isRecording()) {
481         m_displayListRecorder->fillRect(rect, color);
482         return;
483     }
484
485     if (hasShadow())
486         platformContext()->shadowBlur().drawRectShadow(*this, FloatRoundedRect(rect));
487
488     fillRectWithColor(platformContext()->cr(), rect, color);
489 }
490
491 void GraphicsContext::clip(const FloatRect& rect)
492 {
493     if (paintingDisabled())
494         return;
495
496     if (isRecording()) {
497         m_displayListRecorder->clip(rect);
498         return;
499     }
500
501     cairo_t* cr = platformContext()->cr();
502     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
503     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
504     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
505     // The rectangular clip function is traditionally not expected to
506     // antialias. If we don't force antialiased clipping here,
507     // edge fringe artifacts may occur at the layer edges
508     // when a transformation is applied to the GraphicsContext
509     // while drawing the transformed layer.
510     cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr);
511     cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
512     cairo_clip(cr);
513     cairo_set_fill_rule(cr, savedFillRule);
514     cairo_set_antialias(cr, savedAntialiasRule);
515     m_data->clip(rect);
516 }
517
518 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
519 {
520     if (paintingDisabled())
521         return;
522
523     if (isRecording()) {
524         m_displayListRecorder->clipPath(path, clipRule);
525         return;
526     }
527
528     cairo_t* cr = platformContext()->cr();
529     if (!path.isNull())
530         setPathOnCairoContext(cr, path.platformPath()->context());
531
532     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
533     cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
534     cairo_clip(cr);
535     cairo_set_fill_rule(cr, savedFillRule);
536
537     m_data->clip(path);
538 }
539
540 void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect)
541 {
542     if (paintingDisabled())
543         return;
544
545     RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore);
546     RefPtr<cairo_surface_t> surface = image->nativeImageForCurrentFrame();
547     if (surface)
548         platformContext()->pushImageMask(surface.get(), destRect);
549 }
550
551 IntRect GraphicsContext::clipBounds() const
552 {
553     if (paintingDisabled())
554         return IntRect();
555
556     if (isRecording()) {
557         WTFLogAlways("Getting the clip bounds not yet supported with display lists");
558         return IntRect(-2048, -2048, 4096, 4096); // FIXME: display lists.
559     }
560
561     double x1, x2, y1, y2;
562     cairo_clip_extents(platformContext()->cr(), &x1, &y1, &x2, &y2);
563     return enclosingIntRect(FloatRect(x1, y1, x2 - x1, y2 - y1));
564 }
565
566 static inline void adjustFocusRingColor(Color& color)
567 {
568 #if !PLATFORM(GTK)
569     // Force the alpha to 50%.  This matches what the Mac does with outline rings.
570     color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127));
571 #else
572     UNUSED_PARAM(color);
573 #endif
574 }
575
576 static inline void adjustFocusRingLineWidth(float& width)
577 {
578 #if PLATFORM(GTK)
579     width = 2;
580 #else
581     UNUSED_PARAM(width);
582 #endif
583 }
584
585 static inline StrokeStyle focusRingStrokeStyle()
586 {
587 #if PLATFORM(GTK)
588     return DottedStroke;
589 #else
590     return SolidStroke;
591 #endif
592 }
593
594 void GraphicsContext::drawFocusRing(const Path& path, float width, float /* offset */, const Color& color)
595 {
596     // FIXME: We should draw paths that describe a rectangle with rounded corners
597     // so as to be consistent with how we draw rectangular focus rings.
598     Color ringColor = color;
599     adjustFocusRingColor(ringColor);
600     adjustFocusRingLineWidth(width);
601
602     cairo_t* cr = platformContext()->cr();
603     cairo_save(cr);
604     appendWebCorePathToCairoContext(cr, path);
605     setSourceRGBAFromColor(cr, ringColor);
606     cairo_set_line_width(cr, width);
607     setPlatformStrokeStyle(focusRingStrokeStyle());
608     cairo_stroke(cr);
609     cairo_restore(cr);
610 }
611
612 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, float width, float /* offset */, const Color& color)
613 {
614     if (paintingDisabled())
615         return;
616
617     cairo_t* cr = platformContext()->cr();
618     cairo_save(cr);
619     cairo_push_group(cr);
620     cairo_new_path(cr);
621
622 #if PLATFORM(GTK)
623     for (const auto& rect : rects)
624         cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
625 #else
626     unsigned rectCount = rects.size();
627     int radius = (width - 1) / 2;
628     Path path;
629     for (unsigned i = 0; i < rectCount; ++i) {
630         if (i > 0)
631             path.clear();
632         path.addRoundedRect(rects[i], FloatSize(radius, radius));
633         appendWebCorePathToCairoContext(cr, path);
634     }
635 #endif
636     Color ringColor = color;
637     adjustFocusRingColor(ringColor);
638     adjustFocusRingLineWidth(width);
639     setSourceRGBAFromColor(cr, ringColor);
640     cairo_set_line_width(cr, width);
641     setPlatformStrokeStyle(focusRingStrokeStyle());
642
643     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
644     cairo_stroke_preserve(cr);
645
646     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
647     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
648     cairo_fill(cr);
649
650     cairo_pop_group_to_source(cr);
651     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
652     cairo_paint(cr);
653     cairo_restore(cr);
654 }
655
656 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines)
657 {
658     DashArray widths;
659     widths.append(width);
660     widths.append(0);
661     drawLinesForText(origin, widths, printing, doubleUnderlines);
662 }
663
664 void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleUnderlines)
665 {
666     if (paintingDisabled())
667         return;
668
669     if (widths.size() <= 0)
670         return;
671
672     if (isRecording()) {
673         m_displayListRecorder->drawLinesForText(point, widths, printing, doubleUnderlines, strokeThickness());
674         return;
675     }
676
677     Color localStrokeColor(strokeColor());
678
679     FloatRect bounds = computeLineBoundsAndAntialiasingModeForText(point, widths.last(), printing, localStrokeColor);
680
681     Vector<FloatRect, 4> dashBounds;
682     ASSERT(!(widths.size() % 2));
683     dashBounds.reserveInitialCapacity(dashBounds.size() / 2);
684     for (size_t i = 0; i < widths.size(); i += 2)
685         dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y()), FloatSize(widths[i+1] - widths[i], bounds.height())));
686
687     if (doubleUnderlines) {
688         // The space between double underlines is equal to the height of the underline
689         for (size_t i = 0; i < widths.size(); i += 2)
690             dashBounds.append(FloatRect(FloatPoint(bounds.x() + widths[i], bounds.y() + 2 * bounds.height()), FloatSize(widths[i+1] - widths[i], bounds.height())));
691     }
692
693     cairo_t* cr = platformContext()->cr();
694     cairo_save(cr);
695
696     for (auto& dash : dashBounds)
697         fillRectWithColor(cr, dash, localStrokeColor);
698
699     cairo_restore(cr);
700 }
701
702 void GraphicsContext::updateDocumentMarkerResources()
703 {
704     // Unnecessary, since our document markers don't use resources.
705 }
706
707 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
708 {
709     if (paintingDisabled())
710         return;
711
712     cairo_t* cr = platformContext()->cr();
713     cairo_save(cr);
714
715     switch (style) {
716     case DocumentMarkerSpellingLineStyle:
717         cairo_set_source_rgb(cr, 1, 0, 0);
718         break;
719     case DocumentMarkerGrammarLineStyle:
720         cairo_set_source_rgb(cr, 0, 1, 0);
721         break;
722     default:
723         cairo_restore(cr);
724         return;
725     }
726
727     drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
728
729     cairo_restore(cr);
730 }
731
732 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
733 {
734     if (paintingDisabled())
735         return frect;
736
737     if (isRecording()) {
738         WTFLogAlways("GraphicsContext::roundToDevicePixels() is not yet compatible with recording contexts.");
739         return frect;
740     }
741
742     FloatRect result;
743     double x = frect.x();
744     double y = frect.y();
745     cairo_t* cr = platformContext()->cr();
746     cairo_user_to_device(cr, &x, &y);
747     x = round(x);
748     y = round(y);
749     cairo_device_to_user(cr, &x, &y);
750     result.setX(narrowPrecisionToFloat(x));
751     result.setY(narrowPrecisionToFloat(y));
752
753     // We must ensure width and height are at least 1 (or -1) when
754     // we're given float values in the range between 0 and 1 (or -1 and 0).
755     double width = frect.width();
756     double height = frect.height();
757     cairo_user_to_device_distance(cr, &width, &height);
758     if (width > -1 && width < 0)
759         width = -1;
760     else if (width > 0 && width < 1)
761         width = 1;
762     else
763         width = round(width);
764     if (height > -1 && width < 0)
765         height = -1;
766     else if (height > 0 && height < 1)
767         height = 1;
768     else
769         height = round(height);
770     cairo_device_to_user_distance(cr, &width, &height);
771     result.setWidth(narrowPrecisionToFloat(width));
772     result.setHeight(narrowPrecisionToFloat(height));
773
774     return result;
775 }
776
777 void GraphicsContext::translate(float x, float y)
778 {
779     if (paintingDisabled())
780         return;
781
782     if (isRecording()) {
783         m_displayListRecorder->translate(x, y);
784         return;
785     }
786
787     cairo_t* cr = platformContext()->cr();
788     cairo_translate(cr, x, y);
789     m_data->translate(x, y);
790 }
791
792 void GraphicsContext::setPlatformFillColor(const Color&)
793 {
794     // Cairo contexts can't hold separate fill and stroke colors
795     // so we set them just before we actually fill or stroke
796 }
797
798 void GraphicsContext::setPlatformStrokeColor(const Color&)
799 {
800     // Cairo contexts can't hold separate fill and stroke colors
801     // so we set them just before we actually fill or stroke
802 }
803
804 void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
805 {
806     if (paintingDisabled())
807         return;
808
809     ASSERT(!isRecording());
810
811     cairo_set_line_width(platformContext()->cr(), strokeThickness);
812 }
813
814 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
815 {
816     static const double dashPattern[] = { 5.0, 5.0 };
817     static const double dotPattern[] = { 1.0, 1.0 };
818
819     if (paintingDisabled())
820         return;
821
822     ASSERT(!isRecording());
823
824     switch (strokeStyle) {
825     case NoStroke:
826         // FIXME: is it the right way to emulate NoStroke?
827         cairo_set_line_width(platformContext()->cr(), 0);
828         break;
829     case SolidStroke:
830     case DoubleStroke:
831     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=94110 - Needs platform support.
832         cairo_set_dash(platformContext()->cr(), 0, 0, 0);
833         break;
834     case DottedStroke:
835         cairo_set_dash(platformContext()->cr(), dotPattern, 2, 0);
836         break;
837     case DashedStroke:
838         cairo_set_dash(platformContext()->cr(), dashPattern, 2, 0);
839         break;
840     }
841 }
842
843 void GraphicsContext::setURLForRect(const URL&, const IntRect&)
844 {
845     notImplemented();
846 }
847
848 void GraphicsContext::concatCTM(const AffineTransform& transform)
849 {
850     if (paintingDisabled())
851         return;
852
853     if (isRecording()) {
854         m_displayListRecorder->concatCTM(transform);
855         return;
856     }
857
858     cairo_t* cr = platformContext()->cr();
859     const cairo_matrix_t matrix = cairo_matrix_t(transform);
860     cairo_transform(cr, &matrix);
861     m_data->concatCTM(transform);
862 }
863
864 void GraphicsContext::setCTM(const AffineTransform& transform)
865 {
866     if (paintingDisabled())
867         return;
868
869     if (isRecording()) {
870         WTFLogAlways("GraphicsContext::setCTM() is not compatible with recording contexts.");
871         return;
872     }
873
874     cairo_t* cr = platformContext()->cr();
875     const cairo_matrix_t matrix = cairo_matrix_t(transform);
876     cairo_set_matrix(cr, &matrix);
877     m_data->setCTM(transform);
878 }
879
880 void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&)
881 {
882     if (paintingDisabled())
883         return;
884
885     if (m_state.shadowsIgnoreTransforms) {
886         // Meaning that this graphics context is associated with a CanvasRenderingContext
887         // We flip the height since CG and HTML5 Canvas have opposite Y axis
888         m_state.shadowOffset = FloatSize(size.width(), -size.height());
889     }
890
891     // Cairo doesn't support shadows natively, they are drawn manually in the draw* functions using ShadowBlur.
892     platformContext()->shadowBlur().setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur),
893                                                     m_state.shadowOffset,
894                                                     m_state.shadowColor,
895                                                     m_state.shadowsIgnoreTransforms);
896 }
897
898 void GraphicsContext::clearPlatformShadow()
899 {
900     if (paintingDisabled())
901         return;
902
903     platformContext()->shadowBlur().clear();
904 }
905
906 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
907 {
908     if (paintingDisabled())
909         return;
910
911     ASSERT(!isRecording());
912
913     cairo_t* cr = platformContext()->cr();
914     cairo_push_group(cr);
915     m_data->layers.append(opacity);
916 }
917
918 void GraphicsContext::endPlatformTransparencyLayer()
919 {
920     if (paintingDisabled())
921         return;
922
923     ASSERT(!isRecording());
924
925     cairo_t* cr = platformContext()->cr();
926
927     cairo_pop_group_to_source(cr);
928     cairo_paint_with_alpha(cr, m_data->layers.last());
929     m_data->layers.removeLast();
930 }
931
932 bool GraphicsContext::supportsTransparencyLayers()
933 {
934     return true;
935 }
936
937 void GraphicsContext::clearRect(const FloatRect& rect)
938 {
939     if (paintingDisabled())
940         return;
941
942     if (isRecording()) {
943         m_displayListRecorder->clearRect(rect);
944         return;
945     }
946
947     cairo_t* cr = platformContext()->cr();
948
949     cairo_save(cr);
950     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
951     cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
952     cairo_fill(cr);
953     cairo_restore(cr);
954 }
955
956 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
957 {
958     if (paintingDisabled())
959         return;
960
961     if (isRecording()) {
962         m_displayListRecorder->strokeRect(rect, width);
963         return;
964     }
965
966     cairo_t* cr = platformContext()->cr();
967     cairo_save(cr);
968     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
969     cairo_set_line_width(cr, width);
970     shadowAndStrokeCurrentCairoPath(*this);
971     cairo_restore(cr);
972 }
973
974 void GraphicsContext::setLineCap(LineCap lineCap)
975 {
976     if (paintingDisabled())
977         return;
978
979     if (isRecording()) {
980         m_displayListRecorder->setLineCap(lineCap);
981         return;
982     }
983
984     cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
985     switch (lineCap) {
986     case ButtCap:
987         // no-op
988         break;
989     case RoundCap:
990         cairoCap = CAIRO_LINE_CAP_ROUND;
991         break;
992     case SquareCap:
993         cairoCap = CAIRO_LINE_CAP_SQUARE;
994         break;
995     }
996     cairo_set_line_cap(platformContext()->cr(), cairoCap);
997 }
998
999 static inline bool isDashArrayAllZero(const DashArray& dashes)
1000 {
1001     for (auto& dash : dashes) {
1002         if (dash)
1003             return false;
1004     }
1005     return true;
1006 }
1007
1008 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1009 {
1010     if (paintingDisabled())
1011         return;
1012
1013     if (isRecording()) {
1014         m_displayListRecorder->setLineDash(dashes, dashOffset);
1015         return;
1016     }
1017
1018     if (isDashArrayAllZero(dashes))
1019         cairo_set_dash(platformContext()->cr(), 0, 0, 0);
1020     else
1021         cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
1022 }
1023
1024 void GraphicsContext::setLineJoin(LineJoin lineJoin)
1025 {
1026     if (paintingDisabled())
1027         return;
1028
1029     if (isRecording()) {
1030         m_displayListRecorder->setLineJoin(lineJoin);
1031         return;
1032     }
1033
1034     cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
1035     switch (lineJoin) {
1036     case MiterJoin:
1037         // no-op
1038         break;
1039     case RoundJoin:
1040         cairoJoin = CAIRO_LINE_JOIN_ROUND;
1041         break;
1042     case BevelJoin:
1043         cairoJoin = CAIRO_LINE_JOIN_BEVEL;
1044         break;
1045     }
1046     cairo_set_line_join(platformContext()->cr(), cairoJoin);
1047 }
1048
1049 void GraphicsContext::setMiterLimit(float miter)
1050 {
1051     if (paintingDisabled())
1052         return;
1053
1054     cairo_set_miter_limit(platformContext()->cr(), miter);
1055 }
1056
1057 void GraphicsContext::setPlatformAlpha(float alpha)
1058 {
1059     platformContext()->setGlobalAlpha(alpha);
1060 }
1061
1062 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode blendOp)
1063 {
1064     if (paintingDisabled())
1065         return;
1066
1067     cairo_operator_t cairo_op;
1068     if (blendOp == BlendModeNormal)
1069         cairo_op = toCairoOperator(op);
1070     else
1071         cairo_op = toCairoOperator(blendOp);
1072
1073     cairo_set_operator(platformContext()->cr(), cairo_op);
1074 }
1075
1076 void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
1077 {
1078     clipPath(path, windRule);
1079 }
1080
1081 void GraphicsContext::clipOut(const Path& path)
1082 {
1083     if (paintingDisabled())
1084         return;
1085
1086     if (isRecording()) {
1087         m_displayListRecorder->clipOut(path);
1088         return;
1089     }
1090
1091     cairo_t* cr = platformContext()->cr();
1092     double x1, y1, x2, y2;
1093     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1094     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1095     appendWebCorePathToCairoContext(cr, path);
1096
1097     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1098     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1099     cairo_clip(cr);
1100     cairo_set_fill_rule(cr, savedFillRule);
1101 }
1102
1103 void GraphicsContext::rotate(float radians)
1104 {
1105     if (paintingDisabled())
1106         return;
1107
1108     if (isRecording()) {
1109         m_displayListRecorder->rotate(radians);
1110         return;
1111     }
1112
1113     cairo_rotate(platformContext()->cr(), radians);
1114     m_data->rotate(radians);
1115 }
1116
1117 void GraphicsContext::scale(const FloatSize& size)
1118 {
1119     if (paintingDisabled())
1120         return;
1121
1122     if (isRecording()) {
1123         m_displayListRecorder->scale(size);
1124         return;
1125     }
1126
1127     cairo_scale(platformContext()->cr(), size.width(), size.height());
1128     m_data->scale(size);
1129 }
1130
1131 void GraphicsContext::clipOut(const FloatRect& r)
1132 {
1133     if (paintingDisabled())
1134         return;
1135
1136     if (isRecording()) {
1137         m_displayListRecorder->clipOut(r);
1138         return;
1139     }
1140
1141     cairo_t* cr = platformContext()->cr();
1142     double x1, y1, x2, y2;
1143     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1144     cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1145     cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
1146     cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1147     cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1148     cairo_clip(cr);
1149     cairo_set_fill_rule(cr, savedFillRule);
1150 }
1151
1152 void GraphicsContext::platformFillRoundedRect(const FloatRoundedRect& rect, const Color& color)
1153 {
1154     if (paintingDisabled())
1155         return;
1156
1157     ASSERT(!isRecording());
1158
1159     if (hasShadow())
1160         platformContext()->shadowBlur().drawRectShadow(*this, rect);
1161
1162     cairo_t* cr = platformContext()->cr();
1163     cairo_save(cr);
1164     Path path;
1165     path.addRoundedRect(rect);
1166     appendWebCorePathToCairoContext(cr, path);
1167     setSourceRGBAFromColor(cr, color);
1168     cairo_fill(cr);
1169     cairo_restore(cr);
1170 }
1171
1172 void GraphicsContext::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
1173 {
1174     if (paintingDisabled() || !color.isValid())
1175         return;
1176
1177     if (isRecording()) {
1178         m_displayListRecorder->fillRectWithRoundedHole(rect, roundedHoleRect, color);
1179         return;
1180     }
1181
1182     if (this->mustUseShadowBlur())
1183         platformContext()->shadowBlur().drawInsetShadow(*this, rect, roundedHoleRect);
1184
1185     Path path;
1186     path.addRect(rect);
1187     if (!roundedHoleRect.radii().isZero())
1188         path.addRoundedRect(roundedHoleRect);
1189     else
1190         path.addRect(roundedHoleRect.rect());
1191
1192     cairo_t* cr = platformContext()->cr();
1193     cairo_save(cr);
1194     setPathOnCairoContext(platformContext()->cr(), path.platformPath()->context());
1195     fillCurrentCairoPath(*this);
1196     cairo_restore(cr);
1197 }
1198
1199 void GraphicsContext::drawPattern(Image& image, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
1200 {
1201     if (paintingDisabled())
1202         return;
1203
1204     if (isRecording()) {
1205         m_displayListRecorder->drawPattern(image, tileRect, patternTransform, phase, spacing, op, destRect, blendMode);
1206         return;
1207     }
1208
1209     RefPtr<cairo_surface_t> surface = image.nativeImageForCurrentFrame();
1210     if (!surface) // If it's too early we won't have an image yet.
1211         return;
1212
1213     cairo_t* cr = platformContext()->cr();
1214     drawPatternToCairoContext(cr, surface.get(), IntSize(image.size()), tileRect, patternTransform, phase, toCairoOperator(op), destRect);
1215 }
1216
1217 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1218 {
1219     if (paintingDisabled())
1220         return;
1221
1222     ASSERT(!isRecording());
1223
1224     // When true, use the default Cairo backend antialias mode (usually this
1225     // enables standard 'grayscale' antialiasing); false to explicitly disable
1226     // antialiasing.
1227     cairo_set_antialias(platformContext()->cr(), enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
1228 }
1229
1230 void GraphicsContext::setPlatformImageInterpolationQuality(InterpolationQuality quality)
1231 {
1232     ASSERT(!isRecording());
1233
1234     platformContext()->setImageInterpolationQuality(quality);
1235 }
1236
1237 bool GraphicsContext::isAcceleratedContext() const
1238 {
1239     if (isRecording())
1240         return false;
1241
1242     return cairo_surface_get_type(cairo_get_target(platformContext()->cr())) == CAIRO_SURFACE_TYPE_GL;
1243 }
1244
1245 #if ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER)
1246 TransformationMatrix GraphicsContext::get3DTransform() const
1247 {
1248     // FIXME: Can we approximate the transformation better than this?
1249     return getCTM().toTransformationMatrix();
1250 }
1251
1252 void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
1253 {
1254     concatCTM(transform.toAffineTransform());
1255 }
1256
1257 void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
1258 {
1259     setCTM(transform.toAffineTransform());
1260 }
1261 #endif // ENABLE(3D_TRANSFORMS) && USE(TEXTURE_MAPPER)
1262
1263 } // namespace WebCore
1264
1265 #endif // USE(CAIRO)