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