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
67using namespace std;
68
69namespace WebCore {
70
71// A helper which quickly fills a rectangle with a simple color fill.
72static 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
81static 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
89enum PathDrawingStyle {
90    Fill = 1,
91    Stroke = 2,
92    FillAndStroke = Fill + Stroke
93};
94
95static 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
153static 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
164static inline void shadowAndFillCurrentCairoPath(GraphicsContext* context)
165{
166    drawPathShadow(context, Fill);
167    fillCurrentCairoPath(context);
168}
169
170static inline void shadowAndStrokeCurrentCairoPath(GraphicsContext* context)
171{
172    drawPathShadow(context, Stroke);
173    context->platformContext()->prepareForStroking(context->state());
174    cairo_stroke(context->platformContext()->cr());
175}
176
177GraphicsContext::GraphicsContext(cairo_t* cr)
178    : m_updatingControlTints(false)
179    , m_transparencyCount(0)
180{
181    m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
182}
183
184void GraphicsContext::platformInit(PlatformContextCairo* platformContext, bool)
185{
186    m_data = new GraphicsContextPlatformPrivate(platformContext);
187    if (platformContext)
188        m_data->syncContext(platformContext->cr());
189    else
190        setPaintingDisabled(true);
191}
192
193void GraphicsContext::platformDestroy()
194{
195    delete m_data;
196}
197
198AffineTransform 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
209PlatformContextCairo* GraphicsContext::platformContext() const
210{
211    return m_data->platformContext;
212}
213
214void GraphicsContext::savePlatformState()
215{
216    platformContext()->save();
217    m_data->save();
218}
219
220void 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.
233void 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
257static 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
284static 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.
335void 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.
347void GraphicsContext::drawEllipse(const IntRect& rect)
348{
349    if (paintingDisabled())
350        return;
351
352    cairo_t* cr = platformContext()->cr();
353    cairo_save(cr);
354    float yRadius = .5 * rect.height();
355    float xRadius = .5 * rect.width();
356    cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
357    cairo_scale(cr, xRadius, yRadius);
358    cairo_arc(cr, 0., 0., 1., 0., 2 * piFloat);
359    cairo_restore(cr);
360
361    if (fillColor().alpha()) {
362        setSourceRGBAFromColor(cr, fillColor());
363        cairo_fill_preserve(cr);
364    }
365
366    if (strokeStyle() != NoStroke) {
367        setSourceRGBAFromColor(cr, strokeColor());
368        cairo_set_line_width(cr, strokeThickness());
369        cairo_stroke(cr);
370    } else
371        cairo_new_path(cr);
372}
373
374void 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
404void 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
427void 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
437void 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
447void 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
457void 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
468void 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
490void 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
502IntRect 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
509static 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
519static inline void adjustFocusRingLineWidth(int& width)
520{
521#if PLATFORM(GTK)
522    width = 2;
523#else
524    UNUSED_PARAM(width);
525#endif
526}
527
528static inline StrokeStyle focusRingStrokeStyle()
529{
530#if PLATFORM(GTK)
531    return DottedStroke;
532#else
533    return SolidStroke;
534#endif
535}
536
537void 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
555void 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
619FloatRect 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
626void 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
634void 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
668void GraphicsContext::updateDocumentMarkerResources()
669{
670    // Unnecessary, since our document markers don't use resources.
671}
672
673void 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
698FloatRect 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
735void 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
745void 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
751void 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
757void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
758{
759    if (paintingDisabled())
760        return;
761
762    cairo_set_line_width(platformContext()->cr(), strokeThickness);
763}
764
765void 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
792void GraphicsContext::setURLForRect(const URL&, const IntRect&)
793{
794    notImplemented();
795}
796
797void 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
808void 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
819void 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
838void GraphicsContext::clearPlatformShadow()
839{
840    if (paintingDisabled())
841        return;
842
843    platformContext()->shadowBlur().clear();
844}
845
846void 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
856void 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
868bool GraphicsContext::supportsTransparencyLayers()
869{
870    return true;
871}
872
873void 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
887void 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
900void 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
920void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
921{
922    cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
923}
924
925void 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
945void GraphicsContext::setMiterLimit(float miter)
946{
947    if (paintingDisabled())
948        return;
949
950    cairo_set_miter_limit(platformContext()->cr(), miter);
951}
952
953void GraphicsContext::setAlpha(float alpha)
954{
955    platformContext()->setGlobalAlpha(alpha);
956}
957
958void 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
972void 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
993void GraphicsContext::canvasClip(const Path& path, WindRule windRule)
994{
995    clip(path, windRule);
996}
997
998void 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
1015void GraphicsContext::rotate(float radians)
1016{
1017    if (paintingDisabled())
1018        return;
1019
1020    cairo_rotate(platformContext()->cr(), radians);
1021    m_data->rotate(radians);
1022}
1023
1024void 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
1033void 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
1049void 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
1067void 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)
1090void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
1091{
1092    m_data->expose = expose;
1093}
1094
1095GdkEventExpose* GraphicsContext::gdkExposeEvent() const
1096{
1097    return m_data->expose;
1098}
1099
1100GdkWindow* GraphicsContext::gdkWindow() const
1101{
1102    if (!m_data->expose)
1103        return 0;
1104
1105    return m_data->expose->window;
1106}
1107#endif
1108
1109void 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
1120void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1121{
1122    platformContext()->setImageInterpolationQuality(quality);
1123}
1124
1125InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1126{
1127    return platformContext()->imageInterpolationQuality();
1128}
1129
1130bool 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)
1136TransformationMatrix GraphicsContext::get3DTransform() const
1137{
1138    // FIXME: Can we approximate the transformation better than this?
1139    return getCTM().toTransformationMatrix();
1140}
1141
1142void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
1143{
1144    concatCTM(transform.toAffineTransform());
1145}
1146
1147void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
1148{
1149    setCTM(transform.toAffineTransform());
1150}
1151#endif
1152
1153} // namespace WebCore
1154
1155#endif // USE(CAIRO)
1156