1 // Copyright (c) 2008, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "FloatRect.h"
34 #include "ImageBuffer.h"
35 #include "StrokeStyleApplier.h"
39 #include "SkiaUtils.h"
41 #include <wtf/MathExtras.h>
50 Path::Path(const Path& other)
52 m_path = new SkPath(*other.m_path);
60 Path& Path::operator=(const Path& other)
62 *m_path = *other.m_path;
66 bool Path::isEmpty() const
68 return m_path->isEmpty();
71 bool Path::contains(const FloatPoint& point, WindRule rule) const
73 return SkPathContainsPoint(m_path, point,
74 rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
77 void Path::translate(const FloatSize& size)
79 m_path->offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
82 FloatRect Path::boundingRect() const
85 m_path->computeBounds(&rect, SkPath::kExact_BoundsType);
89 void Path::moveTo(const FloatPoint& point)
91 m_path->moveTo(point);
94 void Path::addLineTo(const FloatPoint& point)
96 m_path->lineTo(point);
99 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
101 m_path->quadTo(cp, ep);
104 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
106 m_path->cubicTo(p1, p2, ep);
109 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
111 m_path->arcTo(p1, p2, WebCoreFloatToSkScalar(radius));
114 void Path::closeSubpath()
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);
125 oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
127 float sweep = ea - sa;
128 // check for a circle
129 if (sweep >= 2 * piFloat || sweep <= -2 * piFloat)
130 m_path->addOval(oval);
132 SkScalar startDegrees = WebCoreFloatToSkScalar(sa * 180 / piFloat);
133 SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
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
139 if (anticlockwise && sweepDegrees > 0) {
140 sweepDegrees -= SkIntToScalar(360);
141 } else if (!anticlockwise && sweepDegrees < 0) {
142 sweepDegrees += SkIntToScalar(360);
145 m_path->arcTo(oval, startDegrees, sweepDegrees, false);
149 void Path::addRect(const FloatRect& rect)
151 m_path->addRect(rect);
154 void Path::addEllipse(const FloatRect& rect)
156 m_path->addOval(rect);
164 static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count)
166 for (int i = 0; i < count; i++) {
167 dst[i].setX(SkScalarToFloat(src[i].fX));
168 dst[i].setY(SkScalarToFloat(src[i].fY));
173 void Path::apply(void* info, PathApplierFunction function) const
175 SkPath::Iter iter(*m_path, false);
177 PathElement pathElement;
178 FloatPoint pathPoints[3];
181 switch (iter.next(pts)) {
182 case SkPath::kMove_Verb:
183 pathElement.type = PathElementMoveToPoint;
184 pathElement.points = convertPathPoints(pathPoints, &pts[0], 1);
186 case SkPath::kLine_Verb:
187 pathElement.type = PathElementAddLineToPoint;
188 pathElement.points = convertPathPoints(pathPoints, &pts[1], 1);
190 case SkPath::kQuad_Verb:
191 pathElement.type = PathElementAddQuadCurveToPoint;
192 pathElement.points = convertPathPoints(pathPoints, &pts[1], 2);
194 case SkPath::kCubic_Verb:
195 pathElement.type = PathElementAddCurveToPoint;
196 pathElement.points = convertPathPoints(pathPoints, &pts[1], 3);
198 case SkPath::kClose_Verb:
199 pathElement.type = PathElementCloseSubpath;
200 pathElement.points = convertPathPoints(pathPoints, 0, 0);
202 case SkPath::kDone_Verb:
205 function(info, &pathElement);
209 void Path::transform(const TransformationMatrix& xform)
211 m_path->transform(xform);
214 String Path::debugString() const
218 SkPath::Iter iter(*m_path, false);
221 int numPoints = m_path->getPoints(0, 0);
225 verb = iter.next(pts);
227 case SkPath::kMove_Verb:
228 result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
231 case SkPath::kLine_Verb:
232 if (!iter.isCloseLine()) {
233 result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY);
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);
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);
250 case SkPath::kClose_Verb:
253 case SkPath::kDone_Verb:
256 } while (verb != SkPath::kDone_Verb);
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
262 ASSERT(numPoints==1);
263 m_path->getLastPt(pts);
264 result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY);
267 return result.stripWhiteSpace();
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)
275 context->platformContext()->setupPaintForStroking(&paint, 0, 0);
277 paint.getFillPath(context->platformContext()->currentPath(), &boundingPath);
279 boundingPath.computeBounds(&r, SkPath::kExact_BoundsType);
283 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
285 GraphicsContext* scratch = scratchContext();
287 scratch->beginPath();
288 scratch->addPath(*this);
291 applier->strokeStyle(scratch);
293 FloatRect r = boundingBoxForCurrentStroke(scratch);
298 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
301 GraphicsContext* scratch = scratchContext();
304 applier->strokeStyle(scratch);
307 scratch->platformContext()->setupPaintForStroking(&paint, 0, 0);
309 paint.getFillPath(*platformPath(), &strokePath);
310 bool contains = SkPathContainsPoint(&strokePath, point,
311 SkPath::kWinding_FillType);
316 } // namespace WebCore