|
Line 0
WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h_sec1
|
|
|
1 |
/* |
| 2 |
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved. |
| 3 |
* Copyright (C) 2007 Alp Toker <alp@atoker.com> |
| 4 |
* |
| 5 |
* Redistribution and use in source and binary forms, with or without |
| 6 |
* modification, are permitted provided that the following conditions |
| 7 |
* are met: |
| 8 |
* 1. Redistributions of source code must retain the above copyright |
| 9 |
* notice, this list of conditions and the following disclaimer. |
| 10 |
* 2. Redistributions in binary form must reproduce the above copyright |
| 11 |
* notice, this list of conditions and the following disclaimer in the |
| 12 |
* documentation and/or other materials provided with the distribution. |
| 13 |
* |
| 14 |
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| 15 |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 16 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 17 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| 18 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 19 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 21 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 22 |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 |
*/ |
| 26 |
|
| 27 |
#include "config.h" |
| 28 |
#include "GraphicsContext.h" |
| 29 |
|
| 30 |
#if PLATFORM(CAIRO) |
| 31 |
|
| 32 |
#include "CairoPath.h" |
| 33 |
#include "FloatRect.h" |
| 34 |
#include "Font.h" |
| 35 |
#include "IntRect.h" |
| 36 |
#include "NotImplemented.h" |
| 37 |
#include "Path.h" |
| 38 |
#include "SimpleFontData.h" |
| 39 |
#include <cairo.h> |
| 40 |
#include <math.h> |
| 41 |
#include <stdio.h> |
| 42 |
#include <wtf/MathExtras.h> |
| 43 |
|
| 44 |
#if PLATFORM(WIN) |
| 45 |
#include <cairo-win32.h> |
| 46 |
#endif |
| 47 |
|
| 48 |
#if PLATFORM(GTK) |
| 49 |
#include <gdk/gdk.h> |
| 50 |
#include <pango/pango.h> |
| 51 |
#endif |
| 52 |
|
| 53 |
|
| 54 |
#ifndef M_PI |
| 55 |
#define M_PI 3.14159265358979323846 |
| 56 |
#endif |
| 57 |
|
| 58 |
namespace WebCore { |
| 59 |
|
| 60 |
class GraphicsContextPlatformPrivate { |
| 61 |
public: |
| 62 |
GraphicsContextPlatformPrivate(); |
| 63 |
~GraphicsContextPlatformPrivate(); |
| 64 |
|
| 65 |
cairo_t* cr; |
| 66 |
Vector<float> layers; |
| 67 |
|
| 68 |
#if PLATFORM(GTK) |
| 69 |
GdkEventExpose* expose; |
| 70 |
#endif |
| 71 |
}; |
| 72 |
|
| 73 |
static inline void setColor(cairo_t* cr, const Color& col) |
| 74 |
{ |
| 75 |
float red, green, blue, alpha; |
| 76 |
col.getRGBA(red, green, blue, alpha); |
| 77 |
cairo_set_source_rgba(cr, red, green, blue, alpha); |
| 78 |
} |
| 79 |
|
| 80 |
// A fillRect helper |
| 81 |
static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const Color& col) |
| 82 |
{ |
| 83 |
setColor(cr, col); |
| 84 |
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); |
| 85 |
cairo_set_operator(cr, CAIRO_OPERATOR_OVER); |
| 86 |
cairo_fill(cr); |
| 87 |
} |
| 88 |
|
| 89 |
GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate() |
| 90 |
: cr(0) |
| 91 |
#if PLATFORM(GTK) |
| 92 |
, expose(0) |
| 93 |
#endif |
| 94 |
{ |
| 95 |
} |
| 96 |
|
| 97 |
GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate() |
| 98 |
{ |
| 99 |
cairo_destroy(cr); |
| 100 |
} |
| 101 |
|
| 102 |
#if PLATFORM(WIN) |
| 103 |
GraphicsContext::GraphicsContext(HDC dc) |
| 104 |
: m_common(createGraphicsContextPrivate()) |
| 105 |
, m_data(new GraphicsContextPlatformPrivate) |
| 106 |
{ |
| 107 |
if (dc) { |
| 108 |
cairo_surface_t* surface = cairo_win32_surface_create(dc); |
| 109 |
m_data->cr = cairo_create(surface); |
| 110 |
} else { |
| 111 |
setPaintingDisabled(true); |
| 112 |
m_data->cr = 0; |
| 113 |
} |
| 114 |
} |
| 115 |
#endif |
| 116 |
|
| 117 |
GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr) |
| 118 |
: m_common(createGraphicsContextPrivate()) |
| 119 |
, m_data(new GraphicsContextPlatformPrivate) |
| 120 |
{ |
| 121 |
m_data->cr = cairo_reference(cr); |
| 122 |
setPaintingDisabled(!cr); |
| 123 |
} |
| 124 |
|
| 125 |
GraphicsContext::~GraphicsContext() |
| 126 |
{ |
| 127 |
destroyGraphicsContextPrivate(m_common); |
| 128 |
delete m_data; |
| 129 |
} |
| 130 |
|
| 131 |
cairo_t* GraphicsContext::platformContext() const |
| 132 |
{ |
| 133 |
return m_data->cr; |
| 134 |
} |
| 135 |
|
| 136 |
void GraphicsContext::savePlatformState() |
| 137 |
{ |
| 138 |
cairo_save(m_data->cr); |
| 139 |
} |
| 140 |
|
| 141 |
void GraphicsContext::restorePlatformState() |
| 142 |
{ |
| 143 |
cairo_restore(m_data->cr); |
| 144 |
} |
| 145 |
|
| 146 |
// Draws a filled rectangle with a stroked border. |
| 147 |
void GraphicsContext::drawRect(const IntRect& rect) |
| 148 |
{ |
| 149 |
if (paintingDisabled()) |
| 150 |
return; |
| 151 |
|
| 152 |
cairo_t* cr = m_data->cr; |
| 153 |
cairo_save(cr); |
| 154 |
|
| 155 |
if (fillColor().alpha()) |
| 156 |
fillRectSourceOver(cr, rect, fillColor()); |
| 157 |
|
| 158 |
if (strokeStyle() != NoStroke) { |
| 159 |
setColor(cr, strokeColor()); |
| 160 |
FloatRect r(rect); |
| 161 |
r.inflate(-.5f); |
| 162 |
cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height()); |
| 163 |
cairo_set_line_width(cr, 1.0); |
| 164 |
cairo_stroke(cr); |
| 165 |
} |
| 166 |
|
| 167 |
cairo_restore(cr); |
| 168 |
} |
| 169 |
|
| 170 |
// FIXME: Now that this is refactored, it should be shared by all contexts. |
| 171 |
static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle style) |
| 172 |
{ |
| 173 |
// For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic |
| 174 |
// works out. For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g., |
| 175 |
// (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave |
| 176 |
// us a perfect position, but an odd width gave us a position that is off by exactly 0.5. |
| 177 |
if (style == DottedStroke || style == DashedStroke) { |
| 178 |
if (p1.x() == p2.x()) { |
| 179 |
p1.setY(p1.y() + strokeWidth); |
| 180 |
p2.setY(p2.y() - strokeWidth); |
| 181 |
} |
| 182 |
else { |
| 183 |
p1.setX(p1.x() + strokeWidth); |
| 184 |
p2.setX(p2.x() - strokeWidth); |
| 185 |
} |
| 186 |
} |
| 187 |
|
| 188 |
if (static_cast<int>(strokeWidth) % 2) { |
| 189 |
if (p1.x() == p2.x()) { |
| 190 |
// We're a vertical line. Adjust our x. |
| 191 |
p1.setX(p1.x() + 0.5); |
| 192 |
p2.setX(p2.x() + 0.5); |
| 193 |
} |
| 194 |
else { |
| 195 |
// We're a horizontal line. Adjust our y. |
| 196 |
p1.setY(p1.y() + 0.5); |
| 197 |
p2.setY(p2.y() + 0.5); |
| 198 |
} |
| 199 |
} |
| 200 |
} |
| 201 |
|
| 202 |
// This is only used to draw borders. |
| 203 |
void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) |
| 204 |
{ |
| 205 |
if (paintingDisabled()) |
| 206 |
return; |
| 207 |
|
| 208 |
StrokeStyle style = strokeStyle(); |
| 209 |
if (style == NoStroke) |
| 210 |
return; |
| 211 |
|
| 212 |
cairo_t* cr = m_data->cr; |
| 213 |
cairo_save(cr); |
| 214 |
|
| 215 |
float width = strokeThickness(); |
| 216 |
if (width < 1) |
| 217 |
width = 1; |
| 218 |
|
| 219 |
FloatPoint p1 = point1; |
| 220 |
FloatPoint p2 = point2; |
| 221 |
bool isVerticalLine = (p1.x() == p2.x()); |
| 222 |
|
| 223 |
adjustLineToPixelBoundaries(p1, p2, width, style); |
| 224 |
cairo_set_line_width(cr, width); |
| 225 |
|
| 226 |
int patWidth = 0; |
| 227 |
switch (style) { |
| 228 |
case NoStroke: |
| 229 |
case SolidStroke: |
| 230 |
break; |
| 231 |
case DottedStroke: |
| 232 |
patWidth = static_cast<int>(width); |
| 233 |
break; |
| 234 |
case DashedStroke: |
| 235 |
patWidth = 3*static_cast<int>(width); |
| 236 |
break; |
| 237 |
} |
| 238 |
|
| 239 |
setColor(cr, strokeColor()); |
| 240 |
|
| 241 |
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); |
| 242 |
|
| 243 |
if (patWidth) { |
| 244 |
// Do a rect fill of our endpoints. This ensures we always have the |
| 245 |
// appearance of being a border. We then draw the actual dotted/dashed line. |
| 246 |
if (isVerticalLine) { |
| 247 |
fillRectSourceOver(cr, FloatRect(p1.x() - width/2, p1.y() - width, width, width), strokeColor()); |
| 248 |
fillRectSourceOver(cr, FloatRect(p2.x() - width/2, p2.y(), width, width), strokeColor()); |
| 249 |
} else { |
| 250 |
fillRectSourceOver(cr, FloatRect(p1.x() - width, p1.y() - width/2, width, width), strokeColor()); |
| 251 |
fillRectSourceOver(cr, FloatRect(p2.x(), p2.y() - width/2, width, width), strokeColor()); |
| 252 |
} |
| 253 |
|
| 254 |
// Example: 80 pixels with a width of 30 pixels. |
| 255 |
// Remainder is 20. The maximum pixels of line we could paint |
| 256 |
// will be 50 pixels. |
| 257 |
int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*static_cast<int>(width); |
| 258 |
int remainder = distance%patWidth; |
| 259 |
int coverage = distance-remainder; |
| 260 |
int numSegments = coverage/patWidth; |
| 261 |
|
| 262 |
float patternOffset = 0; |
| 263 |
// Special case 1px dotted borders for speed. |
| 264 |
if (patWidth == 1) |
| 265 |
patternOffset = 1.0; |
| 266 |
else { |
| 267 |
bool evenNumberOfSegments = numSegments%2 == 0; |
| 268 |
if (remainder) |
| 269 |
evenNumberOfSegments = !evenNumberOfSegments; |
| 270 |
if (evenNumberOfSegments) { |
| 271 |
if (remainder) { |
| 272 |
patternOffset += patWidth - remainder; |
| 273 |
patternOffset += remainder/2; |
| 274 |
} |
| 275 |
else |
| 276 |
patternOffset = patWidth/2; |
| 277 |
} |
| 278 |
else if (!evenNumberOfSegments) { |
| 279 |
if (remainder) |
| 280 |
patternOffset = (patWidth - remainder)/2; |
| 281 |
} |
| 282 |
} |
| 283 |
|
| 284 |
double dash = patWidth; |
| 285 |
cairo_set_dash(cr, &dash, 1, patternOffset); |
| 286 |
} |
| 287 |
|
| 288 |
cairo_move_to(cr, p1.x(), p1.y()); |
| 289 |
cairo_line_to(cr, p2.x(), p2.y()); |
| 290 |
|
| 291 |
cairo_stroke(cr); |
| 292 |
cairo_restore(cr); |
| 293 |
} |
| 294 |
|
| 295 |
// This method is only used to draw the little circles used in lists. |
| 296 |
void GraphicsContext::drawEllipse(const IntRect& rect) |
| 297 |
{ |
| 298 |
if (paintingDisabled()) |
| 299 |
return; |
| 300 |
|
| 301 |
cairo_t* cr = m_data->cr; |
| 302 |
cairo_save(cr); |
| 303 |
float yRadius = .5 * rect.height(); |
| 304 |
float xRadius = .5 * rect.width(); |
| 305 |
cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius); |
| 306 |
cairo_scale(cr, xRadius, yRadius); |
| 307 |
cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI); |
| 308 |
cairo_restore(cr); |
| 309 |
|
| 310 |
if (fillColor().alpha()) { |
| 311 |
setColor(cr, fillColor()); |
| 312 |
cairo_fill_preserve(cr); |
| 313 |
} |
| 314 |
|
| 315 |
if (strokeStyle() != NoStroke) { |
| 316 |
setColor(cr, strokeColor()); |
| 317 |
cairo_set_line_width(cr, strokeThickness()); |
| 318 |
cairo_stroke(cr); |
| 319 |
} |
| 320 |
|
| 321 |
cairo_new_path(cr); |
| 322 |
} |
| 323 |
|
| 324 |
// FIXME: This function needs to be adjusted to match the functionality on the Mac side. |
| 325 |
void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) |
| 326 |
{ |
| 327 |
if (paintingDisabled()) |
| 328 |
return; |
| 329 |
|
| 330 |
if (strokeStyle() == NoStroke) |
| 331 |
return; |
| 332 |
|
| 333 |
int x = rect.x(); |
| 334 |
int y = rect.y(); |
| 335 |
float w = rect.width(); |
| 336 |
#if 0 // FIXME: unused so far |
| 337 |
float h = rect.height(); |
| 338 |
float scaleFactor = h / w; |
| 339 |
float reverseScaleFactor = w / h; |
| 340 |
#endif |
| 341 |
float r = w / 2; |
| 342 |
float fa = startAngle; |
| 343 |
float falen = fa + angleSpan; |
| 344 |
|
| 345 |
cairo_t* cr = m_data->cr; |
| 346 |
cairo_save(cr); |
| 347 |
cairo_arc_negative(cr, x + r, y + r, r, -fa * M_PI/180, -falen * M_PI/180); |
| 348 |
setColor(cr, strokeColor()); |
| 349 |
cairo_set_line_width(cr, strokeThickness()); |
| 350 |
cairo_stroke(cr); |
| 351 |
cairo_restore(cr); |
| 352 |
} |
| 353 |
|
| 354 |
void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) |
| 355 |
{ |
| 356 |
if (paintingDisabled()) |
| 357 |
return; |
| 358 |
|
| 359 |
if (npoints <= 1) |
| 360 |
return; |
| 361 |
|
| 362 |
cairo_t* cr = m_data->cr; |
| 363 |
|
| 364 |
cairo_save(cr); |
| 365 |
cairo_set_antialias(cr, shouldAntialias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); |
| 366 |
cairo_move_to(cr, points[0].x(), points[0].y()); |
| 367 |
for (size_t i = 1; i < npoints; i++) |
| 368 |
cairo_line_to(cr, points[i].x(), points[i].y()); |
| 369 |
cairo_close_path(cr); |
| 370 |
|
| 371 |
if (fillColor().alpha()) { |
| 372 |
setColor(cr, fillColor()); |
| 373 |
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); |
| 374 |
cairo_fill_preserve(cr); |
| 375 |
} |
| 376 |
|
| 377 |
if (strokeStyle() != NoStroke) { |
| 378 |
setColor(cr, strokeColor()); |
| 379 |
cairo_set_line_width(cr, strokeThickness()); |
| 380 |
cairo_stroke(cr); |
| 381 |
} |
| 382 |
|
| 383 |
cairo_new_path(cr); |
| 384 |
cairo_restore(cr); |
| 385 |
} |
| 386 |
|
| 387 |
void GraphicsContext::fillRect(const IntRect& rect, const Color& color) |
| 388 |
{ |
| 389 |
if (paintingDisabled()) |
| 390 |
return; |
| 391 |
|
| 392 |
if (color.alpha()) |
| 393 |
fillRectSourceOver(m_data->cr, rect, color); |
| 394 |
} |
| 395 |
|
| 396 |
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) |
| 397 |
{ |
| 398 |
if (paintingDisabled()) |
| 399 |
return; |
| 400 |
|
| 401 |
if (color.alpha()) |
| 402 |
fillRectSourceOver(m_data->cr, rect, color); |
| 403 |
} |
| 404 |
|
| 405 |
void GraphicsContext::clip(const IntRect& rect) |
| 406 |
{ |
| 407 |
if (paintingDisabled()) |
| 408 |
return; |
| 409 |
|
| 410 |
cairo_t* cr = m_data->cr; |
| 411 |
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); |
| 412 |
cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); |
| 413 |
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); |
| 414 |
cairo_clip(cr); |
| 415 |
cairo_set_fill_rule(cr, savedFillRule); |
| 416 |
} |
| 417 |
|
| 418 |
void GraphicsContext::drawFocusRing(const Color& color) |
| 419 |
{ |
| 420 |
if (paintingDisabled()) |
| 421 |
return; |
| 422 |
|
| 423 |
int radius = (focusRingWidth() - 1) / 2; |
| 424 |
int offset = radius + focusRingOffset(); |
| 425 |
|
| 426 |
const Vector<IntRect>& rects = focusRingRects(); |
| 427 |
unsigned rectCount = rects.size(); |
| 428 |
IntRect finalFocusRect; |
| 429 |
for (unsigned i = 0; i < rectCount; i++) { |
| 430 |
IntRect focusRect = rects[i]; |
| 431 |
focusRect.inflate(offset); |
| 432 |
finalFocusRect.unite(focusRect); |
| 433 |
} |
| 434 |
|
| 435 |
cairo_t* cr = m_data->cr; |
| 436 |
cairo_save(cr); |
| 437 |
// FIXME: These rects should be rounded |
| 438 |
cairo_rectangle(cr, finalFocusRect.x(), finalFocusRect.y(), finalFocusRect.width(), finalFocusRect.height()); |
| 439 |
|
| 440 |
// Force the alpha to 50%. This matches what the Mac does with outline rings. |
| 441 |
Color ringColor(color.red(), color.green(), color.blue(), 127); |
| 442 |
setColor(cr, ringColor); |
| 443 |
cairo_stroke(cr); |
| 444 |
cairo_restore(cr); |
| 445 |
} |
| 446 |
|
| 447 |
void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing) |
| 448 |
{ |
| 449 |
if (paintingDisabled()) |
| 450 |
return; |
| 451 |
|
| 452 |
// This is a workaround for http://bugs.webkit.org/show_bug.cgi?id=15659 |
| 453 |
StrokeStyle savedStrokeStyle = strokeStyle(); |
| 454 |
setStrokeStyle(SolidStroke); |
| 455 |
|
| 456 |
IntPoint endPoint = origin + IntSize(width, 0); |
| 457 |
drawLine(origin, endPoint); |
| 458 |
|
| 459 |
setStrokeStyle(savedStrokeStyle); |
| 460 |
} |
| 461 |
|
| 462 |
void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, int width, bool grammar) |
| 463 |
{ |
| 464 |
if (paintingDisabled()) |
| 465 |
return; |
| 466 |
|
| 467 |
#if PLATFORM(GTK) |
| 468 |
cairo_t* cr = m_data->cr; |
| 469 |
cairo_save(cr); |
| 470 |
|
| 471 |
// Convention is green for grammar, red for spelling |
| 472 |
// These need to become configurable |
| 473 |
if (grammar) |
| 474 |
cairo_set_source_rgb(cr, 0, 1, 0); |
| 475 |
else |
| 476 |
cairo_set_source_rgb(cr, 1, 0, 0); |
| 477 |
|
| 478 |
// We ignore most of the provided constants in favour of the platform style |
| 479 |
pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness); |
| 480 |
|
| 481 |
cairo_restore(cr); |
| 482 |
#else |
| 483 |
notImplemented(); |
| 484 |
#endif |
| 485 |
} |
| 486 |
|
| 487 |
FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) |
| 488 |
{ |
| 489 |
FloatRect result; |
| 490 |
double x = frect.x(); |
| 491 |
double y = frect.y(); |
| 492 |
cairo_t* cr = m_data->cr; |
| 493 |
cairo_user_to_device(cr, &x, &y); |
| 494 |
x = round(x); |
| 495 |
y = round(y); |
| 496 |
cairo_device_to_user(cr, &x, &y); |
| 497 |
result.setX(static_cast<float>(x)); |
| 498 |
result.setY(static_cast<float>(y)); |
| 499 |
x = frect.width(); |
| 500 |
y = frect.height(); |
| 501 |
cairo_user_to_device_distance(cr, &x, &y); |
| 502 |
x = round(x); |
| 503 |
y = round(y); |
| 504 |
cairo_device_to_user_distance(cr, &x, &y); |
| 505 |
result.setWidth(static_cast<float>(x)); |
| 506 |
result.setHeight(static_cast<float>(y)); |
| 507 |
return result; |
| 508 |
} |
| 509 |
|
| 510 |
void GraphicsContext::translate(float x, float y) |
| 511 |
{ |
| 512 |
if (paintingDisabled()) |
| 513 |
return; |
| 514 |
|
| 515 |
cairo_t* cr = m_data->cr; |
| 516 |
cairo_translate(cr, x, y); |
| 517 |
} |
| 518 |
|
| 519 |
IntPoint GraphicsContext::origin() |
| 520 |
{ |
| 521 |
cairo_matrix_t matrix; |
| 522 |
cairo_t* cr = m_data->cr; |
| 523 |
cairo_get_matrix(cr, &matrix); |
| 524 |
return IntPoint(static_cast<int>(matrix.x0), static_cast<int>(matrix.y0)); |
| 525 |
} |
| 526 |
|
| 527 |
void GraphicsContext::setPlatformFillColor(const Color& col) |
| 528 |
{ |
| 529 |
// FIXME: this is probably a no-op but I'm not sure |
| 530 |
// notImplemented(); // commented-out because it's chatty and clutters output |
| 531 |
} |
| 532 |
|
| 533 |
void GraphicsContext::setPlatformStrokeColor(const Color& col) |
| 534 |
{ |
| 535 |
// FIXME: this is probably a no-op but I'm not sure |
| 536 |
//notImplemented(); // commented-out because it's chatty and clutters output |
| 537 |
} |
| 538 |
|
| 539 |
void GraphicsContext::setPlatformStrokeThickness(float strokeThickness) |
| 540 |
{ |
| 541 |
if (paintingDisabled()) |
| 542 |
return; |
| 543 |
|
| 544 |
cairo_set_line_width(m_data->cr, strokeThickness); |
| 545 |
} |
| 546 |
|
| 547 |
void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle) |
| 548 |
{ |
| 549 |
static double dashPattern[] = {5.0, 5.0}; |
| 550 |
static double dotPattern[] = {1.0, 1.0}; |
| 551 |
|
| 552 |
if (paintingDisabled()) |
| 553 |
return; |
| 554 |
|
| 555 |
switch (strokeStyle) { |
| 556 |
case NoStroke: |
| 557 |
// FIXME: is it the right way to emulate NoStroke? |
| 558 |
cairo_set_line_width(m_data->cr, 0); |
| 559 |
break; |
| 560 |
case SolidStroke: |
| 561 |
cairo_set_dash(m_data->cr, 0, 0, 0); |
| 562 |
break; |
| 563 |
case DottedStroke: |
| 564 |
cairo_set_dash(m_data->cr, dotPattern, 2, 0); |
| 565 |
break; |
| 566 |
case DashedStroke: |
| 567 |
cairo_set_dash(m_data->cr, dashPattern, 2, 0); |
| 568 |
break; |
| 569 |
default: |
| 570 |
notImplemented(); |
| 571 |
break; |
| 572 |
} |
| 573 |
} |
| 574 |
|
| 575 |
void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) |
| 576 |
{ |
| 577 |
notImplemented(); |
| 578 |
} |
| 579 |
|
| 580 |
void GraphicsContext::concatCTM(const AffineTransform& transform) |
| 581 |
{ |
| 582 |
if (paintingDisabled()) |
| 583 |
return; |
| 584 |
|
| 585 |
cairo_t* cr = m_data->cr; |
| 586 |
const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); |
| 587 |
cairo_transform(cr, matrix); |
| 588 |
} |
| 589 |
|
| 590 |
void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) |
| 591 |
{ |
| 592 |
if (paintingDisabled()) |
| 593 |
return; |
| 594 |
|
| 595 |
clip(rect); |
| 596 |
|
| 597 |
Path p; |
| 598 |
FloatRect r(rect); |
| 599 |
// Add outer ellipse |
| 600 |
p.addEllipse(r); |
| 601 |
// Add inner ellipse |
| 602 |
r.inflate(-thickness); |
| 603 |
p.addEllipse(r); |
| 604 |
addPath(p); |
| 605 |
|
| 606 |
cairo_t* cr = m_data->cr; |
| 607 |
cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); |
| 608 |
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); |
| 609 |
cairo_clip(cr); |
| 610 |
cairo_set_fill_rule(cr, savedFillRule); |
| 611 |
} |
| 612 |
|
| 613 |
|
| 614 |
void GraphicsContext::setShadow(IntSize const&, int, Color const&) |
| 615 |
{ |
| 616 |
notImplemented(); |
| 617 |
} |
| 618 |
|
| 619 |
void GraphicsContext::clearShadow() |
| 620 |
{ |
| 621 |
notImplemented(); |
| 622 |
} |
| 623 |
|
| 624 |
void GraphicsContext::beginTransparencyLayer(float opacity) |
| 625 |
{ |
| 626 |
if (paintingDisabled()) |
| 627 |
return; |
| 628 |
|
| 629 |
cairo_t* cr = m_data->cr; |
| 630 |
cairo_push_group(cr); |
| 631 |
m_data->layers.append(opacity); |
| 632 |
} |
| 633 |
|
| 634 |
void GraphicsContext::endTransparencyLayer() |
| 635 |
{ |
| 636 |
if (paintingDisabled()) |
| 637 |
return; |
| 638 |
|
| 639 |
cairo_t* cr = m_data->cr; |
| 640 |
|
| 641 |
cairo_pop_group_to_source(cr); |
| 642 |
cairo_paint_with_alpha(cr, m_data->layers.last()); |
| 643 |
m_data->layers.removeLast(); |
| 644 |
} |
| 645 |
|
| 646 |
void GraphicsContext::clearRect(const FloatRect& rect) |
| 647 |
{ |
| 648 |
if (paintingDisabled()) |
| 649 |
return; |
| 650 |
|
| 651 |
cairo_t* cr = m_data->cr; |
| 652 |
|
| 653 |
cairo_save(cr); |
| 654 |
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); |
| 655 |
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); |
| 656 |
cairo_fill(cr); |
| 657 |
cairo_restore(cr); |
| 658 |
} |
| 659 |
|
| 660 |
void GraphicsContext::strokeRect(const FloatRect& rect, float width) |
| 661 |
{ |
| 662 |
if (paintingDisabled()) |
| 663 |
return; |
| 664 |
|
| 665 |
cairo_t* cr = m_data->cr; |
| 666 |
cairo_save(cr); |
| 667 |
cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height()); |
| 668 |
setColor(cr, strokeColor()); |
| 669 |
cairo_set_line_width(cr, width); |
| 670 |
cairo_stroke(cr); |
| 671 |
cairo_restore(cr); |
| 672 |
} |
| 673 |
|
| 674 |
void GraphicsContext::setLineCap(LineCap lineCap) |
| 675 |
{ |
| 676 |
if (paintingDisabled()) |
| 677 |
return; |
| 678 |
|
| 679 |
cairo_line_cap_t cairoCap = CAIRO_LINE_CAP_BUTT; |
| 680 |
switch (lineCap) { |
| 681 |
case ButtCap: |
| 682 |
// no-op |
| 683 |
break; |
| 684 |
case RoundCap: |
| 685 |
cairoCap = CAIRO_LINE_CAP_ROUND; |
| 686 |
break; |
| 687 |
case SquareCap: |
| 688 |
cairoCap = CAIRO_LINE_CAP_SQUARE; |
| 689 |
break; |
| 690 |
} |
| 691 |
cairo_set_line_cap(m_data->cr, cairoCap); |
| 692 |
} |
| 693 |
|
| 694 |
void GraphicsContext::setLineJoin(LineJoin lineJoin) |
| 695 |
{ |
| 696 |
if (paintingDisabled()) |
| 697 |
return; |
| 698 |
|
| 699 |
cairo_line_join_t cairoJoin = CAIRO_LINE_JOIN_MITER; |
| 700 |
switch (lineJoin) { |
| 701 |
case MiterJoin: |
| 702 |
// no-op |
| 703 |
break; |
| 704 |
case RoundJoin: |
| 705 |
cairoJoin = CAIRO_LINE_JOIN_ROUND; |
| 706 |
break; |
| 707 |
case BevelJoin: |
| 708 |
cairoJoin = CAIRO_LINE_JOIN_BEVEL; |
| 709 |
break; |
| 710 |
} |
| 711 |
cairo_set_line_join(m_data->cr, cairoJoin); |
| 712 |
} |
| 713 |
|
| 714 |
void GraphicsContext::setMiterLimit(float miter) |
| 715 |
{ |
| 716 |
if (paintingDisabled()) |
| 717 |
return; |
| 718 |
|
| 719 |
cairo_set_miter_limit(m_data->cr, miter); |
| 720 |
} |
| 721 |
|
| 722 |
void GraphicsContext::setAlpha(float) |
| 723 |
{ |
| 724 |
notImplemented(); |
| 725 |
} |
| 726 |
|
| 727 |
static inline cairo_operator_t toCairoOperator(CompositeOperator op) |
| 728 |
{ |
| 729 |
switch (op) { |
| 730 |
case CompositeClear: |
| 731 |
return CAIRO_OPERATOR_CLEAR; |
| 732 |
case CompositeCopy: |
| 733 |
return CAIRO_OPERATOR_SOURCE; |
| 734 |
case CompositeSourceOver: |
| 735 |
return CAIRO_OPERATOR_OVER; |
| 736 |
case CompositeSourceIn: |
| 737 |
return CAIRO_OPERATOR_IN; |
| 738 |
case CompositeSourceOut: |
| 739 |
return CAIRO_OPERATOR_OUT; |
| 740 |
case CompositeSourceAtop: |
| 741 |
return CAIRO_OPERATOR_ATOP; |
| 742 |
case CompositeDestinationOver: |
| 743 |
return CAIRO_OPERATOR_DEST_OVER; |
| 744 |
case CompositeDestinationIn: |
| 745 |
return CAIRO_OPERATOR_DEST_IN; |
| 746 |
case CompositeDestinationOut: |
| 747 |
return CAIRO_OPERATOR_DEST_OUT; |
| 748 |
case CompositeDestinationAtop: |
| 749 |
return CAIRO_OPERATOR_DEST_ATOP; |
| 750 |
case CompositeXOR: |
| 751 |
return CAIRO_OPERATOR_XOR; |
| 752 |
case CompositePlusDarker: |
| 753 |
return CAIRO_OPERATOR_SATURATE; |
| 754 |
case CompositeHighlight: |
| 755 |
// There is no Cairo equivalent for CompositeHighlight. |
| 756 |
return CAIRO_OPERATOR_OVER; |
| 757 |
case CompositePlusLighter: |
| 758 |
return CAIRO_OPERATOR_ADD; |
| 759 |
default: |
| 760 |
return CAIRO_OPERATOR_SOURCE; |
| 761 |
} |
| 762 |
} |
| 763 |
|
| 764 |
void GraphicsContext::setCompositeOperation(CompositeOperator op) |
| 765 |
{ |
| 766 |
if (paintingDisabled()) |
| 767 |
return; |
| 768 |
|
| 769 |
cairo_set_operator(m_data->cr, toCairoOperator(op)); |
| 770 |
} |
| 771 |
|
| 772 |
void GraphicsContext::beginPath() |
| 773 |
{ |
| 774 |
if (paintingDisabled()) |
| 775 |
return; |
| 776 |
|
| 777 |
cairo_t* cr = m_data->cr; |
| 778 |
cairo_new_path(cr); |
| 779 |
} |
| 780 |
|
| 781 |
void GraphicsContext::addPath(const Path& path) |
| 782 |
{ |
| 783 |
if (paintingDisabled()) |
| 784 |
return; |
| 785 |
|
| 786 |
cairo_t* cr = m_data->cr; |
| 787 |
cairo_path_t* p = cairo_copy_path(path.platformPath()->m_cr); |
| 788 |
cairo_append_path(cr, p); |
| 789 |
cairo_path_destroy(p); |
| 790 |
} |
| 791 |
|
| 792 |
void GraphicsContext::clip(const Path& path) |
| 793 |
{ |
| 794 |
if (paintingDisabled()) |
| 795 |
return; |
| 796 |
|
| 797 |
cairo_t* cr = m_data->cr; |
| 798 |
cairo_path_t* p = cairo_copy_path(path.platformPath()->m_cr); |
| 799 |
cairo_append_path(cr, p); |
| 800 |
cairo_path_destroy(p); |
| 801 |
cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); |
| 802 |
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); |
| 803 |
cairo_clip(cr); |
| 804 |
cairo_set_fill_rule(cr, savedFillRule); |
| 805 |
} |
| 806 |
|
| 807 |
void GraphicsContext::clipOut(const Path& path) |
| 808 |
{ |
| 809 |
if (paintingDisabled()) |
| 810 |
return; |
| 811 |
|
| 812 |
cairo_t* cr = m_data->cr; |
| 813 |
double x1, y1, x2, y2; |
| 814 |
cairo_clip_extents(cr, &x1, &y1, &x2, &y2); |
| 815 |
cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1); |
| 816 |
addPath(path); |
| 817 |
|
| 818 |
cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); |
| 819 |
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); |
| 820 |
cairo_clip(cr); |
| 821 |
cairo_set_fill_rule(cr, savedFillRule); |
| 822 |
} |
| 823 |
|
| 824 |
void GraphicsContext::rotate(float radians) |
| 825 |
{ |
| 826 |
if (paintingDisabled()) |
| 827 |
return; |
| 828 |
|
| 829 |
cairo_rotate(m_data->cr, radians); |
| 830 |
} |
| 831 |
|
| 832 |
void GraphicsContext::scale(const FloatSize& size) |
| 833 |
{ |
| 834 |
if (paintingDisabled()) |
| 835 |
return; |
| 836 |
|
| 837 |
cairo_scale(m_data->cr, size.width(), size.height()); |
| 838 |
} |
| 839 |
|
| 840 |
void GraphicsContext::clipOut(const IntRect& r) |
| 841 |
{ |
| 842 |
if (paintingDisabled()) |
| 843 |
return; |
| 844 |
|
| 845 |
cairo_t* cr = m_data->cr; |
| 846 |
double x1, y1, x2, y2; |
| 847 |
cairo_clip_extents(cr, &x1, &y1, &x2, &y2); |
| 848 |
cairo_rectangle(cr, x1, x2, x2 - x1, y2 - y1); |
| 849 |
cairo_rectangle(cr, r.x(), r.y(), r.width(), r.height()); |
| 850 |
cairo_fill_rule_t savedFillRule = cairo_get_fill_rule(cr); |
| 851 |
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); |
| 852 |
cairo_clip(cr); |
| 853 |
cairo_set_fill_rule(cr, savedFillRule); |
| 854 |
} |
| 855 |
|
| 856 |
void GraphicsContext::clipOutEllipseInRect(const IntRect& r) |
| 857 |
{ |
| 858 |
if (paintingDisabled()) |
| 859 |
return; |
| 860 |
|
| 861 |
Path p; |
| 862 |
p.addEllipse(r); |
| 863 |
clipOut(p); |
| 864 |
} |
| 865 |
|
| 866 |
void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) |
| 867 |
{ |
| 868 |
if (paintingDisabled()) |
| 869 |
return; |
| 870 |
|
| 871 |
cairo_t* cr = m_data->cr; |
| 872 |
cairo_save(cr); |
| 873 |
beginPath(); |
| 874 |
addPath(Path::createRoundedRectangle(r, topLeft, topRight, bottomLeft, bottomRight)); |
| 875 |
setColor(cr, color); |
| 876 |
cairo_fill(cr); |
| 877 |
cairo_restore(cr); |
| 878 |
} |
| 879 |
|
| 880 |
#if PLATFORM(GTK) |
| 881 |
void GraphicsContext::setGdkExposeEvent(GdkEventExpose* expose) |
| 882 |
{ |
| 883 |
m_data->expose = expose; |
| 884 |
} |
| 885 |
|
| 886 |
GdkEventExpose* GraphicsContext::gdkExposeEvent() const |
| 887 |
{ |
| 888 |
return m_data->expose; |
| 889 |
} |
| 890 |
|
| 891 |
GdkDrawable* GraphicsContext::gdkDrawable() const |
| 892 |
{ |
| 893 |
if (!m_data->expose) |
| 894 |
return 0; |
| 895 |
|
| 896 |
return GDK_DRAWABLE(m_data->expose->window); |
| 897 |
} |
| 898 |
|
| 899 |
IntPoint GraphicsContext::translatePoint(const IntPoint& point) const |
| 900 |
{ |
| 901 |
cairo_matrix_t tm; |
| 902 |
cairo_get_matrix(m_data->cr, &tm); |
| 903 |
double x = point.x(); |
| 904 |
double y = point.y(); |
| 905 |
|
| 906 |
cairo_matrix_transform_point(&tm, &x, &y); |
| 907 |
return IntPoint(x, y); |
| 908 |
} |
| 909 |
#endif |
| 910 |
|
| 911 |
void GraphicsContext::setUseAntialiasing(bool enable) |
| 912 |
{ |
| 913 |
if (paintingDisabled()) |
| 914 |
return; |
| 915 |
|
| 916 |
// When true, use the default Cairo backend antialias mode (usually this |
| 917 |
// enables standard 'grayscale' antialiasing); false to explicitly disable |
| 918 |
// antialiasing. This is the same strategy as used in drawConvexPolygon(). |
| 919 |
cairo_set_antialias(m_data->cr, enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); |
| 920 |
} |
| 921 |
|
| 922 |
} // namespace WebCore |
| 923 |
|
| 924 |
#endif // PLATFORM(CAIRO) |