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