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