GraphicsContextCairo.cpp 31.5 KB
Newer Older
1 2
/*
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
bdash's avatar
bdash committed
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 Igalia S.L.
8
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (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 33
 */

#include "config.h"
#include "GraphicsContext.h"
darin's avatar
darin committed
34

35 36
#if PLATFORM(CAIRO)

37
#include "AffineTransform.h"
weinig's avatar
weinig committed
38
#include "CairoPath.h"
39
#include "CairoUtilities.h"
40
#include "ContextShadow.h"
41
#include "FloatRect.h"
darin's avatar
darin committed
42
#include "Font.h"
43
#include "GraphicsContextPlatformPrivateCairo.h"
44
#include "OwnPtrCairo.h"
45
#include "IntRect.h"
weinig's avatar
weinig committed
46 47
#include "NotImplemented.h"
#include "Path.h"
48
#include "Pattern.h"
49
#include "RefPtrCairo.h"
mitz@apple.com's avatar
mitz@apple.com committed
50
#include "SimpleFontData.h"
ggaren's avatar
ggaren committed
51 52
#include <cairo.h>
#include <math.h>
kjk's avatar
kjk committed
53
#include <stdio.h>
darin's avatar
darin committed
54
#include <wtf/MathExtras.h>
kevino's avatar
kevino committed
55

zecke's avatar
/:  
zecke committed
56
#if PLATFORM(GTK)
57
#include <gdk/gdk.h>
alp's avatar
alp committed
58
#include <pango/pango.h>
59 60
#elif PLATFORM(WIN)
#include <cairo-win32.h>
61
#endif
kjk's avatar
kjk committed
62

63 64
using namespace std;

65 66 67
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
68 69 70

namespace WebCore {

71
static inline void setPlatformFill(GraphicsContext* context, cairo_t* cr)
72
{
73
    cairo_pattern_t* pattern = 0;
74
    cairo_save(cr);
75 76 77
    
    const GraphicsContextState& state = context->state();
    if (state.fillPattern) {
78
        AffineTransform affine;
79
        pattern = state.fillPattern->createPlatformPattern(affine);
80
        cairo_set_source(cr, pattern);
81 82
    } else if (state.fillGradient)
        cairo_set_source(cr, state.fillGradient->platformGradient());
83
    else
84
        setSourceRGBAFromColor(cr, context->fillColor());
85
    cairo_clip_preserve(cr);
86
    cairo_paint_with_alpha(cr, state.globalAlpha);
87
    cairo_restore(cr);
88 89
    if (pattern)
        cairo_pattern_destroy(pattern);
90 91
}

92
static inline void setPlatformStroke(GraphicsContext* context, cairo_t* cr)
93
{
94
    cairo_pattern_t* pattern = 0;
95
    cairo_save(cr);
96 97 98
    
    const GraphicsContextState& state = context->state();
    if (state.strokePattern) {
99
        AffineTransform affine;
100
        pattern = state.strokePattern->createPlatformPattern(affine);
101
        cairo_set_source(cr, pattern);
102 103
    } else if (state.strokeGradient)
        cairo_set_source(cr, state.strokeGradient->platformGradient());
104
    else  {
105
        Color strokeColor = colorWithOverrideAlpha(context->strokeColor().rgb(), context->strokeColor().alpha() / 255.f * state.globalAlpha);
106
        setSourceRGBAFromColor(cr, strokeColor);
107
    }
108
    if (state.globalAlpha < 1.0f && (state.strokePattern || state.strokeGradient)) {
109
        cairo_push_group(cr);
110
        cairo_paint_with_alpha(cr, state.globalAlpha);
111 112 113
        cairo_pop_group_to_source(cr);
    }
    cairo_stroke_preserve(cr);
114
    cairo_restore(cr);
115 116
    if (pattern)
        cairo_pattern_destroy(pattern);
117 118
}

119
// A fillRect helper
120
static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col)
121
{
122
    setSourceRGBAFromColor(cr, col);
123
    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
124 125 126 127
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_fill(cr);
}

128 129 130 131 132 133 134 135
static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points)
{
    cairo_move_to(context, points[0].x(), points[0].y());
    for (size_t i = 1; i < numPoints; i++)
        cairo_line_to(context, points[i].x(), points[i].y());
    cairo_close_path(context);
}

