| OLD | NEW |
| (Empty) |
| 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 "core/platform/graphics/Path.h" | |
| 32 | |
| 33 #include <math.h> | |
| 34 #include "core/platform/graphics/GraphicsContext.h" | |
| 35 #include "core/platform/graphics/skia/SkiaUtils.h" | |
| 36 #include "platform/geometry/FloatPoint.h" | |
| 37 #include "platform/geometry/FloatRect.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 | |
| 45 Path::Path() | |
| 46 : m_path() | |
| 47 { | |
| 48 } | |
| 49 | |
| 50 Path::Path(const Path& other) | |
| 51 { | |
| 52 m_path = SkPath(other.m_path); | |
| 53 } | |
| 54 | |
| 55 Path::~Path() | |
| 56 { | |
| 57 } | |
| 58 | |
| 59 Path& Path::operator=(const Path& other) | |
| 60 { | |
| 61 m_path = SkPath(other.m_path); | |
| 62 return *this; | |
| 63 } | |
| 64 | |
| 65 bool Path::operator==(const Path& other) const | |
| 66 { | |
| 67 return m_path == other.m_path; | |
| 68 } | |
| 69 | |
| 70 bool Path::contains(const FloatPoint& point, WindRule rule) const | |
| 71 { | |
| 72 return SkPathContainsPoint(m_path, point, rule == RULE_NONZERO ? SkPath::kWi
nding_FillType : SkPath::kEvenOdd_FillType); | |
| 73 } | |
| 74 | |
| 75 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 | |
| 85 FloatRect Path::boundingRect() const | |
| 86 { | |
| 87 return m_path.getBounds(); | |
| 88 } | |
| 89 | |
| 90 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 | |
| 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 | |
| 109 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 | |
| 147 void Path::transform(const AffineTransform& xform) | |
| 148 { | |
| 149 m_path.transform(affineTransformToSkMatrix(xform)); | |
| 150 } | |
| 151 | |
| 152 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 | |
| 164 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 | |
| 172 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 | |
| 180 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, tange
nt.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 | |
| 204 void Path::clear() | |
| 205 { | |
| 206 m_path.reset(); | |
| 207 } | |
| 208 | |
| 209 bool Path::isEmpty() const | |
| 210 { | |
| 211 return m_path.isEmpty(); | |
| 212 } | |
| 213 | |
| 214 bool Path::hasCurrentPoint() const | |
| 215 { | |
| 216 return m_path.getPoints(0, 0); | |
| 217 } | |
| 218 | |
| 219 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 | |
| 235 WindRule Path::windRule() const | |
| 236 { | |
| 237 return m_path.getFillType() == SkPath::kEvenOdd_FillType | |
| 238 ? RULE_EVENODD | |
| 239 : RULE_NONZERO; | |
| 240 } | |
| 241 | |
| 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 | |
| 249 void Path::moveTo(const FloatPoint& point) | |
| 250 { | |
| 251 m_path.moveTo(point); | |
| 252 } | |
| 253 | |
| 254 void Path::addLineTo(const FloatPoint& point) | |
| 255 { | |
| 256 m_path.lineTo(point); | |
| 257 } | |
| 258 | |
| 259 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) | |
| 260 { | |
| 261 m_path.quadTo(cp, ep); | |
| 262 } | |
| 263 | |
| 264 void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const Fl
oatPoint& ep) | |
| 265 { | |
| 266 m_path.cubicTo(p1, p2, ep); | |
| 267 } | |
| 268 | |
| 269 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) | |
| 270 { | |
| 271 m_path.arcTo(p1, p2, WebCoreFloatToSkScalar(radius)); | |
| 272 } | |
| 273 | |
| 274 void Path::closeSubpath() | |
| 275 { | |
| 276 m_path.close(); | |
| 277 } | |
| 278 | |
| 279 void Path::addEllipse(const FloatPoint& p, float radiusX, float radiusY, float s
tartAngle, float endAngle, bool anticlockwise) | |
| 280 { | |
| 281 ASSERT(std::abs(endAngle - startAngle) < 4 * piFloat); | |
| 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 SkScalar s360 = SkIntToScalar(360); | |
| 290 | |
| 291 SkRect oval; | |
| 292 oval.set(cx - radiusXScalar, cy - radiusYScalar, cx + radiusXScalar, cy + ra
diusYScalar); | |
| 293 | |
| 294 float sweep = endAngle - startAngle; | |
| 295 SkScalar startDegrees = WebCoreFloatToSkScalar(startAngle * 180 / piFloat); | |
| 296 SkScalar sweepDegrees = WebCoreFloatToSkScalar(sweep * 180 / piFloat); | |
| 297 | |
| 298 // We can't use SkPath::addOval(), because addOval() makes new sub-path. add
Oval() 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 (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 m_path.arcTo(oval, startDegrees + s360, sweepDegrees - s360, false); | |
| 307 return; | |
| 308 } | |
| 309 if (sweepDegrees <= -s360) { | |
| 310 m_path.arcTo(oval, startDegrees, -s180, false); | |
| 311 m_path.arcTo(oval, startDegrees - s180, -s180, false); | |
| 312 m_path.arcTo(oval, startDegrees - s360, sweepDegrees + s360, false); | |
| 313 return; | |
| 314 } | |
| 315 | |
| 316 m_path.arcTo(oval, startDegrees, sweepDegrees, false); | |
| 317 } | |
| 318 | |
| 319 void Path::addArc(const FloatPoint& p, float radius, float startAngle, float end
Angle, bool anticlockwise) | |
| 320 { | |
| 321 addEllipse(p, radius, radius, startAngle, endAngle, anticlockwise); | |
| 322 } | |
| 323 | |
| 324 void Path::addRect(const FloatRect& rect) | |
| 325 { | |
| 326 m_path.addRect(rect); | |
| 327 } | |
| 328 | |
| 329 void Path::addEllipse(const FloatPoint& p, float radiusX, float radiusY, float r
otation, float startAngle, float endAngle, bool anticlockwise) | |
| 330 { | |
| 331 ASSERT(std::abs(endAngle - startAngle) < 4 * piFloat); | |
| 332 ASSERT(startAngle >= 0 && startAngle < 2 * piFloat); | |
| 333 ASSERT((anticlockwise && (startAngle - endAngle) >= 0) || (!anticlockwise &&
(endAngle - startAngle) >= 0)); | |
| 334 | |
| 335 if (!rotation) { | |
| 336 addEllipse(FloatPoint(p.x(), p.y()), radiusX, radiusY, startAngle, endAn
gle, anticlockwise); | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 // Add an arc after the relevant transform. | |
| 341 AffineTransform ellipseTransform = AffineTransform::translation(p.x(), p.y()
).rotate(rad2deg(rotation)); | |
| 342 ASSERT(ellipseTransform.isInvertible()); | |
| 343 AffineTransform inverseEllipseTransform = ellipseTransform.inverse(); | |
| 344 transform(inverseEllipseTransform); | |
| 345 addEllipse(FloatPoint::zero(), radiusX, radiusY, startAngle, endAngle, antic
lockwise); | |
| 346 transform(ellipseTransform); | |
| 347 } | |
| 348 | |
| 349 void Path::addEllipse(const FloatRect& rect) | |
| 350 { | |
| 351 m_path.addOval(rect); | |
| 352 } | |
| 353 | |
| 354 void Path::addRoundedRect(const RoundedRect& r) | |
| 355 { | |
| 356 addRoundedRect(r.rect(), r.radii().topLeft(), r.radii().topRight(), r.radii(
).bottomLeft(), r.radii().bottomRight()); | |
| 357 } | |
| 358 | |
| 359 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii) | |
| 360 { | |
| 361 if (rect.isEmpty()) | |
| 362 return; | |
| 363 | |
| 364 FloatSize radius(roundingRadii); | |
| 365 FloatSize halfSize(rect.width() / 2, rect.height() / 2); | |
| 366 | |
| 367 // Apply the SVG corner radius constraints, per the rect section of the SVG
shapes spec: if | |
| 368 // one of rx,ry is negative, then the other corner radius value is used. If
both values are | |
| 369 // negative then rx = ry = 0. If rx is greater than half of the width of the
rectangle | |
| 370 // then set rx to half of the width; ry is handled similarly. | |
| 371 | |
| 372 if (radius.width() < 0) | |
| 373 radius.setWidth((radius.height() < 0) ? 0 : radius.height()); | |
| 374 | |
| 375 if (radius.height() < 0) | |
| 376 radius.setHeight(radius.width()); | |
| 377 | |
| 378 if (radius.width() > halfSize.width()) | |
| 379 radius.setWidth(halfSize.width()); | |
| 380 | |
| 381 if (radius.height() > halfSize.height()) | |
| 382 radius.setHeight(halfSize.height()); | |
| 383 | |
| 384 addPathForRoundedRect(rect, radius, radius, radius, radius); | |
| 385 } | |
| 386 | |
| 387 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius,
const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const Float
Size& bottomRightRadius) | |
| 388 { | |
| 389 if (rect.isEmpty()) | |
| 390 return; | |
| 391 | |
| 392 if (rect.width() < topLeftRadius.width() + topRightRadius.width() | |
| 393 || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width
() | |
| 394 || rect.height() < topLeftRadius.height() + bottomLeftRadius.height(
) | |
| 395 || rect.height() < topRightRadius.height() + bottomRightRadius.heigh
t()) { | |
| 396 // If all the radii cannot be accommodated, return a rect. | |
| 397 addRect(rect); | |
| 398 return; | |
| 399 } | |
| 400 | |
| 401 addPathForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius,
bottomRightRadius); | |
| 402 } | |
| 403 | |
| 404 void Path::addPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeft
Radius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, cons
t FloatSize& bottomRightRadius) | |
| 405 { | |
| 406 addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadi
us, bottomRightRadius); | |
| 407 } | |
| 408 | |
| 409 // Approximation of control point positions on a bezier to simulate a quarter of
a circle. | |
| 410 // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3 | |
| 411 static const float gCircleControlPoint = 0.447715f; | |
| 412 | |
| 413 void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topL
eftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, c
onst FloatSize& bottomRightRadius) | |
| 414 { | |
| 415 moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); | |
| 416 | |
| 417 addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y())); | |
| 418 if (topRightRadius.width() > 0 || topRightRadius.height() > 0) | |
| 419 addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCirc
leControlPoint, rect.y()), | |
| 420 FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircle
ControlPoint), | |
| 421 FloatPoint(rect.maxX(), rect.y() + topRightRadius.height())); | |
| 422 addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height()))
; | |
| 423 if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0) | |
| 424 addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius
.height() * gCircleControlPoint), | |
| 425 FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlP
oint, rect.maxY()), | |
| 426 FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY())); | |
| 427 addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY())); | |
| 428 if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0) | |
| 429 addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircl
eControlPoint, rect.maxY()), | |
| 430 FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCirc
leControlPoint), | |
| 431 FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height())); | |
| 432 addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); | |
| 433 if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0) | |
| 434 addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height()
* gCircleControlPoint), | |
| 435 FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, r
ect.y()), | |
| 436 FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); | |
| 437 | |
| 438 closeSubpath(); | |
| 439 } | |
| 440 | |
| 441 void Path::translate(const FloatSize& size) | |
| 442 { | |
| 443 m_path.offset(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(s
ize.height())); | |
| 444 } | |
| 445 | |
| 446 bool Path::unionPath(const Path& other) | |
| 447 { | |
| 448 return Op(m_path, other.m_path, kUnion_PathOp, &m_path); | |
| 449 } | |
| 450 | |
| 451 } | |
| OLD | NEW |