1/*
2 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4 * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org>
5 * Copyright (C) 2008 Nuanti Ltd.
6 * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
7 * Copyright (C) 2010, 2011 Igalia S.L.
8 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "GraphicsContext.h"
34
35#if USE(CAIRO)
36
37#include "AffineTransform.h"
38#include "CairoUtilities.h"
39#include "ContextShadow.h"
40#include "FloatConversion.h"
41#include "FloatRect.h"
42#include "Font.h"
43#include "GraphicsContextPlatformPrivateCairo.h"
44#include "OwnPtrCairo.h"
45#include "IntRect.h"
46#include "NotImplemented.h"
47#include "Path.h"
48#include "Pattern.h"
49#include "PlatformContextCairo.h"
50#include "PlatformPathCairo.h"
51#include "RefPtrCairo.h"
52#include "SimpleFontData.h"
53#include <cairo.h>
54#include <math.h>
55#include <stdio.h>
56#include <wtf/MathExtras.h>
57
58#if PLATFORM(GTK)
59#include <gdk/gdk.h>
60#include <pango/pango.h>
61#elif PLATFORM(WIN)
62#include <cairo-win32.h>
63#endif
64
65using namespace std;
66
67#ifndef M_PI
68#define M_PI 3.14159265358979323846
69#endif
70
71namespace WebCore {
72
73static inline void setPlatformFill(GraphicsContext* context, cairo_t* cr)
74{
75    cairo_pattern_t* pattern = 0;
76    cairo_save(cr);
77
78    const GraphicsContextState& state = context->state();
79    if (state.fillPattern) {
80        AffineTransform affine;
81        pattern = state.fillPattern->createPlatformPattern(affine);
82        cairo_set_source(cr, pattern);
83    } else if (state.fillGradient)
84        cairo_set_source(cr, state.fillGradient->platformGradient());
85    else
86        setSourceRGBAFromColor(cr, context->fillColor());
87    cairo_clip_preserve(cr);
88    cairo_paint_with_alpha(cr, state.globalAlpha);
89    cairo_restore(cr);
90    if (pattern)
91        cairo_pattern_destroy(pattern);
92}
93
94static inline void setPlatformStroke(GraphicsContext* context, cairo_t* cr)
95{
96    cairo_pattern_t* pattern = 0;
97    cairo_save(cr);
98
99    const GraphicsContextState& state = context->state();
100    if (state.strokePattern) {
101        AffineTransform affine;
102        pattern = state.strokePattern->createPlatformPattern(affine);
103        cairo_set_source(cr, pattern);
104    } else if (state.strokeGradient)
105        cairo_set_source(cr, state.strokeGradient->platformGradient());
106    else  {
107        Color strokeColor = colorWithOverrideAlpha(context->strokeColor().rgb(), context->strokeColor().alpha() / 255.f * state.globalAlpha);
108        setSourceRGBAFromColor(cr, strokeColor);
109    }
110    if (state.globalAlpha < 1.0f && (state.strokePattern || state.strokeGradient)) {
111        cairo_push_group(cr);
112        cairo_paint_with_alpha(cr, state.globalAlpha);
113        cairo_pop_group_to_source(cr);
114    }
115    cairo_stroke_preserve(cr);
116    cairo_restore(cr);
117    if (pattern)
118        cairo_pattern_destroy(pattern);
119}
120
121// A fillRect helper
122static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col)
123{
124    setSourceRGBAFromColor(cr, col);
125    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
126    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
127    cairo_fill(cr);
128}
129
130static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points)
131{
132    cairo_move_to(context, points[0].x(), points[0].y());
133    for (size_t i = 1; i < numPoints; i++)
134        cairo_line_to(context, points[i].x(), points[i].y());
135    cairo_close_path(context);
136}
137
138enum PathDrawingStyle {
139    Fill = 1,
140    Stroke = 2,
141    FillAndStroke = Fill + Stroke
142};
143
144static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle drawingStyle)
145{
146    ContextShadow* shadow = context->contextShadow();
147    ASSERT(shadow);
148    if (shadow->m_type == ContextShadow::NoShadow)
149        return;
150
151    // Calculate the extents of the rendered solid paths.
152    cairo_t* cairoContext = context->platformContext()->cr();
153    OwnPtr<cairo_path_t> path(cairo_copy_path(cairoContext));
154
155    FloatRect solidFigureExtents;
156    double x0 = 0;
157    double x1 = 0;
158    double y0 = 0;
159    double y1 = 0;
160    if (drawingStyle & Stroke) {
161        cairo_stroke_extents(cairoContext, &x0, &y0, &x1, &y1);
162        solidFigureExtents = FloatRect(x0, y0, x1 - x0, y1 - y0);
163    }
164    if (drawingStyle & Fill) {
165        cairo_fill_extents(cairoContext, &x0, &y0, &x1, &y1);
166        FloatRect fillExtents(x0, y0, x1 - x0, y1 - y0);
167        solidFigureExtents.unite(fillExtents);
168    }
169
170    cairo_t* shadowContext = shadow->beginShadowLayer(context, solidFigureExtents);
171    if (!shadowContext)
172        return;
173
174    // It's important to copy the context properties to the new shadow
175    // context to preserve things such as the fill rule and stroke width.
176    copyContextProperties(cairoContext, shadowContext);
177    cairo_append_path(shadowContext, path.get());
178
179    if (drawingStyle & Fill)
180        setPlatformFill(context, shadowContext);
181    if (drawingStyle & Stroke)
182        setPlatformStroke(context, shadowContext);
183
184    shadow->endShadowLayer(context);
185}
186
187static void fillCurrentCairoPath(GraphicsContext* context, cairo_t* cairoContext)
188{
189    cairo_set_fill_rule(cairoContext, context->fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
190    drawPathShadow(context, Fill);
191
192    setPlatformFill(context, cairoContext);
193    cairo_new_path(cairoContext);
194}
195
196static void strokeCurrentCairoPath(GraphicsContext* context,  cairo_t* cairoContext)
197{
198    drawPathShadow(context, Stroke);
199    setPlatformStroke(context, cairoContext);
200    cairo_new_path(cairoContext);
201}
202
203GraphicsContext::GraphicsContext(cairo_t* cr)
204    : m_updatingControlTints(false)
205{
206    m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
207}
208
209void GraphicsContext::platformInit(PlatformContextCairo* platformContext)
210{
211    m_data = new GraphicsContextPlatformPrivate(platformContext);
212    if (platformContext)
213        m_data->syncContext(platformContext->cr());
214    else
215        setPaintingDisabled(true);
216}
217
218void GraphicsContext::platformDestroy()
219{
220    delete m_data;
221}
222
223AffineTransform GraphicsContext::getCTM() const
224{
225    cairo_t* cr = platformContext()->cr();
226    cairo_matrix_t m;
227    cairo_get_matrix(cr, &m);
228    return AffineTransform(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
229}
230
231PlatformContextCairo* GraphicsContext::platformContext() const
232{
233    return m_data->platformContext;
234}
235
236void GraphicsContext::savePlatformState()
237{
238    platformContext()->save();
239    m_data->save();
240    m_data->shadowStack.append(m_data->shadow);
241}
242
243void GraphicsContext::restorePlatformState()
244{
245    if (m_data->shadowStack.isEmpty())
246        m_data->shadow = ContextShadow();
247    else {
248        m_data->shadow = m_data->shadowStack.last();
249        m_data->shadowStack.removeLast();
250    }
251
252    platformContext()->restore();
253    m_data->restore();
254}
255
256// Draws a filled rectangle with a stroked border.
257void GraphicsContext::drawRect(const IntRect& rect)
258{
259    if (paintingDisabled())
260        return;
261
262    cairo_t* cr = platformContext()->cr();
263    cairo_save(cr);
264
265    if (fillColor().alpha())
266        fillRectSourceOver(cr, rect, fillColor());
267
268    if (strokeStyle() != NoStroke) {
269        setSourceRGBAFromColor(cr, strokeColor());
270        FloatRect r(rect);
271        r.inflate(-.5f);
272        cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
273        cairo_set_line_width(cr, 1.0);
274        cairo_stroke(cr);
275    }
276
277    cairo_restore(cr);
278}
279
280// This is only used to draw borders.
281void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
282{
283    if (paintingDisabled())
284        return;
285
286    StrokeStyle style = strokeStyle();
287    if (style == NoStroke)
288        return;
289
290    cairo_t* cr = platformContext()->cr();
291    cairo_save(cr);
292
293    float width = strokeThickness();
294    if (width < 1)
295        width = 1;
296
297    FloatPoint p1 = point1;
298    FloatPoint p2 = point2;
299    bool isVerticalLine = (p1.x() == p2.x());
300
301    adjustLineToPixelBoundaries(p1, p2, width, style);
302    cairo_set_line_width(cr, width);
303
304    int patWidth = 0;
305    switch (style) {
306    case NoStroke:
307    case SolidStroke:
308        break;
309    case DottedStroke:
310        patWidth = static_cast<int>(width);
311        break;
312    case DashedStroke:
313        patWidth = 3*static_cast<int>(width);
314        break;
315    }
316
317    setSourceRGBAFromColor(cr, strokeColor());
318
319    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
320
321    if (patWidth) {
322        // Do a rect fill of our endpoints.  This ensures we always have the
323        // appearance of being a border.  We then draw the actual dotted/dashed line.
324        if (isVerticalLine) {
325            fillRectSourceOver(cr, FloatRect(p1.x() - width/2, p1.y() - width, width, width), strokeColor());
326            fillRectSourceOver(cr, FloatRect(p2.x() - width/2, p2.y(), width, width), strokeColor());
327        } else {
328            fillRectSourceOver(cr, FloatRect(p1.x() - width, p1.y() - width/2, width, width), strokeColor());
329            fillRectSourceOver(cr, FloatRect(p2.x(), p2.y() - width/2, width, width), strokeColor());
330        }
331
332        // Example: 80 pixels with a width of 30 pixels.
333        // Remainder is 20.  The maximum pixels of line we could paint
334        // will be 50 pixels.
335        int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*static_cast<int>(width);
336        int remainder = distance%patWidth;
337        int coverage = distance-remainder;
338        int numSegments = coverage/patWidth;
339
340        float patternOffset = 0;
341        // Special case 1px dotted borders for speed.
342        if (patWidth == 1)
343            patternOffset = 1.0;
344        else {
345            bool evenNumberOfSegments = !(numSegments % 2);
346            if (remainder)
347                evenNumberOfSegments = !evenNumberOfSegments;
348            if (evenNumberOfSegments) {
349                if (remainder) {
350                    patternOffset += patWidth - remainder;
351                    patternOffset += remainder / 2;
352                } else
353                    patternOffset = patWidth / 2;
354            } else if (!evenNumberOfSegments) {
355                if (remainder)
356                    patternOffset = (patWidth - remainder) / 2;
357            }
358        }
359
360        double dash = patWidth;
361        cairo_set_dash(cr, &dash, 1, patternOffset);
362    }
363
364    cairo_move_to(cr, p1.x(), p1.y());
365    cairo_line_to(cr, p2.x(), p2.y());
366
367    cairo_stroke(cr);
368    cairo_restore(cr);
369}
370
371// This method is only used to draw the little circles used in lists.
372void GraphicsContext::drawEllipse(const IntRect& rect)
373{
374    if (paintingDisabled())
375        return;
376
377    cairo_t* cr = platformContext()->cr();
378    cairo_save(cr);
379    float yRadius = .5 * rect.height();
380    float xRadius = .5 * rect.width();
381    cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
382    cairo_scale(cr, xRadius, yRadius);
383    cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
384    cairo_restore(cr);
385
386    if (fillColor().alpha()) {
387        setSourceRGBAFromColor(cr, fillColor());
388        cairo_fill_preserve(cr);
389    }
390
391    if (strokeStyle() != NoStroke) {
392        setSourceRGBAFromColor(cr, strokeColor());
393        cairo_set_line_width(cr, strokeThickness());
394        cairo_stroke(cr);
395    } else
396        cairo_new_path(cr);
397}
398
399void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
400{
401    if (paintingDisabled() || strokeStyle() == NoStroke)
402        return;
403
404    int x = rect.x();
405    int y = rect.y();
406    float w = rect.width();
407    float h = rect.height();
408    float scaleFactor = h / w;
409    float reverseScaleFactor = w / h;
410
411    float hRadius = w / 2;
412    float vRadius = h / 2;
413    float fa = startAngle;
414    float falen =  fa + angleSpan;
415
416    cairo_t* cr = platformContext()->cr();
417    cairo_save(cr);
418
419    if (w != h)
420        cairo_scale(cr, 1., scaleFactor);
421
422    cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180);
423
424    if (w != h)
425        cairo_scale(cr, 1., reverseScaleFactor);
426
427    float width = strokeThickness();
428    int patWidth = 0;
429
430    switch (strokeStyle()) {
431    case DottedStroke:
432        patWidth = static_cast<int>(width / 2);
433        break;
434    case DashedStroke:
435        patWidth = 3 * static_cast<int>(width / 2);
436        break;
437    default:
438        break;
439    }
440
441    setSourceRGBAFromColor(cr, strokeColor());
442
443    if (patWidth) {
444        // Example: 80 pixels with a width of 30 pixels.
445        // Remainder is 20.  The maximum pixels of line we could paint
446        // will be 50 pixels.
447        int distance;
448        if (hRadius == vRadius)
449            distance = static_cast<int>((M_PI * hRadius) / 2.0);
450        else // We are elliptical and will have to estimate the distance
451            distance = static_cast<int>((M_PI * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0)) / 2.0);
452
453        int remainder = distance % patWidth;
454        int coverage = distance - remainder;
455        int numSegments = coverage / patWidth;
456
457        float patternOffset = 0.0;
458        // Special case 1px dotted borders for speed.
459        if (patWidth == 1)
460            patternOffset = 1.0;
461        else {
462            bool evenNumberOfSegments = !(numSegments % 2);
463            if (remainder)
464                evenNumberOfSegments = !evenNumberOfSegments;
465            if (evenNumberOfSegments) {
466                if (remainder) {
467                    patternOffset += patWidth - remainder;
468                    patternOffset += remainder / 2.0;
469                } else
470                    patternOffset = patWidth / 2.0;
471            } else {
472                if (remainder)
473                    patternOffset = (patWidth - remainder) / 2.0;
474            }
475        }
476
477        double dash = patWidth;
478        cairo_set_dash(cr, &dash, 1, patternOffset);
479    }
480
481    cairo_stroke(cr);
482    cairo_restore(cr);
483}
484
485void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
486{
487    if (paintingDisabled())
488        return;
489
490    if (npoints <= 1)
491        return;
492
493    cairo_t* cr = platformContext()->cr();
494
495    cairo_save(cr);
496    cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
497    addConvexPolygonToContext(cr, npoints, points);
498
499    if (fillColor().alpha()) {
500        setSourceRGBAFromColor(cr, fillColor());
501        cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
502        cairo_fill_preserve(cr);
503    }
504
505    if (strokeStyle() != NoStroke) {
506        setSourceRGBAFromColor(cr, strokeColor());
507        cairo_set_line_width(cr, strokeThickness());
508        cairo_stroke(cr);
509    } else
510        cairo_new_path(cr);
511
512    cairo_restore(cr);
513}
514
515void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
516{
517    if (paintingDisabled())
518        return;
519
520    if (numPoints <= 1)
521        return;
522
523    cairo_t* cr = platformContext()->cr();
524
525    cairo_new_path(cr);
526    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
527    cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr);
528
529    cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
530    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
531    addConvexPolygonToContext(cr, numPoints, points);
532    cairo_clip(cr);
533
534    cairo_set_antialias(cr, savedAntialiasRule);
535    cairo_set_fill_rule(cr, savedFillRule);
536}
537
538void GraphicsContext::fillPath(const Path& path)
539{
540    if (paintingDisabled())
541        return;
542
543    cairo_t* cr = platformContext()->cr();
544    setPathOnCairoContext(cr, path.platformPath()->context());
545    fillCurrentCairoPath(this, cr);
546}
547
548void GraphicsContext::strokePath(const Path& path)
549{
550    if (paintingDisabled())
551        return;
552
553    cairo_t* cr = platformContext()->cr();
554    setPathOnCairoContext(cr, path.platformPath()->context());
555    strokeCurrentCairoPath(this, cr);
556}
557
558void GraphicsContext::fillRect(const FloatRect& rect)
559{
560    if (paintingDisabled())
561        return;
562
563    cairo_t* cr = platformContext()->cr();
564    cairo_save(cr);
565    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
566    fillCurrentCairoPath(this, cr);
567    cairo_restore(cr);
568}
569
570void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
571{
572    if (paintingDisabled())
573        return;
574
575    if (hasShadow())
576        m_data->shadow.drawRectShadow(this, enclosingIntRect(rect));
577
578    if (color.alpha())
579        fillRectSourceOver(platformContext()->cr(), rect, color);
580}
581
582void GraphicsContext::clip(const FloatRect& rect)
583{
584    if (paintingDisabled())
585        return;
586
587    cairo_t* cr = platformContext()->cr();
588    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
589    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
590    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
591    cairo_clip(cr);
592    cairo_set_fill_rule(cr, savedFillRule);
593    m_data->clip(rect);
594}
595
596void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
597{
598    if (paintingDisabled())
599        return;
600
601    cairo_t* cr = platformContext()->cr();
602    setPathOnCairoContext(cr, path.platformPath()->context());
603    cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
604    cairo_clip(cr);
605}
606
607static inline void adjustFocusRingColor(Color& color)
608{
609#if !PLATFORM(GTK)
610    // Force the alpha to 50%.  This matches what the Mac does with outline rings.
611    color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127));
612#endif
613}
614
615static inline void adjustFocusRingLineWidth(int& width)
616{
617#if PLATFORM(GTK)
618    width = 2;
619#endif
620}
621
622static inline StrokeStyle focusRingStrokeStyle()
623{
624#if PLATFORM(GTK)
625    return DottedStroke;
626#else
627    return SolidStroke;
628#endif
629}
630
631void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color)
632{
633    // FIXME: We should draw paths that describe a rectangle with rounded corners
634    // so as to be consistent with how we draw rectangular focus rings.
635    Color ringColor = color;
636    adjustFocusRingColor(ringColor);
637    adjustFocusRingLineWidth(width);
638
639    cairo_t* cr = platformContext()->cr();
640    cairo_save(cr);
641    appendWebCorePathToCairoContext(cr, path);
642    setSourceRGBAFromColor(cr, ringColor);
643    cairo_set_line_width(cr, width);
644    setPlatformStrokeStyle(focusRingStrokeStyle());
645    cairo_stroke(cr);
646    cairo_restore(cr);
647}
648
649void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
650{
651    if (paintingDisabled())
652        return;
653
654    unsigned rectCount = rects.size();
655
656    cairo_t* cr = platformContext()->cr();
657    cairo_save(cr);
658    cairo_push_group(cr);
659    cairo_new_path(cr);
660
661#if PLATFORM(GTK)
662#ifdef GTK_API_VERSION_2
663    GdkRegion* reg = gdk_region_new();
664#else
665    cairo_region_t* reg = cairo_region_create();
666#endif
667
668    for (unsigned i = 0; i < rectCount; i++) {
669#ifdef GTK_API_VERSION_2
670        GdkRectangle rect = rects[i];
671        gdk_region_union_with_rect(reg, &rect);
672#else
673        cairo_rectangle_int_t rect = rects[i];
674        cairo_region_union_rectangle(reg, &rect);
675#endif
676    }
677    gdk_cairo_region(cr, reg);
678#ifdef GTK_API_VERSION_2
679    gdk_region_destroy(reg);
680#else
681    cairo_region_destroy(reg);
682#endif
683#else
684    int radius = (width - 1) / 2;
685    Path path;
686    for (unsigned i = 0; i < rectCount; ++i) {
687        if (i > 0)
688            path.clear();
689        path.addRoundedRect(rects[i], FloatSize(radius, radius));
690        appendWebCorePathToCairoContext(cr, path);
691    }
692#endif
693    Color ringColor = color;
694    adjustFocusRingColor(ringColor);
695    adjustFocusRingLineWidth(width);
696    setSourceRGBAFromColor(cr, ringColor);
697    cairo_set_line_width(cr, width);
698    setPlatformStrokeStyle(focusRingStrokeStyle());
699
700    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
701    cairo_stroke_preserve(cr);
702
703    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
704    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
705    cairo_fill(cr);
706
707    cairo_pop_group_to_source(cr);
708    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
709    cairo_paint(cr);
710    cairo_restore(cr);
711}
712
713void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing)
714{
715    if (paintingDisabled())
716        return;
717
718    FloatPoint endPoint = origin + FloatSize(width, 0);
719
720    // FIXME: Loss of precision here. Might consider rounding.
721    drawLine(IntPoint(origin.x(), origin.y()), IntPoint(endPoint.x(), endPoint.y()));
722}
723
724#if !PLATFORM(GTK)
725#include "DrawErrorUnderline.h"
726#endif
727
728void GraphicsContext::drawLineForTextChecking(const FloatPoint& origin, float width, TextCheckingLineStyle style)
729{
730    if (paintingDisabled())
731        return;
732
733    cairo_t* cr = platformContext()->cr();
734    cairo_save(cr);
735
736    switch (style) {
737    case TextCheckingSpellingLineStyle:
738        cairo_set_source_rgb(cr, 1, 0, 0);
739        break;
740    case TextCheckingGrammarLineStyle:
741        cairo_set_source_rgb(cr, 0, 1, 0);
742        break;
743    default:
744        cairo_restore(cr);
745        return;
746    }
747
748#if PLATFORM(GTK)
749    // We ignore most of the provided constants in favour of the platform style
750    pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
751#else
752    drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
753#endif
754
755    cairo_restore(cr);
756}
757
758FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
759{
760    FloatRect result;
761    double x = frect.x();
762    double y = frect.y();
763    cairo_t* cr = platformContext()->cr();
764    cairo_user_to_device(cr, &x, &y);
765    x = round(x);
766    y = round(y);
767    cairo_device_to_user(cr, &x, &y);
768    result.setX(narrowPrecisionToFloat(x));
769    result.setY(narrowPrecisionToFloat(y));
770
771    // We must ensure width and height are at least 1 (or -1) when
772    // we're given float values in the range between 0 and 1 (or -1 and 0).
773    double width = frect.width();
774    double height = frect.height();
775    cairo_user_to_device_distance(cr, &width, &height);
776    if (width > -1 && width < 0)
777        width = -1;
778    else if (width > 0 && width < 1)
779        width = 1;
780    else
781        width = round(width);
782    if (height > -1 && width < 0)
783        height = -1;
784    else if (height > 0 && height < 1)
785        height = 1;
786    else
787        height = round(height);
788    cairo_device_to_user_distance(cr, &width, &height);
789    result.setWidth(narrowPrecisionToFloat(width));
790    result.setHeight(narrowPrecisionToFloat(height));
791
792    return result;
793}
794
795void GraphicsContext::translate(float x, float y)
796{
797    if (paintingDisabled())
798        return;
799
800    cairo_t* cr = platformContext()->cr();
801    cairo_translate(cr, x, y);
802    m_data->translate(x, y);
803}
804
805void GraphicsContext::setPlatformFillColor(const Color& col, ColorSpace colorSpace)
806{
807    // Cairo contexts can't hold separate fill and stroke colors
808    // so we set them just before we actually fill or stroke
809}
810
811void GraphicsContext::setPlatformStrokeColor(const Color& col, ColorSpace colorSpace)
812{
813    // Cairo contexts can't hold separate fill and stroke colors
814    // so we set them just before we actually fill or stroke
815}
816
817void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
818{
819    if (paintingDisabled())
820        return;
821
822    cairo_set_line_width(platformContext()->cr(), strokeThickness);
823}
824
825void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
826{
827    static double dashPattern[] = {5.0, 5.0};
828    static double dotPattern[] = {1.0, 1.0};
829
830    if (paintingDisabled())
831        return;
832
833    switch (strokeStyle) {
834    case NoStroke:
835        // FIXME: is it the right way to emulate NoStroke?
836        cairo_set_line_width(platformContext()->cr(), 0);
837        break;
838    case SolidStroke:
839        cairo_set_dash(platformContext()->cr(), 0, 0, 0);
840        break;
841    case DottedStroke:
842        cairo_set_dash(platformContext()->cr(), dotPattern, 2, 0);
843        break;
844    case DashedStroke:
845        cairo_set_dash(platformContext()->cr(), dashPattern, 2, 0);
846        break;
847    }
848}
849
850void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
851{
852    notImplemented();
853}
854
855void GraphicsContext::concatCTM(const AffineTransform& transform)
856{
857    if (paintingDisabled())
858        return;
859
860    cairo_t* cr = platformContext()->cr();
861    const cairo_matrix_t matrix = cairo_matrix_t(transform);
862    cairo_transform(cr, &matrix);
863    m_data->concatCTM(transform);
864}
865
866void GraphicsContext::setCTM(const AffineTransform& transform)
867{
868    if (paintingDisabled())
869        return;
870
871    cairo_t* cr = platformContext()->cr();
872    const cairo_matrix_t matrix = cairo_matrix_t(transform);
873    cairo_set_matrix(cr, &matrix);
874    m_data->setCTM(transform);
875}
876
877void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
878{
879    if (paintingDisabled())
880        return;
881
882    cairo_t* cr = platformContext()->cr();
883    clip(rect);
884
885    Path p;
886    FloatRect r(rect);
887    // Add outer ellipse
888    p.addEllipse(r);
889    // Add inner ellipse
890    r.inflate(-thickness);
891    p.addEllipse(r);
892    appendWebCorePathToCairoContext(cr, p);
893
894    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
895    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
896    cairo_clip(cr);
897    cairo_set_fill_rule(cr, savedFillRule);
898}
899
900void GraphicsContext::setPlatformShadow(FloatSize const& size, float blur, Color const& color, ColorSpace)
901{
902    // Cairo doesn't support shadows natively, they are drawn manually in the draw* functions
903    if (m_state.shadowsIgnoreTransforms) {
904        // Meaning that this graphics context is associated with a CanvasRenderingContext
905        // We flip the height since CG and HTML5 Canvas have opposite Y axis
906        m_state.shadowOffset = FloatSize(size.width(), -size.height());
907        m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height()));
908    } else
909        m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height()));
910
911    m_data->shadow.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms);
912}
913
914ContextShadow* GraphicsContext::contextShadow()
915{
916    return &m_data->shadow;
917}
918
919void GraphicsContext::clearPlatformShadow()
920{
921    m_data->shadow.clear();
922}
923
924void GraphicsContext::beginTransparencyLayer(float opacity)
925{
926    if (paintingDisabled())
927        return;
928
929    cairo_t* cr = platformContext()->cr();
930    cairo_push_group(cr);
931    m_data->layers.append(opacity);
932    m_data->beginTransparencyLayer();
933}
934
935void GraphicsContext::endTransparencyLayer()
936{
937    if (paintingDisabled())
938        return;
939
940    cairo_t* cr = platformContext()->cr();
941
942    cairo_pop_group_to_source(cr);
943    cairo_paint_with_alpha(cr, m_data->layers.last());
944    m_data->layers.removeLast();
945    m_data->endTransparencyLayer();
946}
947
948void GraphicsContext::clearRect(const FloatRect& rect)
949{
950    if (paintingDisabled())
951        return;
952
953    cairo_t* cr = platformContext()->cr();
954
955    cairo_save(cr);
956    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
957    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
958    cairo_fill(cr);
959    cairo_restore(cr);
960}
961
962void GraphicsContext::strokeRect(const FloatRect& rect, float width)
963{
964    if (paintingDisabled())
965        return;
966
967    cairo_t* cr = platformContext()->cr();
968    cairo_save(cr);
969    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
970    cairo_set_line_width(cr, width);
971    strokeCurrentCairoPath(this, cr);
972    cairo_restore(cr);
973}
974
975void GraphicsContext::setLineCap(LineCap lineCap)
976{
977    if (paintingDisabled())
978        return;
979
980    cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT;
981    switch (lineCap) {
982    case ButtCap:
983        // no-op
984        break;
985    case RoundCap:
986        cairoCap = CAIRO_LINE_CAP_ROUND;
987        break;
988    case SquareCap:
989        cairoCap = CAIRO_LINE_CAP_SQUARE;
990        break;
991    }
992    cairo_set_line_cap(platformContext()->cr(), cairoCap);
993}
994
995void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
996{
997    cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
998}
999
1000void GraphicsContext::setLineJoin(LineJoin lineJoin)
1001{
1002    if (paintingDisabled())
1003        return;
1004
1005    cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER;
1006    switch (lineJoin) {
1007    case MiterJoin:
1008        // no-op
1009        break;
1010    case RoundJoin:
1011        cairoJoin = CAIRO_LINE_JOIN_ROUND;
1012        break;
1013    case BevelJoin:
1014        cairoJoin = CAIRO_LINE_JOIN_BEVEL;
1015        break;
1016    }
1017    cairo_set_line_join(platformContext()->cr(), cairoJoin);
1018}
1019
1020void GraphicsContext::setMiterLimit(float miter)
1021{
1022    if (paintingDisabled())
1023        return;
1024
1025    cairo_set_miter_limit(platformContext()->cr(), miter);
1026}
1027
1028void GraphicsContext::setAlpha(float alpha)
1029{
1030    m_state.globalAlpha = alpha;
1031}
1032
1033float GraphicsContext::getAlpha()
1034{
1035    return m_state.globalAlpha;
1036}
1037
1038void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
1039{
1040    if (paintingDisabled())
1041        return;
1042
1043    cairo_set_operator(platformContext()->cr(), toCairoOperator(op));
1044}
1045
1046void GraphicsContext::clip(const Path& path)
1047{
1048    if (paintingDisabled())
1049        return;
1050
1051    cairo_t* cr = platformContext()->cr();
1052    OwnPtr<cairo_path_t> p(cairo_copy_path(path.platformPath()->context()));
1053    cairo_append_path(cr, p.get());
1054    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1055    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
1056    cairo_clip(cr);
1057    cairo_set_fill_rule(cr, savedFillRule);
1058    m_data->clip(path);
1059}
1060
1061void GraphicsContext::canvasClip(const Path& path)
1062{
1063    clip(path);
1064}
1065
1066void GraphicsContext::clipOut(const Path& path)
1067{
1068    if (paintingDisabled())
1069        return;
1070
1071    cairo_t* cr = platformContext()->cr();
1072    double x1, y1, x2, y2;
1073    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1074    cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1075    appendWebCorePathToCairoContext(cr, path);
1076
1077    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1078    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1079    cairo_clip(cr);
1080    cairo_set_fill_rule(cr, savedFillRule);
1081}
1082
1083void GraphicsContext::rotate(float radians)
1084{
1085    if (paintingDisabled())
1086        return;
1087
1088    cairo_rotate(platformContext()->cr(), radians);
1089    m_data->rotate(radians);
1090}
1091
1092void GraphicsContext::scale(const FloatSize& size)
1093{
1094    if (paintingDisabled())
1095        return;
1096
1097    cairo_scale(platformContext()->cr(), size.width(), size.height());
1098    m_data->scale(size);
1099}
1100
1101void GraphicsContext::clipOut(const IntRect& r)
1102{
1103    if (paintingDisabled())
1104        return;
1105
1106    cairo_t* cr = platformContext()->cr();
1107    double x1, y1, x2, y2;
1108    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
1109    cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
1110    cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
1111    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
1112    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
1113    cairo_clip(cr);
1114    cairo_set_fill_rule(cr, savedFillRule);
1115}
1116
1117static inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile)
1118{
1119    FloatPoint phase = dest.location();
1120    phase.move(-tile.x(), -tile.y());
1121
1122    return phase;
1123}
1124
1125void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
1126{
1127    if (paintingDisabled())
1128        return;
1129
1130    if (hasShadow())
1131        m_data->shadow.drawRectShadow(this, r, topLeft, topRight, bottomLeft, bottomRight);
1132
1133    cairo_t* cr = platformContext()->cr();
1134    cairo_save(cr);
1135    Path path;
1136    path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight);
1137    appendWebCorePathToCairoContext(cr, path);
1138    setSourceRGBAFromColor(cr, color);
1139    cairo_fill(cr);
1140    cairo_restore(cr);
1141}
1142
1143#if PLATFORM(GTK)
1144void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
1145{
1146    m_data->expose = expose;
1147}
1148
1149GdkEventExpose* GraphicsContext::gdkExposeEvent() const
1150{
1151    return m_data->expose;
1152}
1153
1154GdkWindow* GraphicsContext::gdkWindow() const
1155{
1156    if (!m_data->expose)
1157        return 0;
1158
1159    return m_data->expose->window;
1160}
1161#endif
1162
1163void GraphicsContext::setPlatformShouldAntialias(bool enable)
1164{
1165    if (paintingDisabled())
1166        return;
1167
1168    // When true, use the default Cairo backend antialias mode (usually this
1169    // enables standard 'grayscale' antialiasing); false to explicitly disable
1170    // antialiasing. This is the same strategy as used in drawConvexPolygon().
1171    cairo_set_antialias(platformContext()->cr(), enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
1172}
1173
1174void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
1175{
1176}
1177
1178InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1179{
1180    return InterpolationDefault;
1181}
1182
1183} // namespace WebCore
1184
1185#endif // USE(CAIRO)
1186