136 137 138 139 140
enum PathDrawingStyle { 
    Fill = 1,
    Stroke = 2,
    FillAndStroke = Fill + Stroke
};
141

142
static inline void drawPathShadow(GraphicsContext* context, PathDrawingStyle drawingStyle)
143
{
144 145 146
    ContextShadow* shadow = context->contextShadow();
    ASSERT(shadow);
    if (shadow->m_type == ContextShadow::NoShadow)
147
        return;
148

149 150
    // Calculate the extents of the rendered solid paths.
    cairo_t* cairoContext = context->platformContext();
151
    OwnPtr<cairo_path_t> path(cairo_copy_path(cairoContext));
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

    FloatRect solidFigureExtents;
    double x0 = 0;
    double x1 = 0;
    double y0 = 0;
    double y1 = 0;
    if (drawingStyle & Stroke) {
        cairo_stroke_extents(cairoContext, &x0, &y0, &x1, &y1);
        solidFigureExtents = FloatRect(x0, y0, x1 - x0, y1 - y0);
    }
    if (drawingStyle & Fill) {
        cairo_fill_extents(cairoContext, &x0, &y0, &x1, &y1);
        FloatRect fillExtents(x0, y0, x1 - x0, y1 - y0);
        solidFigureExtents.unite(fillExtents);
    }
167

168 169 170
    cairo_t* shadowContext = shadow->beginShadowLayer(cairoContext, solidFigureExtents);
    if (!shadowContext)
        return;
171

172 173 174
    // It's important to copy the context properties to the new shadow
    // context to preserve things such as the fill rule and stroke width.
    copyContextProperties(cairoContext, shadowContext);
175
    cairo_append_path(shadowContext, path.get());
176

177
    if (drawingStyle & Fill)
178
        setPlatformFill(context, shadowContext);
179
    if (drawingStyle & Stroke)
180
        setPlatformStroke(context, shadowContext);
181

182
    shadow->endShadowLayer(cairoContext);
183 184
}

