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