Home | History | Annotate | Download | only in cairo
      1 /*
      2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
      3  * Copyright (C) 2007 Alp Toker <alp (at) atoker.com>
      4  * Copyright (C) 2008, 2009 Dirk Schulze <krit (at) webkit.org>
      5  * Copyright (C) 2008 Nuanti Ltd.
      6  * Copyright (C) 2009 Brent Fulgham <bfulgham (at) 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 
     65 using namespace std;
     66 
     67 #ifndef M_PI
     68 #define M_PI 3.14159265358979323846
     69 #endif
     70 
     71 namespace WebCore {
     72 
     73 static 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 
     94 static 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
    122 static 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 
    130 static 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 
    138 enum PathDrawingStyle {
    139     Fill = 1,
    140     Stroke = 2,
    141     FillAndStroke = Fill + Stroke
    142 };
    143 
    144 static 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 
    187 static 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 
    196 static void strokeCurrentCairoPath(GraphicsContext* context,  cairo_t* cairoContext)
    197 {
    198     drawPathShadow(context, Stroke);
    199     setPlatformStroke(context, cairoContext);
    200     cairo_new_path(cairoContext);
    201 }
    202 
    203 GraphicsContext::GraphicsContext(cairo_t* cr)
    204     : m_updatingControlTints(false)
    205 {
    206     m_data = new GraphicsContextPlatformPrivateToplevel(new PlatformContextCairo(cr));
    207 }
    208 
    209 void 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 
    218 void GraphicsContext::platformDestroy()
    219 {
    220     delete m_data;
    221 }
    222 
    223 AffineTransform 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 
    231 PlatformContextCairo* GraphicsContext::platformContext() const
    232 {
    233     return m_data->platformContext;
    234 }
    235 
    236 void GraphicsContext::savePlatformState()
    237 {
    238     platformContext()->save();
    239     m_data->save();
    240     m_data->shadowStack.append(m_data->shadow);
    241 }
    242 
    243 void 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.
    257 void 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.
    281 void 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.
    372 void 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 
    399 void 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 
    485 void 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 
    515 void 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 
    538 void 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 
    548 void 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 
    558 void 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 
    570 void 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 
    582 void 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 
    596 void 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 
    607 static 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 
    615 static inline void adjustFocusRingLineWidth(int& width)
    616 {
    617 #if PLATFORM(GTK)
    618     width = 2;
    619 #endif
    620 }
    621 
    622 static inline StrokeStyle focusRingStrokeStyle()
    623 {
    624 #if PLATFORM(GTK)
    625     return DottedStroke;
    626 #else
    627     return SolidStroke;
    628 #endif
    629 }
    630 
    631 void 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 
    649 void 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 
    713 void 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 
    728 void 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 
    758 FloatRect 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 
    795 void 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 
    805 void 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 
    811 void 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 
    817 void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
    818 {
    819     if (paintingDisabled())
    820         return;
    821 
    822     cairo_set_line_width(platformContext()->cr(), strokeThickness);
    823 }
    824 
    825 void 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 
    850 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
    851 {
    852     notImplemented();
    853 }
    854 
    855 void 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 
    866 void 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 
    877 void 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 
    900 void 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 
    914 ContextShadow* GraphicsContext::contextShadow()
    915 {
    916     return &m_data->shadow;
    917 }
    918 
    919 void GraphicsContext::clearPlatformShadow()
    920 {
    921     m_data->shadow.clear();
    922 }
    923 
    924 void 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 
    935 void 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 
    948 void 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 
    962 void 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 
    975 void 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 
    995 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
    996 {
    997     cairo_set_dash(platformContext()->cr(), dashes.data(), dashes.size(), dashOffset);
    998 }
    999 
   1000 void 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 
   1020 void GraphicsContext::setMiterLimit(float miter)
   1021 {
   1022     if (paintingDisabled())
   1023         return;
   1024 
   1025     cairo_set_miter_limit(platformContext()->cr(), miter);
   1026 }
   1027 
   1028 void GraphicsContext::setAlpha(float alpha)
   1029 {
   1030     m_state.globalAlpha = alpha;
   1031 }
   1032 
   1033 float GraphicsContext::getAlpha()
   1034 {
   1035     return m_state.globalAlpha;
   1036 }
   1037 
   1038 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
   1039 {
   1040     if (paintingDisabled())
   1041         return;
   1042 
   1043     cairo_set_operator(platformContext()->cr(), toCairoOperator(op));
   1044 }
   1045 
   1046 void 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 
   1061 void GraphicsContext::canvasClip(const Path& path)
   1062 {
   1063     clip(path);
   1064 }
   1065 
   1066 void 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 
   1083 void GraphicsContext::rotate(float radians)
   1084 {
   1085     if (paintingDisabled())
   1086         return;
   1087 
   1088     cairo_rotate(platformContext()->cr(), radians);
   1089     m_data->rotate(radians);
   1090 }
   1091 
   1092 void 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 
   1101 void 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 
   1117 static 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 
   1125 void 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)
   1144 void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose)
   1145 {
   1146     m_data->expose = expose;
   1147 }
   1148 
   1149 GdkEventExpose* GraphicsContext::gdkExposeEvent() const
   1150 {
   1151     return m_data->expose;
   1152 }
   1153 
   1154 GdkWindow* GraphicsContext::gdkWindow() const
   1155 {
   1156     if (!m_data->expose)
   1157         return 0;
   1158 
   1159     return m_data->expose->window;
   1160 }
   1161 #endif
   1162 
   1163 void 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 
   1174 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
   1175 {
   1176 }
   1177 
   1178 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
   1179 {
   1180     return InterpolationDefault;
   1181 }
   1182 
   1183 } // namespace WebCore
   1184 
   1185 #endif // USE(CAIRO)
   1186