• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2003, 2006 Apple Computer, Inc.  All rights reserved.
3  *                     2006 Rob Buis <buis@kde.org>
4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2013 Google Inc. All rights reserved.
6  * Copyright (C) 2013 Intel Corporation. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "config.h"
31 #include "platform/graphics/Path.h"
32 
33 #include <math.h>
34 #include "platform/geometry/FloatPoint.h"
35 #include "platform/geometry/FloatRect.h"
36 #include "platform/graphics/GraphicsContext.h"
37 #include "platform/graphics/skia/SkiaUtils.h"
38 #include "platform/transforms/AffineTransform.h"
39 #include "third_party/skia/include/core/SkPathMeasure.h"
40 #include "third_party/skia/include/pathops/SkPathOps.h"
41 #include "wtf/MathExtras.h"
42 
43 namespace WebCore {
44 
Path()45 Path::Path()
46     : m_path()
47 {
48 }
49 
Path(const Path & other)50 Path::Path(const Path& other)
51 {
52     m_path = SkPath(other.m_path);
53 }
54 
~Path()55 Path::~Path()
56 {
57 }
58 
operator =(const Path & other)59 Path& Path::operator=(const Path& other)
60 {
61     m_path = SkPath(other.m_path);
62     return *this;
63 }
64 
operator ==(const Path & other) const65 bool Path::operator==(const Path& other) const
66 {
67     return m_path == other.m_path;
68 }
69 
contains(const FloatPoint & point,WindRule rule) const70 bool Path::contains(const FloatPoint& point, WindRule rule) const
71 {
72     return SkPathContainsPoint(m_path, point, rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
73 }
74 
strokeContains(const FloatPoint & point,const StrokeData & strokeData) const75 bool Path::strokeContains(const FloatPoint& point, const StrokeData& strokeData) const
76 {
77     SkPaint paint;
78     strokeData.setupPaint(&paint);
79     SkPath strokePath;
80     paint.getFillPath(m_path, &strokePath);
81 
82     return SkPathContainsPoint(strokePath, point, SkPath::kWinding_FillType);
83 }
84 
boundingRect() const85 FloatRect Path::boundingRect() const
86 {
87     return m_path.getBounds();
88 }
89 
strokeBoundingRect(const StrokeData & strokeData) const90 FloatRect Path::strokeBoundingRect(const StrokeData& strokeData) const
91 {
92     SkPaint paint;
93     strokeData.setupPaint(&paint);
94     SkPath boundingPath;
95     paint.getFillPath(m_path, &boundingPath);
96 
97     return boundingPath.getBounds();
98 }
99 
convertPathPoints(FloatPoint dst[],const SkPoint src[],int count)100 static FloatPoint* convertPathPoints(FloatPoint dst[], const SkPoint src[], int count)
101 {
102     for (int i = 0; i < count; i++) {
103         dst[i].setX(SkScalarToFloat(src[i].fX));
104         dst[i].setY(SkScalarToFloat(src[i].fY));
105     }
106     return dst;
107 }
108 
apply(void * info,PathApplierFunction function) const109 void Path::apply(void* info, PathApplierFunction function) const
110 {
111     SkPath::RawIter iter(m_path);
112     SkPoint pts[4];
113     PathElement pathElement;
114     FloatPoint pathPoints[3];
115 
116     for (;;) {
117         switch (iter.next(pts)) {
118         case SkPath::kMove_Verb:
119             pathElement.type = PathElementMoveToPoint;
120             pathElement.points = convertPathPoints(pathPoints, &pts[0], 1);
121             break;
122         case SkPath::kLine_Verb:
123             pathElement.type = PathElementAddLineToPoint;
124             pathElement.points = convertPathPoints(pathPoints, &pts[1], 1);
125             break;
126         case SkPath::kQuad_Verb:
127             pathElement.type = PathElementAddQuadCurveToPoint;
128             pathElement.points = convertPathPoints(pathPoints, &pts[1], 2);
129             break;
130         case SkPath::kCubic_Verb:
131             pathElement.type = PathElementAddCurveToPoint;
132             pathElement.points = convertPathPoints(pathPoints, &pts[1], 3);
133             break;
134         case SkPath::kClose_Verb:
135             pathElement.type = PathElementCloseSubpath;
136             pathElement.points = convertPathPoints(pathPoints, 0, 0);
137             break;
138         case SkPath::kDone_Verb:
139             return;
140         default: // place-holder for kConic_Verb, when that lands from skia
141             break;
142         }
143         function(info, &pathElement);
144     }
145 }
146 
transform(const AffineTransform & xform)147 void Path::transform(const AffineTransform& xform)
148 {
149     m_path.transform(affineTransformToSkMatrix(xform));
150 }
151 
length() const152 float Path::length() const
153 {
154     SkScalar length = 0;
155     SkPathMeasure measure(m_path, false);
156 
157     do {
158         length += measure.getLength();
159     } while (measure.nextContour());
160 
161     return SkScalarToFloat(length);
162 }
163 
pointAtLength(float length,bool & ok) const164 FloatPoint Path::pointAtLength(float length, bool& ok) const
165 {
166     FloatPoint point;
167     float normal;
168     ok = pointAndNormalAtLength(length, point, normal);
169     return point;
170 }
171 
normalAngleAtLength(float length,bool & ok) const172 float Path::normalAngleAtLength(float length, bool& ok) const
173 {
174     FloatPoint point;
175     float normal;
176     ok = pointAndNormalAtLength(length, point, normal);
177     return normal;
178 }
179 
pointAndNormalAtLength(float length,FloatPoint & point,float & normal) const180 bool Path::pointAndNormalAtLength(float length, FloatPoint& point, float& normal) const
181 {
182     SkPathMeasure measure(m_path, false);
183 
184     do {
185         SkScalar contourLength = measure.getLength();
186         if (length <= contourLength) {
187             SkVector tangent;
188             SkPoint position;
189 
190             if (measure.getPosTan(length, &position, &tangent)) {
191                 normal = rad2deg(SkScalarToFloat(SkScalarATan2(tangent.fY, tangent.fX)));
192                 point = FloatPoint(SkScalarToFloat(position.fX), SkScalarToFloat(position.fY));
193                 return true;
194             }
195         }
196         length -= contourLength;
197     } while (measure.nextContour());
198 
199     normal = 0;
200     point = FloatPoint(0, 0);
201     return false;
202 }
203 
clear()204 void Path::clear()
205 {
206     m_path.reset();
207 }
208 
isEmpty() const209 bool Path::isEmpty() const
210 {
211     return m_path.isEmpty();
212 }
213 
hasCurrentPoint() const214 bool Path::hasCurrentPoint() const
215 {
216     return m_path.getPoints(0, 0);
217 }
218 
currentPoint() const219 FloatPoint Path::currentPoint() const
220 {
221     if (m_path.countPoints() > 0) {
222         SkPoint skResult;
223         m_path.getLastPt(&skResult);
224         FloatPoint result;
225         result.setX(SkScalarToFloat(skResult.fX));
226         result.setY(SkScalarToFloat(skResult.fY));
227         return result;
228     }
229 
230     // FIXME: Why does this return quietNaN? Other ports return 0,0.
231     float quietNaN = std::numeric_limits<float>::quiet_NaN();
232     return FloatPoint(quietNaN, quietNaN);
233 }
234 
windRule() const235 WindRule Path::windRule() const
236 {
237     return m_path.getFillType() == SkPath::kEvenOdd_FillType
238         ? RULE_EVENODD
239         : RULE_NONZERO;
240 }
241 
setWindRule(const WindRule rule)242 void Path::setWindRule(const WindRule rule)
243 {
244     m_path.setFillType(rule == RULE_EVENODD
245         ? SkPath::kEvenOdd_FillType
246         : SkPath::kWinding_FillType);
247 }
248 
moveTo(const FloatPoint & point)249 void Path::moveTo(const FloatPoint& point)
250 {
251     m_path.moveTo(point);
252 }
253 
addLineTo(const FloatPoint & point)254 void Path::addLineTo(const FloatPoint& point)
255 {
256     m_path.lineTo(point);
257 }
258 
addQuadCurveTo(const FloatPoint & cp,const FloatPoint & ep)259 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
260 {
261     m_path.quadTo(cp, ep);
262 }
263 
addBezierCurveTo(const FloatPoint & p1,const FloatPoint & p2,const FloatPoint & ep)264 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
265 {
266     m_path.cubicTo(p1, p2, ep);
267 }
268 
addArcTo(const FloatPoint & p1,const FloatPoint & p2,float radius)269 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
270 {
271     m_path.arcTo(p1, p2, WebCoreFloatToSkScalar(radius));
272 }
273 
closeSubpath()274 void Path::closeSubpath()
275 {
276     m_path.close();
277 }
278 
addEllipse(const FloatPoint & p,float radiusX,float radiusY,float startAngle,float endAngle,bool anticlockwise)279 void Path::addEllipse(const FloatPoint& p, float radiusX, float radiusY, float startAngle, float endAngle, bool anticlockwise)
280 {
281     ASSERT(ellipseIsRenderable(startAngle, endAngle));
282     ASSERT(startAngle >= 0 && startAngle < 2 * piFloat);
283     ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise && (endAngle - startAngle) >= 0));
284 
285     SkScalar cx = WebCoreFloatToSkScalar(p.x());
286     SkScalar cy = WebCoreFloatToSkScalar(p.y());
287     SkScalar radiusXScalar = WebCoreFloatToSkScalar(radiusX);
288     SkScalar radiusYScalar = WebCoreFloatToSkScalar(radiusY);
289 
290     SkRect oval;
291     oval.set(cx - radiusXScalar, cy - radiusYScalar, cx + radiusXScalar, cy + radiusYScalar);
292 
293     float sweep = endAngle - startAngle;
294     SkScalar startDegrees = WebCoreFloatToSkScalar(startAngle * 180 / piFloat);
295     SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat);
296     SkScalar s360 = SkIntToScalar(360);
297 
298     // We can't use SkPath::addOval(), because addOval() makes new sub-path. addOval() calls moveTo() and close() internally.
299 
300     // Use s180, not s360, because SkPath::arcTo(oval, angle, s360, false) draws nothing.
301     SkScalar s180 = SkIntToScalar(180);
302     if (SkScalarNearlyEqual(sweepDegrees, s360)) {
303         // SkPath::arcTo can't handle the sweepAngle that is equal to or greater than 2Pi.
304         m_path.arcTo(oval, startDegrees, s180, false);
305         m_path.arcTo(oval, startDegrees + s180, s180, false);
306         return;
307     }
308     if (SkScalarNearlyEqual(sweepDegrees, -s360)) {
309         m_path.arcTo(oval, startDegrees, -s180, false);
310         m_path.arcTo(oval, startDegrees - s180, -s180, false);
311         return;
312     }
313 
314     m_path.arcTo(oval, startDegrees, sweepDegrees, false);
315 }
316 
addArc(const FloatPoint & p,float radius,float startAngle,float endAngle,bool anticlockwise)317 void Path::addArc(const FloatPoint& p, float radius, float startAngle, float endAngle, bool anticlockwise)
318 {
319     addEllipse(p, radius, radius, startAngle, endAngle, anticlockwise);
320 }
321 
addRect(const FloatRect & rect)322 void Path::addRect(const FloatRect& rect)
323 {
324     m_path.addRect(rect);
325 }
326 
addEllipse(const FloatPoint & p,float radiusX,float radiusY,float rotation,float startAngle,float endAngle,bool anticlockwise)327 void Path::addEllipse(const FloatPoint& p, float radiusX, float radiusY, float rotation, float startAngle, float endAngle, bool anticlockwise)
328 {
329     ASSERT(ellipseIsRenderable(startAngle, endAngle));
330     ASSERT(startAngle >= 0 && startAngle < 2 * piFloat);
331     ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise && (endAngle - startAngle) >= 0));
332 
333     if (!rotation) {
334         addEllipse(FloatPoint(p.x(), p.y()), radiusX, radiusY, startAngle, endAngle, anticlockwise);
335         return;
336     }
337 
338     // Add an arc after the relevant transform.
339     AffineTransform ellipseTransform = AffineTransform::translation(p.x(), p.y()).rotate(rad2deg(rotation));
340     ASSERT(ellipseTransform.isInvertible());
341     AffineTransform inverseEllipseTransform = ellipseTransform.inverse();
342     transform(inverseEllipseTransform);
343     addEllipse(FloatPoint::zero(), radiusX, radiusY, startAngle, endAngle, anticlockwise);
344     transform(ellipseTransform);
345 }
346 
addEllipse(const FloatRect & rect)347 void Path::addEllipse(const FloatRect& rect)
348 {
349     m_path.addOval(rect);
350 }
351 
addRoundedRect(const RoundedRect & r)352 void Path::addRoundedRect(const RoundedRect& r)
353 {
354     addRoundedRect(r.rect(), r.radii().topLeft(), r.radii().topRight(), r.radii().bottomLeft(), r.radii().bottomRight());
355 }
356 
addRoundedRect(const FloatRect & rect,const FloatSize & roundingRadii)357 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii)
358 {
359     if (rect.isEmpty())
360         return;
361 
362     FloatSize radius(roundingRadii);
363     FloatSize halfSize(rect.width() / 2, rect.height() / 2);
364 
365     // Apply the SVG corner radius constraints, per the rect section of the SVG shapes spec: if
366     // one of rx,ry is negative, then the other corner radius value is used. If both values are
367     // negative then rx = ry = 0. If rx is greater than half of the width of the rectangle
368     // then set rx to half of the width; ry is handled similarly.
369 
370     if (radius.width() < 0)
371         radius.setWidth((radius.height() < 0) ? 0 : radius.height());
372 
373     if (radius.height() < 0)
374         radius.setHeight(radius.width());
375 
376     if (radius.width() > halfSize.width())
377         radius.setWidth(halfSize.width());
378 
379     if (radius.height() > halfSize.height())
380         radius.setHeight(halfSize.height());
381 
382     addPathForRoundedRect(rect, radius, radius, radius, radius);
383 }
384 
addRoundedRect(const FloatRect & rect,const FloatSize & topLeftRadius,const FloatSize & topRightRadius,const FloatSize & bottomLeftRadius,const FloatSize & bottomRightRadius)385 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
386 {
387     if (rect.isEmpty())
388         return;
389 
390     if (rect.width() < topLeftRadius.width() + topRightRadius.width()
391             || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width()
392             || rect.height() < topLeftRadius.height() + bottomLeftRadius.height()
393             || rect.height() < topRightRadius.height() + bottomRightRadius.height()) {
394         // If all the radii cannot be accommodated, return a rect.
395         addRect(rect);
396         return;
397     }
398 
399     addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
400 }
401 
addPathForRoundedRect(const FloatRect & rect,const FloatSize & topLeftRadius,const FloatSize & topRightRadius,const FloatSize & bottomLeftRadius,const FloatSize & bottomRightRadius)402 void Path::addPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
403 {
404     addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
405 }
406 
407 // Approximation of control point positions on a bezier to simulate a quarter of a circle.
408 // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3
409 static const float gCircleControlPoint = 0.447715f;
410 
addBeziersForRoundedRect(const FloatRect & rect,const FloatSize & topLeftRadius,const FloatSize & topRightRadius,const FloatSize & bottomLeftRadius,const FloatSize & bottomRightRadius)411 void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
412 {
413     moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
414 
415     addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y()));
416     if (topRightRadius.width() > 0 || topRightRadius.height() > 0)
417         addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()),
418             FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint),
419             FloatPoint(rect.maxX(), rect.y() + topRightRadius.height()));
420     addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height()));
421     if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0)
422         addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint),
423             FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()),
424             FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY()));
425     addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY()));
426     if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0)
427         addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()),
428             FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint),
429             FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height()));
430     addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height()));
431     if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0)
432         addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint),
433             FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()),
434             FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
435 
436     closeSubpath();
437 }
438 
translate(const FloatSize & size)439 void Path::translate(const FloatSize& size)
440 {
441     m_path.offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
442 }
443 
unionPath(const Path & other)444 bool Path::unionPath(const Path& other)
445 {
446     return Op(m_path, other.m_path, kUnion_PathOp, &m_path);
447 }
448 
449 #if !ASSERT_DISABLED
ellipseIsRenderable(float startAngle,float endAngle)450 bool ellipseIsRenderable(float startAngle, float endAngle)
451 {
452     return (std::abs(endAngle - startAngle) < 2 * piFloat)
453         || WebCoreFloatNearlyEqual(std::abs(endAngle - startAngle), 2 * piFloat);
454 }
455 #endif
456 
457 }
458