OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrAAConvexTessellator.h" | 8 #include "GrAAConvexTessellator.h" |
9 #include "SkCanvas.h" | 9 #include "SkCanvas.h" |
10 #include "SkPath.h" | 10 #include "SkPath.h" |
(...skipping 14 matching lines...) Expand all Loading... |
25 static const SkScalar kQuadTolerance = 0.2f; | 25 static const SkScalar kQuadTolerance = 0.2f; |
26 static const SkScalar kCubicTolerance = 0.2f; | 26 static const SkScalar kCubicTolerance = 0.2f; |
27 static const SkScalar kConicTolerance = 0.5f; | 27 static const SkScalar kConicTolerance = 0.5f; |
28 | 28 |
29 // dot product below which we use a round cap between curve segments | 29 // dot product below which we use a round cap between curve segments |
30 static const SkScalar kRoundCapThreshold = 0.8f; | 30 static const SkScalar kRoundCapThreshold = 0.8f; |
31 | 31 |
32 // dot product above which we consider two adjacent curves to be part of the "sa
me" curve | 32 // dot product above which we consider two adjacent curves to be part of the "sa
me" curve |
33 static const SkScalar kCurveConnectionThreshold = 0.8f; | 33 static const SkScalar kCurveConnectionThreshold = 0.8f; |
34 | 34 |
35 static SkScalar intersect(const SkPoint& p0, const SkPoint& n0, | 35 static bool intersect(const SkPoint& p0, const SkPoint& n0, |
36 const SkPoint& p1, const SkPoint& n1) { | 36 const SkPoint& p1, const SkPoint& n1, |
| 37 SkScalar* t) { |
37 const SkPoint v = p1 - p0; | 38 const SkPoint v = p1 - p0; |
38 SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX; | 39 SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX; |
39 return (v.fX * n1.fY - v.fY * n1.fX) / perpDot; | 40 if (SkScalarNearlyZero(perpDot)) { |
| 41 return false; |
| 42 } |
| 43 *t = (v.fX * n1.fY - v.fY * n1.fX) / perpDot; |
| 44 SkASSERT(SkScalarIsFinite(*t)); |
| 45 return true; |
40 } | 46 } |
41 | 47 |
42 // This is a special case version of intersect where we have the vector | 48 // This is a special case version of intersect where we have the vector |
43 // perpendicular to the second line rather than the vector parallel to it. | 49 // perpendicular to the second line rather than the vector parallel to it. |
44 static SkScalar perp_intersect(const SkPoint& p0, const SkPoint& n0, | 50 static SkScalar perp_intersect(const SkPoint& p0, const SkPoint& n0, |
45 const SkPoint& p1, const SkPoint& perp) { | 51 const SkPoint& p1, const SkPoint& perp) { |
46 const SkPoint v = p1 - p0; | 52 const SkPoint v = p1 - p0; |
47 SkScalar perpDot = n0.dot(perp); | 53 SkScalar perpDot = n0.dot(perp); |
48 return v.dot(perp) / perpDot; | 54 return v.dot(perp) / perpDot; |
49 } | 55 } |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 // The polygon state is captured in the Ring class while the GrAAConvexTessellat
or | 217 // The polygon state is captured in the Ring class while the GrAAConvexTessellat
or |
212 // controls the iteration. The CandidateVerts holds the formative points for the | 218 // controls the iteration. The CandidateVerts holds the formative points for the |
213 // next ring. | 219 // next ring. |
214 bool GrAAConvexTessellator::tessellate(const SkMatrix& m, const SkPath& path) { | 220 bool GrAAConvexTessellator::tessellate(const SkMatrix& m, const SkPath& path) { |
215 if (!this->extractFromPath(m, path)) { | 221 if (!this->extractFromPath(m, path)) { |
216 return false; | 222 return false; |
217 } | 223 } |
218 | 224 |
219 SkScalar coverage = 1.0f; | 225 SkScalar coverage = 1.0f; |
220 SkScalar scaleFactor = 0.0f; | 226 SkScalar scaleFactor = 0.0f; |
221 if (fStrokeWidth >= 0.0f) { | 227 |
| 228 if (SkStrokeRec::kStrokeAndFill_Style == fStyle) { |
222 SkASSERT(m.isSimilarity()); | 229 SkASSERT(m.isSimilarity()); |
223 scaleFactor = m.getMaxScale(); // x and y scale are the same | 230 scaleFactor = m.getMaxScale(); // x and y scale are the same |
224 SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth; | 231 SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth; |
| 232 Ring outerStrokeAndAARing; |
| 233 this->createOuterRing(fInitialRing, |
| 234 effectiveStrokeWidth / 2 + kAntialiasingRadius, 0.
0, |
| 235 &outerStrokeAndAARing); |
| 236 |
| 237 // discard all the triangles added between the originating ring and the
new outer ring |
| 238 fIndices.rewind(); |
| 239 |
| 240 outerStrokeAndAARing.init(*this); |
| 241 |
| 242 outerStrokeAndAARing.makeOriginalRing(); |
| 243 |
| 244 // Add the outer stroke ring's normals to the originating ring's normals |
| 245 // so it can also act as an originating ring |
| 246 fNorms.setCount(fNorms.count() + outerStrokeAndAARing.numPts()); |
| 247 for (int i = 0; i < outerStrokeAndAARing.numPts(); ++i) { |
| 248 SkASSERT(outerStrokeAndAARing.index(i) < fNorms.count()); |
| 249 fNorms[outerStrokeAndAARing.index(i)] = outerStrokeAndAARing.norm(i)
; |
| 250 } |
| 251 |
| 252 // the bisectors are only needed for the computation of the outer ring |
| 253 fBisectors.rewind(); |
| 254 |
| 255 Ring* insetAARing; |
| 256 this->createInsetRings(outerStrokeAndAARing, |
| 257 0.0f, 0.0f, 2*kAntialiasingRadius, 1.0f, |
| 258 &insetAARing); |
| 259 |
| 260 SkDEBUGCODE(this->validate();) |
| 261 return true; |
| 262 } |
| 263 |
| 264 if (SkStrokeRec::kStroke_Style == fStyle) { |
| 265 SkASSERT(fStrokeWidth >= 0.0f); |
| 266 SkASSERT(m.isSimilarity()); |
| 267 scaleFactor = m.getMaxScale(); // x and y scale are the same |
| 268 SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth; |
225 Ring outerStrokeRing; | 269 Ring outerStrokeRing; |
226 this->createOuterRing(fInitialRing, effectiveStrokeWidth / 2 - kAntialia
singRadius, | 270 this->createOuterRing(fInitialRing, effectiveStrokeWidth / 2 - kAntialia
singRadius, |
227 coverage, &outerStrokeRing); | 271 coverage, &outerStrokeRing); |
228 outerStrokeRing.init(*this); | 272 outerStrokeRing.init(*this); |
229 Ring outerAARing; | 273 Ring outerAARing; |
230 this->createOuterRing(outerStrokeRing, kAntialiasingRadius * 2, 0.0f, &o
uterAARing); | 274 this->createOuterRing(outerStrokeRing, kAntialiasingRadius * 2, 0.0f, &o
uterAARing); |
231 } else { | 275 } else { |
232 Ring outerAARing; | 276 Ring outerAARing; |
233 this->createOuterRing(fInitialRing, kAntialiasingRadius, 0.0f, &outerAAR
ing); | 277 this->createOuterRing(fInitialRing, kAntialiasingRadius, 0.0f, &outerAAR
ing); |
234 } | 278 } |
235 | 279 |
236 // the bisectors are only needed for the computation of the outer ring | 280 // the bisectors are only needed for the computation of the outer ring |
237 fBisectors.rewind(); | 281 fBisectors.rewind(); |
238 if (fStrokeWidth >= 0.0f && fInitialRing.numPts() > 2) { | 282 if (SkStrokeRec::kStroke_Style == fStyle && fInitialRing.numPts() > 2) { |
| 283 SkASSERT(fStrokeWidth >= 0.0f); |
239 SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth; | 284 SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth; |
240 Ring* insetStrokeRing; | 285 Ring* insetStrokeRing; |
241 SkScalar strokeDepth = effectiveStrokeWidth / 2 - kAntialiasingRadius; | 286 SkScalar strokeDepth = effectiveStrokeWidth / 2 - kAntialiasingRadius; |
242 if (this->createInsetRings(fInitialRing, 0.0f, coverage, strokeDepth, co
verage, | 287 if (this->createInsetRings(fInitialRing, 0.0f, coverage, strokeDepth, co
verage, |
243 &insetStrokeRing)) { | 288 &insetStrokeRing)) { |
244 Ring* insetAARing; | 289 Ring* insetAARing; |
245 this->createInsetRings(*insetStrokeRing, strokeDepth, coverage, stro
keDepth + | 290 this->createInsetRings(*insetStrokeRing, strokeDepth, coverage, stro
keDepth + |
246 kAntialiasingRadius * 2, 0.0f, &insetAARing); | 291 kAntialiasingRadius * 2, 0.0f, &insetAARing); |
247 } | 292 } |
248 } else { | 293 } else { |
249 Ring* insetAARing; | 294 Ring* insetAARing; |
250 this->createInsetRings(fInitialRing, 0.0f, 0.5f, kAntialiasingRadius, 1.
0f, &insetAARing); | 295 this->createInsetRings(fInitialRing, 0.0f, 0.5f, kAntialiasingRadius, 1.
0f, &insetAARing); |
251 } | 296 } |
252 | 297 |
253 SkDEBUGCODE(this->validate();) | 298 SkDEBUGCODE(this->validate();) |
254 return true; | 299 return true; |
255 } | 300 } |
256 | 301 |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 | 428 |
384 // Make all the normals face outwards rather than along the edge | 429 // Make all the normals face outwards rather than along the edge |
385 for (int cur = 0; cur < fNorms.count(); ++cur) { | 430 for (int cur = 0; cur < fNorms.count(); ++cur) { |
386 fNorms[cur].setOrthog(fNorms[cur], fSide); | 431 fNorms[cur].setOrthog(fNorms[cur], fSide); |
387 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length())); | 432 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length())); |
388 } | 433 } |
389 | 434 |
390 this->computeBisectors(); | 435 this->computeBisectors(); |
391 } else if (this->numPts() == 2) { | 436 } else if (this->numPts() == 2) { |
392 // We've got two points, so we're degenerate. | 437 // We've got two points, so we're degenerate. |
393 if (fStrokeWidth < 0.0f) { | 438 if (fStyle == SkStrokeRec::kFill_Style) { |
394 // it's a fill, so we don't need to worry about degenerate paths | 439 // it's a fill, so we don't need to worry about degenerate paths |
395 return false; | 440 return false; |
396 } | 441 } |
397 // For stroking, we still need to process the degenerate path, so fix it
up | 442 // For stroking, we still need to process the degenerate path, so fix it
up |
398 fSide = SkPoint::kLeft_Side; | 443 fSide = SkPoint::kLeft_Side; |
399 | 444 |
400 // Make all the normals face outwards rather than along the edge | 445 // Make all the normals face outwards rather than along the edge |
401 for (int cur = 0; cur < fNorms.count(); ++cur) { | 446 for (int cur = 0; cur < fNorms.count(); ++cur) { |
402 fNorms[cur].setOrthog(fNorms[cur], fSide); | 447 fNorms[cur].setOrthog(fNorms[cur], fSide); |
403 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length())); | 448 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length())); |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 int lastIdx = previousRing.index(numPts - 1); | 624 int lastIdx = previousRing.index(numPts - 1); |
580 this->addTri(lastIdx, firstPerpIdx, previousRing.index(0)); | 625 this->addTri(lastIdx, firstPerpIdx, previousRing.index(0)); |
581 this->addTri(lastIdx, lastPerpIdx, firstPerpIdx); | 626 this->addTri(lastIdx, lastPerpIdx, firstPerpIdx); |
582 | 627 |
583 this->validate(); | 628 this->validate(); |
584 } | 629 } |
585 | 630 |
586 // Something went wrong in the creation of the next ring. If we're filling the s
hape, just go ahead | 631 // Something went wrong in the creation of the next ring. If we're filling the s
hape, just go ahead |
587 // and fan it. | 632 // and fan it. |
588 void GrAAConvexTessellator::terminate(const Ring& ring) { | 633 void GrAAConvexTessellator::terminate(const Ring& ring) { |
589 if (fStrokeWidth < 0.0f) { | 634 if (fStyle != SkStrokeRec::kStroke_Style) { |
590 this->fanRing(ring); | 635 this->fanRing(ring); |
591 } | 636 } |
592 } | 637 } |
593 | 638 |
594 static SkScalar compute_coverage(SkScalar depth, SkScalar initialDepth, SkScalar
initialCoverage, | 639 static SkScalar compute_coverage(SkScalar depth, SkScalar initialDepth, SkScalar
initialCoverage, |
595 SkScalar targetDepth, SkScalar targetCoverage) { | 640 SkScalar targetDepth, SkScalar targetCoverage) { |
596 if (SkScalarNearlyEqual(initialDepth, targetDepth)) { | 641 if (SkScalarNearlyEqual(initialDepth, targetDepth)) { |
597 return targetCoverage; | 642 return targetCoverage; |
598 } | 643 } |
599 SkScalar result = (depth - initialDepth) / (targetDepth - initialDepth) * | 644 SkScalar result = (depth - initialDepth) / (targetDepth - initialDepth) * |
600 (targetCoverage - initialCoverage) + initialCoverage; | 645 (targetCoverage - initialCoverage) + initialCoverage; |
601 return SkScalarClampMax(result, 1.0f); | 646 return SkScalarClampMax(result, 1.0f); |
602 } | 647 } |
603 | 648 |
604 // return true when processing is complete | 649 // return true when processing is complete |
605 bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing
, | 650 bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing
, |
606 SkScalar initialDepth, SkScalar init
ialCoverage, | 651 SkScalar initialDepth, SkScalar init
ialCoverage, |
607 SkScalar targetDepth, SkScalar targe
tCoverage, | 652 SkScalar targetDepth, SkScalar targe
tCoverage, |
608 bool forceNew) { | 653 bool forceNew) { |
609 bool done = false; | 654 bool done = false; |
610 | 655 |
611 fCandidateVerts.rewind(); | 656 fCandidateVerts.rewind(); |
612 | 657 |
613 // Loop through all the points in the ring and find the intersection with th
e smallest depth | 658 // Loop through all the points in the ring and find the intersection with th
e smallest depth |
614 SkScalar minDist = SK_ScalarMax, minT = 0.0f; | 659 SkScalar minDist = SK_ScalarMax, minT = 0.0f; |
615 int minEdgeIdx = -1; | 660 int minEdgeIdx = -1; |
616 | 661 |
617 for (int cur = 0; cur < lastRing.numPts(); ++cur) { | 662 for (int cur = 0; cur < lastRing.numPts(); ++cur) { |
618 int next = (cur + 1) % lastRing.numPts(); | 663 int next = (cur + 1) % lastRing.numPts(); |
619 SkScalar t = intersect(this->point(lastRing.index(cur)), lastRing.bisec
tor(cur), | 664 |
620 this->point(lastRing.index(next)), lastRing.bisec
tor(next)); | 665 SkScalar t; |
| 666 bool result = intersect(this->point(lastRing.index(cur)), lastRing.bise
ctor(cur), |
| 667 this->point(lastRing.index(next)), lastRing.bise
ctor(next), |
| 668 &t); |
| 669 if (!result) { |
| 670 continue; |
| 671 } |
621 SkScalar dist = -t * lastRing.norm(cur).dot(lastRing.bisector(cur)); | 672 SkScalar dist = -t * lastRing.norm(cur).dot(lastRing.bisector(cur)); |
622 | 673 |
623 if (minDist > dist) { | 674 if (minDist > dist) { |
624 minDist = dist; | 675 minDist = dist; |
625 minT = t; | 676 minT = t; |
626 minEdgeIdx = cur; | 677 minEdgeIdx = cur; |
627 } | 678 } |
628 } | 679 } |
629 | 680 |
630 if (minEdgeIdx == -1) { | 681 if (minEdgeIdx == -1) { |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
738 dst[i] = nextRing->index(dst[i]); | 789 dst[i] = nextRing->index(dst[i]); |
739 } | 790 } |
740 | 791 |
741 for (int i = 0; i < lastRing.numPts(); ++i) { | 792 for (int i = 0; i < lastRing.numPts(); ++i) { |
742 int next = (i + 1) % lastRing.numPts(); | 793 int next = (i + 1) % lastRing.numPts(); |
743 | 794 |
744 this->addTri(lastRing.index(i), lastRing.index(next), dst[next]); | 795 this->addTri(lastRing.index(i), lastRing.index(next), dst[next]); |
745 this->addTri(lastRing.index(i), dst[next], dst[i]); | 796 this->addTri(lastRing.index(i), dst[next], dst[i]); |
746 } | 797 } |
747 | 798 |
748 if (done && fStrokeWidth < 0.0f) { | 799 if (done && fStyle != SkStrokeRec::kStroke_Style) { |
749 // fill | 800 // fill or stroke-and-fill |
750 this->fanRing(*nextRing); | 801 this->fanRing(*nextRing); |
751 } | 802 } |
752 | 803 |
753 if (nextRing->numPts() < 3) { | 804 if (nextRing->numPts() < 3) { |
754 done = true; | 805 done = true; |
755 } | 806 } |
756 return done; | 807 return done; |
757 } | 808 } |
758 | 809 |
759 void GrAAConvexTessellator::validate() const { | 810 void GrAAConvexTessellator::validate() const { |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
853 fNorms.pop(); | 904 fNorms.pop(); |
854 fCurveState.pop(); | 905 fCurveState.pop(); |
855 // double-check that the new last point is not a duplicate of the new po
int. In an ideal | 906 // double-check that the new last point is not a duplicate of the new po
int. In an ideal |
856 // world this wouldn't be necessary (since it's only possible for non-co
nvex paths), but | 907 // world this wouldn't be necessary (since it's only possible for non-co
nvex paths), but |
857 // floating point precision issues mean it can actually happen on paths
that were determined | 908 // floating point precision issues mean it can actually happen on paths
that were determined |
858 // to be convex. | 909 // to be convex. |
859 if (duplicate_pt(p, this->lastPoint())) { | 910 if (duplicate_pt(p, this->lastPoint())) { |
860 return; | 911 return; |
861 } | 912 } |
862 } | 913 } |
863 SkScalar initialRingCoverage = fStrokeWidth < 0.0f ? 0.5f : 1.0f; | 914 SkScalar initialRingCoverage = (SkStrokeRec::kFill_Style == fStyle) ? 0.5f :
1.0f; |
864 this->addPt(p, 0.0f, initialRingCoverage, false, curve); | 915 this->addPt(p, 0.0f, initialRingCoverage, false, curve); |
865 if (this->numPts() > 1) { | 916 if (this->numPts() > 1) { |
866 *fNorms.push() = fPts.top() - fPts[fPts.count()-2]; | 917 *fNorms.push() = fPts.top() - fPts[fPts.count()-2]; |
867 SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top()); | 918 SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top()); |
868 SkASSERT(len > 0.0f); | 919 SkASSERT(len > 0.0f); |
869 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length())); | 920 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length())); |
870 } | 921 } |
871 } | 922 } |
872 | 923 |
873 void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, CurveState curv
e) { | 924 void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, CurveState curv
e) { |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1043 | 1094 |
1044 SkString num; | 1095 SkString num; |
1045 num.printf("%d", i); | 1096 num.printf("%d", i); |
1046 canvas->drawText(num.c_str(), num.size(), | 1097 canvas->drawText(num.c_str(), num.size(), |
1047 this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f
), | 1098 this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f
), |
1048 paint); | 1099 paint); |
1049 } | 1100 } |
1050 } | 1101 } |
1051 | 1102 |
1052 #endif | 1103 #endif |
OLD | NEW |