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
Path()45 Path::Path()
46 {
47 m_path = new SkPath;
48 }
49
Path(const Path & other)50 Path::Path(const Path& other)
51 {
52 m_path = new SkPath(*other.m_path);
53 }
54
~Path()55 Path::~Path()
56 {
57 delete m_path;
58 }
59
operator =(const Path & other)60 Path& Path::operator=(const Path& other)
61 {
62 *m_path = *other.m_path;
63 return *this;
64 }
65
isEmpty() const66 bool Path::isEmpty() const
67 {
68 return m_path->isEmpty();
69 }
70
hasCurrentPoint() const71 bool Path::hasCurrentPoint() const
72 {
73 return m_path->getPoints(NULL, 0) != 0;
74 }
75
contains(const FloatPoint & point,WindRule rule) const76 bool Path::contains(const FloatPoint& point, WindRule rule) const
77 {
78 return SkPathContainsPoint(m_path, point,
79 rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
80 }
81
translate(const FloatSize & size)82 void Path::translate(const FloatSize& size)
83 {
84 m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
85 }
86
boundingRect() const87 FloatRect Path::boundingRect() const
88 {
89 return m_path->getBounds();
90 }
91
moveTo(const FloatPoint & point)92 void Path::moveTo(const FloatPoint& point)
93 {
94 m_path->moveTo(point);
95 }
96
addLineTo(const FloatPoint & point)97 void Path::addLineTo(const FloatPoint& point)
98 {
99 m_path->lineTo(point);
100 }
101
addQuadCurveTo(const FloatPoint & cp,const FloatPoint & ep)102 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
103 {
104 m_path->quadTo(cp, ep);
105 }
106
addBezierCurveTo(const FloatPoint & p1,const FloatPoint & p2,const FloatPoint & ep)107 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
108 {
109 m_path->cubicTo(p1, p2, ep);
110 }
111
addArcTo(const FloatPoint & p1,const FloatPoint & p2,float radius)112 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
113 {
114 m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius));
115 }
116
closeSubpath()117 void Path::closeSubpath()
118 {
119 m_path->close();
120 }
121
addArc(const FloatPoint & p,float r,float sa,float ea,bool anticlockwise)122 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool anticlockwise) {
123 SkScalar cx = WebCoreFloatToSkScalar(p.x());
124 SkScalar cy = WebCoreFloatToSkScalar(p.y());
125 SkScalar radius = WebCoreFloatToSkScalar(r);
126
127 SkRect oval;
128 oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
129
130 float sweep = ea - sa;
131 // check for a circle
132 if (sweep >= 2 * piFloat || sweep <= -2 * piFloat)
133 m_path->addOval(oval);
134 else {
135 SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat);
136 SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
137
138 // Counterclockwise arcs should be drawn with negative sweeps, while
139 // clockwise arcs should be drawn with positive sweeps. Check to see
140 // if the situation is reversed and correct it by adding or subtracting
141 // a full circle
142 if (anticlockwise && sweepDegrees > 0) {
143 sweepDegrees -= SkIntToScalar(360);
144 } else if (!anticlockwise && sweepDegrees < 0) {
145 sweepDegrees += SkIntToScalar(360);
146 }
147
148 m_path->arcTo(oval, startDegrees, sweepDegrees, false);
149 }
150 }
151
addRect(const FloatRect & rect)152 void Path::addRect(const FloatRect& rect)
153 {
154 m_path->addRect(rect);
155 }
156
addEllipse(const FloatRect & rect)157 void Path::addEllipse(const FloatRect& rect)
158 {
159 m_path->addOval(rect);
160 }
161
clear()162 void Path::clear()
163 {
164 m_path->reset();
165 }
166
convertPathPoints(FloatPoint dst[],const SkPoint src[],int count)167 static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count)
168 {
169 for (int i = 0; i < count; i++) {
170 dst[i].setX(SkScalarToFloat(src[i].fX));
171 dst[i].setY(SkScalarToFloat(src[i].fY));
172 }
173 return dst;
174 }
175
apply(void * info,PathApplierFunction function) const176 void Path::apply(void* info, PathApplierFunction function) const
177 {
178 SkPath::Iter iter(*m_path, false);
179 SkPoint pts[4];
180 PathElement pathElement;
181 FloatPoint pathPoints[3];
182
183 for (;;) {
184 switch (iter.next(pts)) {
185 case SkPath::kMove_Verb:
186 pathElement.type = PathElementMoveToPoint;
187 pathElement.points = convertPathPoints(pathPoints, &pts[0], 1);
188 break;
189 case SkPath::kLine_Verb:
190 pathElement.type = PathElementAddLineToPoint;
191 pathElement.points = convertPathPoints(pathPoints, &pts[1], 1);
192 break;
193 case SkPath::kQuad_Verb:
194 pathElement.type = PathElementAddQuadCurveToPoint;
195 pathElement.points = convertPathPoints(pathPoints, &pts[1], 2);
196 break;
197 case SkPath::kCubic_Verb:
198 pathElement.type = PathElementAddCurveToPoint;
199 pathElement.points = convertPathPoints(pathPoints, &pts[1], 3);
200 break;
201 case SkPath::kClose_Verb:
202 pathElement.type = PathElementCloseSubpath;
203 pathElement.points = convertPathPoints(pathPoints, 0, 0);
204 break;
205 case SkPath::kDone_Verb:
206 return;
207 }
208 function(info, &pathElement);
209 }
210 }
211
transform(const TransformationMatrix & xform)212 void Path::transform(const TransformationMatrix& xform)
213 {
214 m_path->transform(xform);
215 }
216
debugString() const217 String Path::debugString() const
218 {
219 String result;
220
221 SkPath::Iter iter(*m_path, false);
222 SkPoint pts[4];
223
224 int numPoints = m_path->getPoints(0, 0);
225 SkPath::Verb verb;
226
227 do {
228 verb = iter.next(pts);
229 switch (verb) {
230 case SkPath::kMove_Verb:
231 result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
232 numPoints -= 1;
233 break;
234 case SkPath::kLine_Verb:
235 if (!iter.isCloseLine()) {
236 result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY);
237 numPoints -= 1;
238 }
239 break;
240 case SkPath::kQuad_Verb:
241 result += String::format("Q%.2f,%.2f,%.2f,%.2f ",
242 pts[1].fX, pts[1].fY,
243 pts[2].fX, pts[2].fY);
244 numPoints -= 2;
245 break;
246 case SkPath::kCubic_Verb:
247 result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ",
248 pts[1].fX, pts[1].fY,
249 pts[2].fX, pts[2].fY,
250 pts[3].fX, pts[3].fY);
251 numPoints -= 3;
252 break;
253 case SkPath::kClose_Verb:
254 result += "Z ";
255 break;
256 case SkPath::kDone_Verb:
257 break;
258 }
259 } while (verb != SkPath::kDone_Verb);
260
261 // If you have a path that ends with an M, Skia will not iterate the
262 // trailing M. That's nice of it, but Apple's paths output the trailing M
263 // and we want out layout dumps to look like theirs
264 if (numPoints) {
265 ASSERT(numPoints==1);
266 m_path->getLastPt(pts);
267 result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
268 }
269
270 return result.stripWhiteSpace();
271 }
272
273 // Computes the bounding box for the stroke and style currently selected into
274 // the given bounding box. This also takes into account the stroke width.
boundingBoxForCurrentStroke(const GraphicsContext * context)275 static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context)
276 {
277 SkPaint paint;
278 context->platformContext()->setupPaintForStroking(&paint, 0, 0);
279 SkPath boundingPath;
280 paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath);
281 return boundingPath.getBounds();
282 }
283
strokeBoundingRect(StrokeStyleApplier * applier)284 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
285 {
286 GraphicsContext* scratch = scratchContext();
287 scratch->save();
288 scratch->beginPath();
289 scratch->addPath(*this);
290
291 if (applier)
292 applier->strokeStyle(scratch);
293
294 FloatRect r = boundingBoxForCurrentStroke(scratch);
295 scratch->restore();
296 return r;
297 }
298
strokeContains(StrokeStyleApplier * applier,const FloatPoint & point) const299 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
300 {
301 ASSERT(applier);
302 GraphicsContext* scratch = scratchContext();
303 scratch->save();
304
305 applier->strokeStyle(scratch);
306
307 SkPaint paint;
308 scratch->platformContext()->setupPaintForStroking(&paint, 0, 0);
309 SkPath strokePath;
310 paint.getFillPath(*platformPath(), &strokePath);
311 bool contains = SkPathContainsPoint(&strokePath, point,
312 SkPath::kWinding_FillType);
313
314 scratch->restore();
315 return contains;
316 }
317 } // namespace WebCore
318