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