1/* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/core/SkPathBuilder.h" 9#include "include/core/SkRRect.h" 10#include "include/private/SkPathRef.h" 11#include "include/private/SkSafe32.h" 12#include "src/core/SkGeometry.h" 13#include "src/core/SkPathPriv.h" 14// need SkDVector 15#include "src/pathops/SkPathOpsPoint.h" 16 17SkPathBuilder::SkPathBuilder() { 18 this->reset(); 19} 20 21SkPathBuilder::SkPathBuilder(SkPathFillType ft) { 22 this->reset(); 23 fFillType = ft; 24} 25 26SkPathBuilder::SkPathBuilder(const SkPath& src) { 27 *this = src; 28} 29 30SkPathBuilder::~SkPathBuilder() { 31} 32 33SkPathBuilder& SkPathBuilder::reset() { 34 fPts.reset(); 35 fVerbs.reset(); 36 fConicWeights.reset(); 37 fFillType = SkPathFillType::kWinding; 38 fIsVolatile = false; 39 40 // these are internal state 41 42 fSegmentMask = 0; 43 fLastMovePoint = {0, 0}; 44 fLastMoveIndex = -1; // illegal 45 fNeedsMoveVerb = true; 46 47 // testing 48 fOverrideConvexity = SkPathConvexity::kUnknown; 49 50 return *this; 51} 52 53SkPathBuilder& SkPathBuilder::operator=(const SkPath& src) { 54 this->reset().setFillType(src.getFillType()); 55 56 for (auto [verb, pts, w] : SkPathPriv::Iterate(src)) { 57 switch (verb) { 58 case SkPathVerb::kMove: this->moveTo(pts[0]); break; 59 case SkPathVerb::kLine: this->lineTo(pts[1]); break; 60 case SkPathVerb::kQuad: this->quadTo(pts[1], pts[2]); break; 61 case SkPathVerb::kConic: this->conicTo(pts[1], pts[2], w[0]); break; 62 case SkPathVerb::kCubic: this->cubicTo(pts[1], pts[2], pts[3]); break; 63 case SkPathVerb::kClose: this->close(); break; 64 } 65 } 66 return *this; 67} 68 69void SkPathBuilder::incReserve(int extraPtCount, int extraVbCount) { 70 fPts.setReserve( Sk32_sat_add(fPts.count(), extraPtCount)); 71 fVerbs.setReserve(Sk32_sat_add(fVerbs.count(), extraVbCount)); 72} 73 74SkRect SkPathBuilder::computeBounds() const { 75 SkRect bounds; 76 bounds.setBounds(fPts.begin(), fPts.count()); 77 return bounds; 78} 79 80/* 81 * Some old behavior in SkPath -- should we keep it? 82 * 83 * After each edit (i.e. adding a verb) 84 this->setConvexityType(SkPathConvexity::kUnknown); 85 this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection); 86 */ 87 88SkPathBuilder& SkPathBuilder::moveTo(SkPoint pt) { 89 // only needed while SkPath is mutable 90 fLastMoveIndex = SkToInt(fPts.size()); 91 92 fPts.push_back(pt); 93 fVerbs.push_back((uint8_t)SkPathVerb::kMove); 94 95 fLastMovePoint = pt; 96 fNeedsMoveVerb = false; 97 return *this; 98} 99 100SkPathBuilder& SkPathBuilder::lineTo(SkPoint pt) { 101 this->ensureMove(); 102 103 fPts.push_back(pt); 104 fVerbs.push_back((uint8_t)SkPathVerb::kLine); 105 106 fSegmentMask |= kLine_SkPathSegmentMask; 107 return *this; 108} 109 110SkPathBuilder& SkPathBuilder::quadTo(SkPoint pt1, SkPoint pt2) { 111 this->ensureMove(); 112 113 SkPoint* p = fPts.append(2); 114 p[0] = pt1; 115 p[1] = pt2; 116 fVerbs.push_back((uint8_t)SkPathVerb::kQuad); 117 118 fSegmentMask |= kQuad_SkPathSegmentMask; 119 return *this; 120} 121 122SkPathBuilder& SkPathBuilder::conicTo(SkPoint pt1, SkPoint pt2, SkScalar w) { 123 this->ensureMove(); 124 125 SkPoint* p = fPts.append(2); 126 p[0] = pt1; 127 p[1] = pt2; 128 fVerbs.push_back((uint8_t)SkPathVerb::kConic); 129 fConicWeights.push_back(w); 130 131 fSegmentMask |= kConic_SkPathSegmentMask; 132 return *this; 133} 134 135SkPathBuilder& SkPathBuilder::cubicTo(SkPoint pt1, SkPoint pt2, SkPoint pt3) { 136 this->ensureMove(); 137 138 SkPoint* p = fPts.append(3); 139 p[0] = pt1; 140 p[1] = pt2; 141 p[2] = pt3; 142 fVerbs.push_back((uint8_t)SkPathVerb::kCubic); 143 144 fSegmentMask |= kCubic_SkPathSegmentMask; 145 return *this; 146} 147 148SkPathBuilder& SkPathBuilder::close() { 149 if (fVerbs.count() > 0) { 150 this->ensureMove(); 151 152 fVerbs.push_back((uint8_t)SkPathVerb::kClose); 153 154 // fLastMovePoint stays where it is -- the previous moveTo 155 fNeedsMoveVerb = true; 156 } 157 return *this; 158} 159 160/////////////////////////////////////////////////////////////////////////////////////////// 161 162SkPathBuilder& SkPathBuilder::rLineTo(SkPoint p1) { 163 this->ensureMove(); 164 return this->lineTo(fPts.back() + p1); 165} 166 167SkPathBuilder& SkPathBuilder::rQuadTo(SkPoint p1, SkPoint p2) { 168 this->ensureMove(); 169 SkPoint base = fPts.back(); 170 return this->quadTo(base + p1, base + p2); 171} 172 173SkPathBuilder& SkPathBuilder::rConicTo(SkPoint p1, SkPoint p2, SkScalar w) { 174 this->ensureMove(); 175 SkPoint base = fPts.back(); 176 return this->conicTo(base + p1, base + p2, w); 177} 178 179SkPathBuilder& SkPathBuilder::rCubicTo(SkPoint p1, SkPoint p2, SkPoint p3) { 180 this->ensureMove(); 181 SkPoint base = fPts.back(); 182 return this->cubicTo(base + p1, base + p2, base + p3); 183} 184 185/////////////////////////////////////////////////////////////////////////////////////////// 186 187SkPath SkPathBuilder::make(sk_sp<SkPathRef> pr) const { 188 auto convexity = SkPathConvexity::kUnknown; 189 SkPathFirstDirection dir = SkPathFirstDirection::kUnknown; 190 191 switch (fIsA) { 192 case kIsA_Oval: 193 pr->setIsOval( true, fIsACCW, fIsAStart); 194 convexity = SkPathConvexity::kConvex; 195 dir = fIsACCW ? SkPathFirstDirection::kCCW : SkPathFirstDirection::kCW; 196 break; 197 case kIsA_RRect: 198 pr->setIsRRect(true, fIsACCW, fIsAStart); 199 convexity = SkPathConvexity::kConvex; 200 dir = fIsACCW ? SkPathFirstDirection::kCCW : SkPathFirstDirection::kCW; 201 break; 202 default: break; 203 } 204 205 if (fOverrideConvexity != SkPathConvexity::kUnknown) { 206 convexity = fOverrideConvexity; 207 } 208 209 // Wonder if we can combine convexity and dir internally... 210 // unknown, convex_cw, convex_ccw, concave 211 // Do we ever have direction w/o convexity, or viceversa (inside path)? 212 // 213 auto path = SkPath(std::move(pr), fFillType, fIsVolatile, convexity, dir); 214 215 // This hopefully can go away in the future when Paths are immutable, 216 // but if while they are still editable, we need to correctly set this. 217 const uint8_t* start = path.fPathRef->verbsBegin(); 218 const uint8_t* stop = path.fPathRef->verbsEnd(); 219 if (start < stop) { 220 SkASSERT(fLastMoveIndex >= 0); 221 // peek at the last verb, to know if our last contour is closed 222 const bool isClosed = (stop[-1] == (uint8_t)SkPathVerb::kClose); 223 path.fLastMoveToIndex = isClosed ? ~fLastMoveIndex : fLastMoveIndex; 224 } 225 226 return path; 227} 228 229SkPath SkPathBuilder::snapshot() const { 230 return this->make(sk_sp<SkPathRef>(new SkPathRef(fPts, 231 fVerbs, 232 fConicWeights, 233 fSegmentMask))); 234} 235 236SkPath SkPathBuilder::detach() { 237 auto path = this->make(sk_sp<SkPathRef>(new SkPathRef(std::move(fPts), 238 std::move(fVerbs), 239 std::move(fConicWeights), 240 fSegmentMask))); 241 this->reset(); 242 return path; 243} 244 245/////////////////////////////////////////////////////////////////////////////////////////////////// 246 247static bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 248 SkPoint* pt) { 249 if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) { 250 // Chrome uses this path to move into and out of ovals. If not 251 // treated as a special case the moves can distort the oval's 252 // bounding box (and break the circle special case). 253 pt->set(oval.fRight, oval.centerY()); 254 return true; 255 } else if (0 == oval.width() && 0 == oval.height()) { 256 // Chrome will sometimes create 0 radius round rects. Having degenerate 257 // quad segments in the path prevents the path from being recognized as 258 // a rect. 259 // TODO: optimizing the case where only one of width or height is zero 260 // should also be considered. This case, however, doesn't seem to be 261 // as common as the single point case. 262 pt->set(oval.fRight, oval.fTop); 263 return true; 264 } 265 return false; 266} 267 268// Return the unit vectors pointing at the start/stop points for the given start/sweep angles 269// 270static void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, 271 SkVector* startV, SkVector* stopV, SkRotationDirection* dir) { 272 SkScalar startRad = SkDegreesToRadians(startAngle), 273 stopRad = SkDegreesToRadians(startAngle + sweepAngle); 274 275 startV->fY = SkScalarSinSnapToZero(startRad); 276 startV->fX = SkScalarCosSnapToZero(startRad); 277 stopV->fY = SkScalarSinSnapToZero(stopRad); 278 stopV->fX = SkScalarCosSnapToZero(stopRad); 279 280 /* If the sweep angle is nearly (but less than) 360, then due to precision 281 loss in radians-conversion and/or sin/cos, we may end up with coincident 282 vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead 283 of drawing a nearly complete circle (good). 284 e.g. canvas.drawArc(0, 359.99, ...) 285 -vs- canvas.drawArc(0, 359.9, ...) 286 We try to detect this edge case, and tweak the stop vector 287 */ 288 if (*startV == *stopV) { 289 SkScalar sw = SkScalarAbs(sweepAngle); 290 if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { 291 // make a guess at a tiny angle (in radians) to tweak by 292 SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); 293 // not sure how much will be enough, so we use a loop 294 do { 295 stopRad -= deltaRad; 296 stopV->fY = SkScalarSinSnapToZero(stopRad); 297 stopV->fX = SkScalarCosSnapToZero(stopRad); 298 } while (*startV == *stopV); 299 } 300 } 301 *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection; 302} 303 304/** 305 * If this returns 0, then the caller should just line-to the singlePt, else it should 306 * ignore singlePt and append the specified number of conics. 307 */ 308static int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop, 309 SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], 310 SkPoint* singlePt) { 311 SkMatrix matrix; 312 313 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); 314 matrix.postTranslate(oval.centerX(), oval.centerY()); 315 316 int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics); 317 if (0 == count) { 318 matrix.mapXY(stop.x(), stop.y(), singlePt); 319 } 320 return count; 321} 322 323static bool nearly_equal(const SkPoint& a, const SkPoint& b) { 324 return SkScalarNearlyEqual(a.fX, b.fX) 325 && SkScalarNearlyEqual(a.fY, b.fY); 326} 327 328SkPathBuilder& SkPathBuilder::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 329 bool forceMoveTo) { 330 if (oval.width() < 0 || oval.height() < 0) { 331 return *this; 332 } 333 334 if (fVerbs.count() == 0) { 335 forceMoveTo = true; 336 } 337 338 SkPoint lonePt; 339 if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) { 340 return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt); 341 } 342 343 SkVector startV, stopV; 344 SkRotationDirection dir; 345 angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir); 346 347 SkPoint singlePt; 348 349 // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently 350 // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous 351 // arcs from the same oval. 352 auto addPt = [forceMoveTo, this](const SkPoint& pt) { 353 if (forceMoveTo) { 354 this->moveTo(pt); 355 } else if (!nearly_equal(fPts.back(), pt)) { 356 this->lineTo(pt); 357 } 358 }; 359 360 // At this point, we know that the arc is not a lone point, but startV == stopV 361 // indicates that the sweepAngle is too small such that angles_to_unit_vectors 362 // cannot handle it. 363 if (startV == stopV) { 364 SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle); 365 SkScalar radiusX = oval.width() / 2; 366 SkScalar radiusY = oval.height() / 2; 367 // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle 368 // is very small and radius is huge, the expected behavior here is to draw a line. But 369 // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot. 370 singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle), 371 oval.centerY() + radiusY * SkScalarSin(endAngle)); 372 addPt(singlePt); 373 return *this; 374 } 375 376 SkConic conics[SkConic::kMaxConicsForArc]; 377 int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt); 378 if (count) { 379 this->incReserve(count * 2 + 1); 380 const SkPoint& pt = conics[0].fPts[0]; 381 addPt(pt); 382 for (int i = 0; i < count; ++i) { 383 this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW); 384 } 385 } else { 386 addPt(singlePt); 387 } 388 return *this; 389} 390 391SkPathBuilder& SkPathBuilder::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { 392 if (oval.isEmpty() || 0 == sweepAngle) { 393 return *this; 394 } 395 396 const SkScalar kFullCircleAngle = SkIntToScalar(360); 397 398 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { 399 // We can treat the arc as an oval if it begins at one of our legal starting positions. 400 // See SkPath::addOval() docs. 401 SkScalar startOver90 = startAngle / 90.f; 402 SkScalar startOver90I = SkScalarRoundToScalar(startOver90); 403 SkScalar error = startOver90 - startOver90I; 404 if (SkScalarNearlyEqual(error, 0)) { 405 // Index 1 is at startAngle == 0. 406 SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f); 407 startIndex = startIndex < 0 ? startIndex + 4.f : startIndex; 408 return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW, 409 (unsigned) startIndex); 410 } 411 } 412 return this->arcTo(oval, startAngle, sweepAngle, true); 413} 414 415SkPathBuilder& SkPathBuilder::arcTo(SkPoint p1, SkPoint p2, SkScalar radius) { 416 this->ensureMove(); 417 418 if (radius == 0) { 419 return this->lineTo(p1); 420 } 421 422 // need to know our prev pt so we can construct tangent vectors 423 SkPoint start = fPts.back(); 424 425 // need double precision for these calcs. 426 SkDVector befored, afterd; 427 befored.set({p1.fX - start.fX, p1.fY - start.fY}).normalize(); 428 afterd.set({p2.fX - p1.fX, p2.fY - p1.fY}).normalize(); 429 double cosh = befored.dot(afterd); 430 double sinh = befored.cross(afterd); 431 432 if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) { 433 return this->lineTo(p1); 434 } 435 436 // safe to convert back to floats now 437 SkVector before = befored.asSkVector(); 438 SkVector after = afterd.asSkVector(); 439 SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh)); 440 SkScalar xx = p1.fX - dist * before.fX; 441 SkScalar yy = p1.fY - dist * before.fY; 442 after.setLength(dist); 443 this->lineTo(xx, yy); 444 SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5)); 445 return this->conicTo(p1, p1 + after, weight); 446} 447 448// This converts the SVG arc to conics. 449// Partly adapted from Niko's code in kdelibs/kdecore/svgicons. 450// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic() 451// See also SVG implementation notes: 452// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter 453// Note that arcSweep bool value is flipped from the original implementation. 454SkPathBuilder& SkPathBuilder::arcTo(SkPoint rad, SkScalar angle, SkPathBuilder::ArcSize arcLarge, 455 SkPathDirection arcSweep, SkPoint endPt) { 456 this->ensureMove(); 457 458 SkPoint srcPts[2] = { fPts.back(), endPt }; 459 460 // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") 461 // joining the endpoints. 462 // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters 463 if (!rad.fX || !rad.fY) { 464 return this->lineTo(endPt); 465 } 466 // If the current point and target point for the arc are identical, it should be treated as a 467 // zero length path. This ensures continuity in animations. 468 if (srcPts[0] == srcPts[1]) { 469 return this->lineTo(endPt); 470 } 471 SkScalar rx = SkScalarAbs(rad.fX); 472 SkScalar ry = SkScalarAbs(rad.fY); 473 SkVector midPointDistance = srcPts[0] - srcPts[1]; 474 midPointDistance *= 0.5f; 475 476 SkMatrix pointTransform; 477 pointTransform.setRotate(-angle); 478 479 SkPoint transformedMidPoint; 480 pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1); 481 SkScalar squareRx = rx * rx; 482 SkScalar squareRy = ry * ry; 483 SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX; 484 SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY; 485 486 // Check if the radii are big enough to draw the arc, scale radii if not. 487 // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii 488 SkScalar radiiScale = squareX / squareRx + squareY / squareRy; 489 if (radiiScale > 1) { 490 radiiScale = SkScalarSqrt(radiiScale); 491 rx *= radiiScale; 492 ry *= radiiScale; 493 } 494 495 pointTransform.setScale(1 / rx, 1 / ry); 496 pointTransform.preRotate(-angle); 497 498 SkPoint unitPts[2]; 499 pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts)); 500 SkVector delta = unitPts[1] - unitPts[0]; 501 502 SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY; 503 SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); 504 505 SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared); 506 if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) { // flipped from the original implementation 507 scaleFactor = -scaleFactor; 508 } 509 delta.scale(scaleFactor); 510 SkPoint centerPoint = unitPts[0] + unitPts[1]; 511 centerPoint *= 0.5f; 512 centerPoint.offset(-delta.fY, delta.fX); 513 unitPts[0] -= centerPoint; 514 unitPts[1] -= centerPoint; 515 SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX); 516 SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX); 517 SkScalar thetaArc = theta2 - theta1; 518 if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) { // arcSweep flipped from the original implementation 519 thetaArc += SK_ScalarPI * 2; 520 } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) { // arcSweep flipped from the original implementation 521 thetaArc -= SK_ScalarPI * 2; 522 } 523 524 // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272) 525 // so we do a quick check here. The precise tolerance amount is just made up. 526 // PI/million happens to fix the bug in 9272, but a larger value is probably 527 // ok too. 528 if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) { 529 return this->lineTo(endPt); 530 } 531 532 pointTransform.setRotate(angle); 533 pointTransform.preScale(rx, ry); 534 535 // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd 536 int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3))); 537 SkScalar thetaWidth = thetaArc / segments; 538 SkScalar t = SkScalarTan(0.5f * thetaWidth); 539 if (!SkScalarIsFinite(t)) { 540 return *this; 541 } 542 SkScalar startTheta = theta1; 543 SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf); 544 auto scalar_is_integer = [](SkScalar scalar) -> bool { 545 return scalar == SkScalarFloorToScalar(scalar); 546 }; 547 bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) && 548 scalar_is_integer(rx) && scalar_is_integer(ry) && 549 scalar_is_integer(endPt.fX) && scalar_is_integer(endPt.fY); 550 551 for (int i = 0; i < segments; ++i) { 552 SkScalar endTheta = startTheta + thetaWidth, 553 sinEndTheta = SkScalarSinSnapToZero(endTheta), 554 cosEndTheta = SkScalarCosSnapToZero(endTheta); 555 556 unitPts[1].set(cosEndTheta, sinEndTheta); 557 unitPts[1] += centerPoint; 558 unitPts[0] = unitPts[1]; 559 unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta); 560 SkPoint mapped[2]; 561 pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts)); 562 /* 563 Computing the arc width introduces rounding errors that cause arcs to start 564 outside their marks. A round rect may lose convexity as a result. If the input 565 values are on integers, place the conic on integers as well. 566 */ 567 if (expectIntegers) { 568 for (SkPoint& point : mapped) { 569 point.fX = SkScalarRoundToScalar(point.fX); 570 point.fY = SkScalarRoundToScalar(point.fY); 571 } 572 } 573 this->conicTo(mapped[0], mapped[1], w); 574 startTheta = endTheta; 575 } 576 577#ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT 578 // The final point should match the input point (by definition); replace it to 579 // ensure that rounding errors in the above math don't cause any problems. 580 fPts.back() = endPt; 581#endif 582 return *this; 583} 584 585/////////////////////////////////////////////////////////////////////////////////////////// 586 587namespace { 588 template <unsigned N> class PointIterator { 589 public: 590 PointIterator(SkPathDirection dir, unsigned startIndex) 591 : fCurrent(startIndex % N) 592 , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1) 593 {} 594 595 const SkPoint& current() const { 596 SkASSERT(fCurrent < N); 597 return fPts[fCurrent]; 598 } 599 600 const SkPoint& next() { 601 fCurrent = (fCurrent + fAdvance) % N; 602 return this->current(); 603 } 604 605 protected: 606 SkPoint fPts[N]; 607 608 private: 609 unsigned fCurrent; 610 unsigned fAdvance; 611 }; 612 613 class RectPointIterator : public PointIterator<4> { 614 public: 615 RectPointIterator(const SkRect& rect, SkPathDirection dir, unsigned startIndex) 616 : PointIterator(dir, startIndex) { 617 618 fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop); 619 fPts[1] = SkPoint::Make(rect.fRight, rect.fTop); 620 fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom); 621 fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom); 622 } 623 }; 624 625 class OvalPointIterator : public PointIterator<4> { 626 public: 627 OvalPointIterator(const SkRect& oval, SkPathDirection dir, unsigned startIndex) 628 : PointIterator(dir, startIndex) { 629 630 const SkScalar cx = oval.centerX(); 631 const SkScalar cy = oval.centerY(); 632 633 fPts[0] = SkPoint::Make(cx, oval.fTop); 634 fPts[1] = SkPoint::Make(oval.fRight, cy); 635 fPts[2] = SkPoint::Make(cx, oval.fBottom); 636 fPts[3] = SkPoint::Make(oval.fLeft, cy); 637 } 638 }; 639 640 class RRectPointIterator : public PointIterator<8> { 641 public: 642 RRectPointIterator(const SkRRect& rrect, SkPathDirection dir, unsigned startIndex) 643 : PointIterator(dir, startIndex) 644 { 645 const SkRect& bounds = rrect.getBounds(); 646 const SkScalar L = bounds.fLeft; 647 const SkScalar T = bounds.fTop; 648 const SkScalar R = bounds.fRight; 649 const SkScalar B = bounds.fBottom; 650 651 fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T); 652 fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T); 653 fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY); 654 fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY); 655 fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B); 656 fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B); 657 fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY); 658 fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY); 659 } 660 }; 661} // anonymous namespace 662 663 664SkPathBuilder& SkPathBuilder::addRect(const SkRect& rect, SkPathDirection dir, unsigned index) { 665 const int kPts = 4; // moveTo + 3 lines 666 const int kVerbs = 5; // moveTo + 3 lines + close 667 this->incReserve(kPts, kVerbs); 668 669 RectPointIterator iter(rect, dir, index); 670 671 this->moveTo(iter.current()); 672 this->lineTo(iter.next()); 673 this->lineTo(iter.next()); 674 this->lineTo(iter.next()); 675 return this->close(); 676} 677 678SkPathBuilder& SkPathBuilder::addOval(const SkRect& oval, SkPathDirection dir, unsigned index) { 679 const IsA prevIsA = fIsA; 680 681 const int kPts = 9; // moveTo + 4 conics(2 pts each) 682 const int kVerbs = 6; // moveTo + 4 conics + close 683 this->incReserve(kPts, kVerbs); 684 685 OvalPointIterator ovalIter(oval, dir, index); 686 RectPointIterator rectIter(oval, dir, index + (dir == SkPathDirection::kCW ? 0 : 1)); 687 688 // The corner iterator pts are tracking "behind" the oval/radii pts. 689 690 this->moveTo(ovalIter.current()); 691 for (unsigned i = 0; i < 4; ++i) { 692 this->conicTo(rectIter.next(), ovalIter.next(), SK_ScalarRoot2Over2); 693 } 694 this->close(); 695 696 if (prevIsA == kIsA_JustMoves) { 697 fIsA = kIsA_Oval; 698 fIsACCW = (dir == SkPathDirection::kCCW); 699 fIsAStart = index % 4; 700 } 701 return *this; 702} 703 704SkPathBuilder& SkPathBuilder::addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned index) { 705 const IsA prevIsA = fIsA; 706 const SkRect& bounds = rrect.getBounds(); 707 708 if (rrect.isRect() || rrect.isEmpty()) { 709 // degenerate(rect) => radii points are collapsing 710 this->addRect(bounds, dir, (index + 1) / 2); 711 } else if (rrect.isOval()) { 712 // degenerate(oval) => line points are collapsing 713 this->addOval(bounds, dir, index / 2); 714 } else { 715 // we start with a conic on odd indices when moving CW vs. even indices when moving CCW 716 const bool startsWithConic = ((index & 1) == (dir == SkPathDirection::kCW)); 717 const SkScalar weight = SK_ScalarRoot2Over2; 718 719 const int kVerbs = startsWithConic 720 ? 9 // moveTo + 4x conicTo + 3x lineTo + close 721 : 10; // moveTo + 4x lineTo + 4x conicTo + close 722 this->incReserve(kVerbs); 723 724 RRectPointIterator rrectIter(rrect, dir, index); 725 // Corner iterator indices follow the collapsed radii model, 726 // adjusted such that the start pt is "behind" the radii start pt. 727 const unsigned rectStartIndex = index / 2 + (dir == SkPathDirection::kCW ? 0 : 1); 728 RectPointIterator rectIter(bounds, dir, rectStartIndex); 729 730 this->moveTo(rrectIter.current()); 731 if (startsWithConic) { 732 for (unsigned i = 0; i < 3; ++i) { 733 this->conicTo(rectIter.next(), rrectIter.next(), weight); 734 this->lineTo(rrectIter.next()); 735 } 736 this->conicTo(rectIter.next(), rrectIter.next(), weight); 737 // final lineTo handled by close(). 738 } else { 739 for (unsigned i = 0; i < 4; ++i) { 740 this->lineTo(rrectIter.next()); 741 this->conicTo(rectIter.next(), rrectIter.next(), weight); 742 } 743 } 744 this->close(); 745 } 746 747 if (prevIsA == kIsA_JustMoves) { 748 fIsA = kIsA_RRect; 749 fIsACCW = (dir == SkPathDirection::kCCW); 750 fIsAStart = index % 8; 751 } 752 return *this; 753} 754 755SkPathBuilder& SkPathBuilder::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) { 756 if (r >= 0) { 757 this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir); 758 } 759 return *this; 760} 761 762SkPathBuilder& SkPathBuilder::addPolygon(const SkPoint pts[], int count, bool isClosed) { 763 if (count <= 0) { 764 return *this; 765 } 766 767 this->moveTo(pts[0]); 768 this->polylineTo(&pts[1], count - 1); 769 if (isClosed) { 770 this->close(); 771 } 772 return *this; 773} 774 775SkPathBuilder& SkPathBuilder::polylineTo(const SkPoint pts[], int count) { 776 if (count > 0) { 777 this->ensureMove(); 778 779 this->incReserve(count, count); 780 memcpy(fPts.append(count), pts, count * sizeof(SkPoint)); 781 memset(fVerbs.append(count), (uint8_t)SkPathVerb::kLine, count); 782 fSegmentMask |= kLine_SkPathSegmentMask; 783 } 784 return *this; 785} 786 787////////////////////////////////////////////////////////////////////////////////////////////////// 788 789SkPathBuilder& SkPathBuilder::offset(SkScalar dx, SkScalar dy) { 790 for (auto& p : fPts) { 791 p += {dx, dy}; 792 } 793 return *this; 794} 795 796SkPathBuilder& SkPathBuilder::addPath(const SkPath& src) { 797 SkPath::RawIter iter(src); 798 SkPoint pts[4]; 799 SkPath::Verb verb; 800 801 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 802 switch (verb) { 803 case SkPath::kMove_Verb: this->moveTo (pts[0]); break; 804 case SkPath::kLine_Verb: this->lineTo (pts[1]); break; 805 case SkPath::kQuad_Verb: this->quadTo (pts[1], pts[2]); break; 806 case SkPath::kCubic_Verb: this->cubicTo(pts[1], pts[2], pts[3]); break; 807 case SkPath::kConic_Verb: this->conicTo(pts[1], pts[2], iter.conicWeight()); break; 808 case SkPath::kClose_Verb: this->close(); break; 809 case SkPath::kDone_Verb: SkUNREACHABLE; 810 } 811 } 812 813 return *this; 814} 815 816SkPathBuilder& SkPathBuilder::privateReverseAddPath(const SkPath& src) { 817 818 const uint8_t* verbsBegin = src.fPathRef->verbsBegin(); 819 const uint8_t* verbs = src.fPathRef->verbsEnd(); 820 const SkPoint* pts = src.fPathRef->pointsEnd(); 821 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd(); 822 823 bool needMove = true; 824 bool needClose = false; 825 while (verbs > verbsBegin) { 826 uint8_t v = *--verbs; 827 int n = SkPathPriv::PtsInVerb(v); 828 829 if (needMove) { 830 --pts; 831 this->moveTo(pts->fX, pts->fY); 832 needMove = false; 833 } 834 pts -= n; 835 switch ((SkPathVerb)v) { 836 case SkPathVerb::kMove: 837 if (needClose) { 838 this->close(); 839 needClose = false; 840 } 841 needMove = true; 842 pts += 1; // so we see the point in "if (needMove)" above 843 break; 844 case SkPathVerb::kLine: 845 this->lineTo(pts[0]); 846 break; 847 case SkPathVerb::kQuad: 848 this->quadTo(pts[1], pts[0]); 849 break; 850 case SkPathVerb::kConic: 851 this->conicTo(pts[1], pts[0], *--conicWeights); 852 break; 853 case SkPathVerb::kCubic: 854 this->cubicTo(pts[2], pts[1], pts[0]); 855 break; 856 case SkPathVerb::kClose: 857 needClose = true; 858 break; 859 default: 860 SkDEBUGFAIL("unexpected verb"); 861 } 862 } 863 return *this; 864} 865