Line data Source code
1 : /*
2 : * Copyright 2015 Google Inc.
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 "GrAAConvexTessellator.h"
9 : #include "SkCanvas.h"
10 : #include "SkPath.h"
11 : #include "SkPoint.h"
12 : #include "SkString.h"
13 : #include "GrPathUtils.h"
14 :
15 : // Next steps:
16 : // add an interactive sample app slide
17 : // add debug check that all points are suitably far apart
18 : // test more degenerate cases
19 :
20 : // The tolerance for fusing vertices and eliminating colinear lines (It is in device space).
21 : static const SkScalar kClose = (SK_Scalar1 / 16);
22 : static const SkScalar kCloseSqd = kClose * kClose;
23 :
24 : // tesselation tolerance values, in device space pixels
25 : static const SkScalar kQuadTolerance = 0.2f;
26 : static const SkScalar kCubicTolerance = 0.2f;
27 : static const SkScalar kConicTolerance = 0.5f;
28 :
29 : // dot product below which we use a round cap between curve segments
30 : static const SkScalar kRoundCapThreshold = 0.8f;
31 :
32 : // dot product above which we consider two adjacent curves to be part of the "same" curve
33 : static const SkScalar kCurveConnectionThreshold = 0.8f;
34 :
35 0 : static bool intersect(const SkPoint& p0, const SkPoint& n0,
36 : const SkPoint& p1, const SkPoint& n1,
37 : SkScalar* t) {
38 0 : const SkPoint v = p1 - p0;
39 0 : SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX;
40 0 : if (SkScalarNearlyZero(perpDot)) {
41 0 : return false;
42 : }
43 0 : *t = (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
44 0 : SkASSERT(SkScalarIsFinite(*t));
45 0 : return true;
46 : }
47 :
48 : // This is a special case version of intersect where we have the vector
49 : // perpendicular to the second line rather than the vector parallel to it.
50 0 : static SkScalar perp_intersect(const SkPoint& p0, const SkPoint& n0,
51 : const SkPoint& p1, const SkPoint& perp) {
52 0 : const SkPoint v = p1 - p0;
53 0 : SkScalar perpDot = n0.dot(perp);
54 0 : return v.dot(perp) / perpDot;
55 : }
56 :
57 0 : static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
58 0 : SkScalar distSq = p0.distanceToSqd(p1);
59 0 : return distSq < kCloseSqd;
60 : }
61 :
62 0 : static SkScalar abs_dist_from_line(const SkPoint& p0, const SkVector& v, const SkPoint& test) {
63 0 : SkPoint testV = test - p0;
64 0 : SkScalar dist = testV.fX * v.fY - testV.fY * v.fX;
65 0 : return SkScalarAbs(dist);
66 : }
67 :
68 0 : int GrAAConvexTessellator::addPt(const SkPoint& pt,
69 : SkScalar depth,
70 : SkScalar coverage,
71 : bool movable,
72 : CurveState curve) {
73 0 : this->validate();
74 :
75 0 : int index = fPts.count();
76 0 : *fPts.push() = pt;
77 0 : *fCoverages.push() = coverage;
78 0 : *fMovable.push() = movable;
79 0 : *fCurveState.push() = curve;
80 :
81 0 : this->validate();
82 0 : return index;
83 : }
84 :
85 0 : void GrAAConvexTessellator::popLastPt() {
86 0 : this->validate();
87 :
88 0 : fPts.pop();
89 0 : fCoverages.pop();
90 0 : fMovable.pop();
91 0 : fCurveState.pop();
92 :
93 0 : this->validate();
94 0 : }
95 :
96 0 : void GrAAConvexTessellator::popFirstPtShuffle() {
97 0 : this->validate();
98 :
99 0 : fPts.removeShuffle(0);
100 0 : fCoverages.removeShuffle(0);
101 0 : fMovable.removeShuffle(0);
102 0 : fCurveState.removeShuffle(0);
103 :
104 0 : this->validate();
105 0 : }
106 :
107 0 : void GrAAConvexTessellator::updatePt(int index,
108 : const SkPoint& pt,
109 : SkScalar depth,
110 : SkScalar coverage) {
111 0 : this->validate();
112 0 : SkASSERT(fMovable[index]);
113 :
114 0 : fPts[index] = pt;
115 0 : fCoverages[index] = coverage;
116 0 : }
117 :
118 0 : void GrAAConvexTessellator::addTri(int i0, int i1, int i2) {
119 0 : if (i0 == i1 || i1 == i2 || i2 == i0) {
120 0 : return;
121 : }
122 :
123 0 : *fIndices.push() = i0;
124 0 : *fIndices.push() = i1;
125 0 : *fIndices.push() = i2;
126 : }
127 :
128 0 : void GrAAConvexTessellator::rewind() {
129 0 : fPts.rewind();
130 0 : fCoverages.rewind();
131 0 : fMovable.rewind();
132 0 : fIndices.rewind();
133 0 : fNorms.rewind();
134 0 : fCurveState.rewind();
135 0 : fInitialRing.rewind();
136 0 : fCandidateVerts.rewind();
137 : #if GR_AA_CONVEX_TESSELLATOR_VIZ
138 : fRings.rewind(); // TODO: leak in this case!
139 : #else
140 0 : fRings[0].rewind();
141 0 : fRings[1].rewind();
142 : #endif
143 0 : }
144 :
145 0 : void GrAAConvexTessellator::computeBisectors() {
146 0 : fBisectors.setCount(fNorms.count());
147 :
148 0 : int prev = fBisectors.count() - 1;
149 0 : for (int cur = 0; cur < fBisectors.count(); prev = cur, ++cur) {
150 0 : fBisectors[cur] = fNorms[cur] + fNorms[prev];
151 0 : if (!fBisectors[cur].normalize()) {
152 0 : SkASSERT(SkPoint::kLeft_Side == fSide || SkPoint::kRight_Side == fSide);
153 0 : fBisectors[cur].setOrthog(fNorms[cur], (SkPoint::Side)-fSide);
154 : SkVector other;
155 0 : other.setOrthog(fNorms[prev], fSide);
156 0 : fBisectors[cur] += other;
157 0 : SkAssertResult(fBisectors[cur].normalize());
158 : } else {
159 0 : fBisectors[cur].negate(); // make the bisector face in
160 : }
161 0 : if (fCurveState[prev] == kIndeterminate_CurveState) {
162 0 : if (fCurveState[cur] == kSharp_CurveState) {
163 0 : fCurveState[prev] = kSharp_CurveState;
164 : } else {
165 0 : if (SkScalarAbs(fNorms[cur].dot(fNorms[prev])) > kCurveConnectionThreshold) {
166 0 : fCurveState[prev] = kCurve_CurveState;
167 0 : fCurveState[cur] = kCurve_CurveState;
168 : } else {
169 0 : fCurveState[prev] = kSharp_CurveState;
170 0 : fCurveState[cur] = kSharp_CurveState;
171 : }
172 : }
173 : }
174 :
175 0 : SkASSERT(SkScalarNearlyEqual(1.0f, fBisectors[cur].length()));
176 : }
177 0 : }
178 :
179 : // Create as many rings as we need to (up to a predefined limit) to reach the specified target
180 : // depth. If we are in fill mode, the final ring will automatically be fanned.
181 0 : bool GrAAConvexTessellator::createInsetRings(Ring& previousRing, SkScalar initialDepth,
182 : SkScalar initialCoverage, SkScalar targetDepth,
183 : SkScalar targetCoverage, Ring** finalRing) {
184 : static const int kMaxNumRings = 8;
185 :
186 0 : if (previousRing.numPts() < 3) {
187 0 : return false;
188 : }
189 0 : Ring* currentRing = &previousRing;
190 : int i;
191 0 : for (i = 0; i < kMaxNumRings; ++i) {
192 0 : Ring* nextRing = this->getNextRing(currentRing);
193 0 : SkASSERT(nextRing != currentRing);
194 :
195 0 : bool done = this->createInsetRing(*currentRing, nextRing, initialDepth, initialCoverage,
196 0 : targetDepth, targetCoverage, i == 0);
197 0 : currentRing = nextRing;
198 0 : if (done) {
199 0 : break;
200 : }
201 0 : currentRing->init(*this);
202 : }
203 :
204 0 : if (kMaxNumRings == i) {
205 : // Bail if we've exceeded the amount of time we want to throw at this.
206 0 : this->terminate(*currentRing);
207 0 : return false;
208 : }
209 0 : bool done = currentRing->numPts() >= 3;
210 0 : if (done) {
211 0 : currentRing->init(*this);
212 : }
213 0 : *finalRing = currentRing;
214 0 : return done;
215 : }
216 :
217 : // The general idea here is to, conceptually, start with the original polygon and slide
218 : // the vertices along the bisectors until the first intersection. At that
219 : // point two of the edges collapse and the process repeats on the new polygon.
220 : // The polygon state is captured in the Ring class while the GrAAConvexTessellator
221 : // controls the iteration. The CandidateVerts holds the formative points for the
222 : // next ring.
223 0 : bool GrAAConvexTessellator::tessellate(const SkMatrix& m, const SkPath& path) {
224 0 : if (!this->extractFromPath(m, path)) {
225 0 : return false;
226 : }
227 :
228 0 : SkScalar coverage = 1.0f;
229 0 : SkScalar scaleFactor = 0.0f;
230 :
231 0 : if (SkStrokeRec::kStrokeAndFill_Style == fStyle) {
232 0 : SkASSERT(m.isSimilarity());
233 0 : scaleFactor = m.getMaxScale(); // x and y scale are the same
234 0 : SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth;
235 0 : Ring outerStrokeAndAARing;
236 0 : this->createOuterRing(fInitialRing,
237 0 : effectiveStrokeWidth / 2 + kAntialiasingRadius, 0.0,
238 0 : &outerStrokeAndAARing);
239 :
240 : // discard all the triangles added between the originating ring and the new outer ring
241 0 : fIndices.rewind();
242 :
243 0 : outerStrokeAndAARing.init(*this);
244 :
245 0 : outerStrokeAndAARing.makeOriginalRing();
246 :
247 : // Add the outer stroke ring's normals to the originating ring's normals
248 : // so it can also act as an originating ring
249 0 : fNorms.setCount(fNorms.count() + outerStrokeAndAARing.numPts());
250 0 : for (int i = 0; i < outerStrokeAndAARing.numPts(); ++i) {
251 0 : SkASSERT(outerStrokeAndAARing.index(i) < fNorms.count());
252 0 : fNorms[outerStrokeAndAARing.index(i)] = outerStrokeAndAARing.norm(i);
253 : }
254 :
255 : // the bisectors are only needed for the computation of the outer ring
256 0 : fBisectors.rewind();
257 :
258 : Ring* insetAARing;
259 : this->createInsetRings(outerStrokeAndAARing,
260 : 0.0f, 0.0f, 2*kAntialiasingRadius, 1.0f,
261 0 : &insetAARing);
262 :
263 0 : SkDEBUGCODE(this->validate();)
264 0 : return true;
265 : }
266 :
267 0 : if (SkStrokeRec::kStroke_Style == fStyle) {
268 0 : SkASSERT(fStrokeWidth >= 0.0f);
269 0 : SkASSERT(m.isSimilarity());
270 0 : scaleFactor = m.getMaxScale(); // x and y scale are the same
271 0 : SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth;
272 0 : Ring outerStrokeRing;
273 0 : this->createOuterRing(fInitialRing, effectiveStrokeWidth / 2 - kAntialiasingRadius,
274 0 : coverage, &outerStrokeRing);
275 0 : outerStrokeRing.init(*this);
276 0 : Ring outerAARing;
277 0 : this->createOuterRing(outerStrokeRing, kAntialiasingRadius * 2, 0.0f, &outerAARing);
278 : } else {
279 0 : Ring outerAARing;
280 0 : this->createOuterRing(fInitialRing, kAntialiasingRadius, 0.0f, &outerAARing);
281 : }
282 :
283 : // the bisectors are only needed for the computation of the outer ring
284 0 : fBisectors.rewind();
285 0 : if (SkStrokeRec::kStroke_Style == fStyle && fInitialRing.numPts() > 2) {
286 0 : SkASSERT(fStrokeWidth >= 0.0f);
287 0 : SkScalar effectiveStrokeWidth = scaleFactor * fStrokeWidth;
288 : Ring* insetStrokeRing;
289 0 : SkScalar strokeDepth = effectiveStrokeWidth / 2 - kAntialiasingRadius;
290 0 : if (this->createInsetRings(fInitialRing, 0.0f, coverage, strokeDepth, coverage,
291 : &insetStrokeRing)) {
292 : Ring* insetAARing;
293 0 : this->createInsetRings(*insetStrokeRing, strokeDepth, coverage, strokeDepth +
294 0 : kAntialiasingRadius * 2, 0.0f, &insetAARing);
295 : }
296 : } else {
297 : Ring* insetAARing;
298 0 : this->createInsetRings(fInitialRing, 0.0f, 0.5f, kAntialiasingRadius, 1.0f, &insetAARing);
299 : }
300 :
301 0 : SkDEBUGCODE(this->validate();)
302 0 : return true;
303 : }
304 :
305 0 : SkScalar GrAAConvexTessellator::computeDepthFromEdge(int edgeIdx, const SkPoint& p) const {
306 0 : SkASSERT(edgeIdx < fNorms.count());
307 :
308 0 : SkPoint v = p - fPts[edgeIdx];
309 0 : SkScalar depth = -fNorms[edgeIdx].dot(v);
310 0 : return depth;
311 : }
312 :
313 : // Find a point that is 'desiredDepth' away from the 'edgeIdx'-th edge and lies
314 : // along the 'bisector' from the 'startIdx'-th point.
315 0 : bool GrAAConvexTessellator::computePtAlongBisector(int startIdx,
316 : const SkVector& bisector,
317 : int edgeIdx,
318 : SkScalar desiredDepth,
319 : SkPoint* result) const {
320 0 : const SkPoint& norm = fNorms[edgeIdx];
321 :
322 : // First find the point where the edge and the bisector intersect
323 : SkPoint newP;
324 :
325 0 : SkScalar t = perp_intersect(fPts[startIdx], bisector, fPts[edgeIdx], norm);
326 0 : if (SkScalarNearlyEqual(t, 0.0f)) {
327 : // the start point was one of the original ring points
328 0 : SkASSERT(startIdx < fPts.count());
329 0 : newP = fPts[startIdx];
330 0 : } else if (t < 0.0f) {
331 0 : newP = bisector;
332 0 : newP.scale(t);
333 0 : newP += fPts[startIdx];
334 : } else {
335 0 : return false;
336 : }
337 :
338 : // Then offset along the bisector from that point the correct distance
339 0 : SkScalar dot = bisector.dot(norm);
340 0 : t = -desiredDepth / dot;
341 0 : *result = bisector;
342 0 : result->scale(t);
343 0 : *result += newP;
344 :
345 0 : return true;
346 : }
347 :
348 0 : bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) {
349 0 : SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
350 :
351 : // Outer ring: 3*numPts
352 : // Middle ring: numPts
353 : // Presumptive inner ring: numPts
354 0 : this->reservePts(5*path.countPoints());
355 : // Outer ring: 12*numPts
356 : // Middle ring: 0
357 : // Presumptive inner ring: 6*numPts + 6
358 0 : fIndices.setReserve(18*path.countPoints() + 6);
359 :
360 0 : fNorms.setReserve(path.countPoints());
361 :
362 : // TODO: is there a faster way to extract the points from the path? Perhaps
363 : // get all the points via a new entry point, transform them all in bulk
364 : // and then walk them to find duplicates?
365 0 : SkPath::Iter iter(path, true);
366 : SkPoint pts[4];
367 : SkPath::Verb verb;
368 0 : while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) {
369 0 : switch (verb) {
370 : case SkPath::kLine_Verb:
371 0 : this->lineTo(m, pts[1], kSharp_CurveState);
372 0 : break;
373 : case SkPath::kQuad_Verb:
374 0 : this->quadTo(m, pts);
375 0 : break;
376 : case SkPath::kCubic_Verb:
377 0 : this->cubicTo(m, pts);
378 0 : break;
379 : case SkPath::kConic_Verb:
380 0 : this->conicTo(m, pts, iter.conicWeight());
381 0 : break;
382 : case SkPath::kMove_Verb:
383 : case SkPath::kClose_Verb:
384 : case SkPath::kDone_Verb:
385 0 : break;
386 : }
387 : }
388 :
389 0 : if (this->numPts() < 2) {
390 0 : return false;
391 : }
392 :
393 : // check if last point is a duplicate of the first point. If so, remove it.
394 0 : if (duplicate_pt(fPts[this->numPts()-1], fPts[0])) {
395 0 : this->popLastPt();
396 0 : fNorms.pop();
397 : }
398 :
399 0 : SkASSERT(fPts.count() == fNorms.count()+1);
400 0 : if (this->numPts() >= 3) {
401 0 : if (abs_dist_from_line(fPts.top(), fNorms.top(), fPts[0]) < kClose) {
402 : // The last point is on the line from the second to last to the first point.
403 0 : this->popLastPt();
404 0 : fNorms.pop();
405 : }
406 :
407 0 : *fNorms.push() = fPts[0] - fPts.top();
408 0 : SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
409 0 : SkASSERT(len > 0.0f);
410 0 : SkASSERT(fPts.count() == fNorms.count());
411 : }
412 :
413 0 : if (this->numPts() >= 3 && abs_dist_from_line(fPts[0], fNorms.top(), fPts[1]) < kClose) {
414 : // The first point is on the line from the last to the second.
415 0 : this->popFirstPtShuffle();
416 0 : fNorms.removeShuffle(0);
417 0 : fNorms[0] = fPts[1] - fPts[0];
418 0 : SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms[0]);
419 0 : SkASSERT(len > 0.0f);
420 0 : SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[0].length()));
421 : }
422 :
423 0 : if (this->numPts() >= 3) {
424 : // Check the cross product of the final trio
425 0 : SkScalar cross = SkPoint::CrossProduct(fNorms[0], fNorms.top());
426 0 : if (cross > 0.0f) {
427 0 : fSide = SkPoint::kRight_Side;
428 : } else {
429 0 : fSide = SkPoint::kLeft_Side;
430 : }
431 :
432 : // Make all the normals face outwards rather than along the edge
433 0 : for (int cur = 0; cur < fNorms.count(); ++cur) {
434 0 : fNorms[cur].setOrthog(fNorms[cur], fSide);
435 0 : SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length()));
436 : }
437 :
438 0 : this->computeBisectors();
439 0 : } else if (this->numPts() == 2) {
440 : // We've got two points, so we're degenerate.
441 0 : if (fStyle == SkStrokeRec::kFill_Style) {
442 : // it's a fill, so we don't need to worry about degenerate paths
443 0 : return false;
444 : }
445 : // For stroking, we still need to process the degenerate path, so fix it up
446 0 : fSide = SkPoint::kLeft_Side;
447 :
448 : // Make all the normals face outwards rather than along the edge
449 0 : for (int cur = 0; cur < fNorms.count(); ++cur) {
450 0 : fNorms[cur].setOrthog(fNorms[cur], fSide);
451 0 : SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length()));
452 : }
453 :
454 0 : fNorms.push(SkPoint::Make(-fNorms[0].fX, -fNorms[0].fY));
455 : // we won't actually use the bisectors, so just push zeroes
456 0 : fBisectors.push(SkPoint::Make(0.0, 0.0));
457 0 : fBisectors.push(SkPoint::Make(0.0, 0.0));
458 : } else {
459 0 : return false;
460 : }
461 :
462 0 : fCandidateVerts.setReserve(this->numPts());
463 0 : fInitialRing.setReserve(this->numPts());
464 0 : for (int i = 0; i < this->numPts(); ++i) {
465 0 : fInitialRing.addIdx(i, i);
466 : }
467 0 : fInitialRing.init(fNorms, fBisectors);
468 :
469 0 : this->validate();
470 0 : return true;
471 : }
472 :
473 0 : GrAAConvexTessellator::Ring* GrAAConvexTessellator::getNextRing(Ring* lastRing) {
474 : #if GR_AA_CONVEX_TESSELLATOR_VIZ
475 : Ring* ring = *fRings.push() = new Ring;
476 : ring->setReserve(fInitialRing.numPts());
477 : ring->rewind();
478 : return ring;
479 : #else
480 : // Flip flop back and forth between fRings[0] & fRings[1]
481 0 : int nextRing = (lastRing == &fRings[0]) ? 1 : 0;
482 0 : fRings[nextRing].setReserve(fInitialRing.numPts());
483 0 : fRings[nextRing].rewind();
484 0 : return &fRings[nextRing];
485 : #endif
486 : }
487 :
488 0 : void GrAAConvexTessellator::fanRing(const Ring& ring) {
489 : // fan out from point 0
490 0 : int startIdx = ring.index(0);
491 0 : for (int cur = ring.numPts() - 2; cur >= 0; --cur) {
492 0 : this->addTri(startIdx, ring.index(cur), ring.index(cur + 1));
493 : }
494 0 : }
495 :
496 0 : void GrAAConvexTessellator::createOuterRing(const Ring& previousRing, SkScalar outset,
497 : SkScalar coverage, Ring* nextRing) {
498 0 : const int numPts = previousRing.numPts();
499 0 : if (numPts == 0) {
500 0 : return;
501 : }
502 :
503 0 : int prev = numPts - 1;
504 0 : int lastPerpIdx = -1, firstPerpIdx = -1;
505 :
506 0 : const SkScalar outsetSq = outset * outset;
507 0 : SkScalar miterLimitSq = outset * fMiterLimit;
508 0 : miterLimitSq = miterLimitSq * miterLimitSq;
509 0 : for (int cur = 0; cur < numPts; ++cur) {
510 0 : int originalIdx = previousRing.index(cur);
511 : // For each vertex of the original polygon we add at least two points to the
512 : // outset polygon - one extending perpendicular to each impinging edge. Connecting these
513 : // two points yields a bevel join. We need one additional point for a mitered join, and
514 : // a round join requires one or more points depending upon curvature.
515 :
516 : // The perpendicular point for the last edge
517 0 : SkPoint normal1 = previousRing.norm(prev);
518 0 : SkPoint perp1 = normal1;
519 0 : perp1.scale(outset);
520 0 : perp1 += this->point(originalIdx);
521 :
522 : // The perpendicular point for the next edge.
523 0 : SkPoint normal2 = previousRing.norm(cur);
524 0 : SkPoint perp2 = normal2;
525 0 : perp2.scale(outset);
526 0 : perp2 += fPts[originalIdx];
527 :
528 0 : CurveState curve = fCurveState[originalIdx];
529 :
530 : // We know it isn't a duplicate of the prior point (since it and this
531 : // one are just perpendicular offsets from the non-merged polygon points)
532 0 : int perp1Idx = this->addPt(perp1, -outset, coverage, false, curve);
533 0 : nextRing->addIdx(perp1Idx, originalIdx);
534 :
535 : int perp2Idx;
536 : // For very shallow angles all the corner points could fuse.
537 0 : if (duplicate_pt(perp2, this->point(perp1Idx))) {
538 0 : perp2Idx = perp1Idx;
539 : } else {
540 0 : perp2Idx = this->addPt(perp2, -outset, coverage, false, curve);
541 : }
542 :
543 0 : if (perp2Idx != perp1Idx) {
544 0 : if (curve == kCurve_CurveState) {
545 : // bevel or round depending upon curvature
546 0 : SkScalar dotProd = normal1.dot(normal2);
547 0 : if (dotProd < kRoundCapThreshold) {
548 : // Currently we "round" by creating a single extra point, which produces
549 : // good results for common cases. For thick strokes with high curvature, we will
550 : // need to add more points; for the time being we simply fall back to software
551 : // rendering for thick strokes.
552 0 : SkPoint miter = previousRing.bisector(cur);
553 0 : miter.setLength(-outset);
554 0 : miter += fPts[originalIdx];
555 :
556 : // For very shallow angles all the corner points could fuse
557 0 : if (!duplicate_pt(miter, this->point(perp1Idx))) {
558 : int miterIdx;
559 0 : miterIdx = this->addPt(miter, -outset, coverage, false, kSharp_CurveState);
560 0 : nextRing->addIdx(miterIdx, originalIdx);
561 : // The two triangles for the corner
562 0 : this->addTri(originalIdx, perp1Idx, miterIdx);
563 0 : this->addTri(originalIdx, miterIdx, perp2Idx);
564 : }
565 : } else {
566 0 : this->addTri(originalIdx, perp1Idx, perp2Idx);
567 : }
568 : } else {
569 0 : switch (fJoin) {
570 : case SkPaint::Join::kMiter_Join: {
571 : // The bisector outset point
572 0 : SkPoint miter = previousRing.bisector(cur);
573 0 : SkScalar dotProd = normal1.dot(normal2);
574 0 : SkScalar sinHalfAngleSq = SkScalarHalf(SK_Scalar1 + dotProd);
575 0 : SkScalar lengthSq = outsetSq / sinHalfAngleSq;
576 0 : if (lengthSq > miterLimitSq) {
577 : // just bevel it
578 0 : this->addTri(originalIdx, perp1Idx, perp2Idx);
579 0 : break;
580 : }
581 0 : miter.setLength(-SkScalarSqrt(lengthSq));
582 0 : miter += fPts[originalIdx];
583 :
584 : // For very shallow angles all the corner points could fuse
585 0 : if (!duplicate_pt(miter, this->point(perp1Idx))) {
586 : int miterIdx;
587 0 : miterIdx = this->addPt(miter, -outset, coverage, false,
588 0 : kSharp_CurveState);
589 0 : nextRing->addIdx(miterIdx, originalIdx);
590 : // The two triangles for the corner
591 0 : this->addTri(originalIdx, perp1Idx, miterIdx);
592 0 : this->addTri(originalIdx, miterIdx, perp2Idx);
593 : }
594 0 : break;
595 : }
596 : case SkPaint::Join::kBevel_Join:
597 0 : this->addTri(originalIdx, perp1Idx, perp2Idx);
598 0 : break;
599 : default:
600 : // kRound_Join is unsupported for now. GrAALinearizingConvexPathRenderer is
601 : // only willing to draw mitered or beveled, so we should never get here.
602 0 : SkASSERT(false);
603 : }
604 : }
605 :
606 0 : nextRing->addIdx(perp2Idx, originalIdx);
607 : }
608 :
609 0 : if (0 == cur) {
610 : // Store the index of the first perpendicular point to finish up
611 0 : firstPerpIdx = perp1Idx;
612 0 : SkASSERT(-1 == lastPerpIdx);
613 : } else {
614 : // The triangles for the previous edge
615 0 : int prevIdx = previousRing.index(prev);
616 0 : this->addTri(prevIdx, perp1Idx, originalIdx);
617 0 : this->addTri(prevIdx, lastPerpIdx, perp1Idx);
618 : }
619 :
620 : // Track the last perpendicular outset point so we can construct the
621 : // trailing edge triangles.
622 0 : lastPerpIdx = perp2Idx;
623 0 : prev = cur;
624 : }
625 :
626 : // pick up the final edge rect
627 0 : int lastIdx = previousRing.index(numPts - 1);
628 0 : this->addTri(lastIdx, firstPerpIdx, previousRing.index(0));
629 0 : this->addTri(lastIdx, lastPerpIdx, firstPerpIdx);
630 :
631 0 : this->validate();
632 : }
633 :
634 : // Something went wrong in the creation of the next ring. If we're filling the shape, just go ahead
635 : // and fan it.
636 0 : void GrAAConvexTessellator::terminate(const Ring& ring) {
637 0 : if (fStyle != SkStrokeRec::kStroke_Style) {
638 0 : this->fanRing(ring);
639 : }
640 0 : }
641 :
642 0 : static SkScalar compute_coverage(SkScalar depth, SkScalar initialDepth, SkScalar initialCoverage,
643 : SkScalar targetDepth, SkScalar targetCoverage) {
644 0 : if (SkScalarNearlyEqual(initialDepth, targetDepth)) {
645 0 : return targetCoverage;
646 : }
647 0 : SkScalar result = (depth - initialDepth) / (targetDepth - initialDepth) *
648 0 : (targetCoverage - initialCoverage) + initialCoverage;
649 0 : return SkScalarClampMax(result, 1.0f);
650 : }
651 :
652 : // return true when processing is complete
653 0 : bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing,
654 : SkScalar initialDepth, SkScalar initialCoverage,
655 : SkScalar targetDepth, SkScalar targetCoverage,
656 : bool forceNew) {
657 0 : bool done = false;
658 :
659 0 : fCandidateVerts.rewind();
660 :
661 : // Loop through all the points in the ring and find the intersection with the smallest depth
662 0 : SkScalar minDist = SK_ScalarMax, minT = 0.0f;
663 0 : int minEdgeIdx = -1;
664 :
665 0 : for (int cur = 0; cur < lastRing.numPts(); ++cur) {
666 0 : int next = (cur + 1) % lastRing.numPts();
667 :
668 : SkScalar t;
669 0 : bool result = intersect(this->point(lastRing.index(cur)), lastRing.bisector(cur),
670 : this->point(lastRing.index(next)), lastRing.bisector(next),
671 0 : &t);
672 0 : if (!result) {
673 0 : continue;
674 : }
675 0 : SkScalar dist = -t * lastRing.norm(cur).dot(lastRing.bisector(cur));
676 :
677 0 : if (minDist > dist) {
678 0 : minDist = dist;
679 0 : minT = t;
680 0 : minEdgeIdx = cur;
681 : }
682 : }
683 :
684 0 : if (minEdgeIdx == -1) {
685 0 : return false;
686 : }
687 0 : SkPoint newPt = lastRing.bisector(minEdgeIdx);
688 0 : newPt.scale(minT);
689 0 : newPt += this->point(lastRing.index(minEdgeIdx));
690 :
691 0 : SkScalar depth = this->computeDepthFromEdge(lastRing.origEdgeID(minEdgeIdx), newPt);
692 0 : if (depth >= targetDepth) {
693 : // None of the bisectors intersect before reaching the desired depth.
694 : // Just step them all to the desired depth
695 0 : depth = targetDepth;
696 0 : done = true;
697 : }
698 :
699 : // 'dst' stores where each point in the last ring maps to/transforms into
700 : // in the next ring.
701 0 : SkTDArray<int> dst;
702 0 : dst.setCount(lastRing.numPts());
703 :
704 : // Create the first point (who compares with no one)
705 0 : if (!this->computePtAlongBisector(lastRing.index(0),
706 0 : lastRing.bisector(0),
707 : lastRing.origEdgeID(0),
708 : depth, &newPt)) {
709 0 : this->terminate(lastRing);
710 0 : return true;
711 : }
712 0 : dst[0] = fCandidateVerts.addNewPt(newPt,
713 : lastRing.index(0), lastRing.origEdgeID(0),
714 0 : !this->movable(lastRing.index(0)));
715 :
716 : // Handle the middle points (who only compare with the prior point)
717 0 : for (int cur = 1; cur < lastRing.numPts()-1; ++cur) {
718 0 : if (!this->computePtAlongBisector(lastRing.index(cur),
719 0 : lastRing.bisector(cur),
720 : lastRing.origEdgeID(cur),
721 : depth, &newPt)) {
722 0 : this->terminate(lastRing);
723 0 : return true;
724 : }
725 0 : if (!duplicate_pt(newPt, fCandidateVerts.lastPoint())) {
726 0 : dst[cur] = fCandidateVerts.addNewPt(newPt,
727 : lastRing.index(cur), lastRing.origEdgeID(cur),
728 0 : !this->movable(lastRing.index(cur)));
729 : } else {
730 0 : dst[cur] = fCandidateVerts.fuseWithPrior(lastRing.origEdgeID(cur));
731 : }
732 : }
733 :
734 : // Check on the last point (handling the wrap around)
735 0 : int cur = lastRing.numPts()-1;
736 0 : if (!this->computePtAlongBisector(lastRing.index(cur),
737 0 : lastRing.bisector(cur),
738 : lastRing.origEdgeID(cur),
739 : depth, &newPt)) {
740 0 : this->terminate(lastRing);
741 0 : return true;
742 : }
743 0 : bool dupPrev = duplicate_pt(newPt, fCandidateVerts.lastPoint());
744 0 : bool dupNext = duplicate_pt(newPt, fCandidateVerts.firstPoint());
745 :
746 0 : if (!dupPrev && !dupNext) {
747 0 : dst[cur] = fCandidateVerts.addNewPt(newPt,
748 : lastRing.index(cur), lastRing.origEdgeID(cur),
749 0 : !this->movable(lastRing.index(cur)));
750 0 : } else if (dupPrev && !dupNext) {
751 0 : dst[cur] = fCandidateVerts.fuseWithPrior(lastRing.origEdgeID(cur));
752 0 : } else if (!dupPrev && dupNext) {
753 0 : dst[cur] = fCandidateVerts.fuseWithNext();
754 : } else {
755 0 : bool dupPrevVsNext = duplicate_pt(fCandidateVerts.firstPoint(), fCandidateVerts.lastPoint());
756 :
757 0 : if (!dupPrevVsNext) {
758 0 : dst[cur] = fCandidateVerts.fuseWithPrior(lastRing.origEdgeID(cur));
759 : } else {
760 0 : const int fused = fCandidateVerts.fuseWithBoth();
761 0 : dst[cur] = fused;
762 0 : const int targetIdx = dst[cur - 1];
763 0 : for (int i = cur - 1; i >= 0 && dst[i] == targetIdx; i--) {
764 0 : dst[i] = fused;
765 : }
766 : }
767 : }
768 :
769 : // Fold the new ring's points into the global pool
770 0 : for (int i = 0; i < fCandidateVerts.numPts(); ++i) {
771 : int newIdx;
772 0 : if (fCandidateVerts.needsToBeNew(i) || forceNew) {
773 : // if the originating index is still valid then this point wasn't
774 : // fused (and is thus movable)
775 : SkScalar coverage = compute_coverage(depth, initialDepth, initialCoverage,
776 0 : targetDepth, targetCoverage);
777 0 : newIdx = this->addPt(fCandidateVerts.point(i), depth, coverage,
778 0 : fCandidateVerts.originatingIdx(i) != -1, kSharp_CurveState);
779 : } else {
780 0 : SkASSERT(fCandidateVerts.originatingIdx(i) != -1);
781 0 : this->updatePt(fCandidateVerts.originatingIdx(i), fCandidateVerts.point(i), depth,
782 0 : targetCoverage);
783 0 : newIdx = fCandidateVerts.originatingIdx(i);
784 : }
785 :
786 0 : nextRing->addIdx(newIdx, fCandidateVerts.origEdge(i));
787 : }
788 :
789 : // 'dst' currently has indices into the ring. Remap these to be indices
790 : // into the global pool since the triangulation operates in that space.
791 0 : for (int i = 0; i < dst.count(); ++i) {
792 0 : dst[i] = nextRing->index(dst[i]);
793 : }
794 :
795 0 : for (int i = 0; i < lastRing.numPts(); ++i) {
796 0 : int next = (i + 1) % lastRing.numPts();
797 :
798 0 : this->addTri(lastRing.index(i), lastRing.index(next), dst[next]);
799 0 : this->addTri(lastRing.index(i), dst[next], dst[i]);
800 : }
801 :
802 0 : if (done && fStyle != SkStrokeRec::kStroke_Style) {
803 : // fill or stroke-and-fill
804 0 : this->fanRing(*nextRing);
805 : }
806 :
807 0 : if (nextRing->numPts() < 3) {
808 0 : done = true;
809 : }
810 0 : return done;
811 : }
812 :
813 0 : void GrAAConvexTessellator::validate() const {
814 0 : SkASSERT(fPts.count() == fMovable.count());
815 0 : SkASSERT(fPts.count() == fCoverages.count());
816 0 : SkASSERT(fPts.count() == fCurveState.count());
817 0 : SkASSERT(0 == (fIndices.count() % 3));
818 0 : SkASSERT(!fBisectors.count() || fBisectors.count() == fNorms.count());
819 0 : }
820 :
821 : //////////////////////////////////////////////////////////////////////////////
822 0 : void GrAAConvexTessellator::Ring::init(const GrAAConvexTessellator& tess) {
823 0 : this->computeNormals(tess);
824 0 : this->computeBisectors(tess);
825 0 : }
826 :
827 0 : void GrAAConvexTessellator::Ring::init(const SkTDArray<SkVector>& norms,
828 : const SkTDArray<SkVector>& bisectors) {
829 0 : for (int i = 0; i < fPts.count(); ++i) {
830 0 : fPts[i].fNorm = norms[i];
831 0 : fPts[i].fBisector = bisectors[i];
832 : }
833 0 : }
834 :
835 : // Compute the outward facing normal at each vertex.
836 0 : void GrAAConvexTessellator::Ring::computeNormals(const GrAAConvexTessellator& tess) {
837 0 : for (int cur = 0; cur < fPts.count(); ++cur) {
838 0 : int next = (cur + 1) % fPts.count();
839 :
840 0 : fPts[cur].fNorm = tess.point(fPts[next].fIndex) - tess.point(fPts[cur].fIndex);
841 0 : SkPoint::Normalize(&fPts[cur].fNorm);
842 0 : fPts[cur].fNorm.setOrthog(fPts[cur].fNorm, tess.side());
843 : }
844 0 : }
845 :
846 0 : void GrAAConvexTessellator::Ring::computeBisectors(const GrAAConvexTessellator& tess) {
847 0 : int prev = fPts.count() - 1;
848 0 : for (int cur = 0; cur < fPts.count(); prev = cur, ++cur) {
849 0 : fPts[cur].fBisector = fPts[cur].fNorm + fPts[prev].fNorm;
850 0 : if (!fPts[cur].fBisector.normalize()) {
851 0 : SkASSERT(SkPoint::kLeft_Side == tess.side() || SkPoint::kRight_Side == tess.side());
852 0 : fPts[cur].fBisector.setOrthog(fPts[cur].fNorm, (SkPoint::Side)-tess.side());
853 : SkVector other;
854 0 : other.setOrthog(fPts[prev].fNorm, tess.side());
855 0 : fPts[cur].fBisector += other;
856 0 : SkAssertResult(fPts[cur].fBisector.normalize());
857 : } else {
858 0 : fPts[cur].fBisector.negate(); // make the bisector face in
859 : }
860 : }
861 0 : }
862 :
863 : //////////////////////////////////////////////////////////////////////////////
864 : #ifdef SK_DEBUG
865 : // Is this ring convex?
866 0 : bool GrAAConvexTessellator::Ring::isConvex(const GrAAConvexTessellator& tess) const {
867 0 : if (fPts.count() < 3) {
868 0 : return true;
869 : }
870 :
871 0 : SkPoint prev = tess.point(fPts[0].fIndex) - tess.point(fPts.top().fIndex);
872 0 : SkPoint cur = tess.point(fPts[1].fIndex) - tess.point(fPts[0].fIndex);
873 0 : SkScalar minDot = prev.fX * cur.fY - prev.fY * cur.fX;
874 0 : SkScalar maxDot = minDot;
875 :
876 0 : prev = cur;
877 0 : for (int i = 1; i < fPts.count(); ++i) {
878 0 : int next = (i + 1) % fPts.count();
879 :
880 0 : cur = tess.point(fPts[next].fIndex) - tess.point(fPts[i].fIndex);
881 0 : SkScalar dot = prev.fX * cur.fY - prev.fY * cur.fX;
882 :
883 0 : minDot = SkMinScalar(minDot, dot);
884 0 : maxDot = SkMaxScalar(maxDot, dot);
885 :
886 0 : prev = cur;
887 : }
888 :
889 0 : if (SkScalarNearlyEqual(maxDot, 0.0f, 0.005f)) {
890 0 : maxDot = 0;
891 : }
892 0 : if (SkScalarNearlyEqual(minDot, 0.0f, 0.005f)) {
893 0 : minDot = 0;
894 : }
895 0 : return (maxDot >= 0.0f) == (minDot >= 0.0f);
896 : }
897 :
898 : #endif
899 :
900 0 : void GrAAConvexTessellator::lineTo(const SkPoint& p, CurveState curve) {
901 0 : if (this->numPts() > 0 && duplicate_pt(p, this->lastPoint())) {
902 0 : return;
903 : }
904 :
905 0 : SkASSERT(fPts.count() <= 1 || fPts.count() == fNorms.count()+1);
906 0 : if (this->numPts() >= 2 && abs_dist_from_line(fPts.top(), fNorms.top(), p) < kClose) {
907 : // The old last point is on the line from the second to last to the new point
908 0 : this->popLastPt();
909 0 : fNorms.pop();
910 : // double-check that the new last point is not a duplicate of the new point. In an ideal
911 : // world this wouldn't be necessary (since it's only possible for non-convex paths), but
912 : // floating point precision issues mean it can actually happen on paths that were
913 : // determined to be convex.
914 0 : if (duplicate_pt(p, this->lastPoint())) {
915 0 : return;
916 : }
917 : }
918 0 : SkScalar initialRingCoverage = (SkStrokeRec::kFill_Style == fStyle) ? 0.5f : 1.0f;
919 0 : this->addPt(p, 0.0f, initialRingCoverage, false, curve);
920 0 : if (this->numPts() > 1) {
921 0 : *fNorms.push() = fPts.top() - fPts[fPts.count()-2];
922 0 : SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
923 0 : SkASSERT(len > 0.0f);
924 0 : SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length()));
925 : }
926 : }
927 :
928 0 : void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, CurveState curve) {
929 0 : m.mapPoints(&p, 1);
930 0 : this->lineTo(p, curve);
931 0 : }
932 :
933 0 : void GrAAConvexTessellator::quadTo(const SkPoint pts[3]) {
934 0 : int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
935 0 : fPointBuffer.setReserve(maxCount);
936 0 : SkPoint* target = fPointBuffer.begin();
937 0 : int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
938 0 : kQuadTolerance, &target, maxCount);
939 0 : fPointBuffer.setCount(count);
940 0 : for (int i = 0; i < count - 1; i++) {
941 0 : this->lineTo(fPointBuffer[i], kCurve_CurveState);
942 : }
943 0 : this->lineTo(fPointBuffer[count - 1], kIndeterminate_CurveState);
944 0 : }
945 :
946 0 : void GrAAConvexTessellator::quadTo(const SkMatrix& m, SkPoint pts[3]) {
947 0 : m.mapPoints(pts, 3);
948 0 : this->quadTo(pts);
949 0 : }
950 :
951 0 : void GrAAConvexTessellator::cubicTo(const SkMatrix& m, SkPoint pts[4]) {
952 0 : m.mapPoints(pts, 4);
953 0 : int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
954 0 : fPointBuffer.setReserve(maxCount);
955 0 : SkPoint* target = fPointBuffer.begin();
956 0 : int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
957 0 : kCubicTolerance, &target, maxCount);
958 0 : fPointBuffer.setCount(count);
959 0 : for (int i = 0; i < count - 1; i++) {
960 0 : this->lineTo(fPointBuffer[i], kCurve_CurveState);
961 : }
962 0 : this->lineTo(fPointBuffer[count - 1], kIndeterminate_CurveState);
963 0 : }
964 :
965 : // include down here to avoid compilation errors caused by "-" overload in SkGeometry.h
966 : #include "SkGeometry.h"
967 :
968 0 : void GrAAConvexTessellator::conicTo(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
969 0 : m.mapPoints(pts, 3);
970 0 : SkAutoConicToQuads quadder;
971 0 : const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
972 0 : SkPoint lastPoint = *(quads++);
973 0 : int count = quadder.countQuads();
974 0 : for (int i = 0; i < count; ++i) {
975 : SkPoint quadPts[3];
976 0 : quadPts[0] = lastPoint;
977 0 : quadPts[1] = quads[0];
978 0 : quadPts[2] = i == count - 1 ? pts[2] : quads[1];
979 0 : this->quadTo(quadPts);
980 0 : lastPoint = quadPts[2];
981 0 : quads += 2;
982 : }
983 0 : }
984 :
985 : //////////////////////////////////////////////////////////////////////////////
986 : #if GR_AA_CONVEX_TESSELLATOR_VIZ
987 : static const SkScalar kPointRadius = 0.02f;
988 : static const SkScalar kArrowStrokeWidth = 0.0f;
989 : static const SkScalar kArrowLength = 0.2f;
990 : static const SkScalar kEdgeTextSize = 0.1f;
991 : static const SkScalar kPointTextSize = 0.02f;
992 :
993 : static void draw_point(SkCanvas* canvas, const SkPoint& p, SkScalar paramValue, bool stroke) {
994 : SkPaint paint;
995 : SkASSERT(paramValue <= 1.0f);
996 : int gs = int(255*paramValue);
997 : paint.setARGB(255, gs, gs, gs);
998 :
999 : canvas->drawCircle(p.fX, p.fY, kPointRadius, paint);
1000 :
1001 : if (stroke) {
1002 : SkPaint stroke;
1003 : stroke.setColor(SK_ColorYELLOW);
1004 : stroke.setStyle(SkPaint::kStroke_Style);
1005 : stroke.setStrokeWidth(kPointRadius/3.0f);
1006 : canvas->drawCircle(p.fX, p.fY, kPointRadius, stroke);
1007 : }
1008 : }
1009 :
1010 : static void draw_line(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, SkColor color) {
1011 : SkPaint p;
1012 : p.setColor(color);
1013 :
1014 : canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, p);
1015 : }
1016 :
1017 : static void draw_arrow(SkCanvas*canvas, const SkPoint& p, const SkPoint &n,
1018 : SkScalar len, SkColor color) {
1019 : SkPaint paint;
1020 : paint.setColor(color);
1021 : paint.setStrokeWidth(kArrowStrokeWidth);
1022 : paint.setStyle(SkPaint::kStroke_Style);
1023 :
1024 : canvas->drawLine(p.fX, p.fY,
1025 : p.fX + len * n.fX, p.fY + len * n.fY,
1026 : paint);
1027 : }
1028 :
1029 : void GrAAConvexTessellator::Ring::draw(SkCanvas* canvas, const GrAAConvexTessellator& tess) const {
1030 : SkPaint paint;
1031 : paint.setTextSize(kEdgeTextSize);
1032 :
1033 : for (int cur = 0; cur < fPts.count(); ++cur) {
1034 : int next = (cur + 1) % fPts.count();
1035 :
1036 : draw_line(canvas,
1037 : tess.point(fPts[cur].fIndex),
1038 : tess.point(fPts[next].fIndex),
1039 : SK_ColorGREEN);
1040 :
1041 : SkPoint mid = tess.point(fPts[cur].fIndex) + tess.point(fPts[next].fIndex);
1042 : mid.scale(0.5f);
1043 :
1044 : if (fPts.count()) {
1045 : draw_arrow(canvas, mid, fPts[cur].fNorm, kArrowLength, SK_ColorRED);
1046 : mid.fX += (kArrowLength/2) * fPts[cur].fNorm.fX;
1047 : mid.fY += (kArrowLength/2) * fPts[cur].fNorm.fY;
1048 : }
1049 :
1050 : SkString num;
1051 : num.printf("%d", this->origEdgeID(cur));
1052 : canvas->drawText(num.c_str(), num.size(), mid.fX, mid.fY, paint);
1053 :
1054 : if (fPts.count()) {
1055 : draw_arrow(canvas, tess.point(fPts[cur].fIndex), fPts[cur].fBisector,
1056 : kArrowLength, SK_ColorBLUE);
1057 : }
1058 : }
1059 : }
1060 :
1061 : void GrAAConvexTessellator::draw(SkCanvas* canvas) const {
1062 : for (int i = 0; i < fIndices.count(); i += 3) {
1063 : SkASSERT(fIndices[i] < this->numPts()) ;
1064 : SkASSERT(fIndices[i+1] < this->numPts()) ;
1065 : SkASSERT(fIndices[i+2] < this->numPts()) ;
1066 :
1067 : draw_line(canvas,
1068 : this->point(this->fIndices[i]), this->point(this->fIndices[i+1]),
1069 : SK_ColorBLACK);
1070 : draw_line(canvas,
1071 : this->point(this->fIndices[i+1]), this->point(this->fIndices[i+2]),
1072 : SK_ColorBLACK);
1073 : draw_line(canvas,
1074 : this->point(this->fIndices[i+2]), this->point(this->fIndices[i]),
1075 : SK_ColorBLACK);
1076 : }
1077 :
1078 : fInitialRing.draw(canvas, *this);
1079 : for (int i = 0; i < fRings.count(); ++i) {
1080 : fRings[i]->draw(canvas, *this);
1081 : }
1082 :
1083 : for (int i = 0; i < this->numPts(); ++i) {
1084 : draw_point(canvas,
1085 : this->point(i), 0.5f + (this->depth(i)/(2 * kAntialiasingRadius)),
1086 : !this->movable(i));
1087 :
1088 : SkPaint paint;
1089 : paint.setTextSize(kPointTextSize);
1090 : paint.setTextAlign(SkPaint::kCenter_Align);
1091 : if (this->depth(i) <= -kAntialiasingRadius) {
1092 : paint.setColor(SK_ColorWHITE);
1093 : }
1094 :
1095 : SkString num;
1096 : num.printf("%d", i);
1097 : canvas->drawText(num.c_str(), num.size(),
1098 : this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f),
1099 : paint);
1100 : }
1101 : }
1102 :
1103 : #endif
|