4ba0b6e6d85858c69c3dc51eb75529138aa19994
[WebKit-https.git] / WebCore / platform / graphics / cairo / PathCairo.cpp
1 /*
2     Copyright (C) 2007 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
3     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <wildfox@kde.org>
4                   2004, 2005, 2006 Rob Buis <buis@kde.org>
5                   2005, 2007 Apple Inc. All Rights reserved.
6                   2007 Alp Toker <alp@atoker.com>
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     aint with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "Path.h"
26
27 #include "AffineTransform.h"
28 #include "CairoPath.h"
29 #include "FloatRect.h"
30 #include "GraphicsContext.h"
31 #include "NotImplemented.h"
32 #include "PlatformString.h"
33 #include "StrokeStyleApplier.h"
34
35 #include <cairo.h>
36 #include <math.h>
37 #include <wtf/MathExtras.h>
38
39 namespace WebCore {
40
41 Path::Path()
42     : m_path(new CairoPath())
43 {
44 }
45
46 Path::~Path()
47 {
48     delete m_path;
49 }
50
51 Path::Path(const Path& other)
52     : m_path(new CairoPath())
53 {
54     cairo_t* cr = platformPath()->m_cr;
55     cairo_path_t* p = cairo_copy_path(other.platformPath()->m_cr);
56     cairo_append_path(cr, p);
57     cairo_path_destroy(p);
58 }
59
60 Path& Path::operator=(const Path& other)
61 {
62     if (&other == this)
63         return *this;
64
65     clear();
66     cairo_t* cr = platformPath()->m_cr;
67     cairo_path_t* p = cairo_copy_path(other.platformPath()->m_cr);
68     cairo_append_path(cr, p);
69     cairo_path_destroy(p);
70     return *this;
71 }
72
73 void Path::clear()
74 {
75     cairo_t* cr = platformPath()->m_cr;
76     cairo_new_path(cr);
77 }
78
79 bool Path::isEmpty() const
80 {
81     cairo_t* cr = platformPath()->m_cr;
82 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,5,10)
83     return !cairo_has_current_point(cr);
84 #else
85     cairo_path_t* p = cairo_copy_path(cr);
86     bool hasData = p->num_data;
87     cairo_path_destroy(p);
88     return !hasData;
89 #endif
90 }
91
92 void Path::translate(const FloatSize& p)
93 {
94     cairo_t* cr = platformPath()->m_cr;
95     cairo_translate(cr, p.width(), p.height());
96 }
97
98 void Path::moveTo(const FloatPoint& p)
99 {
100     cairo_t* cr = platformPath()->m_cr;
101     cairo_move_to(cr, p.x(), p.y());
102 }
103
104 void Path::addLineTo(const FloatPoint& p)
105 {
106     cairo_t* cr = platformPath()->m_cr;
107     cairo_line_to(cr, p.x(), p.y());
108 }
109
110 void Path::addRect(const FloatRect& rect)
111 {
112     cairo_t* cr = platformPath()->m_cr;
113     cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
114 }
115
116 /*
117  * inspired by libsvg-cairo
118  */
119 void Path::addQuadCurveTo(const FloatPoint& controlPoint, const FloatPoint& point)
120 {
121     cairo_t* cr = platformPath()->m_cr;
122     double x, y;
123     double x1 = controlPoint.x();
124     double y1 = controlPoint.y();
125     double x2 = point.x();
126     double y2 = point.y();
127     cairo_get_current_point(cr, &x, &y);
128     cairo_curve_to(cr,
129                    x  + 2.0 / 3.0 * (x1 - x),  y  + 2.0 / 3.0 * (y1 - y),
130                    x2 + 2.0 / 3.0 * (x1 - x2), y2 + 2.0 / 3.0 * (y1 - y2),
131                    x2, y2);
132 }
133
134 void Path::addBezierCurveTo(const FloatPoint& controlPoint1, const FloatPoint& controlPoint2, const FloatPoint& controlPoint3)
135 {
136     cairo_t* cr = platformPath()->m_cr;
137     cairo_curve_to(cr, controlPoint1.x(), controlPoint1.y(),
138                    controlPoint2.x(), controlPoint2.y(),
139                    controlPoint3.x(), controlPoint3.y());
140 }
141
142 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise)
143 {
144     // http://bugs.webkit.org/show_bug.cgi?id=16449
145     // cairo_arc() functions hang or crash when passed inf as radius or start/end angle
146     if (!isfinite(r) || !isfinite(sa) || !isfinite(ea))
147         return;
148
149     cairo_t* cr = platformPath()->m_cr;
150     if (anticlockwise)
151         cairo_arc_negative(cr, p.x(), p.y(), r, sa, ea);
152     else
153         cairo_arc(cr, p.x(), p.y(), r, sa, ea);
154 }
155
156 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
157 {
158     // FIXME: cairo_arc_to not yet in cairo see cairo.h
159     // cairo_arc_to(m_cr, p1.x(), p1.y(), p2.x(), p2.y());
160     notImplemented();
161 }
162
163 void Path::addEllipse(const FloatRect& rect)
164 {
165     cairo_t* cr = platformPath()->m_cr;
166     cairo_save(cr);
167     float yRadius = .5 * rect.height();
168     float xRadius = .5 * rect.width();
169     cairo_translate(cr, rect.x() + xRadius, rect.y() + yRadius);
170     cairo_scale(cr, xRadius, yRadius);
171     cairo_arc(cr, 0., 0., 1., 0., 2 * piDouble);
172     cairo_restore(cr);
173 }
174
175 void Path::closeSubpath()
176 {
177     cairo_t* cr = platformPath()->m_cr;
178     cairo_close_path(cr);
179 }
180
181 FloatRect Path::boundingRect() const
182 {
183     cairo_t* cr = platformPath()->m_cr;
184     double x0, x1, y0, y1;
185 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 6, 0)
186     cairo_path_extents(cr, &x0, &y0, &x1, &y1);
187 #else
188     cairo_stroke_extents(cr, &x0, &y0, &x1, &y1);
189 #endif
190     return FloatRect(x0, y0, x1 - x0, y1 - y0);
191 }
192
193 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
194 {
195     cairo_t* cr = platformPath()->m_cr;
196     if (applier) {
197         GraphicsContext gc(cr);
198         applier->strokeStyle(&gc);
199     }
200
201     double x0, x1, y0, y1;
202     cairo_stroke_extents(cr, &x0, &y0, &x1, &y1);
203     return FloatRect(x0, y0, x1 - x0, y1 - y0);
204 }
205
206 bool Path::contains(const FloatPoint& point, WindRule rule) const
207 {
208     if (!boundingRect().contains(point))
209         return false;
210
211     cairo_t* cr = platformPath()->m_cr;
212     cairo_fill_rule_t cur = cairo_get_fill_rule(cr);
213     cairo_set_fill_rule(cr, rule == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
214     bool contains = cairo_in_fill(cr, point.x(), point.y());
215     cairo_set_fill_rule(cr, cur);
216     return contains;
217 }
218
219 void Path::apply(void* info, PathApplierFunction function) const
220 {
221     cairo_t* cr = platformPath()->m_cr;
222     cairo_path_t* path = cairo_copy_path(cr);
223     cairo_path_data_t* data;
224     PathElement pelement;
225     FloatPoint points[3];
226     pelement.points = points;
227
228     for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
229         data = &path->data[i];
230         switch (data->header.type) {
231         case CAIRO_PATH_MOVE_TO:
232             pelement.type = PathElementMoveToPoint;
233             pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
234             function(info, &pelement);
235             break;
236         case CAIRO_PATH_LINE_TO:
237             pelement.type = PathElementAddLineToPoint;
238             pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
239             function(info, &pelement);
240             break;
241         case CAIRO_PATH_CURVE_TO:
242             pelement.type = PathElementAddCurveToPoint;
243             pelement.points[0] = FloatPoint(data[1].point.x,data[1].point.y);
244             pelement.points[1] = FloatPoint(data[2].point.x,data[2].point.y);
245             pelement.points[2] = FloatPoint(data[3].point.x,data[3].point.y);
246             function(info, &pelement);
247             break;
248         case CAIRO_PATH_CLOSE_PATH:
249             pelement.type = PathElementCloseSubpath;
250             function(info, &pelement);
251             break;
252         }
253     }
254     cairo_path_destroy(path);
255 }
256
257 void Path::transform(const AffineTransform& trans)
258 {
259     cairo_t* m_cr = platformPath()->m_cr;
260     cairo_matrix_t c_matrix = cairo_matrix_t(trans);
261     cairo_matrix_invert(&c_matrix);
262     cairo_transform(m_cr, &c_matrix);
263 }
264
265 String Path::debugString() const
266 {
267     if (isEmpty())
268         return String();
269
270     String pathString;
271     cairo_path_t* path = cairo_copy_path(platformPath()->m_cr);
272     cairo_path_data_t* data;
273
274     for (int i = 0; i < path->num_data; i += path->data[i].header.length) {
275         data = &path->data[i];
276         switch (data->header.type) {
277         case CAIRO_PATH_MOVE_TO:
278             if (i < (path->num_data - path->data[i].header.length))
279                 pathString += String::format("M%.2f,%.2f ",
280                                       data[1].point.x, data[1].point.y);
281             break;
282         case CAIRO_PATH_LINE_TO:
283             pathString += String::format("L%.2f,%.2f ",
284                                       data[1].point.x, data[1].point.y);
285             break;
286         case CAIRO_PATH_CURVE_TO:
287             pathString += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
288                                       data[1].point.x, data[1].point.y,
289                                       data[2].point.x, data[2].point.y,
290                                       data[3].point.x, data[3].point.y);
291             break;
292         case CAIRO_PATH_CLOSE_PATH:
293             pathString += "Z ";
294             break;
295         }
296     }
297
298     cairo_path_destroy(path);
299     return pathString.simplifyWhiteSpace();
300 }
301
302 } // namespace WebCore