185
static void fillCurrentCairoPath(GraphicsContext* context, cairo_t* cairoContext)
186 187
{
    cairo_set_fill_rule(cairoContext, context->fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
188
    drawPathShadow(context, Fill);
189

190
    setPlatformFill(context, cairoContext);
191 192 193
    cairo_new_path(cairoContext);
}

194
static void strokeCurrentCairoPath(GraphicsContext* context,  cairo_t* cairoContext)
195
{
196 197
    drawPathShadow(context, Stroke);
    setPlatformStroke(context, cairoContext);
198 199 200
    cairo_new_path(cairoContext);
}

201
void GraphicsContext::platformInit(PlatformGraphicsContext* cr)
202
{
203
    m_data = new GraphicsContextPlatformPrivate;
204
    m_data->cr = cairo_reference(cr);
205
    m_data->syncContext(cr);
206
    setPaintingDisabled(!cr);
207 208
}

209
void GraphicsContext::platformDestroy()
210 211 212 213
{
    delete m_data;
}

214
AffineTransform GraphicsContext::getCTM() const
215 216 217 218 219 220 221
{
    cairo_t* cr = platformContext();
    cairo_matrix_t m;
    cairo_get_matrix(cr, &m);
    return AffineTransform(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
}

222 223
cairo_t* GraphicsContext::platformContext() const
{
224
    return m_data->cr;
225 226
}

227
void GraphicsContext::savePlatformState()
228
{
229
    cairo_save(m_data->cr);
230
    m_data->save();
231
    m_data->shadowStack.append(m_data->shadow);
232 233
}

234
void GraphicsContext::restorePlatformState()
235
{
236
    cairo_restore(m_data->cr);
237
    m_data->restore();
238 239 240 241 242 243 244

    if (m_data->shadowStack.isEmpty())
        m_data->shadow = ContextShadow();
    else {
        m_data->shadow = m_data->shadowStack.last();
        m_data->shadowStack.removeLast();
    }
245 246 247
}

// Draws a filled rectangle with a stroked border.
248
void GraphicsContext::drawRect(const IntRect& rect)
249
{
250
    if (paintingDisabled())
251
        return;
252 253 254

    cairo_t* cr = m_data->cr;
    cairo_save(cr);
255

256
    if (fillColor().alpha())
257
        fillRectSourceOver(cr, rect, fillColor());
258

259
    if (strokeStyle() != NoStroke) {
260
        setSourceRGBAFromColor(cr, strokeColor());
261 262
        FloatRect r(rect);
        r.inflate(-.5f);
263 264 265
        cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height());
        cairo_set_line_width(cr, 1.0);
        cairo_stroke(cr);
266
    }
267

268
    cairo_restore(cr);
269 270 271
}

// This is only used to draw borders.
272
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
273
{
274
    if (paintingDisabled())
275 276
        return;

277 278
    StrokeStyle style = strokeStyle();
    if (style == NoStroke)
279
        return;
andersca's avatar
andersca committed
280

281 282
    cairo_t* cr = m_data->cr;
    cairo_save(cr);
andersca's avatar
andersca committed
283

284
    float width = strokeThickness();
285 286 287
    if (width < 1)
        width = 1;

288 289 290
    FloatPoint p1 = point1;
    FloatPoint p2 = point2;
    bool isVerticalLine = (p1.x() == p2.x());
291

292
    adjustLineToPixelBoundaries(p1, p2, width, style);
293
    cairo_set_line_width(cr, width);
294 295

    int patWidth = 0;
296 297 298
    switch (style) {
    case NoStroke:
    case SolidStroke:
299
        break;
300
    case DottedStroke:
301
        patWidth = static_cast<int>(width);
302
        break;
303
    case DashedStroke:
304
        patWidth = 3*static_cast<int>(width);
305 306 307
        break;
    }

308
    setSourceRGBAFromColor(cr, strokeColor());
309 310 311

    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);

312 313 314
    if (patWidth) {
        // Do a rect fill of our endpoints.  This ensures we always have the
        // appearance of being a border.  We then draw the actual dotted/dashed line.
315
        if (isVerticalLine) {
316 317
            fillRectSourceOver(cr, FloatRect(p1.x() - width/2, p1.y() - width, width, width), strokeColor());
            fillRectSourceOver(cr, FloatRect(p2.x() - width/2, p2.y(), width, width), strokeColor());
318
        } else {
319 320
            fillRectSourceOver(cr, FloatRect(p1.x() - width, p1.y() - width/2, width, width), strokeColor());
            fillRectSourceOver(cr, FloatRect(p2.x(), p2.y() - width/2, width, width), strokeColor());
321
        }
322

323 324 325
        // Example: 80 pixels with a width of 30 pixels.
        // Remainder is 20.  The maximum pixels of line we could paint
        // will be 50 pixels.
326
        int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*static_cast<int>(width);
327 328 329 330 331 332 333 334 335
        int remainder = distance%patWidth;
        int coverage = distance-remainder;
        int numSegments = coverage/patWidth;

        float patternOffset = 0;
        // Special case 1px dotted borders for speed.
        if (patWidth == 1)
            patternOffset = 1.0;
        else {
336
            bool evenNumberOfSegments = !(numSegments % 2);
337 338 339 340 341
            if (remainder)
                evenNumberOfSegments = !evenNumberOfSegments;
            if (evenNumberOfSegments) {
                if (remainder) {
                    patternOffset += patWidth - remainder;
342 343 344 345
                    patternOffset += remainder / 2;
                } else
                    patternOffset = patWidth / 2;
            } else if (!evenNumberOfSegments) {
346
                if (remainder)
347
                    patternOffset = (patWidth - remainder) / 2;
348 349
            }
        }
350

351
        double dash = patWidth;
352
        cairo_set_dash(cr, &dash, 1, patternOffset);
353 354
    }

355 356
    cairo_move_to(cr, p1.x(), p1.y());
    cairo_line_to(cr, p2.x(), p2.y());
357

358 359
    cairo_stroke(cr);
    cairo_restore(cr);
360 361 362
}

