1 /* 2 * Copyright 2008 The Android Open Source Project 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 "SkStrokerPriv.h" 9 10 #include "SkGeometry.h" 11 #include "SkMacros.h" 12 #include "SkPathPriv.h" 13 #include "SkPointPriv.h" 14 #include "SkTo.h" 15 16 #include <utility> 17 18 enum { 19 kTangent_RecursiveLimit, 20 kCubic_RecursiveLimit, 21 kConic_RecursiveLimit, 22 kQuad_RecursiveLimit 23 }; 24 25 // quads with extreme widths (e.g. (0,1) (1,6) (0,3) width=5e7) recurse to point of failure 26 // largest seen for normal cubics : 5, 26 27 // largest seen for normal quads : 11 28 static const int kRecursiveLimits[] = { 5*3, 26*3, 11*3, 11*3 }; // 3x limits seen in practice 29 30 static_assert(0 == kTangent_RecursiveLimit, "cubic_stroke_relies_on_tangent_equalling_zero"); 31 static_assert(1 == kCubic_RecursiveLimit, "cubic_stroke_relies_on_cubic_equalling_one"); 32 static_assert(SK_ARRAY_COUNT(kRecursiveLimits) == kQuad_RecursiveLimit + 1, 33 "recursive_limits_mismatch"); 34 35 #if defined SK_DEBUG && QUAD_STROKE_APPROX_EXTENDED_DEBUGGING 36 int gMaxRecursion[SK_ARRAY_COUNT(kRecursiveLimits)] = { 0 }; 37 #endif 38 #ifndef DEBUG_QUAD_STROKER 39 #define DEBUG_QUAD_STROKER 0 40 #endif 41 42 #if DEBUG_QUAD_STROKER 43 /* Enable to show the decisions made in subdividing the curve -- helpful when the resulting 44 stroke has more than the optimal number of quadratics and lines */ 45 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ 46 SkDebugf("[%d] %s " format "\n", depth, __FUNCTION__, __VA_ARGS__), \ 47 SkDebugf(" " #resultType " t=(%g,%g)\n", quadPts->fStartT, quadPts->fEndT), \ 48 resultType 49 #define STROKER_DEBUG_PARAMS(...) , __VA_ARGS__ 50 #else 51 #define STROKER_RESULT(resultType, depth, quadPts, format, ...) \ 52 resultType 53 #define STROKER_DEBUG_PARAMS(...) 54 #endif 55 56 static inline bool degenerate_vector(const SkVector& v) { 57 return !SkPointPriv::CanNormalize(v.fX, v.fY); 58 } 59 60 static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, SkScalar scale, 61 SkScalar radius, 62 SkVector* normal, SkVector* unitNormal) { 63 if (!unitNormal->setNormalize((after.fX - before.fX) * scale, 64 (after.fY - before.fY) * scale)) { 65 return false; 66 } 67 SkPointPriv::RotateCCW(unitNormal); 68 unitNormal->scale(radius, normal); 69 return true; 70 } 71 72 static bool set_normal_unitnormal(const SkVector& vec, 73 SkScalar radius, 74 SkVector* normal, SkVector* unitNormal) { 75 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { 76 return false; 77 } 78 SkPointPriv::RotateCCW(unitNormal); 79 unitNormal->scale(radius, normal); 80 return true; 81 } 82 83 /////////////////////////////////////////////////////////////////////////////// 84 85 struct SkQuadConstruct { // The state of the quad stroke under construction. 86 SkPoint fQuad[3]; // the stroked quad parallel to the original curve 87 SkPoint fTangentStart; // a point tangent to fQuad[0] 88 SkPoint fTangentEnd; // a point tangent to fQuad[2] 89 SkScalar fStartT; // a segment of the original curve 90 SkScalar fMidT; // " 91 SkScalar fEndT; // " 92 bool fStartSet; // state to share common points across structs 93 bool fEndSet; // " 94 bool fOppositeTangents; // set if coincident tangents have opposite directions 95 96 // return false if start and end are too close to have a unique middle 97 bool init(SkScalar start, SkScalar end) { 98 fStartT = start; 99 fMidT = (start + end) * SK_ScalarHalf; 100 fEndT = end; 101 fStartSet = fEndSet = false; 102 return fStartT < fMidT && fMidT < fEndT; 103 } 104 105 bool initWithStart(SkQuadConstruct* parent) { 106 if (!init(parent->fStartT, parent->fMidT)) { 107 return false; 108 } 109 fQuad[0] = parent->fQuad[0]; 110 fTangentStart = parent->fTangentStart; 111 fStartSet = true; 112 return true; 113 } 114 115 bool initWithEnd(SkQuadConstruct* parent) { 116 if (!init(parent->fMidT, parent->fEndT)) { 117 return false; 118 } 119 fQuad[2] = parent->fQuad[2]; 120 fTangentEnd = parent->fTangentEnd; 121 fEndSet = true; 122 return true; 123 } 124 }; 125 126 class SkPathStroker { 127 public: 128 SkPathStroker(const SkPath& src, 129 SkScalar radius, SkScalar miterLimit, SkPaint::Cap, 130 SkPaint::Join, SkScalar resScale, 131 bool canIgnoreCenter); 132 133 bool hasOnlyMoveTo() const { return 0 == fSegmentCount; } 134 SkPoint moveToPt() const { return fFirstPt; } 135 136 void moveTo(const SkPoint&); 137 void lineTo(const SkPoint&, const SkPath::Iter* iter = nullptr); 138 void quadTo(const SkPoint&, const SkPoint&); 139 void conicTo(const SkPoint&, const SkPoint&, SkScalar weight); 140 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); 141 void close(bool isLine) { this->finishContour(true, isLine); } 142 143 void done(SkPath* dst, bool isLine) { 144 this->finishContour(false, isLine); 145 dst->swap(fOuter); 146 } 147 148 SkScalar getResScale() const { return fResScale; } 149 150 bool isCurrentContourEmpty() const { 151 return fInner.isZeroLengthSincePoint(0) && 152 fOuter.isZeroLengthSincePoint(fFirstOuterPtIndexInContour); 153 } 154 155 private: 156 SkScalar fRadius; 157 SkScalar fInvMiterLimit; 158 SkScalar fResScale; 159 SkScalar fInvResScale; 160 SkScalar fInvResScaleSquared; 161 162 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; 163 SkPoint fFirstPt, fPrevPt; // on original path 164 SkPoint fFirstOuterPt; 165 int fFirstOuterPtIndexInContour; 166 int fSegmentCount; 167 bool fPrevIsLine; 168 bool fCanIgnoreCenter; 169 170 SkStrokerPriv::CapProc fCapper; 171 SkStrokerPriv::JoinProc fJoiner; 172 173 SkPath fInner, fOuter, fCusper; // outer is our working answer, inner is temp 174 175 enum StrokeType { 176 kOuter_StrokeType = 1, // use sign-opposite values later to flip perpendicular axis 177 kInner_StrokeType = -1 178 } fStrokeType; 179 180 enum ResultType { 181 kSplit_ResultType, // the caller should split the quad stroke in two 182 kDegenerate_ResultType, // the caller should add a line 183 kQuad_ResultType, // the caller should (continue to try to) add a quad stroke 184 }; 185 186 enum ReductionType { 187 kPoint_ReductionType, // all curve points are practically identical 188 kLine_ReductionType, // the control point is on the line between the ends 189 kQuad_ReductionType, // the control point is outside the line between the ends 190 kDegenerate_ReductionType, // the control point is on the line but outside the ends 191 kDegenerate2_ReductionType, // two control points are on the line but outside ends (cubic) 192 kDegenerate3_ReductionType, // three areas of max curvature found (for cubic) 193 }; 194 195 enum IntersectRayType { 196 kCtrlPt_RayType, 197 kResultType_RayType, 198 }; 199 200 int fRecursionDepth; // track stack depth to abort if numerics run amok 201 bool fFoundTangents; // do less work until tangents meet (cubic) 202 bool fJoinCompleted; // previous join was not degenerate 203 204 void addDegenerateLine(const SkQuadConstruct* ); 205 static ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction); 206 static ReductionType CheckCubicLinear(const SkPoint cubic[4], SkPoint reduction[3], 207 const SkPoint** tanPtPtr); 208 static ReductionType CheckQuadLinear(const SkPoint quad[3], SkPoint* reduction); 209 ResultType compareQuadConic(const SkConic& , SkQuadConstruct* ) const; 210 ResultType compareQuadCubic(const SkPoint cubic[4], SkQuadConstruct* ); 211 ResultType compareQuadQuad(const SkPoint quad[3], SkQuadConstruct* ); 212 void conicPerpRay(const SkConic& , SkScalar t, SkPoint* tPt, SkPoint* onPt, 213 SkPoint* tangent) const; 214 void conicQuadEnds(const SkConic& , SkQuadConstruct* ) const; 215 bool conicStroke(const SkConic& , SkQuadConstruct* ); 216 bool cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* ) const; 217 void cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt, 218 SkPoint* tangent) const; 219 void cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* ); 220 void cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* , SkPoint* mid) const; 221 bool cubicStroke(const SkPoint cubic[4], SkQuadConstruct* ); 222 void init(StrokeType strokeType, SkQuadConstruct* , SkScalar tStart, SkScalar tEnd); 223 ResultType intersectRay(SkQuadConstruct* , IntersectRayType STROKER_DEBUG_PARAMS(int) ) const; 224 bool ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const; 225 void quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt, 226 SkPoint* tangent) const; 227 bool quadStroke(const SkPoint quad[3], SkQuadConstruct* ); 228 void setConicEndNormal(const SkConic& , 229 const SkVector& normalAB, const SkVector& unitNormalAB, 230 SkVector* normalBC, SkVector* unitNormalBC); 231 void setCubicEndNormal(const SkPoint cubic[4], 232 const SkVector& normalAB, const SkVector& unitNormalAB, 233 SkVector* normalCD, SkVector* unitNormalCD); 234 void setQuadEndNormal(const SkPoint quad[3], 235 const SkVector& normalAB, const SkVector& unitNormalAB, 236 SkVector* normalBC, SkVector* unitNormalBC); 237 void setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, SkPoint* tangent) const; 238 static bool SlightAngle(SkQuadConstruct* ); 239 ResultType strokeCloseEnough(const SkPoint stroke[3], const SkPoint ray[2], 240 SkQuadConstruct* STROKER_DEBUG_PARAMS(int depth) ) const; 241 ResultType tangentsMeet(const SkPoint cubic[4], SkQuadConstruct* ); 242 243 void finishContour(bool close, bool isLine); 244 bool preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, 245 bool isLine); 246 void postJoinTo(const SkPoint&, const SkVector& normal, 247 const SkVector& unitNormal); 248 249 void line_to(const SkPoint& currPt, const SkVector& normal); 250 }; 251 252 /////////////////////////////////////////////////////////////////////////////// 253 254 bool SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, 255 SkVector* unitNormal, bool currIsLine) { 256 SkASSERT(fSegmentCount >= 0); 257 258 SkScalar prevX = fPrevPt.fX; 259 SkScalar prevY = fPrevPt.fY; 260 261 if (!set_normal_unitnormal(fPrevPt, currPt, fResScale, fRadius, normal, unitNormal)) { 262 if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper) { 263 return false; 264 } 265 /* Square caps and round caps draw even if the segment length is zero. 266 Since the zero length segment has no direction, set the orientation 267 to upright as the default orientation */ 268 normal->set(fRadius, 0); 269 unitNormal->set(1, 0); 270 } 271 272 if (fSegmentCount == 0) { 273 fFirstNormal = *normal; 274 fFirstUnitNormal = *unitNormal; 275 fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY); 276 277 fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY); 278 fInner.moveTo(prevX - normal->fX, prevY - normal->fY); 279 } else { // we have a previous segment 280 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal, 281 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); 282 } 283 fPrevIsLine = currIsLine; 284 return true; 285 } 286 287 void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, 288 const SkVector& unitNormal) { 289 fJoinCompleted = true; 290 fPrevPt = currPt; 291 fPrevUnitNormal = unitNormal; 292 fPrevNormal = normal; 293 fSegmentCount += 1; 294 } 295 296 void SkPathStroker::finishContour(bool close, bool currIsLine) { 297 if (fSegmentCount > 0) { 298 SkPoint pt; 299 300 if (close) { 301 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, 302 fFirstUnitNormal, fRadius, fInvMiterLimit, 303 fPrevIsLine, currIsLine); 304 fOuter.close(); 305 306 if (fCanIgnoreCenter) { 307 // If we can ignore the center just make sure the larger of the two paths 308 // is preserved and don't add the smaller one. 309 if (fInner.getBounds().contains(fOuter.getBounds())) { 310 fInner.swap(fOuter); 311 } 312 } else { 313 // now add fInner as its own contour 314 fInner.getLastPt(&pt); 315 fOuter.moveTo(pt.fX, pt.fY); 316 fOuter.reversePathTo(fInner); 317 fOuter.close(); 318 } 319 } else { // add caps to start and end 320 // cap the end 321 fInner.getLastPt(&pt); 322 fCapper(&fOuter, fPrevPt, fPrevNormal, pt, 323 currIsLine ? &fInner : nullptr); 324 fOuter.reversePathTo(fInner); 325 // cap the start 326 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, 327 fPrevIsLine ? &fInner : nullptr); 328 fOuter.close(); 329 } 330 if (!fCusper.isEmpty()) { 331 fOuter.addPath(fCusper); 332 fCusper.rewind(); 333 } 334 } 335 // since we may re-use fInner, we rewind instead of reset, to save on 336 // reallocating its internal storage. 337 fInner.rewind(); 338 fSegmentCount = -1; 339 fFirstOuterPtIndexInContour = fOuter.countPoints(); 340 } 341 342 /////////////////////////////////////////////////////////////////////////////// 343 344 SkPathStroker::SkPathStroker(const SkPath& src, 345 SkScalar radius, SkScalar miterLimit, 346 SkPaint::Cap cap, SkPaint::Join join, SkScalar resScale, 347 bool canIgnoreCenter) 348 : fRadius(radius) 349 , fResScale(resScale) 350 , fCanIgnoreCenter(canIgnoreCenter) { 351 352 /* This is only used when join is miter_join, but we initialize it here 353 so that it is always defined, to fis valgrind warnings. 354 */ 355 fInvMiterLimit = 0; 356 357 if (join == SkPaint::kMiter_Join) { 358 if (miterLimit <= SK_Scalar1) { 359 join = SkPaint::kBevel_Join; 360 } else { 361 fInvMiterLimit = SkScalarInvert(miterLimit); 362 } 363 } 364 fCapper = SkStrokerPriv::CapFactory(cap); 365 fJoiner = SkStrokerPriv::JoinFactory(join); 366 fSegmentCount = -1; 367 fFirstOuterPtIndexInContour = 0; 368 fPrevIsLine = false; 369 370 // Need some estimate of how large our final result (fOuter) 371 // and our per-contour temp (fInner) will be, so we don't spend 372 // extra time repeatedly growing these arrays. 373 // 374 // 3x for result == inner + outer + join (swag) 375 // 1x for inner == 'wag' (worst contour length would be better guess) 376 fOuter.incReserve(src.countPoints() * 3); 377 fOuter.setIsVolatile(true); 378 fInner.incReserve(src.countPoints()); 379 fInner.setIsVolatile(true); 380 // TODO : write a common error function used by stroking and filling 381 // The '4' below matches the fill scan converter's error term 382 fInvResScale = SkScalarInvert(resScale * 4); 383 fInvResScaleSquared = fInvResScale * fInvResScale; 384 fRecursionDepth = 0; 385 } 386 387 void SkPathStroker::moveTo(const SkPoint& pt) { 388 if (fSegmentCount > 0) { 389 this->finishContour(false, false); 390 } 391 fSegmentCount = 0; 392 fFirstPt = fPrevPt = pt; 393 fJoinCompleted = false; 394 } 395 396 void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { 397 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); 398 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); 399 } 400 401 static bool has_valid_tangent(const SkPath::Iter* iter) { 402 SkPath::Iter copy = *iter; 403 SkPath::Verb verb; 404 SkPoint pts[4]; 405 while ((verb = copy.next(pts))) { 406 switch (verb) { 407 case SkPath::kMove_Verb: 408 return false; 409 case SkPath::kLine_Verb: 410 if (pts[0] == pts[1]) { 411 continue; 412 } 413 return true; 414 case SkPath::kQuad_Verb: 415 case SkPath::kConic_Verb: 416 if (pts[0] == pts[1] && pts[0] == pts[2]) { 417 continue; 418 } 419 return true; 420 case SkPath::kCubic_Verb: 421 if (pts[0] == pts[1] && pts[0] == pts[2] && pts[0] == pts[3]) { 422 continue; 423 } 424 return true; 425 case SkPath::kClose_Verb: 426 case SkPath::kDone_Verb: 427 return false; 428 } 429 } 430 return false; 431 } 432 433 void SkPathStroker::lineTo(const SkPoint& currPt, const SkPath::Iter* iter) { 434 bool teenyLine = SkPointPriv::EqualsWithinTolerance(fPrevPt, currPt, SK_ScalarNearlyZero * fInvResScale); 435 if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper && teenyLine) { 436 return; 437 } 438 if (teenyLine && (fJoinCompleted || (iter && has_valid_tangent(iter)))) { 439 return; 440 } 441 SkVector normal, unitNormal; 442 443 if (!this->preJoinTo(currPt, &normal, &unitNormal, true)) { 444 return; 445 } 446 this->line_to(currPt, normal); 447 this->postJoinTo(currPt, normal, unitNormal); 448 } 449 450 void SkPathStroker::setQuadEndNormal(const SkPoint quad[3], const SkVector& normalAB, 451 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) { 452 if (!set_normal_unitnormal(quad[1], quad[2], fResScale, fRadius, normalBC, unitNormalBC)) { 453 *normalBC = normalAB; 454 *unitNormalBC = unitNormalAB; 455 } 456 } 457 458 void SkPathStroker::setConicEndNormal(const SkConic& conic, const SkVector& normalAB, 459 const SkVector& unitNormalAB, SkVector* normalBC, SkVector* unitNormalBC) { 460 setQuadEndNormal(conic.fPts, normalAB, unitNormalAB, normalBC, unitNormalBC); 461 } 462 463 void SkPathStroker::setCubicEndNormal(const SkPoint cubic[4], const SkVector& normalAB, 464 const SkVector& unitNormalAB, SkVector* normalCD, SkVector* unitNormalCD) { 465 SkVector ab = cubic[1] - cubic[0]; 466 SkVector cd = cubic[3] - cubic[2]; 467 468 bool degenerateAB = degenerate_vector(ab); 469 bool degenerateCD = degenerate_vector(cd); 470 471 if (degenerateAB && degenerateCD) { 472 goto DEGENERATE_NORMAL; 473 } 474 475 if (degenerateAB) { 476 ab = cubic[2] - cubic[0]; 477 degenerateAB = degenerate_vector(ab); 478 } 479 if (degenerateCD) { 480 cd = cubic[3] - cubic[1]; 481 degenerateCD = degenerate_vector(cd); 482 } 483 if (degenerateAB || degenerateCD) { 484 DEGENERATE_NORMAL: 485 *normalCD = normalAB; 486 *unitNormalCD = unitNormalAB; 487 return; 488 } 489 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); 490 } 491 492 void SkPathStroker::init(StrokeType strokeType, SkQuadConstruct* quadPts, SkScalar tStart, 493 SkScalar tEnd) { 494 fStrokeType = strokeType; 495 fFoundTangents = false; 496 quadPts->init(tStart, tEnd); 497 } 498 499 // returns the distance squared from the point to the line 500 static SkScalar pt_to_line(const SkPoint& pt, const SkPoint& lineStart, const SkPoint& lineEnd) { 501 SkVector dxy = lineEnd - lineStart; 502 if (degenerate_vector(dxy)) { 503 return SkPointPriv::DistanceToSqd(pt, lineStart); 504 } 505 SkVector ab0 = pt - lineStart; 506 SkScalar numer = dxy.dot(ab0); 507 SkScalar denom = dxy.dot(dxy); 508 SkScalar t = numer / denom; 509 SkPoint hit; 510 hit.fX = lineStart.fX * (1 - t) + lineEnd.fX * t; 511 hit.fY = lineStart.fY * (1 - t) + lineEnd.fY * t; 512 return SkPointPriv::DistanceToSqd(hit, pt); 513 } 514 515 /* Given a cubic, determine if all four points are in a line. 516 Return true if the inner points is close to a line connecting the outermost points. 517 518 Find the outermost point by looking for the largest difference in X or Y. 519 Given the indices of the outermost points, and that outer_1 is greater than outer_2, 520 this table shows the index of the smaller of the remaining points: 521 522 outer_2 523 0 1 2 3 524 outer_1 ---------------- 525 0 | - 2 1 1 526 1 | - - 0 0 527 2 | - - - 0 528 3 | - - - - 529 530 If outer_1 == 0 and outer_2 == 1, the smaller of the remaining indices (2 and 3) is 2. 531 532 This table can be collapsed to: (1 + (2 >> outer_2)) >> outer_1 533 534 Given three indices (outer_1 outer_2 mid_1) from 0..3, the remaining index is: 535 536 mid_2 == (outer_1 ^ outer_2 ^ mid_1) 537 */ 538 static bool cubic_in_line(const SkPoint cubic[4]) { 539 SkScalar ptMax = -1; 540 int outer1 SK_INIT_TO_AVOID_WARNING; 541 int outer2 SK_INIT_TO_AVOID_WARNING; 542 for (int index = 0; index < 3; ++index) { 543 for (int inner = index + 1; inner < 4; ++inner) { 544 SkVector testDiff = cubic[inner] - cubic[index]; 545 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(testDiff.fY)); 546 if (ptMax < testMax) { 547 outer1 = index; 548 outer2 = inner; 549 ptMax = testMax; 550 } 551 } 552 } 553 SkASSERT(outer1 >= 0 && outer1 <= 2); 554 SkASSERT(outer2 >= 1 && outer2 <= 3); 555 SkASSERT(outer1 < outer2); 556 int mid1 = (1 + (2 >> outer2)) >> outer1; 557 SkASSERT(mid1 >= 0 && mid1 <= 2); 558 SkASSERT(outer1 != mid1 && outer2 != mid1); 559 int mid2 = outer1 ^ outer2 ^ mid1; 560 SkASSERT(mid2 >= 1 && mid2 <= 3); 561 SkASSERT(mid2 != outer1 && mid2 != outer2 && mid2 != mid1); 562 SkASSERT(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f); 563 SkScalar lineSlop = ptMax * ptMax * 0.00001f; // this multiplier is pulled out of the air 564 return pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= lineSlop 565 && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= lineSlop; 566 } 567 568 /* Given quad, see if all there points are in a line. 569 Return true if the inside point is close to a line connecting the outermost points. 570 571 Find the outermost point by looking for the largest difference in X or Y. 572 Since the XOR of the indices is 3 (0 ^ 1 ^ 2) 573 the missing index equals: outer_1 ^ outer_2 ^ 3 574 */ 575 static bool quad_in_line(const SkPoint quad[3]) { 576 SkScalar ptMax = -1; 577 int outer1 SK_INIT_TO_AVOID_WARNING; 578 int outer2 SK_INIT_TO_AVOID_WARNING; 579 for (int index = 0; index < 2; ++index) { 580 for (int inner = index + 1; inner < 3; ++inner) { 581 SkVector testDiff = quad[inner] - quad[index]; 582 SkScalar testMax = SkTMax(SkScalarAbs(testDiff.fX), SkScalarAbs(testDiff.fY)); 583 if (ptMax < testMax) { 584 outer1 = index; 585 outer2 = inner; 586 ptMax = testMax; 587 } 588 } 589 } 590 SkASSERT(outer1 >= 0 && outer1 <= 1); 591 SkASSERT(outer2 >= 1 && outer2 <= 2); 592 SkASSERT(outer1 < outer2); 593 int mid = outer1 ^ outer2 ^ 3; 594 const float kCurvatureSlop = 0.000005f; // this multiplier is pulled out of the air 595 SkScalar lineSlop = ptMax * ptMax * kCurvatureSlop; 596 return pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= lineSlop; 597 } 598 599 static bool conic_in_line(const SkConic& conic) { 600 return quad_in_line(conic.fPts); 601 } 602 603 SkPathStroker::ReductionType SkPathStroker::CheckCubicLinear(const SkPoint cubic[4], 604 SkPoint reduction[3], const SkPoint** tangentPtPtr) { 605 bool degenerateAB = degenerate_vector(cubic[1] - cubic[0]); 606 bool degenerateBC = degenerate_vector(cubic[2] - cubic[1]); 607 bool degenerateCD = degenerate_vector(cubic[3] - cubic[2]); 608 if (degenerateAB & degenerateBC & degenerateCD) { 609 return kPoint_ReductionType; 610 } 611 if (degenerateAB + degenerateBC + degenerateCD == 2) { 612 return kLine_ReductionType; 613 } 614 if (!cubic_in_line(cubic)) { 615 *tangentPtPtr = degenerateAB ? &cubic[2] : &cubic[1]; 616 return kQuad_ReductionType; 617 } 618 SkScalar tValues[3]; 619 int count = SkFindCubicMaxCurvature(cubic, tValues); 620 int rCount = 0; 621 // Now loop over the t-values, and reject any that evaluate to either end-point 622 for (int index = 0; index < count; ++index) { 623 SkScalar t = tValues[index]; 624 if (0 >= t || t >= 1) { 625 continue; 626 } 627 SkEvalCubicAt(cubic, t, &reduction[rCount], nullptr, nullptr); 628 if (reduction[rCount] != cubic[0] && reduction[rCount] != cubic[3]) { 629 ++rCount; 630 } 631 } 632 if (rCount == 0) { 633 return kLine_ReductionType; 634 } 635 static_assert(kQuad_ReductionType + 1 == kDegenerate_ReductionType, "enum_out_of_whack"); 636 static_assert(kQuad_ReductionType + 2 == kDegenerate2_ReductionType, "enum_out_of_whack"); 637 static_assert(kQuad_ReductionType + 3 == kDegenerate3_ReductionType, "enum_out_of_whack"); 638 639 return (ReductionType) (kQuad_ReductionType + rCount); 640 } 641 642 SkPathStroker::ReductionType SkPathStroker::CheckConicLinear(const SkConic& conic, 643 SkPoint* reduction) { 644 bool degenerateAB = degenerate_vector(conic.fPts[1] - conic.fPts[0]); 645 bool degenerateBC = degenerate_vector(conic.fPts[2] - conic.fPts[1]); 646 if (degenerateAB & degenerateBC) { 647 return kPoint_ReductionType; 648 } 649 if (degenerateAB | degenerateBC) { 650 return kLine_ReductionType; 651 } 652 if (!conic_in_line(conic)) { 653 return kQuad_ReductionType; 654 } 655 // SkFindConicMaxCurvature would be a better solution, once we know how to 656 // implement it. Quad curvature is a reasonable substitute 657 SkScalar t = SkFindQuadMaxCurvature(conic.fPts); 658 if (0 == t) { 659 return kLine_ReductionType; 660 } 661 conic.evalAt(t, reduction, nullptr); 662 return kDegenerate_ReductionType; 663 } 664 665 SkPathStroker::ReductionType SkPathStroker::CheckQuadLinear(const SkPoint quad[3], 666 SkPoint* reduction) { 667 bool degenerateAB = degenerate_vector(quad[1] - quad[0]); 668 bool degenerateBC = degenerate_vector(quad[2] - quad[1]); 669 if (degenerateAB & degenerateBC) { 670 return kPoint_ReductionType; 671 } 672 if (degenerateAB | degenerateBC) { 673 return kLine_ReductionType; 674 } 675 if (!quad_in_line(quad)) { 676 return kQuad_ReductionType; 677 } 678 SkScalar t = SkFindQuadMaxCurvature(quad); 679 if (0 == t || 1 == t) { 680 return kLine_ReductionType; 681 } 682 *reduction = SkEvalQuadAt(quad, t); 683 return kDegenerate_ReductionType; 684 } 685 686 void SkPathStroker::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) { 687 const SkConic conic(fPrevPt, pt1, pt2, weight); 688 SkPoint reduction; 689 ReductionType reductionType = CheckConicLinear(conic, &reduction); 690 if (kPoint_ReductionType == reductionType) { 691 /* If the stroke consists of a moveTo followed by a degenerate curve, treat it 692 as if it were followed by a zero-length line. Lines without length 693 can have square and round end caps. */ 694 this->lineTo(pt2); 695 return; 696 } 697 if (kLine_ReductionType == reductionType) { 698 this->lineTo(pt2); 699 return; 700 } 701 if (kDegenerate_ReductionType == reductionType) { 702 this->lineTo(reduction); 703 SkStrokerPriv::JoinProc saveJoiner = fJoiner; 704 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join); 705 this->lineTo(pt2); 706 fJoiner = saveJoiner; 707 return; 708 } 709 SkASSERT(kQuad_ReductionType == reductionType); 710 SkVector normalAB, unitAB, normalBC, unitBC; 711 if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) { 712 this->lineTo(pt2); 713 return; 714 } 715 SkQuadConstruct quadPts; 716 this->init(kOuter_StrokeType, &quadPts, 0, 1); 717 (void) this->conicStroke(conic, &quadPts); 718 this->init(kInner_StrokeType, &quadPts, 0, 1); 719 (void) this->conicStroke(conic, &quadPts); 720 this->setConicEndNormal(conic, normalAB, unitAB, &normalBC, &unitBC); 721 this->postJoinTo(pt2, normalBC, unitBC); 722 } 723 724 void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { 725 const SkPoint quad[3] = { fPrevPt, pt1, pt2 }; 726 SkPoint reduction; 727 ReductionType reductionType = CheckQuadLinear(quad, &reduction); 728 if (kPoint_ReductionType == reductionType) { 729 /* If the stroke consists of a moveTo followed by a degenerate curve, treat it 730 as if it were followed by a zero-length line. Lines without length 731 can have square and round end caps. */ 732 this->lineTo(pt2); 733 return; 734 } 735 if (kLine_ReductionType == reductionType) { 736 this->lineTo(pt2); 737 return; 738 } 739 if (kDegenerate_ReductionType == reductionType) { 740 this->lineTo(reduction); 741 SkStrokerPriv::JoinProc saveJoiner = fJoiner; 742 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join); 743 this->lineTo(pt2); 744 fJoiner = saveJoiner; 745 return; 746 } 747 SkASSERT(kQuad_ReductionType == reductionType); 748 SkVector normalAB, unitAB, normalBC, unitBC; 749 if (!this->preJoinTo(pt1, &normalAB, &unitAB, false)) { 750 this->lineTo(pt2); 751 return; 752 } 753 SkQuadConstruct quadPts; 754 this->init(kOuter_StrokeType, &quadPts, 0, 1); 755 (void) this->quadStroke(quad, &quadPts); 756 this->init(kInner_StrokeType, &quadPts, 0, 1); 757 (void) this->quadStroke(quad, &quadPts); 758 this->setQuadEndNormal(quad, normalAB, unitAB, &normalBC, &unitBC); 759 760 this->postJoinTo(pt2, normalBC, unitBC); 761 } 762 763 // Given a point on the curve and its derivative, scale the derivative by the radius, and 764 // compute the perpendicular point and its tangent. 765 void SkPathStroker::setRayPts(const SkPoint& tPt, SkVector* dxy, SkPoint* onPt, 766 SkPoint* tangent) const { 767 SkPoint oldDxy = *dxy; 768 if (!dxy->setLength(fRadius)) { // consider moving double logic into SkPoint::setLength 769 double xx = oldDxy.fX; 770 double yy = oldDxy.fY; 771 double dscale = fRadius / sqrt(xx * xx + yy * yy); 772 dxy->fX = SkDoubleToScalar(xx * dscale); 773 dxy->fY = SkDoubleToScalar(yy * dscale); 774 } 775 SkScalar axisFlip = SkIntToScalar(fStrokeType); // go opposite ways for outer, inner 776 onPt->fX = tPt.fX + axisFlip * dxy->fY; 777 onPt->fY = tPt.fY - axisFlip * dxy->fX; 778 if (tangent) { 779 tangent->fX = onPt->fX + dxy->fX; 780 tangent->fY = onPt->fY + dxy->fY; 781 } 782 } 783 784 // Given a conic and t, return the point on curve, its perpendicular, and the perpendicular tangent. 785 // Returns false if the perpendicular could not be computed (because the derivative collapsed to 0) 786 void SkPathStroker::conicPerpRay(const SkConic& conic, SkScalar t, SkPoint* tPt, SkPoint* onPt, 787 SkPoint* tangent) const { 788 SkVector dxy; 789 conic.evalAt(t, tPt, &dxy); 790 if (dxy.fX == 0 && dxy.fY == 0) { 791 dxy = conic.fPts[2] - conic.fPts[0]; 792 } 793 this->setRayPts(*tPt, &dxy, onPt, tangent); 794 } 795 796 // Given a conic and a t range, find the start and end if they haven't been found already. 797 void SkPathStroker::conicQuadEnds(const SkConic& conic, SkQuadConstruct* quadPts) const { 798 if (!quadPts->fStartSet) { 799 SkPoint conicStartPt; 800 this->conicPerpRay(conic, quadPts->fStartT, &conicStartPt, &quadPts->fQuad[0], 801 &quadPts->fTangentStart); 802 quadPts->fStartSet = true; 803 } 804 if (!quadPts->fEndSet) { 805 SkPoint conicEndPt; 806 this->conicPerpRay(conic, quadPts->fEndT, &conicEndPt, &quadPts->fQuad[2], 807 &quadPts->fTangentEnd); 808 quadPts->fEndSet = true; 809 } 810 } 811 812 813 // Given a cubic and t, return the point on curve, its perpendicular, and the perpendicular tangent. 814 void SkPathStroker::cubicPerpRay(const SkPoint cubic[4], SkScalar t, SkPoint* tPt, SkPoint* onPt, 815 SkPoint* tangent) const { 816 SkVector dxy; 817 SkPoint chopped[7]; 818 SkEvalCubicAt(cubic, t, tPt, &dxy, nullptr); 819 if (dxy.fX == 0 && dxy.fY == 0) { 820 const SkPoint* cPts = cubic; 821 if (SkScalarNearlyZero(t)) { 822 dxy = cubic[2] - cubic[0]; 823 } else if (SkScalarNearlyZero(1 - t)) { 824 dxy = cubic[3] - cubic[1]; 825 } else { 826 // If the cubic inflection falls on the cusp, subdivide the cubic 827 // to find the tangent at that point. 828 SkChopCubicAt(cubic, chopped, t); 829 dxy = chopped[3] - chopped[2]; 830 if (dxy.fX == 0 && dxy.fY == 0) { 831 dxy = chopped[3] - chopped[1]; 832 cPts = chopped; 833 } 834 } 835 if (dxy.fX == 0 && dxy.fY == 0) { 836 dxy = cPts[3] - cPts[0]; 837 } 838 } 839 setRayPts(*tPt, &dxy, onPt, tangent); 840 } 841 842 // Given a cubic and a t range, find the start and end if they haven't been found already. 843 void SkPathStroker::cubicQuadEnds(const SkPoint cubic[4], SkQuadConstruct* quadPts) { 844 if (!quadPts->fStartSet) { 845 SkPoint cubicStartPt; 846 this->cubicPerpRay(cubic, quadPts->fStartT, &cubicStartPt, &quadPts->fQuad[0], 847 &quadPts->fTangentStart); 848 quadPts->fStartSet = true; 849 } 850 if (!quadPts->fEndSet) { 851 SkPoint cubicEndPt; 852 this->cubicPerpRay(cubic, quadPts->fEndT, &cubicEndPt, &quadPts->fQuad[2], 853 &quadPts->fTangentEnd); 854 quadPts->fEndSet = true; 855 } 856 } 857 858 void SkPathStroker::cubicQuadMid(const SkPoint cubic[4], const SkQuadConstruct* quadPts, 859 SkPoint* mid) const { 860 SkPoint cubicMidPt; 861 this->cubicPerpRay(cubic, quadPts->fMidT, &cubicMidPt, mid, nullptr); 862 } 863 864 // Given a quad and t, return the point on curve, its perpendicular, and the perpendicular tangent. 865 void SkPathStroker::quadPerpRay(const SkPoint quad[3], SkScalar t, SkPoint* tPt, SkPoint* onPt, 866 SkPoint* tangent) const { 867 SkVector dxy; 868 SkEvalQuadAt(quad, t, tPt, &dxy); 869 if (dxy.fX == 0 && dxy.fY == 0) { 870 dxy = quad[2] - quad[0]; 871 } 872 setRayPts(*tPt, &dxy, onPt, tangent); 873 } 874 875 // Find the intersection of the stroke tangents to construct a stroke quad. 876 // Return whether the stroke is a degenerate (a line), a quad, or must be split. 877 // Optionally compute the quad's control point. 878 SkPathStroker::ResultType SkPathStroker::intersectRay(SkQuadConstruct* quadPts, 879 IntersectRayType intersectRayType STROKER_DEBUG_PARAMS(int depth)) const { 880 const SkPoint& start = quadPts->fQuad[0]; 881 const SkPoint& end = quadPts->fQuad[2]; 882 SkVector aLen = quadPts->fTangentStart - start; 883 SkVector bLen = quadPts->fTangentEnd - end; 884 /* Slopes match when denom goes to zero: 885 axLen / ayLen == bxLen / byLen 886 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen 887 byLen * axLen == ayLen * bxLen 888 byLen * axLen - ayLen * bxLen ( == denom ) 889 */ 890 SkScalar denom = aLen.cross(bLen); 891 if (denom == 0 || !SkScalarIsFinite(denom)) { 892 quadPts->fOppositeTangents = aLen.dot(bLen) < 0; 893 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, "denom == 0"); 894 } 895 quadPts->fOppositeTangents = false; 896 SkVector ab0 = start - end; 897 SkScalar numerA = bLen.cross(ab0); 898 SkScalar numerB = aLen.cross(ab0); 899 if ((numerA >= 0) == (numerB >= 0)) { // if the control point is outside the quad ends 900 // if the perpendicular distances from the quad points to the opposite tangent line 901 // are small, a straight line is good enough 902 SkScalar dist1 = pt_to_line(start, end, quadPts->fTangentEnd); 903 SkScalar dist2 = pt_to_line(end, start, quadPts->fTangentStart); 904 if (SkTMax(dist1, dist2) <= fInvResScaleSquared) { 905 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, 906 "SkTMax(dist1=%g, dist2=%g) <= fInvResScaleSquared", dist1, dist2); 907 } 908 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, 909 "(numerA=%g >= 0) == (numerB=%g >= 0)", numerA, numerB); 910 } 911 // check to see if the denominator is teeny relative to the numerator 912 // if the offset by one will be lost, the ratio is too large 913 numerA /= denom; 914 bool validDivide = numerA > numerA - 1; 915 if (validDivide) { 916 if (kCtrlPt_RayType == intersectRayType) { 917 SkPoint* ctrlPt = &quadPts->fQuad[1]; 918 // the intersection of the tangents need not be on the tangent segment 919 // so 0 <= numerA <= 1 is not necessarily true 920 ctrlPt->fX = start.fX * (1 - numerA) + quadPts->fTangentStart.fX * numerA; 921 ctrlPt->fY = start.fY * (1 - numerA) + quadPts->fTangentStart.fY * numerA; 922 } 923 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, 924 "(numerA=%g >= 0) != (numerB=%g >= 0)", numerA, numerB); 925 } 926 quadPts->fOppositeTangents = aLen.dot(bLen) < 0; 927 // if the lines are parallel, straight line is good enough 928 return STROKER_RESULT(kDegenerate_ResultType, depth, quadPts, 929 "SkScalarNearlyZero(denom=%g)", denom); 930 } 931 932 // Given a cubic and a t-range, determine if the stroke can be described by a quadratic. 933 SkPathStroker::ResultType SkPathStroker::tangentsMeet(const SkPoint cubic[4], 934 SkQuadConstruct* quadPts) { 935 this->cubicQuadEnds(cubic, quadPts); 936 return this->intersectRay(quadPts, kResultType_RayType STROKER_DEBUG_PARAMS(fRecursionDepth)); 937 } 938 939 // Intersect the line with the quad and return the t values on the quad where the line crosses. 940 static int intersect_quad_ray(const SkPoint line[2], const SkPoint quad[3], SkScalar roots[2]) { 941 SkVector vec = line[1] - line[0]; 942 SkScalar r[3]; 943 for (int n = 0; n < 3; ++n) { 944 r[n] = (quad[n].fY - line[0].fY) * vec.fX - (quad[n].fX - line[0].fX) * vec.fY; 945 } 946 SkScalar A = r[2]; 947 SkScalar B = r[1]; 948 SkScalar C = r[0]; 949 A += C - 2 * B; // A = a - 2*b + c 950 B -= C; // B = -(b - c) 951 return SkFindUnitQuadRoots(A, 2 * B, C, roots); 952 } 953 954 // Return true if the point is close to the bounds of the quad. This is used as a quick reject. 955 bool SkPathStroker::ptInQuadBounds(const SkPoint quad[3], const SkPoint& pt) const { 956 SkScalar xMin = SkTMin(SkTMin(quad[0].fX, quad[1].fX), quad[2].fX); 957 if (pt.fX + fInvResScale < xMin) { 958 return false; 959 } 960 SkScalar xMax = SkTMax(SkTMax(quad[0].fX, quad[1].fX), quad[2].fX); 961 if (pt.fX - fInvResScale > xMax) { 962 return false; 963 } 964 SkScalar yMin = SkTMin(SkTMin(quad[0].fY, quad[1].fY), quad[2].fY); 965 if (pt.fY + fInvResScale < yMin) { 966 return false; 967 } 968 SkScalar yMax = SkTMax(SkTMax(quad[0].fY, quad[1].fY), quad[2].fY); 969 if (pt.fY - fInvResScale > yMax) { 970 return false; 971 } 972 return true; 973 } 974 975 static bool points_within_dist(const SkPoint& nearPt, const SkPoint& farPt, SkScalar limit) { 976 return SkPointPriv::DistanceToSqd(nearPt, farPt) <= limit * limit; 977 } 978 979 static bool sharp_angle(const SkPoint quad[3]) { 980 SkVector smaller = quad[1] - quad[0]; 981 SkVector larger = quad[1] - quad[2]; 982 SkScalar smallerLen = SkPointPriv::LengthSqd(smaller); 983 SkScalar largerLen = SkPointPriv::LengthSqd(larger); 984 if (smallerLen > largerLen) { 985 using std::swap; 986 swap(smaller, larger); 987 largerLen = smallerLen; 988 } 989 if (!smaller.setLength(largerLen)) { 990 return false; 991 } 992 SkScalar dot = smaller.dot(larger); 993 return dot > 0; 994 } 995 996 SkPathStroker::ResultType SkPathStroker::strokeCloseEnough(const SkPoint stroke[3], 997 const SkPoint ray[2], SkQuadConstruct* quadPts STROKER_DEBUG_PARAMS(int depth)) const { 998 SkPoint strokeMid = SkEvalQuadAt(stroke, SK_ScalarHalf); 999 // measure the distance from the curve to the quad-stroke midpoint, compare to radius 1000 if (points_within_dist(ray[0], strokeMid, fInvResScale)) { // if the difference is small 1001 if (sharp_angle(quadPts->fQuad)) { 1002 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, 1003 "sharp_angle (1) =%g,%g, %g,%g, %g,%g", 1004 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, 1005 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, 1006 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); 1007 } 1008 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, 1009 "points_within_dist(ray[0]=%g,%g, strokeMid=%g,%g, fInvResScale=%g)", 1010 ray[0].fX, ray[0].fY, strokeMid.fX, strokeMid.fY, fInvResScale); 1011 } 1012 // measure the distance to quad's bounds (quick reject) 1013 // an alternative : look for point in triangle 1014 if (!ptInQuadBounds(stroke, ray[0])) { // if far, subdivide 1015 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, 1016 "!pt_in_quad_bounds(stroke=(%g,%g %g,%g %g,%g), ray[0]=%g,%g)", 1017 stroke[0].fX, stroke[0].fY, stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY, 1018 ray[0].fX, ray[0].fY); 1019 } 1020 // measure the curve ray distance to the quad-stroke 1021 SkScalar roots[2]; 1022 int rootCount = intersect_quad_ray(ray, stroke, roots); 1023 if (rootCount != 1) { 1024 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, 1025 "rootCount=%d != 1", rootCount); 1026 } 1027 SkPoint quadPt = SkEvalQuadAt(stroke, roots[0]); 1028 SkScalar error = fInvResScale * (SK_Scalar1 - SkScalarAbs(roots[0] - 0.5f) * 2); 1029 if (points_within_dist(ray[0], quadPt, error)) { // if the difference is small, we're done 1030 if (sharp_angle(quadPts->fQuad)) { 1031 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, 1032 "sharp_angle (2) =%g,%g, %g,%g, %g,%g", 1033 quadPts->fQuad[0].fX, quadPts->fQuad[0].fY, 1034 quadPts->fQuad[1].fX, quadPts->fQuad[1].fY, 1035 quadPts->fQuad[2].fX, quadPts->fQuad[2].fY); 1036 } 1037 return STROKER_RESULT(kQuad_ResultType, depth, quadPts, 1038 "points_within_dist(ray[0]=%g,%g, quadPt=%g,%g, error=%g)", 1039 ray[0].fX, ray[0].fY, quadPt.fX, quadPt.fY, error); 1040 } 1041 // otherwise, subdivide 1042 return STROKER_RESULT(kSplit_ResultType, depth, quadPts, "%s", "fall through"); 1043 } 1044 1045 SkPathStroker::ResultType SkPathStroker::compareQuadCubic(const SkPoint cubic[4], 1046 SkQuadConstruct* quadPts) { 1047 // get the quadratic approximation of the stroke 1048 this->cubicQuadEnds(cubic, quadPts); 1049 ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType 1050 STROKER_DEBUG_PARAMS(fRecursionDepth) ); 1051 if (resultType != kQuad_ResultType) { 1052 return resultType; 1053 } 1054 // project a ray from the curve to the stroke 1055 SkPoint ray[2]; // points near midpoint on quad, midpoint on cubic 1056 this->cubicPerpRay(cubic, quadPts->fMidT, &ray[1], &ray[0], nullptr); 1057 return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts 1058 STROKER_DEBUG_PARAMS(fRecursionDepth)); 1059 } 1060 1061 SkPathStroker::ResultType SkPathStroker::compareQuadConic(const SkConic& conic, 1062 SkQuadConstruct* quadPts) const { 1063 // get the quadratic approximation of the stroke 1064 this->conicQuadEnds(conic, quadPts); 1065 ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType 1066 STROKER_DEBUG_PARAMS(fRecursionDepth) ); 1067 if (resultType != kQuad_ResultType) { 1068 return resultType; 1069 } 1070 // project a ray from the curve to the stroke 1071 SkPoint ray[2]; // points near midpoint on quad, midpoint on conic 1072 this->conicPerpRay(conic, quadPts->fMidT, &ray[1], &ray[0], nullptr); 1073 return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts 1074 STROKER_DEBUG_PARAMS(fRecursionDepth)); 1075 } 1076 1077 SkPathStroker::ResultType SkPathStroker::compareQuadQuad(const SkPoint quad[3], 1078 SkQuadConstruct* quadPts) { 1079 // get the quadratic approximation of the stroke 1080 if (!quadPts->fStartSet) { 1081 SkPoint quadStartPt; 1082 this->quadPerpRay(quad, quadPts->fStartT, &quadStartPt, &quadPts->fQuad[0], 1083 &quadPts->fTangentStart); 1084 quadPts->fStartSet = true; 1085 } 1086 if (!quadPts->fEndSet) { 1087 SkPoint quadEndPt; 1088 this->quadPerpRay(quad, quadPts->fEndT, &quadEndPt, &quadPts->fQuad[2], 1089 &quadPts->fTangentEnd); 1090 quadPts->fEndSet = true; 1091 } 1092 ResultType resultType = this->intersectRay(quadPts, kCtrlPt_RayType 1093 STROKER_DEBUG_PARAMS(fRecursionDepth)); 1094 if (resultType != kQuad_ResultType) { 1095 return resultType; 1096 } 1097 // project a ray from the curve to the stroke 1098 SkPoint ray[2]; 1099 this->quadPerpRay(quad, quadPts->fMidT, &ray[1], &ray[0], nullptr); 1100 return this->strokeCloseEnough(quadPts->fQuad, ray, quadPts 1101 STROKER_DEBUG_PARAMS(fRecursionDepth)); 1102 } 1103 1104 void SkPathStroker::addDegenerateLine(const SkQuadConstruct* quadPts) { 1105 const SkPoint* quad = quadPts->fQuad; 1106 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; 1107 path->lineTo(quad[2].fX, quad[2].fY); 1108 } 1109 1110 bool SkPathStroker::cubicMidOnLine(const SkPoint cubic[4], const SkQuadConstruct* quadPts) const { 1111 SkPoint strokeMid; 1112 this->cubicQuadMid(cubic, quadPts, &strokeMid); 1113 SkScalar dist = pt_to_line(strokeMid, quadPts->fQuad[0], quadPts->fQuad[2]); 1114 return dist < fInvResScaleSquared; 1115 } 1116 1117 bool SkPathStroker::cubicStroke(const SkPoint cubic[4], SkQuadConstruct* quadPts) { 1118 if (!fFoundTangents) { 1119 ResultType resultType = this->tangentsMeet(cubic, quadPts); 1120 if (kQuad_ResultType != resultType) { 1121 if ((kDegenerate_ResultType == resultType 1122 || points_within_dist(quadPts->fQuad[0], quadPts->fQuad[2], 1123 fInvResScale)) && cubicMidOnLine(cubic, quadPts)) { 1124 addDegenerateLine(quadPts); 1125 return true; 1126 } 1127 } else { 1128 fFoundTangents = true; 1129 } 1130 } 1131 if (fFoundTangents) { 1132 ResultType resultType = this->compareQuadCubic(cubic, quadPts); 1133 if (kQuad_ResultType == resultType) { 1134 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; 1135 const SkPoint* stroke = quadPts->fQuad; 1136 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); 1137 return true; 1138 } 1139 if (kDegenerate_ResultType == resultType) { 1140 if (!quadPts->fOppositeTangents) { 1141 addDegenerateLine(quadPts); 1142 return true; 1143 } 1144 } 1145 } 1146 if (!SkScalarIsFinite(quadPts->fQuad[2].fX) || !SkScalarIsFinite(quadPts->fQuad[2].fY)) { 1147 return false; // just abort if projected quad isn't representable 1148 } 1149 #if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING 1150 SkDEBUGCODE(gMaxRecursion[fFoundTangents] = SkTMax(gMaxRecursion[fFoundTangents], 1151 fRecursionDepth + 1)); 1152 #endif 1153 if (++fRecursionDepth > kRecursiveLimits[fFoundTangents]) { 1154 return false; // just abort if projected quad isn't representable 1155 } 1156 SkQuadConstruct half; 1157 if (!half.initWithStart(quadPts)) { 1158 addDegenerateLine(quadPts); 1159 --fRecursionDepth; 1160 return true; 1161 } 1162 if (!this->cubicStroke(cubic, &half)) { 1163 return false; 1164 } 1165 if (!half.initWithEnd(quadPts)) { 1166 addDegenerateLine(quadPts); 1167 --fRecursionDepth; 1168 return true; 1169 } 1170 if (!this->cubicStroke(cubic, &half)) { 1171 return false; 1172 } 1173 --fRecursionDepth; 1174 return true; 1175 } 1176 1177 bool SkPathStroker::conicStroke(const SkConic& conic, SkQuadConstruct* quadPts) { 1178 ResultType resultType = this->compareQuadConic(conic, quadPts); 1179 if (kQuad_ResultType == resultType) { 1180 const SkPoint* stroke = quadPts->fQuad; 1181 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; 1182 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); 1183 return true; 1184 } 1185 if (kDegenerate_ResultType == resultType) { 1186 addDegenerateLine(quadPts); 1187 return true; 1188 } 1189 #if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING 1190 SkDEBUGCODE(gMaxRecursion[kConic_RecursiveLimit] = SkTMax(gMaxRecursion[kConic_RecursiveLimit], 1191 fRecursionDepth + 1)); 1192 #endif 1193 if (++fRecursionDepth > kRecursiveLimits[kConic_RecursiveLimit]) { 1194 return false; // just abort if projected quad isn't representable 1195 } 1196 SkQuadConstruct half; 1197 (void) half.initWithStart(quadPts); 1198 if (!this->conicStroke(conic, &half)) { 1199 return false; 1200 } 1201 (void) half.initWithEnd(quadPts); 1202 if (!this->conicStroke(conic, &half)) { 1203 return false; 1204 } 1205 --fRecursionDepth; 1206 return true; 1207 } 1208 1209 bool SkPathStroker::quadStroke(const SkPoint quad[3], SkQuadConstruct* quadPts) { 1210 ResultType resultType = this->compareQuadQuad(quad, quadPts); 1211 if (kQuad_ResultType == resultType) { 1212 const SkPoint* stroke = quadPts->fQuad; 1213 SkPath* path = fStrokeType == kOuter_StrokeType ? &fOuter : &fInner; 1214 path->quadTo(stroke[1].fX, stroke[1].fY, stroke[2].fX, stroke[2].fY); 1215 return true; 1216 } 1217 if (kDegenerate_ResultType == resultType) { 1218 addDegenerateLine(quadPts); 1219 return true; 1220 } 1221 #if QUAD_STROKE_APPROX_EXTENDED_DEBUGGING 1222 SkDEBUGCODE(gMaxRecursion[kQuad_RecursiveLimit] = SkTMax(gMaxRecursion[kQuad_RecursiveLimit], 1223 fRecursionDepth + 1)); 1224 #endif 1225 if (++fRecursionDepth > kRecursiveLimits[kQuad_RecursiveLimit]) { 1226 return false; // just abort if projected quad isn't representable 1227 } 1228 SkQuadConstruct half; 1229 (void) half.initWithStart(quadPts); 1230 if (!this->quadStroke(quad, &half)) { 1231 return false; 1232 } 1233 (void) half.initWithEnd(quadPts); 1234 if (!this->quadStroke(quad, &half)) { 1235 return false; 1236 } 1237 --fRecursionDepth; 1238 return true; 1239 } 1240 1241 void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, 1242 const SkPoint& pt3) { 1243 const SkPoint cubic[4] = { fPrevPt, pt1, pt2, pt3 }; 1244 SkPoint reduction[3]; 1245 const SkPoint* tangentPt; 1246 ReductionType reductionType = CheckCubicLinear(cubic, reduction, &tangentPt); 1247 if (kPoint_ReductionType == reductionType) { 1248 /* If the stroke consists of a moveTo followed by a degenerate curve, treat it 1249 as if it were followed by a zero-length line. Lines without length 1250 can have square and round end caps. */ 1251 this->lineTo(pt3); 1252 return; 1253 } 1254 if (kLine_ReductionType == reductionType) { 1255 this->lineTo(pt3); 1256 return; 1257 } 1258 if (kDegenerate_ReductionType <= reductionType && kDegenerate3_ReductionType >= reductionType) { 1259 this->lineTo(reduction[0]); 1260 SkStrokerPriv::JoinProc saveJoiner = fJoiner; 1261 fJoiner = SkStrokerPriv::JoinFactory(SkPaint::kRound_Join); 1262 if (kDegenerate2_ReductionType <= reductionType) { 1263 this->lineTo(reduction[1]); 1264 } 1265 if (kDegenerate3_ReductionType == reductionType) { 1266 this->lineTo(reduction[2]); 1267 } 1268 this->lineTo(pt3); 1269 fJoiner = saveJoiner; 1270 return; 1271 } 1272 SkASSERT(kQuad_ReductionType == reductionType); 1273 SkVector normalAB, unitAB, normalCD, unitCD; 1274 if (!this->preJoinTo(*tangentPt, &normalAB, &unitAB, false)) { 1275 this->lineTo(pt3); 1276 return; 1277 } 1278 SkScalar tValues[2]; 1279 int count = SkFindCubicInflections(cubic, tValues); 1280 SkScalar lastT = 0; 1281 for (int index = 0; index <= count; ++index) { 1282 SkScalar nextT = index < count ? tValues[index] : 1; 1283 SkQuadConstruct quadPts; 1284 this->init(kOuter_StrokeType, &quadPts, lastT, nextT); 1285 (void) this->cubicStroke(cubic, &quadPts); 1286 this->init(kInner_StrokeType, &quadPts, lastT, nextT); 1287 (void) this->cubicStroke(cubic, &quadPts); 1288 lastT = nextT; 1289 } 1290 SkScalar cusp = SkFindCubicCusp(cubic); 1291 if (cusp > 0) { 1292 SkPoint cuspLoc; 1293 SkEvalCubicAt(cubic, cusp, &cuspLoc, nullptr, nullptr); 1294 fCusper.addCircle(cuspLoc.fX, cuspLoc.fY, fRadius); 1295 } 1296 // emit the join even if one stroke succeeded but the last one failed 1297 // this avoids reversing an inner stroke with a partial path followed by another moveto 1298 this->setCubicEndNormal(cubic, normalAB, unitAB, &normalCD, &unitCD); 1299 1300 this->postJoinTo(pt3, normalCD, unitCD); 1301 } 1302 1303 /////////////////////////////////////////////////////////////////////////////// 1304 /////////////////////////////////////////////////////////////////////////////// 1305 1306 #include "SkPaintDefaults.h" 1307 1308 SkStroke::SkStroke() { 1309 fWidth = SK_Scalar1; 1310 fMiterLimit = SkPaintDefaults_MiterLimit; 1311 fResScale = 1; 1312 fCap = SkPaint::kDefault_Cap; 1313 fJoin = SkPaint::kDefault_Join; 1314 fDoFill = false; 1315 } 1316 1317 SkStroke::SkStroke(const SkPaint& p) { 1318 fWidth = p.getStrokeWidth(); 1319 fMiterLimit = p.getStrokeMiter(); 1320 fResScale = 1; 1321 fCap = (uint8_t)p.getStrokeCap(); 1322 fJoin = (uint8_t)p.getStrokeJoin(); 1323 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); 1324 } 1325 1326 SkStroke::SkStroke(const SkPaint& p, SkScalar width) { 1327 fWidth = width; 1328 fMiterLimit = p.getStrokeMiter(); 1329 fResScale = 1; 1330 fCap = (uint8_t)p.getStrokeCap(); 1331 fJoin = (uint8_t)p.getStrokeJoin(); 1332 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); 1333 } 1334 1335 void SkStroke::setWidth(SkScalar width) { 1336 SkASSERT(width >= 0); 1337 fWidth = width; 1338 } 1339 1340 void SkStroke::setMiterLimit(SkScalar miterLimit) { 1341 SkASSERT(miterLimit >= 0); 1342 fMiterLimit = miterLimit; 1343 } 1344 1345 void SkStroke::setCap(SkPaint::Cap cap) { 1346 SkASSERT((unsigned)cap < SkPaint::kCapCount); 1347 fCap = SkToU8(cap); 1348 } 1349 1350 void SkStroke::setJoin(SkPaint::Join join) { 1351 SkASSERT((unsigned)join < SkPaint::kJoinCount); 1352 fJoin = SkToU8(join); 1353 } 1354 1355 /////////////////////////////////////////////////////////////////////////////// 1356 1357 // If src==dst, then we use a tmp path to record the stroke, and then swap 1358 // its contents with src when we're done. 1359 class AutoTmpPath { 1360 public: 1361 AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) { 1362 if (&src == *dst) { 1363 *dst = &fTmpDst; 1364 fSwapWithSrc = true; 1365 } else { 1366 (*dst)->reset(); 1367 fSwapWithSrc = false; 1368 } 1369 } 1370 1371 ~AutoTmpPath() { 1372 if (fSwapWithSrc) { 1373 fTmpDst.swap(*const_cast<SkPath*>(&fSrc)); 1374 } 1375 } 1376 1377 private: 1378 SkPath fTmpDst; 1379 const SkPath& fSrc; 1380 bool fSwapWithSrc; 1381 }; 1382 1383 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { 1384 SkASSERT(dst); 1385 1386 SkScalar radius = SkScalarHalf(fWidth); 1387 1388 AutoTmpPath tmp(src, &dst); 1389 1390 if (radius <= 0) { 1391 return; 1392 } 1393 1394 // If src is really a rect, call our specialty strokeRect() method 1395 { 1396 SkRect rect; 1397 bool isClosed; 1398 SkPath::Direction dir; 1399 if (src.isRect(&rect, &isClosed, &dir) && isClosed) { 1400 this->strokeRect(rect, dst, dir); 1401 // our answer should preserve the inverseness of the src 1402 if (src.isInverseFillType()) { 1403 SkASSERT(!dst->isInverseFillType()); 1404 dst->toggleInverseFillType(); 1405 } 1406 return; 1407 } 1408 } 1409 1410 // We can always ignore centers for stroke and fill convex line-only paths 1411 // TODO: remove the line-only restriction 1412 bool ignoreCenter = fDoFill && (src.getSegmentMasks() == SkPath::kLine_SegmentMask) && 1413 src.isLastContourClosed() && src.isConvex(); 1414 1415 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), this->getJoin(), 1416 fResScale, ignoreCenter); 1417 SkPath::Iter iter(src, false); 1418 SkPath::Verb lastSegment = SkPath::kMove_Verb; 1419 1420 for (;;) { 1421 SkPoint pts[4]; 1422 switch (iter.next(pts, false)) { 1423 case SkPath::kMove_Verb: 1424 stroker.moveTo(pts[0]); 1425 break; 1426 case SkPath::kLine_Verb: 1427 stroker.lineTo(pts[1], &iter); 1428 lastSegment = SkPath::kLine_Verb; 1429 break; 1430 case SkPath::kQuad_Verb: 1431 stroker.quadTo(pts[1], pts[2]); 1432 lastSegment = SkPath::kQuad_Verb; 1433 break; 1434 case SkPath::kConic_Verb: { 1435 stroker.conicTo(pts[1], pts[2], iter.conicWeight()); 1436 lastSegment = SkPath::kConic_Verb; 1437 break; 1438 } break; 1439 case SkPath::kCubic_Verb: 1440 stroker.cubicTo(pts[1], pts[2], pts[3]); 1441 lastSegment = SkPath::kCubic_Verb; 1442 break; 1443 case SkPath::kClose_Verb: 1444 if (SkPaint::kButt_Cap != this->getCap()) { 1445 /* If the stroke consists of a moveTo followed by a close, treat it 1446 as if it were followed by a zero-length line. Lines without length 1447 can have square and round end caps. */ 1448 if (stroker.hasOnlyMoveTo()) { 1449 stroker.lineTo(stroker.moveToPt()); 1450 goto ZERO_LENGTH; 1451 } 1452 /* If the stroke consists of a moveTo followed by one or more zero-length 1453 verbs, then followed by a close, treat is as if it were followed by a 1454 zero-length line. Lines without length can have square & round end caps. */ 1455 if (stroker.isCurrentContourEmpty()) { 1456 ZERO_LENGTH: 1457 lastSegment = SkPath::kLine_Verb; 1458 break; 1459 } 1460 } 1461 stroker.close(lastSegment == SkPath::kLine_Verb); 1462 break; 1463 case SkPath::kDone_Verb: 1464 goto DONE; 1465 } 1466 } 1467 DONE: 1468 stroker.done(dst, lastSegment == SkPath::kLine_Verb); 1469 1470 if (fDoFill && !ignoreCenter) { 1471 if (SkPathPriv::CheapIsFirstDirection(src, SkPathPriv::kCCW_FirstDirection)) { 1472 dst->reverseAddPath(src); 1473 } else { 1474 dst->addPath(src); 1475 } 1476 } else { 1477 // Seems like we can assume that a 2-point src would always result in 1478 // a convex stroke, but testing has proved otherwise. 1479 // TODO: fix the stroker to make this assumption true (without making 1480 // it slower that the work that will be done in computeConvexity()) 1481 #if 0 1482 // this test results in a non-convex stroke :( 1483 static void test(SkCanvas* canvas) { 1484 SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 }; 1485 SkPaint paint; 1486 paint.setStrokeWidth(7); 1487 paint.setStrokeCap(SkPaint::kRound_Cap); 1488 canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); 1489 } 1490 #endif 1491 #if 0 1492 if (2 == src.countPoints()) { 1493 dst->setIsConvex(true); 1494 } 1495 #endif 1496 } 1497 1498 // our answer should preserve the inverseness of the src 1499 if (src.isInverseFillType()) { 1500 SkASSERT(!dst->isInverseFillType()); 1501 dst->toggleInverseFillType(); 1502 } 1503 } 1504 1505 static SkPath::Direction reverse_direction(SkPath::Direction dir) { 1506 static const SkPath::Direction gOpposite[] = { SkPath::kCCW_Direction, SkPath::kCW_Direction }; 1507 return gOpposite[dir]; 1508 } 1509 1510 static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) { 1511 SkPoint pts[8]; 1512 1513 if (SkPath::kCW_Direction == dir) { 1514 pts[0].set(r.fLeft, outer.fTop); 1515 pts[1].set(r.fRight, outer.fTop); 1516 pts[2].set(outer.fRight, r.fTop); 1517 pts[3].set(outer.fRight, r.fBottom); 1518 pts[4].set(r.fRight, outer.fBottom); 1519 pts[5].set(r.fLeft, outer.fBottom); 1520 pts[6].set(outer.fLeft, r.fBottom); 1521 pts[7].set(outer.fLeft, r.fTop); 1522 } else { 1523 pts[7].set(r.fLeft, outer.fTop); 1524 pts[6].set(r.fRight, outer.fTop); 1525 pts[5].set(outer.fRight, r.fTop); 1526 pts[4].set(outer.fRight, r.fBottom); 1527 pts[3].set(r.fRight, outer.fBottom); 1528 pts[2].set(r.fLeft, outer.fBottom); 1529 pts[1].set(outer.fLeft, r.fBottom); 1530 pts[0].set(outer.fLeft, r.fTop); 1531 } 1532 path->addPoly(pts, 8, true); 1533 } 1534 1535 void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst, 1536 SkPath::Direction dir) const { 1537 SkASSERT(dst != nullptr); 1538 dst->reset(); 1539 1540 SkScalar radius = SkScalarHalf(fWidth); 1541 if (radius <= 0) { 1542 return; 1543 } 1544 1545 SkScalar rw = origRect.width(); 1546 SkScalar rh = origRect.height(); 1547 if ((rw < 0) ^ (rh < 0)) { 1548 dir = reverse_direction(dir); 1549 } 1550 SkRect rect(origRect); 1551 rect.sort(); 1552 // reassign these, now that we know they'll be >= 0 1553 rw = rect.width(); 1554 rh = rect.height(); 1555 1556 SkRect r(rect); 1557 r.outset(radius, radius); 1558 1559 SkPaint::Join join = (SkPaint::Join)fJoin; 1560 if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) { 1561 join = SkPaint::kBevel_Join; 1562 } 1563 1564 switch (join) { 1565 case SkPaint::kMiter_Join: 1566 dst->addRect(r, dir); 1567 break; 1568 case SkPaint::kBevel_Join: 1569 addBevel(dst, rect, r, dir); 1570 break; 1571 case SkPaint::kRound_Join: 1572 dst->addRoundRect(r, radius, radius, dir); 1573 break; 1574 default: 1575 break; 1576 } 1577 1578 if (fWidth < SkMinScalar(rw, rh) && !fDoFill) { 1579 r = rect; 1580 r.inset(radius, radius); 1581 dst->addRect(r, reverse_direction(dir)); 1582 } 1583 } 1584