2009-01-14 David Levin <levin@chromium.org>
[WebKit-https.git] / WebCore / platform / graphics / skia / PathSkia.cpp
1 // Copyright (c) 2008, Google Inc.
2 // All rights reserved.
3 // 
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 // 
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "config.h"
31 #include "Path.h"
32
33 #include "FloatRect.h"
34 #include "ImageBuffer.h"
35 #include "StrokeStyleApplier.h"
36
37 #include "SkPath.h"
38 #include "SkRegion.h"
39 #include "SkiaUtils.h"
40
41 #include <wtf/MathExtras.h>
42
43 namespace WebCore {
44
45 Path::Path()
46 {
47     m_path = new SkPath;
48 }
49
50 Path::Path(const Path& other)
51 {
52     m_path = new SkPath(*other.m_path);
53 }
54
55 Path::~Path()
56 {
57     delete m_path;
58 }
59
60 Path& Path::operator=(const Path& other)
61 {
62     *m_path = *other.m_path;
63     return *this;
64 }
65
66 bool Path::isEmpty() const
67 {
68     return m_path->isEmpty();
69 }
70
71 bool Path::contains(const FloatPoint& point, WindRule rule) const
72 {
73     return SkPathContainsPoint(m_path, point,
74       rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
75 }
76
77 void Path::translate(const FloatSize& size)
78 {
79     m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
80 }
81
82 FloatRect Path::boundingRect() const
83 {
84     SkRect rect;
85     m_path->computeBounds(&rect, SkPath::kExact_BoundsType);
86     return rect;
87 }
88
89 void Path::moveTo(const FloatPoint& point)
90 {
91     m_path->moveTo(point);
92 }
93
94 void Path::addLineTo(const FloatPoint& point)
95 {
96     m_path->lineTo(point);
97 }
98
99 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
100 {
101     m_path->quadTo(cp, ep);
102 }
103
104 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
105 {
106     m_path->cubicTo(p1, p2, ep);
107 }
108
109 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
110 {
111     m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius));
112 }
113
114 void Path::closeSubpath()
115 {
116     m_path->close();
117 }
118
119 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise) {
120     SkScalar cx = WebCoreFloatToSkScalar(p.x());
121     SkScalar cy = WebCoreFloatToSkScalar(p.y());
122     SkScalar radius = WebCoreFloatToSkScalar(r);
123
124     SkRect oval;
125     oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
126
127     float sweep = ea - sa;
128     // check for a circle
129     if (sweep >= 2 * piFloat || sweep <= -2 * piFloat)
130         m_path->addOval(oval);
131     else {
132         SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat);
133         SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
134
135         // Counterclockwise arcs should be drawn with negative sweeps, while
136         // clockwise arcs should be drawn with positive sweeps. Check to see
137         // if the situation is reversed and correct it by adding or subtracting
138         // a full circle
139         if (anticlockwise && sweepDegrees > 0) {
140             sweepDegrees -= SkIntToScalar(360);
141         } else if (!anticlockwise && sweepDegrees < 0) {
142             sweepDegrees += SkIntToScalar(360);
143         }
144
145         m_path->arcTo(oval, startDegrees, sweepDegrees, false);
146     }
147 }
148
149 void Path::addRect(const FloatRect& rect)
150 {
151     m_path->addRect(rect);
152 }
153
154 void Path::addEllipse(const FloatRect& rect)
155 {
156     m_path->addOval(rect);
157 }
158
159 void Path::clear()
160 {
161     m_path->reset();
162 }
163
164 static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count)
165 {
166     for (int i = 0; i < count; i++) {
167         dst[i].setX(SkScalarToFloat(src[i].fX));
168         dst[i].setY(SkScalarToFloat(src[i].fY));
169     }
170     return dst;
171 }
172
173 void Path::apply(void* info, PathApplierFunction function) const
174 {
175     SkPath::Iter iter(*m_path, false);
176     SkPoint pts[4];
177     PathElement pathElement;
178     FloatPoint pathPoints[3];
179
180     for (;;) {
181         switch (iter.next(pts)) {
182         case SkPath::kMove_Verb:
183             pathElement.type = PathElementMoveToPoint;
184             pathElement.points = convertPathPoints(pathPoints, &pts[0], 1);
185             break;
186         case SkPath::kLine_Verb:
187             pathElement.type = PathElementAddLineToPoint;
188             pathElement.points = convertPathPoints(pathPoints, &pts[1], 1);
189             break;
190         case SkPath::kQuad_Verb:
191             pathElement.type = PathElementAddQuadCurveToPoint;
192             pathElement.points = convertPathPoints(pathPoints, &pts[1], 2);
193             break;
194         case SkPath::kCubic_Verb:
195             pathElement.type = PathElementAddCurveToPoint;
196             pathElement.points = convertPathPoints(pathPoints, &pts[1], 3);
197             break;
198         case SkPath::kClose_Verb:
199             pathElement.type = PathElementCloseSubpath;
200             pathElement.points = convertPathPoints(pathPoints, 0, 0);
201             break;
202         case SkPath::kDone_Verb:
203             return;
204         }
205         function(info, &pathElement);
206     }
207 }
208
209 void Path::transform(const TransformationMatrix& xform)
210 {
211     m_path->transform(xform);
212 }
213
214 String Path::debugString() const
215 {
216     String result;
217
218     SkPath::Iter iter(*m_path, false);
219     SkPoint pts[4];
220
221     int numPoints = m_path->getPoints(0, 0);
222     SkPath::Verb verb;
223
224     do {
225         verb = iter.next(pts);
226         switch (verb) {
227         case SkPath::kMove_Verb:
228             result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
229             numPoints -= 1;
230             break;
231         case SkPath::kLine_Verb:
232           if (!iter.isCloseLine()) {
233                 result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY); 
234                 numPoints -= 1;
235             }
236             break;
237         case SkPath::kQuad_Verb:
238             result += String::format("Q%.2f,%.2f,%.2f,%.2f ",
239                 pts[1].fX, pts[1].fY,
240                 pts[2].fX, pts[2].fY);
241             numPoints -= 2;
242             break;
243         case SkPath::kCubic_Verb:
244             result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
245                 pts[1].fX, pts[1].fY,
246                 pts[2].fX, pts[2].fY,
247                 pts[3].fX, pts[3].fY);
248             numPoints -= 3;
249             break;
250         case SkPath::kClose_Verb:
251             result += "Z ";
252             break;
253         case SkPath::kDone_Verb:
254             break;
255         }
256     } while (verb != SkPath::kDone_Verb);
257
258     // If you have a path that ends with an M, Skia will not iterate the
259     // trailing M. That's nice of it, but Apple's paths output the trailing M
260     // and we want out layout dumps to look like theirs
261     if (numPoints) {
262         ASSERT(numPoints==1);
263         m_path->getLastPt(pts);
264         result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
265     }
266
267     return result.stripWhiteSpace();
268 }
269
270 // Computes the bounding box for the stroke and style currently selected into
271 // the given bounding box. This also takes into account the stroke width.
272 static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context)
273 {
274     SkPaint paint;
275     context->platformContext()->setupPaintForStroking(&paint, 0, 0);
276     SkPath boundingPath;
277     paint.getFillPath(context->platformContext()->currentPath(), &boundingPath);
278     SkRect r;
279     boundingPath.computeBounds(&r, SkPath::kExact_BoundsType);
280     return r;
281 }
282
283 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
284 {
285     GraphicsContext* scratch = scratchContext();
286     scratch->save();
287     scratch->beginPath();
288     scratch->addPath(*this);
289
290     if (applier)
291         applier->strokeStyle(scratch);
292
293     FloatRect r = boundingBoxForCurrentStroke(scratch);
294     scratch->restore();
295     return r;
296 }
297
298 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
299 {
300     ASSERT(applier);
301     GraphicsContext* scratch = scratchContext();
302     scratch->save();
303
304     applier->strokeStyle(scratch);
305
306     SkPaint paint;
307     scratch->platformContext()->setupPaintForStroking(&paint, 0, 0);
308     SkPath strokePath;
309     paint.getFillPath(*platformPath(), &strokePath);
310     bool contains = SkPathContainsPoint(&strokePath, point,
311                                         SkPath::kWinding_FillType);
312
313     scratch->restore();
314     return contains;
315 }
316 } // namespace WebCore