// This method is only used to draw the little circles used in lists.
363
void GraphicsContext::drawEllipse(const IntRect& rect)
364
{
365
    if (paintingDisabled())
366
        return;
367 368 369

    cairo_t* cr = m_data->cr;
    cairo_save(cr);
370 371
    float yRadius = .5 * rect.height();
    float xRadius = .5 * rect.width();
372 373 374 375
    cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
    cairo_scale(cr, xRadius, yRadius);
    cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
    cairo_restore(cr);
376

377
    if (fillColor().alpha()) {
378
        setSourceRGBAFromColor(cr, fillColor());
379
        cairo_fill_preserve(cr);
380
    }
bdash's avatar
bdash committed
381

382
    if (strokeStyle() != NoStroke) {
383
        setSourceRGBAFromColor(cr, strokeColor());
384 385
        cairo_set_line_width(cr, strokeThickness());
        cairo_stroke(cr);
386 387
    } else
        cairo_new_path(cr);
388 389
}

390
void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
darin's avatar
darin committed
391
{
392
    if (paintingDisabled() || strokeStyle() == NoStroke)
393 394
        return;

bdakin's avatar
bdakin committed
395 396
    int x = rect.x();
    int y = rect.y();
397 398
    float w = rect.width();
    float h = rect.height();
bdakin's avatar
bdakin committed
399 400
    float scaleFactor = h / w;
    float reverseScaleFactor = w / h;
401 402 403

    float hRadius = w / 2;
    float vRadius = h / 2;
404 405 406 407 408
    float fa = startAngle;
    float falen =  fa + angleSpan;

    cairo_t* cr = m_data->cr;
    cairo_save(cr);
409 410 411

    if (w != h)
        cairo_scale(cr, 1., scaleFactor);
412

413 414 415 416 417 418 419
    cairo_arc_negative(cr, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, -fa * M_PI/180, -falen * M_PI/180);

    if (w != h)
        cairo_scale(cr, 1., reverseScaleFactor);

    float width = strokeThickness();
    int patWidth = 0;
420

421
    switch (strokeStyle()) {
422 423 424 425 426 427 428 429
    case DottedStroke:
        patWidth = static_cast<int>(width / 2);
        break;
    case DashedStroke:
        patWidth = 3 * static_cast<int>(width / 2);
        break;
    default:
        break;
430 431
    }

432
    setSourceRGBAFromColor(cr, strokeColor());
433 434 435 436 437 438 439 440 441 442

    if (patWidth) {
        // Example: 80 pixels with a width of 30 pixels.
        // Remainder is 20.  The maximum pixels of line we could paint
        // will be 50 pixels.
        int distance;
        if (hRadius == vRadius)
            distance = static_cast<int>((M_PI * hRadius) / 2.0);
        else // We are elliptical and will have to estimate the distance
            distance = static_cast<int>((M_PI * sqrtf((hRadius * hRadius + vRadius * vRadius) / 2.0)) / 2.0);
443

444 445 446 447 448 449 450 451 452
        int remainder = distance % patWidth;
        int coverage = distance - remainder;
        int numSegments = coverage / patWidth;

        float patternOffset = 0.0;
        // Special case 1px dotted borders for speed.
        if (patWidth == 1)
            patternOffset = 1.0;
        else {
453
            bool evenNumberOfSegments = !(numSegments % 2);
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
            if (remainder)
                evenNumberOfSegments = !evenNumberOfSegments;
            if (evenNumberOfSegments) {
                if (remainder) {
                    patternOffset += patWidth - remainder;
                    patternOffset += remainder / 2.0;
                } else
                    patternOffset = patWidth / 2.0;
            } else {
                if (remainder)
                    patternOffset = (patWidth - remainder) / 2.0;
            }
        }

        double dash = patWidth;
        cairo_set_dash(cr, &dash, 1, patternOffset);
    }

472 473
    cairo_stroke(cr);
    cairo_restore(cr);
474 475
}

aroben's avatar
aroben committed
476
void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
477
{
478
    if (paintingDisabled())
479 480 481 482 483
        return;

    if (npoints <= 1)
        return;

484
    cairo_t* cr = m_data->cr;
485

486 487
    cairo_save(cr);
    cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
488
    addConvexPolygonToContext(cr, npoints, points);
489

490
    if (fillColor().alpha()) {
491
        setSourceRGBAFromColor(cr, fillColor());
492 493
        cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
        cairo_fill_preserve(cr);
494 495
    }

496
    if (strokeStyle() != NoStroke) {
497
        setSourceRGBAFromColor(cr, strokeColor());
498 499
        cairo_set_line_width(cr, strokeThickness());
        cairo_stroke(cr);
500 501
    } else
        cairo_new_path(cr);
bdash's avatar
bdash committed
502

503
    cairo_restore(cr);
504 505
}

