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