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