506
void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
507 508 509 510 511 512
{
    if (paintingDisabled())
        return;

    if (numPoints <= 1)
        return;
513 514 515 516 517 518 519

    cairo_t* cr = m_data->cr;

    cairo_new_path(cr);
    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
    cairo_antialias_t savedAntialiasRule = cairo_get_antialias(cr);

520
    cairo_set_antialias(cr, antialiased ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE);
521 522 523 524 525 526
    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
    addConvexPolygonToContext(cr, numPoints, points);
    cairo_clip(cr);

    cairo_set_antialias(cr, savedAntialiasRule);
    cairo_set_fill_rule(cr, savedFillRule);
527 528
}

529
void GraphicsContext::fillPath(const Path& path)
530
{
531
    if (paintingDisabled())
532 533
        return;

eric@webkit.org's avatar
eric@webkit.org committed
534
    cairo_t* cr = m_data->cr;
535
    setPathOnCairoContext(cr, path.platformPath()->context());
536
    fillCurrentCairoPath(this, cr);
eric@webkit.org's avatar
eric@webkit.org committed
537 538
}

539
void GraphicsContext::strokePath(const Path& path)
eric@webkit.org's avatar
eric@webkit.org committed
540 541 542 543 544
{
    if (paintingDisabled())
        return;

    cairo_t* cr = m_data->cr;
545
    setPathOnCairoContext(cr, path.platformPath()->context());
546
    strokeCurrentCairoPath(this, cr);
eric@webkit.org's avatar
eric@webkit.org committed
547 548 549 550 551 552 553 554
}

void GraphicsContext::fillRect(const FloatRect& rect)
{
    if (paintingDisabled())
        return;

    cairo_t* cr = m_data->cr;
555
    cairo_save(cr);
eric@webkit.org's avatar
eric@webkit.org committed
556
    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
557
    fillCurrentCairoPath(this, cr);
558
    cairo_restore(cr);
559 560
}

561
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
562 563 564 565
{
    if (paintingDisabled())
        return;

566 567 568
    if (m_data->hasShadow())
        m_data->shadow.drawRectShadow(this, enclosingIntRect(rect));

569
    if (color.alpha())
570
        fillRectSourceOver(m_data->cr, rect, color);
571 572
}

eric@webkit.org's avatar
eric@webkit.org committed
573
void GraphicsContext::clip(const FloatRect& rect)
574
{
575
    if (paintingDisabled())
576 577
        return;

578 579
    cairo_t* cr = m_data->cr;
    cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
580 581
    cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr);
    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
582
    cairo_clip(cr);
583
    cairo_set_fill_rule(cr, savedFillRule);
584
    m_data->clip(rect);
585 586
}

