Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: src/gpu/GrAAConvexTessellator.cpp

Issue 1084943003: Add GrAAConvexTessellator class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
14 // Next steps:
15 // use in AAConvexPathRenderer
16 // add an interactive sample app slide
17 // add a combo gm/bench
18 // add debug check that all points are suitably far apart
19 // test more degenerate cases
20
21 // Add swap & converted flag to ring
22
23 // The tolerance for fusing vertices and eliminating colinear lines (It is in de vice space).
24 static const SkScalar kClose = (SK_Scalar1 / 16);
25 static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
26
27 static SkScalar intersect(const SkPoint& p0, const SkPoint& n0,
28 const SkPoint& p1, const SkPoint& n1) {
29 const SkPoint v = p1 - p0;
30
31 SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX;
32 return (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
33 }
34
35 static SkScalar perp_intersect(const SkPoint& p0, const SkPoint& n0,
bsalomon 2015/04/24 15:44:44 could use a tiny comment here
robertphillips 2015/05/05 15:21:32 Done.
36 const SkPoint& p1, const SkPoint& perp) {
37 const SkPoint v = p1 - p0;
38 SkScalar perpDot = n0.dot(perp);
39 return v.dot(perp) / perpDot;
40 }
41
42 static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
43 SkScalar distSq = p0.distanceToSqd(p1);
44 return distSq < kCloseSqd;
45 }
46
47 static SkScalar abs_dist_from_line(const SkPoint& p0, const SkPoint& p1, const S kPoint& test) {
bsalomon 2015/04/24 15:44:44 Wondering if we could use SkPoint::distanceToLineB
robertphillips 2015/05/05 15:21:33 Done. The new version uses the vector computed for
48 // TODO: update this to use the normals computed for a ring rather than reco mputing
49 SkPoint v = p1 - p0;
50 v.normalize();
51
52 SkPoint testV = test - p0;
53 SkScalar dist = testV.fX * v.fY - testV.fY * v.fX;
54 return SkScalarAbs(dist);
55 }
56
57 int GrAAConvexTessellator::addPt(const SkPoint& pt,
58 SkScalar depth,
59 bool movable) {
60 this->validate();
61
62 int index = fPts.count();
63 *fPts.push() = pt;
64 *fDepths.push() = depth;
65 *fMovable.push() = movable;
66
67 this->validate();
68 return index;
69 }
70
71 void GrAAConvexTessellator::popLastPt() {
72 this->validate();
73
74 fPts.pop();
75 fDepths.pop();
76 fMovable.pop();
77
78 this->validate();
79 }
80
81 void GrAAConvexTessellator::popFirstPtShuffle() {
82 this->validate();
83
84 fPts.removeShuffle(0);
85 fDepths.removeShuffle(0);
86 fMovable.removeShuffle(0);
87
88 this->validate();
89 }
90
91 void GrAAConvexTessellator::updatePt(int index,
92 const SkPoint& pt,
93 SkScalar depth) {
94 this->validate();
95 SkASSERT(fMovable[index]);
96
97 fPts[index] = pt;
98 fDepths[index] = depth;
99 }
100
101 void GrAAConvexTessellator::addTri(int i0, int i1, int i2) {
102 if (i0 == i1 || i1 == i2 || i2 == i0) {
103 return;
104 }
105
106 *fIndices.push() = i0;
107 *fIndices.push() = i1;
108 *fIndices.push() = i2;
109 }
110
111 // The general idea here is to, conceptually, start with the original polygon an d slide
112 // the vertices along the bisectors until the first intersection. At that
113 // point two of the edges collapse and the process repeats on the new polygon.
114 // The polygon state is captured in the GrRing class while the GrAAConvexTessell ator
115 // controls the iteration.
116 bool GrAAConvexTessellator::tessellate(const SkMatrix& m, const SkPath& path) {
117 static const int kMaxNumRings = 7;
118
119 if (!this->extractFromPath(m, path)) {
120 return false;
121 }
122
123 this->createOuterRing(fInitialRing);
124
125 GrRing* lastRing = &fInitialRing;
126 int i;
127 for (i = 0; i < kMaxNumRings; ++i) {
128 GrRing* nextRing = this->getNextRing(lastRing);
129
130 if (this->createInsetRing(*lastRing, nextRing)) {
131 break;
132 }
133
134 if (nextRing->numPts0() < 3) {
135 break;
136 }
137
138 nextRing->init(*this);
139 lastRing = nextRing;
140 }
141
142 if (kMaxNumRings == i) {
143 // If we've exceeded the amount of time we want to throw at this, set
144 // the depth of all points in the final ring to 'fTargetDepth' and
145 // create a fan.
146 for (int i = 0; i < lastRing->numPts0(); ++i) {
147 this->fDepths[lastRing->index(i)] = fTargetDepth;
148 }
149 this->fanRing(*lastRing);
150 }
151
152 this->validate();
153 SkDEBUGCODE(this->checkAllDepths();)
154 return true;
155 }
156
157 // Find a point that is 'desiredDepth' away from the 'edgeIdx'-th edge and lies
158 // along the 'bisector' from the 'startIdx'-th point.
159 SkPoint GrAAConvexTessellator::computePtAlongBisector(int startIdx,
160 const SkVector& bisector,
161 int edgeIdx,
162 SkScalar desiredDepth) con st {
163 const SkPoint& norm = fInitialRing.norm1(edgeIdx);
164
165 // First find the point where the edge and the bisector intersect
166 SkPoint newP;
167 SkScalar t = perp_intersect(fPts[startIdx], bisector, fPts[edgeIdx], norm);
168 if (SkScalarNearlyEqual(t, 0.0f)) {
169 // the start point was one of the original ring points
170 SkASSERT(startIdx < fInitialRing.numPts0());
171 newP = fPts[startIdx];
172 } else {
173 SkASSERT(t < 0.0f);
174 newP = bisector;
175 newP.scale(t);
176 newP += fPts[startIdx];
177 }
178
179 // Then offset along the bisector from that point the correct distance
180 t = -desiredDepth / bisector.dot(norm);
181 SkASSERT(t > 0.0f);
182 SkPoint result = bisector;
183 result.scale(t);
184 result += newP;
185
186 return result;
187 }
188
189 bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat h) {
190 SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
191 SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
192
193 // Outer ring: 3*numPts
194 // Middle ring: numPts
195 // Presumptive inner ring: numPts
196 this->reservePts(5*path.countPoints());
197 // Outer ring: 12*numPts
198 // Middle ring: 0
199 // Presumptive inner ring: 6*numPts + 6
200 fIndices.setReserve(18*path.countPoints() + 6);
201
202 SkScalar minCross = SK_ScalarMax, maxCross = -SK_ScalarMax;
203
204 // TODO: can we reuse the GrRing structure in this process?
205 SkPath::Iter iter(path, true);
206 SkPoint pts[4];
207 SkPath::Verb verb;
208 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
bsalomon 2015/04/24 15:44:44 Do we really need to iterate here? We know all th
robertphillips 2015/05/05 15:21:33 I've added a TODO. I think we would need a new ent
209 switch (verb) {
210 case SkPath::kLine_Verb:
211 m.mapPoints(&pts[1], 1);
212 if (this->numPts() > 0 && duplicate_pt(pts[1], this->lastPoint() )) {
213 continue;
214 }
215
216 if (this->numPts() >= 2 &&
217 abs_dist_from_line(fPts[this->numPts()-1], fPts[this->numPts ()-2], pts[1]) <
218 kClose) {
219 // The old last point is on the line from the second to last to the new point
220 this->popLastPt();
221 }
222
223 this->addPt(pts[1], 0.0f, false);
224
225 if (this->numPts() >= 3) {
226 int cur = this->numPts()-1;
227
228 SkScalar cross = SkPoint::CrossProduct(fPts[cur] - fPts[cur- 1],
229 fPts[cur-1] - fPts[cu r-2]);
230 if (maxCross < cross) {
bsalomon 2015/04/24 15:44:44 maxCross = SkTMax(maxCross, cross); minCross = SkT
robertphillips 2015/05/05 15:21:32 Done.
231 maxCross = cross;
232 }
233 if (minCross > cross) {
234 minCross = cross;
235 }
236 }
237 break;
238 case SkPath::kQuad_Verb:
239 case SkPath::kConic_Verb:
240 case SkPath::kCubic_Verb:
241 SkASSERT(false);
242 break;
243 case SkPath::kMove_Verb:
244 case SkPath::kClose_Verb:
245 case SkPath::kDone_Verb:
246 break;
247 }
248 }
249
250 // check if last point is a duplicate of the first point. If so, remove it.
251 if (duplicate_pt(fPts[this->numPts()-1], fPts[0])) {
252 this->popLastPt();
253 }
254
255 if (this->numPts() >= 3 &&
256 abs_dist_from_line(fPts[this->numPts()-1], fPts[this->numPts()-2], fPts[ 0]) < kClose) {
257 // The last point is on the line from the second to last to the first po int.
258 this->popLastPt();
259 }
260
261 if (this->numPts() >= 3 &&
262 abs_dist_from_line(fPts[0], fPts[this->numPts()-1], fPts[1]) < kClose) {
263 // The first point is on the line from the last to the second.
264 this->popFirstPtShuffle();
265 SkASSERT(0);
266 }
267
268 if (this->numPts() < 3) {
269 return false;
270 }
271
272 // Check the cross produce of the final trio
273 SkScalar cross = SkPoint::CrossProduct(fPts[1] - fPts[0], fPts[0] - fPts[fPt s.count()-1]);
274 if (maxCross < cross) {
275 maxCross = cross;
276 }
277 if (minCross > cross) {
278 minCross = cross;
279 }
280
281 SkPoint::Side side;
bsalomon 2015/04/24 15:44:44 It wouldn't surprise me much if the asserts here w
robertphillips 2015/05/05 15:21:32 I think the colinear cleanup pass should remove th
282 if (maxCross > 0.0f) {
283 SkASSERT(minCross >= 0.0f);
284 side = SkPoint::kRight_Side;
285 } else {
286 SkASSERT(minCross <= 0.0f);
287 side = SkPoint::kLeft_Side;
288 }
289
290 fInitialRing.setReserve(this->numPts());
291 fInitialRing.setSide(side);
292 for (int i = 0; i < this->numPts(); ++i) {
293 fInitialRing.addIdx1(i, i);
294 }
295 fInitialRing.init(*this);
296
297 this->validate();
298 return true;
299 }
300
301 GrRing* GrAAConvexTessellator::getNextRing(GrRing* lastRing) {
302 #if GR_AA_CONVEX_TESSELLATOR_VIZ
303 GrRing* ring = *fRings.push() = SkNEW(GrRing);
304 ring->setReserve(fInitialRing.numPts0());
305 ring->setSide(fInitialRing.side());
306 ring->rewind();
307 return ring;
308 #else
309 // Flip flop back and forth between fRings[0] & fRings[1]
bsalomon 2015/04/24 15:44:44 Is this different than int nextRing = lastRing ==
robertphillips 2015/05/05 15:21:32 Done.
310 if (lastRing == &fInitialRing) {
311 fRings[0].setReserve(fInitialRing.numPts0());
312 fRings[0].setSide(fInitialRing.side());
313 fRings[0].rewind();
314 return &fRings[0];
315 } else if (lastRing == &fRings[0]) {
316 fRings[1].setReserve(fInitialRing.numPts0());
317 fRings[1].setSide(fInitialRing.side());
318 fRings[1].rewind();
319 return &fRings[1];
320 } else {
321 SkASSERT(lastRing == &fRings[1]);
322 fRings[0].setReserve(fInitialRing.numPts0());
323 fRings[0].setSide(fInitialRing.side());
324 fRings[0].rewind();
325 return &fRings[0];
326 }
327 #endif
328 }
329
330 void GrAAConvexTessellator::fanRing(const GrRing& ring) {
331 // fan out from point 0
332 for (int cur = 1; cur < ring.numPts2()-1; ++cur) {
333 this->addTri(ring.index(0), ring.index(cur), ring.index(cur+1));
334 }
335 }
336
337 void GrAAConvexTessellator::createOuterRing(const GrRing& ring) {
338 // For now, we're only generating one outer ring (at the start). This
339 // could be relaxed for stroking use cases.
340 SkASSERT(0 == fIndices.count());
341
342 const int numPts = ring.numPts0();
343
344 int prev = numPts - 1;
345 int lastOut = -1, firstOut, newIdx0, newIdx1, newIdx2;
346 for (int cur = 0; cur < numPts; ++cur) {
bsalomon 2015/04/24 15:44:44 Maybe an explanation somewhere that what we're doi
robertphillips 2015/05/05 15:21:33 Done.
347 SkPoint temp = ring.norm1(prev);
348 temp.scale(fTargetDepth);
349 temp += this->point(ring.index(cur));
350
351 if (lastOut > -1 && duplicate_pt(temp, this->point(lastOut))) {
bsalomon 2015/04/24 15:44:44 "With a very shallow angle between two edges, the
robertphillips 2015/05/05 15:21:32 Done-ish. I think it is still useful to track the
352 SkASSERT(lastOut == this->numPts()-1);
353 newIdx0 = this->numPts()-1;
354 } else {
355 newIdx0 = this->addPt(temp, -fTargetDepth, false);
356 }
357
358 temp = ring.bisector(cur);
359 temp.scale(-fTargetDepth); // the bisectors point in
360 temp += this->point(ring.index(cur));
361
362 if (duplicate_pt(temp, this->point(newIdx0))) {
363 newIdx1 = newIdx0;
364 } else {
365 newIdx1 = this->addPt(temp, -fTargetDepth, false);
366 }
367
368 temp = ring.norm1(cur);
369 temp.scale(fTargetDepth);
370 temp += this->point(ring.index(cur));
371
372 if (duplicate_pt(temp, this->point(newIdx1))) {
373 newIdx2 = newIdx1;
374 } else {
375 newIdx2 = this->addPt(temp, -fTargetDepth, false);
376 }
377
378 // The previous edge
379 if (lastOut != -1) {
380 this->addTri(ring.index(prev), newIdx0, ring.index(cur));
381 this->addTri(ring.index(prev), lastOut, newIdx0);
382 } else {
383 firstOut = newIdx0;
384 }
385
386 // The cap around the corner
387 this->addTri(ring.index(cur), newIdx0, newIdx1);
388 this->addTri(ring.index(cur), newIdx1, newIdx2);
389
390 prev = cur;
391 lastOut = newIdx2;
392 }
393
394 // pick up the final edge rect
395 this->addTri(ring.index(numPts-1), firstOut, ring.index(0));
396 this->addTri(ring.index(numPts-1), lastOut, firstOut);
397
398 this->validate();
399 }
400
401 // return true when processing is complete
402 bool GrAAConvexTessellator::createInsetRing(const GrRing& lastRing, GrRing* next Ring) {
403 bool done = false;
404
405 // Loop through all the points in the ring and find the intersection with th e smallest depth
406 SkScalar minDist = SK_ScalarMax, minT;
407 int minEdgeIdx;
408
409 for (int cur = 0; cur < lastRing.numPts0(); ++cur) {
410 int next = (cur + 1) % lastRing.numPts0();
bsalomon 2015/04/24 15:44:44 wonder if we can avoid int mod.
411
412 SkScalar t = intersect(this->point(lastRing.index(cur)), lastRing.bisec tor(cur),
413 this->point(lastRing.index(next)), lastRing.bisec tor(next));
414 SkScalar dist = -t * lastRing.norm1(cur).dot(lastRing.bisector(cur));
415
416 if (minDist > dist) {
417 minDist = dist;
418 minT = t;
419 minEdgeIdx = cur;
420 }
421 }
422
423 SkPoint newPt = lastRing.bisector(minEdgeIdx);
424 newPt.scale(minT);
425 newPt += this->point(lastRing.index(minEdgeIdx));
426
427 SkScalar depth = fInitialRing.computeDepthFromEdge(*this,
428 lastRing.origEdgeID(minEd geIdx),
429 newPt);
430 // TODO: if this assert consistently holds we don't need the above computeDe pthFromEdge
431 SkASSERT(SkScalarNearlyEqual(depth, minDist + this->depth(lastRing.index(min EdgeIdx))));
432
433 if (depth >= fTargetDepth) {
434 // None of the bisectors intersect before reaching the desired depth.
435 // Just step them all to the desired depth
436 depth = fTargetDepth;
437 done = true;
438 }
439
440 // 'dst' is the index into the vertex array each point in the current poly m aps to/
bsalomon 2015/04/24 15:44:44 maybe say "... in the last ring maps to/transforms
robertphillips 2015/05/05 15:21:32 Done.
441 // transforms into
442 // TODO: can/should 'dst' be moved into the GrRing?
443 SkTDArray<int> dst;
444 dst.setCount(lastRing.numPts0());
445
446 // Check on the first point (who compares with no one)
447 newPt = this->computePtAlongBisector(lastRing.index(0),
448 lastRing.bisector(0),
449 lastRing.origEdgeID(0),
450 depth);
451 dst[0] = nextRing->addNewPt(newPt,
452 lastRing.index(0), lastRing.origEdgeID(0),
453 !this->movable(lastRing.index(0)));
454
455 // Handle the middle points (who only compare with the prior point)
456 for (int cur = 1; cur < lastRing.numPts0()-1; ++cur) {
457 newPt = this->computePtAlongBisector(lastRing.index(cur),
458 lastRing.bisector(cur),
459 lastRing.origEdgeID(cur),
460 depth);
461 if (!duplicate_pt(newPt, nextRing->lastPoint())) {
462 dst[cur] = nextRing->addNewPt(newPt,
463 lastRing.index(cur), lastRing.origEdge ID(cur),
464 !this->movable(lastRing.index(cur)));
465 } else {
466 dst[cur] = nextRing->fuseWithPrior(lastRing.origEdgeID(cur));
467 }
468 }
469
470 // Check on the last point (handling the wrap around)
471 int cur = lastRing.numPts0()-1;
472 newPt = this->computePtAlongBisector(lastRing.index(cur),
473 lastRing.bisector(cur),
474 lastRing.origEdgeID(cur),
475 depth);
476 bool dupPrev = duplicate_pt(newPt, nextRing->lastPoint());
477 bool dupNext = duplicate_pt(newPt, nextRing->firstPoint());
478
479 if (!dupPrev && !dupNext) {
480 dst[cur] = nextRing->addNewPt(newPt,
481 lastRing.index(cur), lastRing.origEdgeID(c ur),
482 !this->movable(lastRing.index(cur)));
483 } else if (dupPrev && !dupNext) {
484 dst[cur] = nextRing->fuseWithPrior(lastRing.origEdgeID(cur));
485 } else if (!dupPrev && dupNext) {
486 dst[cur] = nextRing->fuseWithNext();
487 } else {
488 bool dupPrevVsNext = duplicate_pt(nextRing->firstPoint(), nextRing->last Point());
489
490 if (!dupPrevVsNext) {
491 dst[cur] = nextRing->fuseWithPrior(lastRing.origEdgeID(cur));
492 } else {
493 dst[cur] = dst[cur-1] = nextRing->fuseWithBoth();
494 }
495 }
496
497 // Fold the new ring's points into the global pool
498 for (int i = 0; i < nextRing->numPts2(); ++i) {
499 int newIdx;
500 if (nextRing->needsToBeNew(i)) {
501 // if the originating index is still valid then this point wasn't
502 // fused (and is thus movable)
503 newIdx = this->addPt(nextRing->point(i), depth,
504 nextRing->originatingIdx(i) != -1);
505 } else {
506 SkASSERT(nextRing->originatingIdx(i) != -1);
507 this->updatePt(nextRing->originatingIdx(i), nextRing->point(i), dept h);
508 newIdx = nextRing->originatingIdx(i);
509 }
510
511 nextRing->addIdx1(newIdx, nextRing->origEdge2(i));
512 }
513
514 // 'dst' currently has indices into the ring. Remap these to be indices
515 // into the global pool since the triangulation operates in that space.
516 for (int i = 0; i < dst.count(); ++i) {
517 dst[i] = nextRing->index(dst[i]);
518 }
519
520 for (int cur = 0; cur < lastRing.numPts0(); ++cur) {
521 int next = (cur + 1) % lastRing.numPts0();
522
523 this->addTri(lastRing.index(cur), lastRing.index(next), dst[next]);
524 this->addTri(lastRing.index(cur), dst[next], dst[cur]);
525 }
526
527 if (done) {
528 this->fanRing(*nextRing);
529 }
530
531 return done;
532 }
533
534 void GrAAConvexTessellator::validate() const {
535 SkASSERT(fPts.count() == fDepths.count());
536 SkASSERT(fPts.count() == fMovable.count());
537 SkASSERT(0 == (fIndices.count() % 3));
538 }
539
540 //////////////////////////////////////////////////////////////////////////////
541 void GrRing::setReserve(int numPts) {
542 fIndices.setReserve(numPts);
543 fNorms.setReserve(numPts);
544 fBisectors.setReserve(numPts);
545 fOrigEdgeIds.setReserve(numPts);
546
547 fPts2.setReserve(numPts);
548 fOrigEdgeIds2.setReserve(numPts);
549 fOriginatingIdx2.setReserve(numPts);
550 fNeedsToBeNew2.setReserve(numPts);
551 }
552
553 void GrRing::rewind() {
554 fIndices.rewind();
555 fNorms.rewind();
556 fBisectors.rewind();
557 fOrigEdgeIds.rewind();
558
559 fPts2.rewind();
560 fOrigEdgeIds2.rewind();
561 fOriginatingIdx2.rewind();
562 fNeedsToBeNew2.rewind();
563 }
564
565 void GrRing::init(const GrAAConvexTessellator& tess) {
566 this->computeNormals(tess);
567 this->computeBisectors();
568 SkASSERT(this->isConvex(tess));
569 }
570
571 SkScalar GrRing::computeDepthFromEdge(GrAAConvexTessellator& tess,
572 int edgeIdx,
573 const SkPoint& p) const {
574 SkASSERT(edgeIdx < this->numPts0());
575
576 SkPoint v = p - tess.point(fIndices[edgeIdx]);
577 SkScalar depth = -fNorms[edgeIdx].dot(v);
578 SkASSERT(depth >= 0.0f);
579 return depth;
580 }
581
582 // Compute the outward facing normal at each vertex.
583 void GrRing::computeNormals(const GrAAConvexTessellator& tess) {
584 fNorms.setCount(fIndices.count());
585
586 for (int cur = 0; cur < fIndices.count(); ++cur) {
587 int next = (cur + 1) % fIndices.count();
588
589 fNorms[cur] = tess.point(fIndices[next]) - tess.point(fIndices[cur]);
590 SkScalar len = SkPoint::Normalize(&fNorms[cur]);
591 SkASSERT(len > 0.0f);
592 fNorms[cur].setOrthog(fNorms[cur], fSide);
593
594 SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length()));
595 }
596 }
597
598 void GrRing::computeBisectors() {
599 fBisectors.setCount(fNorms.count());
600
601 int prev = fBisectors.count() - 1;
602 for (int cur = 0; cur < fBisectors.count(); prev = cur, ++cur) {
603 fBisectors[cur] = fNorms[cur] + fNorms[prev];
604 fBisectors[cur].normalize();
605 fBisectors[cur].negate(); // make the bisector face in
606
607 SkASSERT(SkScalarNearlyEqual(1.0f, fBisectors[cur].length()));
608 }
609 }
610
611 void GrRing::validate() const {
612 SkASSERT(fPts2.count() == fOriginatingIdx2.count());
613 SkASSERT(fPts2.count() == fOrigEdgeIds2.count());
614 SkASSERT(fPts2.count() == fNeedsToBeNew2.count());
615 }
616
617 //////////////////////////////////////////////////////////////////////////////
618 #ifdef SK_DEBUG
619 // Is this ring convex?
620 bool GrRing::isConvex(const GrAAConvexTessellator& tess) const {
621 if (fIndices.count() < 3) {
622 return false;
623 }
624
625 SkPoint prev = tess.point(fIndices[0]) - tess.point(fIndices[fIndices.count( )-1]);
626 SkPoint cur = tess.point(fIndices[1]) - tess.point(fIndices[0]);
627 SkScalar minDot = prev.fX * cur.fY - prev.fY * cur.fX;
628 SkScalar maxDot = minDot;
629
630 prev = cur;
631 for (int i = 1; i < fIndices.count(); ++i) {
632 int next = (i + 1) % fIndices.count();
633
634 cur = tess.point(fIndices[next]) - tess.point(fIndices[i]);
635 SkScalar dot = prev.fX * cur.fY - prev.fY * cur.fX;
636
637 minDot = SkMinScalar(minDot, dot);
638 maxDot = SkMaxScalar(maxDot, dot);
639
640 prev = cur;
641 }
642
643 return (maxDot > 0.0f) == (minDot >= 0.0f);
644 }
645
646 static SkScalar capsule_depth(const SkPoint& p0, const SkPoint& p1,
647 const SkPoint& test, SkPoint::Side side,
648 int* sign) {
649 *sign = -1;
650 SkPoint edge = p1 - p0;
651 SkScalar len = SkPoint::Normalize(&edge);
652
653 SkPoint testVec = test - p0;
654
655 SkScalar d0 = edge.dot(testVec);
656 if (d0 < 0.0f) {
657 return SkPoint::Distance(p0, test);
658 }
659 if (d0 > len) {
660 return SkPoint::Distance(p1, test);
661 }
662
663 SkScalar perpDist = testVec.fY * edge.fX - testVec.fX * edge.fY;
664 if (SkPoint::kRight_Side == side) {
665 perpDist = -perpDist;
666 }
667
668 if (perpDist < 0.0f) {
669 perpDist = -perpDist;
670 } else {
671 *sign = 1;
672 }
673 return perpDist;
674 }
675
676 SkScalar GrAAConvexTessellator::computeRealDepth(const SkPoint& p) const {
677 SkScalar minDist = SK_ScalarMax;
678 int closestEdge, closestSign, sign;
679
680 for (int edge = 0; edge < fInitialRing.numPts0(); ++edge) {
681 SkScalar dist = capsule_depth(this->point(edge),
682 this->point((edge+1) % fInitialRing.numPts 0()),
683 p, fInitialRing.side(), &sign);
684 SkASSERT(dist >= 0.0f);
685
686 if (minDist > dist) {
687 minDist = dist;
688 closestEdge = edge;
689 closestSign = sign;
690 }
691 }
692
693 return closestSign * minDist;
694 }
695
696 // Verify that the incrementally computed depths are close to the actual depths.
697 void GrAAConvexTessellator::checkAllDepths() const {
698 for (int cur = 0; cur < this->numPts(); ++cur) {
699 SkScalar realDepth = this->computeRealDepth(this->point(cur));
700 SkASSERT(SkScalarNearlyEqual(realDepth, this->depth(cur)));
701 }
702 }
703 #endif
704
705 //////////////////////////////////////////////////////////////////////////////
706 #if GR_AA_CONVEX_TESSELLATOR_VIZ
707 static const SkScalar kPointRadius = 3.0f;
708 static const SkScalar kArrowStrokeWidth = 0.75f;
709 static const SkScalar kArrowLength = 10.0f;
710 static const SkScalar kEdgeTextSize = 6.0f;
711 static const SkScalar kPointTextSize = 4.0f;
712
713 static void draw_point(SkCanvas* canvas, const SkPoint& p, SkScalar paramValue, bool stroke) {
714 SkPaint paint;
715 SkASSERT(paramValue <= 1.0f);
716 int gs = int(255*paramValue);
717 paint.setARGB(255, gs, gs, gs);
718
719 canvas->drawCircle(p.fX, p.fY, kPointRadius, paint);
720
721 if (stroke) {
722 SkPaint stroke;
723 stroke.setColor(SK_ColorYELLOW);
724 stroke.setStyle(SkPaint::kStroke_Style);
725 stroke.setStrokeWidth(kPointRadius/3.0f);
726 canvas->drawCircle(p.fX, p.fY, kPointRadius, stroke);
727 }
728 }
729
730 static void draw_line(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, Sk Color color) {
731 SkPaint p;
732 p.setColor(color);
733
734 canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, p);
735 }
736
737 static void draw_arrow(SkCanvas*canvas, const SkPoint& p, const SkPoint &n,
738 SkScalar len, SkColor color) {
739 SkPaint paint;
740 paint.setColor(color);
741 paint.setStrokeWidth(kArrowStrokeWidth);
742 paint.setStyle(SkPaint::kStroke_Style);
743
744 canvas->drawLine(p.fX, p.fY,
745 p.fX + len * n.fX, p.fY + len * n.fY,
746 paint);
747 }
748
749 void GrRing::draw(SkCanvas* canvas, const GrAAConvexTessellator& tess) const {
750 SkPaint paint;
751 paint.setTextSize(kEdgeTextSize);
752
753 for (int cur = 0; cur < fIndices.count(); ++cur) {
754 int next = (cur + 1) % fIndices.count();
755
756 draw_line(canvas,
757 tess.point(fIndices[cur]),
758 tess.point(fIndices[next]),
759 SK_ColorGREEN);
760
761 SkPoint mid = tess.point(fIndices[cur]) + tess.point(fIndices[next]);
762 mid.scale(0.5f);
763
764 if (fNorms.count()) {
765 draw_arrow(canvas, mid, fNorms[cur], kArrowLength, SK_ColorRED);
766 mid.fX += (kArrowLength/2) * fNorms[cur].fX;
767 mid.fY += (kArrowLength/2) * fNorms[cur].fY;
768 }
769
770 SkString num;
771 num.printf("%d", this->origEdgeID(cur));
772 canvas->drawText(num.c_str(), num.size(), mid.fX, mid.fY, paint);
773
774 if (fBisectors.count()) {
775 draw_arrow(canvas, tess.point(fIndices[cur]), fBisectors[cur],
776 kArrowLength, SK_ColorBLUE);
777 }
778 }
779 }
780
781 void GrAAConvexTessellator::draw(SkCanvas* canvas) const {
782 for (int i = 0; i < fIndices.count(); i += 3) {
783 SkASSERT(fIndices[i] < this->numPts()) ;
784 SkASSERT(fIndices[i+1] < this->numPts()) ;
785 SkASSERT(fIndices[i+2] < this->numPts()) ;
786
787 draw_line(canvas,
788 this->point(this->fIndices[i]), this->point(this->fIndices[i+1 ]),
789 SK_ColorBLACK);
790 draw_line(canvas,
791 this->point(this->fIndices[i+1]), this->point(this->fIndices[i +2]),
792 SK_ColorBLACK);
793 draw_line(canvas,
794 this->point(this->fIndices[i+2]), this->point(this->fIndices[i ]),
795 SK_ColorBLACK);
796 }
797
798 fInitialRing.draw(canvas, *this);
799 for (int i = 0; i < fRings.count(); ++i) {
800 fRings[i]->draw(canvas, *this);
801 }
802
803 for (int i = 0; i < this->numPts(); ++i) {
804 draw_point(canvas,
805 this->point(i), 0.5f + (this->depth(i)/(2*fTargetDepth)),
806 !this->movable(i));
807
808 SkPaint paint;
809 paint.setTextSize(kPointTextSize);
810 paint.setTextAlign(SkPaint::kCenter_Align);
811 if (this->depth(i) <= -fTargetDepth) {
812 paint.setColor(SK_ColorWHITE);
813 }
814
815 SkString num;
816 num.printf("%d", i);
817 canvas->drawText(num.c_str(), num.size(),
818 this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f ),
819 paint);
820 }
821 }
822
823 #endif
824
OLDNEW
« src/gpu/GrAAConvexTessellator.h ('K') | « src/gpu/GrAAConvexTessellator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698