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