587
void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
eric@webkit.org's avatar
eric@webkit.org committed
588 589 590 591 592
{
    if (paintingDisabled())
        return;

    cairo_t* cr = m_data->cr;
593
    setPathOnCairoContext(cr, path.platformPath()->context());
eric@webkit.org's avatar
eric@webkit.org committed
594 595 596 597
    cairo_set_fill_rule(cr, clipRule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
    cairo_clip(cr);
}

598
static inline void adjustFocusRingColor(Color& color)
599
{
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
#if !PLATFORM(GTK)
    // Force the alpha to 50%.  This matches what the Mac does with outline rings.
    color.setRGB(makeRGBA(color.red(), color.green(), color.blue(), 127));
#endif
}

static inline void adjustFocusRingLineWidth(int& width)
{
#if PLATFORM(GTK)
    width = 2;
#endif
}

static inline StrokeStyle focusRingStrokeStyle()
{
#if PLATFORM(GTK)
    return DottedStroke;
#else
    return SolidStroke;
#endif
}

void GraphicsContext::drawFocusRing(const Path& path, int width, int /* offset */, const Color& color)
{
    // FIXME: We should draw paths that describe a rectangle with rounded corners
    // so as to be consistent with how we draw rectangular focus rings.
    Color ringColor = color;
    adjustFocusRingColor(ringColor);
    adjustFocusRingLineWidth(width);

    cairo_t* cr = m_data->cr;
    cairo_save(cr);
    appendWebCorePathToCairoContext(cr, path);
    setSourceRGBAFromColor(cr, ringColor);
    cairo_set_line_width(cr, width);
    setPlatformStrokeStyle(focusRingStrokeStyle());
    cairo_stroke(cr);
    cairo_restore(cr);
638 639
}

640
void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int /* offset */, const Color& color)
641
{
642
    if (paintingDisabled())
643
        return;
kjk's avatar
kjk committed
644

645
    unsigned rectCount = rects.size();
646 647 648

    cairo_t* cr = m_data->cr;
    cairo_save(cr);
649 650 651 652
    cairo_push_group(cr);
    cairo_new_path(cr);

#if PLATFORM(GTK)
xan@webkit.org's avatar
xan@webkit.org committed
653
#ifdef GTK_API_VERSION_2
654
    GdkRegion* reg = gdk_region_new();
xan@webkit.org's avatar
xan@webkit.org committed
655 656 657 658
#else
    cairo_region_t* reg = cairo_region_create();
#endif

659
    for (unsigned i = 0; i < rectCount; i++) {
xan@webkit.org's avatar
xan@webkit.org committed
660
#ifdef GTK_API_VERSION_2
661 662
        GdkRectangle rect = rects[i];
        gdk_region_union_with_rect(reg, &rect);
xan@webkit.org's avatar
xan@webkit.org committed
663 664 665 666
#else
        cairo_rectangle_int_t rect = rects[i];
        cairo_region_union_rectangle(reg, &rect);
#endif
667 668
    }
    gdk_cairo_region(cr, reg);
xan@webkit.org's avatar
xan@webkit.org committed
669
#ifdef GTK_API_VERSION_2
670
    gdk_region_destroy(reg);
xan@webkit.org's avatar
xan@webkit.org committed
671 672 673
#else
    cairo_region_destroy(reg);
#endif
674
#else
675
    int radius = (width - 1) / 2;
676 677 678 679 680 681 682
    Path path;
    for (unsigned i = 0; i < rectCount; ++i) {
        if (i > 0)
            path.clear();
        path.addRoundedRect(rects[i], FloatSize(radius, radius));
        appendWebCorePathToCairoContext(cr, path);
    }
683 684 685 686
#endif
    Color ringColor = color;
    adjustFocusRingColor(ringColor);
    adjustFocusRingLineWidth(width);
687
    setSourceRGBAFromColor(cr, ringColor);
688
    cairo_set_line_width(cr, width);
689
    setPlatformStrokeStyle(focusRingStrokeStyle());
690 691 692 693 694 695 696 697 698 699 700

    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_stroke_preserve(cr);

    cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
    cairo_fill(cr);

    cairo_pop_group_to_source(cr);
    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    cairo_paint(cr);
701
    cairo_restore(cr);
702 703
}

darin's avatar
darin committed
704
void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
705 706 707 708 709 710 711 712
{
    if (paintingDisabled())
        return;

    IntPoint endPoint = origin + IntSize(width, 0);
    drawLine(origin, endPoint);
}

713 714 715 716
#if !PLATFORM(GTK)
#include "DrawErrorUnderline.h"
#endif

717
void GraphicsContext::drawLineForTextChecking(const IntPoint& origin, int width, TextCheckingLineStyle style)
718
{
alp's avatar
alp committed
719 720 721 722 723 724
    if (paintingDisabled())
        return;

    cairo_t* cr = m_data->cr;
    cairo_save(cr);

725 726
    switch (style) {
    case TextCheckingSpellingLineStyle:
alp's avatar
alp committed
727
        cairo_set_source_rgb(cr, 1, 0, 0);
728 729 730 731 732 733 734 735
        break;
    case TextCheckingGrammarLineStyle:
        cairo_set_source_rgb(cr, 0, 1, 0);
        break;
    default:
        cairo_restore(cr);
        return;
    }
alp's avatar
alp committed
736

737
#if PLATFORM(GTK)
alp's avatar
alp committed
738 739 740
    // We ignore most of the provided constants in favour of the platform style
    pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
#else
741
    drawErrorUnderline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness);
