SkStroke.cpp revision 2e0d727942c26339425bc5ba26189af070081213
1 2/* 3 * Copyright 2008 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkStrokerPriv.h" 11#include "SkGeometry.h" 12#include "SkPath.h" 13 14#define kMaxQuadSubdivide 5 15#define kMaxCubicSubdivide 4 16 17static inline bool degenerate_vector(const SkVector& v) { 18 return !SkPoint::CanNormalize(v.fX, v.fY); 19} 20 21static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) { 22 /* root2/2 is a 45-degree angle 23 make this constant bigger for more subdivisions (but not >= 1) 24 */ 25 static const SkScalar kFlatEnoughNormalDotProd = 26 SK_ScalarSqrt2/2 + SK_Scalar1/10; 27 28 SkASSERT(kFlatEnoughNormalDotProd > 0 && 29 kFlatEnoughNormalDotProd < SK_Scalar1); 30 31 return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd; 32} 33 34static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) { 35 static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000; 36 37 return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd; 38} 39 40static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after, 41 SkScalar radius, 42 SkVector* normal, SkVector* unitNormal) { 43 if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) { 44 return false; 45 } 46 unitNormal->rotateCCW(); 47 unitNormal->scale(radius, normal); 48 return true; 49} 50 51static bool set_normal_unitnormal(const SkVector& vec, 52 SkScalar radius, 53 SkVector* normal, SkVector* unitNormal) { 54 if (!unitNormal->setNormalize(vec.fX, vec.fY)) { 55 return false; 56 } 57 unitNormal->rotateCCW(); 58 unitNormal->scale(radius, normal); 59 return true; 60} 61 62/////////////////////////////////////////////////////////////////////////////// 63 64class SkPathStroker { 65public: 66 SkPathStroker(const SkPath& src, 67 SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap, 68 SkPaint::Join join); 69 70 void moveTo(const SkPoint&); 71 void lineTo(const SkPoint&); 72 void quadTo(const SkPoint&, const SkPoint&); 73 void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); 74 void close(bool isLine) { this->finishContour(true, isLine); } 75 76 void done(SkPath* dst, bool isLine) { 77 this->finishContour(false, isLine); 78 fOuter.addPath(fExtra); 79 dst->swap(fOuter); 80 } 81 82private: 83 SkScalar fRadius; 84 SkScalar fInvMiterLimit; 85 86 SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal; 87 SkPoint fFirstPt, fPrevPt; // on original path 88 SkPoint fFirstOuterPt; 89 int fSegmentCount; 90 bool fPrevIsLine; 91 92 SkStrokerPriv::CapProc fCapper; 93 SkStrokerPriv::JoinProc fJoiner; 94 95 SkPath fInner, fOuter; // outer is our working answer, inner is temp 96 SkPath fExtra; // added as extra complete contours 97 98 void finishContour(bool close, bool isLine); 99 void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal, 100 bool isLine); 101 void postJoinTo(const SkPoint&, const SkVector& normal, 102 const SkVector& unitNormal); 103 104 void line_to(const SkPoint& currPt, const SkVector& normal); 105 void quad_to(const SkPoint pts[3], 106 const SkVector& normalAB, const SkVector& unitNormalAB, 107 SkVector* normalBC, SkVector* unitNormalBC, 108 int subDivide); 109 void cubic_to(const SkPoint pts[4], 110 const SkVector& normalAB, const SkVector& unitNormalAB, 111 SkVector* normalCD, SkVector* unitNormalCD, 112 int subDivide); 113}; 114 115/////////////////////////////////////////////////////////////////////////////// 116 117void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, 118 SkVector* unitNormal, bool currIsLine) { 119 SkASSERT(fSegmentCount >= 0); 120 121 SkScalar prevX = fPrevPt.fX; 122 SkScalar prevY = fPrevPt.fY; 123 124 SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, 125 unitNormal)); 126 127 if (fSegmentCount == 0) { 128 fFirstNormal = *normal; 129 fFirstUnitNormal = *unitNormal; 130 fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY); 131 132 fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY); 133 fInner.moveTo(prevX - normal->fX, prevY - normal->fY); 134 } else { // we have a previous segment 135 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal, 136 fRadius, fInvMiterLimit, fPrevIsLine, currIsLine); 137 } 138 fPrevIsLine = currIsLine; 139} 140 141void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, 142 const SkVector& unitNormal) { 143 fPrevPt = currPt; 144 fPrevUnitNormal = unitNormal; 145 fPrevNormal = normal; 146 fSegmentCount += 1; 147} 148 149void SkPathStroker::finishContour(bool close, bool currIsLine) { 150 if (fSegmentCount > 0) { 151 SkPoint pt; 152 153 if (close) { 154 fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, 155 fFirstUnitNormal, fRadius, fInvMiterLimit, 156 fPrevIsLine, currIsLine); 157 fOuter.close(); 158 // now add fInner as its own contour 159 fInner.getLastPt(&pt); 160 fOuter.moveTo(pt.fX, pt.fY); 161 fOuter.reversePathTo(fInner); 162 fOuter.close(); 163 } else { // add caps to start and end 164 // cap the end 165 fInner.getLastPt(&pt); 166 fCapper(&fOuter, fPrevPt, fPrevNormal, pt, 167 currIsLine ? &fInner : NULL); 168 fOuter.reversePathTo(fInner); 169 // cap the start 170 fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt, 171 fPrevIsLine ? &fInner : NULL); 172 fOuter.close(); 173 } 174 } 175 // since we may re-use fInner, we rewind instead of reset, to save on 176 // reallocating its internal storage. 177 fInner.rewind(); 178 fSegmentCount = -1; 179} 180 181/////////////////////////////////////////////////////////////////////////////// 182 183SkPathStroker::SkPathStroker(const SkPath& src, 184 SkScalar radius, SkScalar miterLimit, 185 SkPaint::Cap cap, SkPaint::Join join) 186 : fRadius(radius) { 187 188 /* This is only used when join is miter_join, but we initialize it here 189 so that it is always defined, to fis valgrind warnings. 190 */ 191 fInvMiterLimit = 0; 192 193 if (join == SkPaint::kMiter_Join) { 194 if (miterLimit <= SK_Scalar1) { 195 join = SkPaint::kBevel_Join; 196 } else { 197 fInvMiterLimit = SkScalarInvert(miterLimit); 198 } 199 } 200 fCapper = SkStrokerPriv::CapFactory(cap); 201 fJoiner = SkStrokerPriv::JoinFactory(join); 202 fSegmentCount = -1; 203 fPrevIsLine = false; 204 205 // Need some estimate of how large our final result (fOuter) 206 // and our per-contour temp (fInner) will be, so we don't spend 207 // extra time repeatedly growing these arrays. 208 // 209 // 3x for result == inner + outer + join (swag) 210 // 1x for inner == 'wag' (worst contour length would be better guess) 211 fOuter.incReserve(src.countPoints() * 3); 212 fInner.incReserve(src.countPoints()); 213} 214 215void SkPathStroker::moveTo(const SkPoint& pt) { 216 if (fSegmentCount > 0) { 217 this->finishContour(false, false); 218 } 219 fSegmentCount = 0; 220 fFirstPt = fPrevPt = pt; 221} 222 223void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { 224 fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY); 225 fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); 226} 227 228void SkPathStroker::lineTo(const SkPoint& currPt) { 229 if (SkPath::IsLineDegenerate(fPrevPt, currPt)) { 230 return; 231 } 232 SkVector normal, unitNormal; 233 234 this->preJoinTo(currPt, &normal, &unitNormal, true); 235 this->line_to(currPt, normal); 236 this->postJoinTo(currPt, normal, unitNormal); 237} 238 239void SkPathStroker::quad_to(const SkPoint pts[3], 240 const SkVector& normalAB, const SkVector& unitNormalAB, 241 SkVector* normalBC, SkVector* unitNormalBC, 242 int subDivide) { 243 if (!set_normal_unitnormal(pts[1], pts[2], fRadius, 244 normalBC, unitNormalBC)) { 245 // pts[1] nearly equals pts[2], so just draw a line to pts[2] 246 this->line_to(pts[2], normalAB); 247 *normalBC = normalAB; 248 *unitNormalBC = unitNormalAB; 249 return; 250 } 251 252 if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) { 253 SkPoint tmp[5]; 254 SkVector norm, unit; 255 256 SkChopQuadAtHalf(pts, tmp); 257 this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide); 258 this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide); 259 } else { 260 SkVector normalB, unitB; 261 SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius, 262 &normalB, &unitB)); 263 264 fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, 265 pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY); 266 fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, 267 pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY); 268 } 269} 270 271void SkPathStroker::cubic_to(const SkPoint pts[4], 272 const SkVector& normalAB, const SkVector& unitNormalAB, 273 SkVector* normalCD, SkVector* unitNormalCD, 274 int subDivide) { 275 SkVector ab = pts[1] - pts[0]; 276 SkVector cd = pts[3] - pts[2]; 277 SkVector normalBC, unitNormalBC; 278 279 bool degenerateAB = degenerate_vector(ab); 280 bool degenerateCD = degenerate_vector(cd); 281 282 if (degenerateAB && degenerateCD) { 283DRAW_LINE: 284 this->line_to(pts[3], normalAB); 285 *normalCD = normalAB; 286 *unitNormalCD = unitNormalAB; 287 return; 288 } 289 290 if (degenerateAB) { 291 ab = pts[2] - pts[0]; 292 degenerateAB = degenerate_vector(ab); 293 } 294 if (degenerateCD) { 295 cd = pts[3] - pts[1]; 296 degenerateCD = degenerate_vector(cd); 297 } 298 if (degenerateAB || degenerateCD) { 299 goto DRAW_LINE; 300 } 301 SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD)); 302 bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius, 303 &normalBC, &unitNormalBC); 304 305 if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) || 306 normals_too_curvy(unitNormalBC, *unitNormalCD)) { 307 // subdivide if we can 308 if (--subDivide < 0) { 309 goto DRAW_LINE; 310 } 311 SkPoint tmp[7]; 312 SkVector norm, unit, dummy, unitDummy; 313 314 SkChopCubicAtHalf(pts, tmp); 315 this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, 316 subDivide); 317 // we use dummys since we already have a valid (and more accurate) 318 // normals for CD 319 this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide); 320 } else { 321 SkVector normalB, normalC; 322 323 // need normals to inset/outset the off-curve pts B and C 324 325 if (0) { // this is normal to the line between our adjacent pts 326 normalB = pts[2] - pts[0]; 327 normalB.rotateCCW(); 328 SkAssertResult(normalB.setLength(fRadius)); 329 330 normalC = pts[3] - pts[1]; 331 normalC.rotateCCW(); 332 SkAssertResult(normalC.setLength(fRadius)); 333 } else { // miter-join 334 SkVector unitBC = pts[2] - pts[1]; 335 unitBC.normalize(); 336 unitBC.rotateCCW(); 337 338 normalB = unitNormalAB + unitBC; 339 normalC = *unitNormalCD + unitBC; 340 341 SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC); 342 SkAssertResult(normalB.setLength(SkScalarDiv(fRadius, 343 SkScalarSqrt((SK_Scalar1 + dot)/2)))); 344 dot = SkPoint::DotProduct(*unitNormalCD, unitBC); 345 SkAssertResult(normalC.setLength(SkScalarDiv(fRadius, 346 SkScalarSqrt((SK_Scalar1 + dot)/2)))); 347 } 348 349 fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY, 350 pts[2].fX + normalC.fX, pts[2].fY + normalC.fY, 351 pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY); 352 353 fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY, 354 pts[2].fX - normalC.fX, pts[2].fY - normalC.fY, 355 pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY); 356 } 357} 358 359void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) { 360 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); 361 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); 362 363 if (degenerateAB | degenerateBC) { 364 if (degenerateAB ^ degenerateBC) { 365 this->lineTo(pt2); 366 } 367 return; 368 } 369 370 SkVector normalAB, unitAB, normalBC, unitBC; 371 372 this->preJoinTo(pt1, &normalAB, &unitAB, false); 373 374 { 375 SkPoint pts[3], tmp[5]; 376 pts[0] = fPrevPt; 377 pts[1] = pt1; 378 pts[2] = pt2; 379 380 if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) { 381 unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY); 382 unitBC.rotateCCW(); 383 if (normals_too_pinchy(unitAB, unitBC)) { 384 normalBC = unitBC; 385 normalBC.scale(fRadius); 386 387 fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY); 388 fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY); 389 fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY); 390 391 fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY); 392 fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY); 393 fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY); 394 395 fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius, 396 SkPath::kCW_Direction); 397 } else { 398 this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC, 399 kMaxQuadSubdivide); 400 SkVector n = normalBC; 401 SkVector u = unitBC; 402 this->quad_to(&tmp[2], n, u, &normalBC, &unitBC, 403 kMaxQuadSubdivide); 404 } 405 } else { 406 this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC, 407 kMaxQuadSubdivide); 408 } 409 } 410 411 this->postJoinTo(pt2, normalBC, unitBC); 412} 413 414void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, 415 const SkPoint& pt3) { 416 bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); 417 bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); 418 bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3); 419 420 if (degenerateAB + degenerateBC + degenerateCD >= 2) { 421 this->lineTo(pt3); 422 return; 423 } 424 425 SkVector normalAB, unitAB, normalCD, unitCD; 426 427 // find the first tangent (which might be pt1 or pt2 428 { 429 const SkPoint* nextPt = &pt1; 430 if (degenerateAB) 431 nextPt = &pt2; 432 this->preJoinTo(*nextPt, &normalAB, &unitAB, false); 433 } 434 435 { 436 SkPoint pts[4], tmp[13]; 437 int i, count; 438 SkVector n, u; 439 SkScalar tValues[3]; 440 441 pts[0] = fPrevPt; 442 pts[1] = pt1; 443 pts[2] = pt2; 444 pts[3] = pt3; 445 446#if 1 447 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); 448#else 449 count = 1; 450 memcpy(tmp, pts, 4 * sizeof(SkPoint)); 451#endif 452 n = normalAB; 453 u = unitAB; 454 for (i = 0; i < count; i++) { 455 this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, 456 kMaxCubicSubdivide); 457 if (i == count - 1) { 458 break; 459 } 460 n = normalCD; 461 u = unitCD; 462 463 } 464 465 // check for too pinchy 466 for (i = 1; i < count; i++) { 467 SkPoint p; 468 SkVector v, c; 469 470 SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c); 471 472 SkScalar dot = SkPoint::DotProduct(c, c); 473 v.scale(SkScalarInvert(dot)); 474 475 if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) { 476 fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction); 477 } 478 } 479 480 } 481 482 this->postJoinTo(pt3, normalCD, unitCD); 483} 484 485/////////////////////////////////////////////////////////////////////////////// 486/////////////////////////////////////////////////////////////////////////////// 487 488#include "SkPaintDefaults.h" 489 490SkStroke::SkStroke() { 491 fWidth = SK_Scalar1; 492 fMiterLimit = SkPaintDefaults_MiterLimit; 493 fCap = SkPaint::kDefault_Cap; 494 fJoin = SkPaint::kDefault_Join; 495 fDoFill = false; 496} 497 498SkStroke::SkStroke(const SkPaint& p) { 499 fWidth = p.getStrokeWidth(); 500 fMiterLimit = p.getStrokeMiter(); 501 fCap = (uint8_t)p.getStrokeCap(); 502 fJoin = (uint8_t)p.getStrokeJoin(); 503 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); 504} 505 506SkStroke::SkStroke(const SkPaint& p, SkScalar width) { 507 fWidth = width; 508 fMiterLimit = p.getStrokeMiter(); 509 fCap = (uint8_t)p.getStrokeCap(); 510 fJoin = (uint8_t)p.getStrokeJoin(); 511 fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style); 512} 513 514void SkStroke::setWidth(SkScalar width) { 515 SkASSERT(width >= 0); 516 fWidth = width; 517} 518 519void SkStroke::setMiterLimit(SkScalar miterLimit) { 520 SkASSERT(miterLimit >= 0); 521 fMiterLimit = miterLimit; 522} 523 524void SkStroke::setCap(SkPaint::Cap cap) { 525 SkASSERT((unsigned)cap < SkPaint::kCapCount); 526 fCap = SkToU8(cap); 527} 528 529void SkStroke::setJoin(SkPaint::Join join) { 530 SkASSERT((unsigned)join < SkPaint::kJoinCount); 531 fJoin = SkToU8(join); 532} 533 534/////////////////////////////////////////////////////////////////////////////// 535 536#ifdef SK_SCALAR_IS_FIXED 537 /* return non-zero if the path is too big, and should be shrunk to avoid 538 overflows during intermediate calculations. Note that we compute the 539 bounds for this. If we had a custom callback/walker for paths, we could 540 perhaps go faster by using that, and just perform the abs | in that 541 routine 542 */ 543 static int needs_to_shrink(const SkPath& path) { 544 const SkRect& r = path.getBounds(); 545 SkFixed mask = SkAbs32(r.fLeft); 546 mask |= SkAbs32(r.fTop); 547 mask |= SkAbs32(r.fRight); 548 mask |= SkAbs32(r.fBottom); 549 // we need the top 3 bits clear (after abs) to avoid overflow 550 return mask >> 29; 551 } 552 553 static void identity_proc(SkPoint pts[], int count) {} 554 static void shift_down_2_proc(SkPoint pts[], int count) { 555 for (int i = 0; i < count; i++) { 556 pts->fX >>= 2; 557 pts->fY >>= 2; 558 pts += 1; 559 } 560 } 561 #define APPLY_PROC(proc, pts, count) proc(pts, count) 562#else // float does need any of this 563 #define APPLY_PROC(proc, pts, count) 564#endif 565 566void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { 567 SkASSERT(&src != NULL && dst != NULL); 568 569 SkScalar radius = SkScalarHalf(fWidth); 570 571 dst->reset(); 572 if (radius <= 0) { 573 return; 574 } 575 576#ifdef SK_SCALAR_IS_FIXED 577 void (*proc)(SkPoint pts[], int count) = identity_proc; 578 if (needs_to_shrink(src)) { 579 proc = shift_down_2_proc; 580 radius >>= 2; 581 if (radius == 0) { 582 return; 583 } 584 } 585#endif 586 587 SkPathStroker stroker(src, radius, fMiterLimit, this->getCap(), 588 this->getJoin()); 589 590 SkPath::Iter iter(src, false); 591 SkPoint pts[4]; 592 SkPath::Verb verb, lastSegment = SkPath::kMove_Verb; 593 594 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 595 switch (verb) { 596 case SkPath::kMove_Verb: 597 APPLY_PROC(proc, &pts[0], 1); 598 stroker.moveTo(pts[0]); 599 break; 600 case SkPath::kLine_Verb: 601 APPLY_PROC(proc, &pts[1], 1); 602 stroker.lineTo(pts[1]); 603 lastSegment = verb; 604 break; 605 case SkPath::kQuad_Verb: 606 APPLY_PROC(proc, &pts[1], 2); 607 stroker.quadTo(pts[1], pts[2]); 608 lastSegment = verb; 609 break; 610 case SkPath::kCubic_Verb: 611 APPLY_PROC(proc, &pts[1], 3); 612 stroker.cubicTo(pts[1], pts[2], pts[3]); 613 lastSegment = verb; 614 break; 615 case SkPath::kClose_Verb: 616 stroker.close(lastSegment == SkPath::kLine_Verb); 617 break; 618 default: 619 break; 620 } 621 } 622 stroker.done(dst, lastSegment == SkPath::kLine_Verb); 623 624#ifdef SK_SCALAR_IS_FIXED 625 // undo our previous down_shift 626 if (shift_down_2_proc == proc) { 627 // need a real shift methid on path. antialias paths could use this too 628 SkMatrix matrix; 629 matrix.setScale(SkIntToScalar(4), SkIntToScalar(4)); 630 dst->transform(matrix); 631 } 632#endif 633 634 if (fDoFill) { 635 if (src.cheapIsDirection(SkPath::kCCW_Direction)) { 636 dst->reverseAddPath(src); 637 } else { 638 dst->addPath(src); 639 } 640 } else { 641 // Seems like we can assume that a 2-point src would always result in 642 // a convex stroke, but testing has proved otherwise. 643 // TODO: fix the stroker to make this assumption true (without making 644 // it slower that the work that will be done in computeConvexity()) 645#if 0 646 // this test results in a non-convex stroke :( 647 static void test(SkCanvas* canvas) { 648 SkPoint pts[] = { 146.333328, 192.333328, 300.333344, 293.333344 }; 649 SkPaint paint; 650 paint.setStrokeWidth(7); 651 paint.setStrokeCap(SkPaint::kRound_Cap); 652 canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); 653 } 654#endif 655#if 0 656 if (2 == src.countPoints()) { 657 dst->setIsConvex(true); 658 } 659#endif 660 } 661 662 // our answer should preserve the inverseness of the src 663 if (src.isInverseFillType()) { 664 SkASSERT(!dst->isInverseFillType()); 665 dst->toggleInverseFillType(); 666 } 667} 668 669