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