alp's avatar
alp committed
742
#endif
743 744

    cairo_restore(cr);
745 746
}

ggaren's avatar
ggaren committed
747 748 749
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
{
    FloatRect result;
750
    double x = frect.x();
ggaren's avatar
ggaren committed
751
    double y = frect.y();
752 753
    cairo_t* cr = m_data->cr;
    cairo_user_to_device(cr, &x, &y);
ggaren's avatar
ggaren committed
754 755
    x = round(x);
    y = round(y);
756 757 758
    cairo_device_to_user(cr, &x, &y);
    result.setX(static_cast<float>(x));
    result.setY(static_cast<float>(y));
ggaren's avatar
ggaren committed
759 760
    x = frect.width();
    y = frect.height();
761
    cairo_user_to_device_distance(cr, &x, &y);
ggaren's avatar
ggaren committed
762 763
    x = round(x);
    y = round(y);
764 765 766 767
    cairo_device_to_user_distance(cr, &x, &y);
    result.setWidth(static_cast<float>(x));
    result.setHeight(static_cast<float>(y));
    return result;
ggaren's avatar
ggaren committed
768 769
}

thatcher's avatar
thatcher committed
770
void GraphicsContext::translate(float x, float y)
ggaren's avatar
ggaren committed
771
{
bdash's avatar
bdash committed
772 773
    if (paintingDisabled())
        return;
774 775 776

    cairo_t* cr = m_data->cr;
    cairo_translate(cr, x, y);
777
    m_data->translate(x, y);
ggaren's avatar
ggaren committed
778 779
}

780
void GraphicsContext::setPlatformFillColor(const Color& col, ColorSpace colorSpace)
kjk's avatar
kjk committed
781
{
eric@webkit.org's avatar
eric@webkit.org committed
782 783
    // Cairo contexts can't hold separate fill and stroke colors
    // so we set them just before we actually fill or stroke
kjk's avatar
kjk committed
784 785
}

786
void GraphicsContext::setPlatformStrokeColor(const Color& col, ColorSpace colorSpace)
kjk's avatar
kjk committed
787
{
eric@webkit.org's avatar
eric@webkit.org committed
788 789
    // Cairo contexts can't hold separate fill and stroke colors
    // so we set them just before we actually fill or stroke
kjk's avatar
kjk committed
790 791 792 793
}

void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
{
bdash's avatar
bdash committed
794 795
    if (paintingDisabled())
        return;
796 797

    cairo_set_line_width(m_data->cr, strokeThickness);
kjk's avatar
kjk committed
798 799 800 801 802 803 804 805 806 807 808 809 810
}

void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
{
    static double dashPattern[] = {5.0, 5.0};
    static double dotPattern[] = {1.0, 1.0};

    if (paintingDisabled())
        return;

    switch (strokeStyle) {
    case NoStroke:
        // FIXME: is it the right way to emulate NoStroke?
811
        cairo_set_line_width(m_data->cr, 0);
kjk's avatar
kjk committed
812 813
        break;
    case SolidStroke:
814
        cairo_set_dash(m_data->cr, 0, 0, 0);
kjk's avatar
kjk committed
815 816
        break;
    case DottedStroke:
817
        cairo_set_dash(m_data->cr, dotPattern, 2, 0);
kjk's avatar
kjk committed
818 819
        break;
    case DashedStroke:
820
        cairo_set_dash(m_data->cr, dashPattern, 2, 0);
kjk's avatar
kjk committed
821 822 823 824
        break;
    }
}

825 826
void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
{
kjk's avatar
kjk committed
827 828 829
    notImplemented();
}

830 831 832 833 834 835 836 837 838 839 840
void GraphicsContext::concatCTM(const AffineTransform& transform)
{
    if (paintingDisabled())
        return;

    cairo_t* cr = m_data->cr;
    const cairo_matrix_t matrix = cairo_matrix_t(transform);
    cairo_transform(cr, &matrix);
    m_data->concatCTM(transform);
}

841 842
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
{
alp's avatar
alp committed
843 844 845
    if (paintingDisabled())
        return;

846
    cairo_t* cr = m_data->cr;
alp's avatar
alp committed
847 848
    clip(rect);

849