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

Unified Diff: src/gpu/GrAAConvexTessellator.cpp

Issue 1120023003: Refugee from Dead Machine 13: New version of the convex AA tessellator Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: update Created 4 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/GrAAConvexTessellator.h ('k') | src/gpu/GrAARectRenderer.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/GrAAConvexTessellator.cpp
diff --git a/src/gpu/GrAAConvexTessellator.cpp b/src/gpu/GrAAConvexTessellator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c1e2dc03675dd3068820ca88e55e53800796cc4
--- /dev/null
+++ b/src/gpu/GrAAConvexTessellator.cpp
@@ -0,0 +1,1223 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAAConvexTessellator.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkString.h"
+
+// Next steps:
+// use in AAConvexPathRenderer
+// add an interactive sample app slide
+// add debug check that all points are suitably far apart
+// test more degenerate cases
+
+// The tolerance for fusing vertices and eliminating colinear lines (It is in device space).
+static const SkScalar kClose = (SK_Scalar1 / 16);
+static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
+
+static void intersect2(const SkPoint& p0, const SkPoint& n0,
+ const SkPoint& p1, const SkPoint& n1,
+ SkScalar* t0) {
+ SkASSERT(SkScalarNearlyEqual(n0.lengthSqd(), 1.0f));
+ SkASSERT(SkScalarNearlyEqual(n1.lengthSqd(), 1.0f));
+
+ const SkPoint v = p1 - p0;
+
+ SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX;
+ *t0 = (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
+
+ if (*t0 < 0.0f) {
+ *t0 = 100000;
+ }
+// SkASSERT(*t0 >= 0.0f);
+}
+
+static void intersect(const SkPoint& p0, const SkPoint& n0,
+ const SkPoint& p1, const SkPoint& n1,
+ SkScalar* t0, SkScalar* t1) {
+ SkASSERT(SkScalarNearlyEqual(n0.lengthSqd(), 1.0f));
+ SkASSERT(SkScalarNearlyEqual(n1.lengthSqd(), 1.0f));
+
+ const SkPoint v = p1 - p0;
+
+ SkScalar perpDot = n0.fX * n1.fY - n0.fY * n1.fX;
+ *t0 = (v.fX * n1.fY - v.fY * n1.fX) / perpDot;
+ *t1 = (v.fX * n0.fY - v.fY * n0.fX) / perpDot;
+
+ SkASSERT(*t0 >= 0.0f && *t1 >= 0.0f);
+}
+
+// This is a special case version of intersect where we have the vector
+// perpendicular to the second line rather than the vector parallel to it.
+static SkScalar perp_intersect(const SkPoint& p0, const SkPoint& n0,
+ const SkPoint& p1, const SkPoint& perp) {
+ SkASSERT(SkScalarNearlyEqual(n0.lengthSqd(), 1.0f));
+ SkASSERT(SkScalarNearlyEqual(perp.lengthSqd(), 1.0f));
+
+ const SkPoint v = p1 - p0;
+ SkScalar perpDot = n0.dot(perp);
+ return v.dot(perp) / perpDot;
+}
+
+static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
+ SkScalar distSq = p0.distanceToSqd(p1);
+ return distSq < kCloseSqd;
+}
+
+static SkScalar abs_dist_from_line(const SkPoint& p0, const SkPoint& p1, const SkPoint& test) {
+ // TODO: update this to use the normals computed for a ring rather than recomputing
+ SkPoint v = p1 - p0;
+ v.normalize();
+
+ SkPoint testV = test - p0;
+ SkScalar dist = testV.fX * v.fY - testV.fY * v.fX;
+ return SkScalarAbs(dist);
+}
+
+int GrAAConvexTessellator::addPt(const SkPoint& pt, SkScalar depth) {
+ this->validate();
+
+ int index = fPts.count();
+ *fPts.push() = pt;
+ *fDepths.push() = depth;
+
+ this->validate();
+ return index;
+}
+
+void GrAAConvexTessellator::popLastPt() {
+ this->validate();
+
+ fPts.pop();
+ fDepths.pop();
+
+ this->validate();
+}
+
+void GrAAConvexTessellator::popFirstPtShuffle() {
+ this->validate();
+
+ fPts.removeShuffle(0);
+ fDepths.removeShuffle(0);
+
+ this->validate();
+}
+
+void GrAAConvexTessellator::addTri(int i0, int i1, int i2) {
+ if (i0 == i1 || i1 == i2 || i2 == i0) {
+ return;
+ }
+
+ *fIndices.push() = i0;
+ *fIndices.push() = i1;
+ *fIndices.push() = i2;
+}
+
+void GrAAConvexTessellator::rewind() {
+ fPts.rewind();
+ fDepths.rewind();
+ fIndices.rewind();
+ fNorms.rewind();
+ fBisectors.rewind();
+
+ fPQueue.rewind();
+ fVerts1.rewind();
+}
+
+void GrAAConvexTessellator::computeNormals() {
+ int numPts = this->numPts();
+
+ fNorms.setCount(numPts);
+
+ for (int cur = 0; cur < numPts; ++cur) {
+ int next = (cur + 1) % numPts;
+
+ fNorms[cur] = fPts[next] - fPts[cur];
+ SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms[cur]);
+ SkASSERT(len > 0.0f);
+ fNorms[cur].setOrthog(fNorms[cur], fSide);
+
+ SkASSERT(SkScalarNearlyEqual(1.0f, fNorms[cur].length()));
+ }
+}
+
+void GrAAConvexTessellator::computeBisectors() {
+ fBisectors.setCount(fNorms.count());
+
+ int prev = fBisectors.count() - 1;
+ for (int cur = 0; cur < fBisectors.count(); prev = cur, ++cur) {
+ fBisectors[cur] = fNorms[cur] + fNorms[prev];
+ fBisectors[cur].normalize();
+ fBisectors[cur].negate(); // make the bisector face in
+
+ SkASSERT(SkScalarNearlyEqual(1.0f, fBisectors[cur].length()));
+ }
+}
+
+// The general idea here is to, conceptually, start with the original polygon and slide
+// the vertices along the bisectors until the first intersection. At that
+// point two of the edges collapse and the process repeats on the new polygon.
+bool GrAAConvexTessellator::tessellate(const SkMatrix& m, const SkPath& path) {
+ if (!this->extractFromPath(m, path)) {
+ return false;
+ }
+
+ this->computeNormals();
+ this->computeBisectors();
+ this->initPQueue();
+
+ this->createOuterRing();
+ this->createInsetRing();
+
+ this->validate();
+ SkDEBUGCODE(this->checkAllDepths();)
+ return true;
+}
+
+SkScalar GrAAConvexTessellator::computeDepthFromEdge(int edgeIdx, const SkPoint& p) const {
+ SkASSERT(edgeIdx < fNorms.count());
+
+ SkPoint v = p - fPts[edgeIdx];
+ SkScalar depth = -fNorms[edgeIdx].dot(v);
+ SkASSERT(depth >= 0.0f);
+ return depth;
+}
+
+// Find a point that is 'desiredDepth' away from the 'edgeIdx'-th edge and lies
+// along the 'bisector' from the 'startIdx'-th point.
+SkPoint GrAAConvexTessellator::computePtAlongBisector(int startIdx,
+ const SkVector& bisector,
+ int edgeIdx,
+ SkScalar desiredDepth) const {
+ const SkPoint& norm = fNorms[edgeIdx];
+
+ // First find the point where the edge and the bisector intersect
+ SkPoint newP;
+ SkScalar t = perp_intersect(fPts[startIdx], bisector, fPts[edgeIdx], norm);
+ if (SkScalarNearlyEqual(t, 0.0f)) {
+ // the start point was one of the original ring points
+ SkASSERT(startIdx < fNorms.count());
+ newP = fPts[startIdx];
+ } else {
+ SkASSERT(t < 0.0f);
+ newP = bisector;
+ newP.scale(t);
+ newP += fPts[startIdx];
+ }
+
+ // Then offset along the bisector from that point the correct distance
+ t = -desiredDepth / bisector.dot(norm);
+ SkASSERT(t > 0.0f);
+ SkPoint result = bisector;
+ result.scale(t);
+ result += newP;
+
+ return result;
+}
+
+bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) {
+ SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
+ SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
+
+ // Outer ring: 3*numPts
+ // Middle ring: numPts
+ // Presumptive inner ring: numPts
+ this->reservePts(5*path.countPoints());
+ // Outer ring: 12*numPts
+ // Middle ring: 0
+ // Presumptive inner ring: 6*numPts + 6
+ fIndices.setReserve(18*path.countPoints() + 6);
+
+ SkScalar minCross = SK_ScalarMax, maxCross = -SK_ScalarMax;
+
+ SkPath::Iter iter(path, true);
+ SkPoint pts[4];
+ SkPath::Verb verb;
+ while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ m.mapPoints(&pts[1], 1);
+ if (this->numPts() > 0 && duplicate_pt(pts[1], fPts.top())) {
+ continue;
+ }
+
+ if (this->numPts() >= 2 &&
+ abs_dist_from_line(fPts[this->numPts()-1], fPts[this->numPts()-2], pts[1]) <
+ kClose) {
+ // The old last point is on the line from the second to last to the new point
+ this->popLastPt();
+ }
+
+ this->addPt(pts[1], 0.0f);
+
+ if (this->numPts() >= 3) {
+ int cur = this->numPts()-1;
+
+ SkScalar cross = SkPoint::CrossProduct(fPts[cur] - fPts[cur-1],
+ fPts[cur-1] - fPts[cur-2]);
+ if (maxCross < cross) {
+ maxCross = cross;
+ }
+ if (minCross > cross) {
+ minCross = cross;
+ }
+ }
+ break;
+ case SkPath::kQuad_Verb:
+ case SkPath::kConic_Verb:
+ case SkPath::kCubic_Verb:
+ SkASSERT(false);
+ break;
+ case SkPath::kMove_Verb:
+ case SkPath::kClose_Verb:
+ case SkPath::kDone_Verb:
+ break;
+ }
+ }
+
+ if (!this->numPts()) {
+ return false;
+ }
+
+ // check if last point is a duplicate of the first point. If so, remove it.
+ if (duplicate_pt(fPts[this->numPts()-1], fPts[0])) {
+ this->popLastPt();
+ }
+
+ if (this->numPts() >= 3 &&
+ abs_dist_from_line(fPts[this->numPts()-1], fPts[this->numPts()-2], fPts[0]) < kClose) {
+ // The last point is on the line from the second to last to the first point.
+ this->popLastPt();
+ }
+
+ if (this->numPts() >= 3 &&
+ abs_dist_from_line(fPts[0], fPts[this->numPts()-1], fPts[1]) < kClose) {
+ // The first point is on the line from the last to the second.
+ this->popFirstPtShuffle();
+ }
+
+ if (this->numPts() < 3) {
+ return false;
+ }
+
+ // Check the cross produce of the final trio
+ SkScalar cross = SkPoint::CrossProduct(fPts[1] - fPts[0], fPts[0] - fPts[fPts.count()-1]);
+ if (maxCross < cross) {
+ maxCross = cross;
+ }
+ if (minCross > cross) {
+ minCross = cross;
+ }
+
+ if (maxCross > 0.0f) {
+ SkASSERT(minCross >= 0.0f);
+ fSide = SkPoint::kRight_Side;
+ } else {
+ SkASSERT(minCross <= 0.0f);
+ fSide = SkPoint::kLeft_Side;
+ }
+
+ this->validate();
+ return true;
+}
+
+void GrAAConvexTessellator::createOuterRing() {
+ // For now, we're only generating one outer ring (at the start). This
+ // could be relaxed for stroking use cases.
+ SkASSERT(0 == fIndices.count());
+ SkASSERT(fPts.count() == fNorms.count());
+
+ const int numPts = fPts.count();
+
+ // For each vertex of the original polygon we add three points to the
+ // outset polygon - one extending perpendicular to each impinging edge
+ // and one along the bisector. Two triangles are added for each corner
+ // and two are added along each edge.
+ int prev = numPts - 1;
+ int lastPerpIdx = -1, firstPerpIdx, newIdx0, newIdx1, newIdx2;
+ for (int cur = 0; cur < numPts; ++cur) {
+ // The perpendicular point for the last edge
+ SkPoint temp = fNorms[prev];
+ temp.scale(fTargetDepth);
+ temp += fPts[cur];
+
+ // We know it isn't a duplicate of the prior point (since it and this
+ // one are just perpendicular offsets from the non-merged polygon points)
+ newIdx0 = this->addPt(temp, -fTargetDepth);
+
+ // The bisector outset point
+ temp = fBisectors[cur];
+ temp.scale(-fTargetDepth); // the bisectors point in
+ temp += fPts[cur];
+
+ // For very shallow angles all the corner points could fuse
+ if (duplicate_pt(temp, fPts[newIdx0])) {
+ newIdx1 = newIdx0;
+ } else {
+ newIdx1 = this->addPt(temp, -fTargetDepth);
+ }
+
+ // The perpendicular point for the next edge.
+ temp = fNorms[cur];
+ temp.scale(fTargetDepth);
+ temp += fPts[cur];
+
+ // For very shallow angles all the corner points could fuse.
+ if (duplicate_pt(temp, fPts[newIdx1])) {
+ newIdx2 = newIdx1;
+ } else {
+ newIdx2 = this->addPt(temp, -fTargetDepth);
+ }
+
+ if (0 == cur) {
+ // Store the index of the first perpendicular point to finish up
+ firstPerpIdx = newIdx0;
+ SkASSERT(-1 == lastPerpIdx);
+ } else {
+ // The triangles for the previous edge
+ this->addTri(prev, newIdx0, cur);
+ this->addTri(prev, lastPerpIdx, newIdx0);
+ }
+
+ // The two triangles for the corner
+ this->addTri(cur, newIdx0, newIdx1);
+ this->addTri(cur, newIdx1, newIdx2);
+
+ prev = cur;
+ // Track the last perpendicular outset point so we can construct the
+ // trailing edge triangles.
+ lastPerpIdx = newIdx2;
+ }
+
+ // pick up the final edge rect
+ this->addTri(numPts-1, firstPerpIdx, 0);
+ this->addTri(numPts-1, lastPerpIdx, firstPerpIdx);
+
+ this->validate();
+}
+
+
+void GrAAConvexTessellator::stepAllIn() {
+#if 1
+ //SkASSERT(fPQueue.count() >= 3);
+
+ // 'dst' is the index into the vertex array each point in the last ring
+ // maps to/transforms into in the next ring.
+ SkTDArray<int> dst;
+ dst.setCount(fVerts1.count());
+ SkTDArray<SkPoint> pts;
+ pts.setReserve(fVerts1.count());
+
+ GrVertInfo* first = &fVerts1[0];
+
+ // Check on the first point (who compares with no one)
+ SkPoint newPt = this->computePtAlongBisector(first->originatingPt(),
+ first->bisector(),
+ first->edgeId(),
+ fTargetDepth);
+ dst[0] = 0;
+ *pts.push() = newPt;
+
+ GrVertInfo* cur;
+ int i;
+ // Handle the middle points (who only compare with the prior point)
+ for (i = 1, cur = first->next(); cur != first->prev(); ++i, cur=cur->next()) {
+ newPt = this->computePtAlongBisector(cur->originatingPt(),
+ cur->bisector(),
+ cur->edgeId(),
+ fTargetDepth);
+ if (!duplicate_pt(newPt, pts.top())) {
+ dst[i] = pts.count();
+ *pts.push() = newPt;
+ } else {
+ dst[i] = pts.count()-1;
+ }
+ }
+ SkASSERT(i == fVerts1.count()-1);
+
+ // Check on the last point (handling the wrap around)
+ newPt = this->computePtAlongBisector(first->prev()->originatingPt(),
+ first->prev()->bisector(),
+ first->prev()->edgeId(),
+ fTargetDepth);
+ bool dupPrev = duplicate_pt(newPt, pts.top());
+ bool dupNext = duplicate_pt(newPt, pts[0]);
+
+ if (!dupPrev && !dupNext) {
+ dst.top() = pts.count();
+ *pts.push() = newPt;
+ } else if (dupPrev && !dupNext) {
+ dst.top() = pts.count()-1;
+ } else if (!dupPrev && dupNext) {
+ dst.top() = 0;
+ } else {
+ bool dupPrevVsNext = duplicate_pt(pts[0], pts.top());
+
+ if (!dupPrevVsNext) {
+ dst.top() = pts.count()-1;
+ } else {
+ if (pts.count() > 1) {
+ pts.pop();
+ }
+
+ dst.top() = 0;
+ dst[dst.count()-2] = 0;
+ }
+ }
+
+ SkTDArray<int> remap; // yuck
+ remap.setCount(pts.count());
+
+ // Fold the new ring's points into the global pool
+ for (int i = 0; i < pts.count(); ++i) {
+ remap[i] = this->addPt(pts[i], fTargetDepth);
+ }
+
+ // 'dst' currently has indices into the ring. Remap these to be indices
+ // into the global pool since the triangulation operates in that space.
+ for (int i = 0; i < dst.count(); ++i) {
+ dst[i] = remap[dst[i]];
+ }
+
+ this->addTri(first->originatingPt(), first->next()->originatingPt(), dst[0]);
+ this->addTri(dst[0], first->next()->originatingPt(), dst[1]);
+
+ for (i = 1, cur = first->next(); cur != first; ++i, cur=cur->next()) {
+ this->addTri(cur->originatingPt(), cur->next()->originatingPt(), dst[i]);
+ this->addTri(dst[i], cur->next()->originatingPt(), dst[(i+1)%dst.count()]);
+ }
+
+ // fan out from point 0
+ for (int cur = 1; cur < remap.count()-1; ++cur) {
+ this->addTri(remap[0], remap[cur], remap[cur+1]);
+ }
+#endif
+}
+
+static void add_to_dll(GrCandidateVert* prev, GrCandidateVert* newV, GrCandidateVert* next) {
+ SkASSERT(newV);
+
+ newV->setPrev(prev);
+ newV->setNext(next);
+
+ if (prev) {
+ prev->setNext(newV);
+ }
+ if (next) {
+ next->setPrev(newV);
+ }
+}
+
+static void rm_from_dll(GrCandidateVert* v) {
+ if (v->prev()) {
+ v->prev()->setNext(v->next());
+ }
+ if (v->next()) {
+ v->next()->setPrev(v->prev());
+ }
+}
+
+void GrAAConvexTessellator::createCandidate(GrVertInfo* v, GrCandidateVert** prev,
+ GrCandidateVert* next, bool checkNext){
+ SkScalar tPrev, tNext;
+ intersect2(this->point(v->originatingPt()), v->bisector(),
+ this->point(v->prev()->originatingPt()), v->prev()->bisector(),
+ &tPrev);
+ intersect2(this->point(v->originatingPt()), v->bisector(),
+ this->point(v->next()->originatingPt()), v->next()->bisector(),
+ &tNext);
+
+ SkScalar t;
+ GrVertInfo* prevSubsumed, *nextSubsumed;
+ if (tPrev < tNext) {
+ t = tPrev;
+ prevSubsumed = v->prev();
+ nextSubsumed = v;
+ } else {
+ t = tNext;
+ prevSubsumed = v;
+ nextSubsumed = v->next();
+ }
+
+ SkPoint newPt = v->bisector();
+ newPt.scale(t);
+ newPt += this->point(v->originatingPt());
+
+ SkScalar depth = this->computeDepthFromEdge(v->edgeId(), newPt);
+
+ if (SkScalarNearlyEqual(depth, (*prev)->depth(), kClose) &&
+ duplicate_pt((*prev)->pt(), newPt)) {
+ (*prev)->setNextSubsumed(v);
+ (*prev)->recomputeBi();
+ return;
+ }
+
+ if (checkNext && SkScalarNearlyEqual(depth, next->depth(), kClose) &&
+ duplicate_pt(next->pt(), newPt)) {
+ next->setPrevSubsumed(v);
+ next->recomputeBi();
+ return;
+ }
+
+ GrCandidateVert* newI = this->get();
+
+ newI->setPt(newPt);
+ newI->setBisector(v->bisector());
+ newI->setDepth(depth);
+ newI->setOriginatingPt(v->originatingPt());
+ newI->setPrevSubsumed(prevSubsumed);
+ newI->setNextSubsumed(nextSubsumed);
+ add_to_dll(*prev, newI, next);
+
+ fPQueue.insert(newI);
+ *prev = newI;
+}
+
+void GrAAConvexTessellator::recompute(GrCandidateVert* v) {
+
+ GrVertInfo* from = v->prevSubsumed();
+ GrVertInfo* to = v->nextSubsumed();
+
+ GrCandidateVert* trailing = v->prev();
+ GrCandidateVert* leading = v->next();
+
+ // First retract 'v'
+ rm_from_dll(v);
+ fPQueue.remove(v);
+ this->release(v);
+
+ for (GrVertInfo* cur = from; cur != to; cur = cur->next()) {
+ this->createCandidate(cur, &trailing, leading, false);
+ }
+
+ this->createCandidate(to, &trailing, leading, true);
+}
+
+void GrAAConvexTessellator::initPQueue() {
+
+ fVerts1.setCount(fPts.count());
+ fCandidates1.setCount(fPts.count());
+ for (int i = fPts.count()-1; i >= 0; --i) {
+ fCandidates1[i].setOriginatingPt(i);
+ this->release(&fCandidates1[i]);
+ }
+ fPQueue.setReserve(fPts.count());
+
+ int prev = fPts.count()-1;
+ for (int cur = 0; cur < fPts.count(); prev = cur, ++cur) {
+ int next = (cur + 1) % fPts.count();
+
+ fVerts1[cur].setOriginatingPt(cur);
+ fVerts1[cur].setPrev(&fVerts1[prev]);
+ fVerts1[cur].setNext(&fVerts1[next]);
+
+ fVerts1[cur].setBisector(fBisectors[cur]);
+ fVerts1[cur].setEdgeId(cur);
+ }
+
+ SkScalar tTemp, tPrev, tNext;
+ intersect(fPts.top(), fBisectors.top(),
+ fPts[0], fBisectors[0],
+ &tTemp, &tPrev);
+
+ GrCandidateVert* prevI = NULL;
+
+ // Add all the candidate points to a priority queue sorted by the depth of the first
+ // intersection between the bisector of its originating point and its two neighbor's bisectors
+ for (int cur = 0; cur < fPts.count(); ++cur) {
+ int next = (cur + 1) % fPts.count();
+
+ intersect(fPts[cur], fBisectors[cur],
+ fPts[next], fBisectors[next],
+ &tNext, &tTemp);
+
+ SkScalar t;
+ GrVertInfo* prevSubsumed, *nextSubsumed;
+ if (tPrev < tNext) {
+ t = tPrev;
+ prevSubsumed = fVerts1[cur].prev();
+ nextSubsumed = &fVerts1[cur];
+ } else {
+ t = tNext;
+ prevSubsumed = &fVerts1[cur];
+ nextSubsumed = fVerts1[cur].next();
+ }
+
+ SkPoint newPt = fBisectors[cur];
+ newPt.scale(t);
+ newPt += fPts[cur];
+
+ SkScalar depth = this->computeDepthFromEdge(cur, newPt);
+
+ if (prevI && duplicate_pt(prevI->pt(), newPt)) {
+ SkASSERT(SkScalarNearlyEqual(prevI->depth(), depth));
+ // fuse with prior
+ prevI->setNextSubsumed(nextSubsumed);
+ SkVector newBi = prevI->prevSubsumed()->bisector();
+ newBi += prevI->nextSubsumed()->bisector();
+ newBi.normalize();
+ prevI->setBisector(newBi);
+ tPrev = tTemp;
+ continue;
+ }
+
+ GrCandidateVert* newI = this->get();
+
+ newI->setPt(newPt);
+ newI->setBisector(fBisectors[cur]);
+ newI->setDepth(depth);
+ newI->setOriginatingPt(cur);
+ newI->setPrevSubsumed(prevSubsumed);
+ newI->setNextSubsumed(nextSubsumed);
+ add_to_dll(prevI, newI, NULL);
+
+ fPQueue.insert(newI);
+
+ prevI = newI;
+ tPrev = tTemp;
+ }
+
+ prevI->setNext(&fCandidates1[0]);
+ fCandidates1[0].setPrev(prevI);
+
+ if (fCandidates1.count() > 1) {
+ if (duplicate_pt(prevI->pt(), fCandidates1[0].pt())) {
+ SkASSERT(SkScalarNearlyEqual(prevI->depth(), fCandidates1[0].depth()));
+
+ fCandidates1[0].setPrevSubsumed(prevI->prevSubsumed());
+ SkVector newBi = fCandidates1[0].prevSubsumed()->bisector();
+ newBi += fCandidates1[0].nextSubsumed()->bisector();
+ newBi.normalize();
+ fCandidates1[0].setBisector(newBi);
+ rm_from_dll(prevI);
+ fPQueue.remove(prevI);
+ this->release(prevI);
+ }
+ }
+}
+
+static void rm_from_dllV(GrVertInfo* v) {
+ v->prev()->setNext(v->next());
+ v->next()->setPrev(v->prev());
+ v->setNext(NULL);
+ v->setPrev(NULL);
+}
+
+static void add_to_dllV(GrVertInfo* v, GrVertInfo* prev, GrVertInfo* next) {
+ v->setPrev(prev);
+ v->setNext(next);
+
+ prev->setNext(v);
+ next->setPrev(v);
+}
+
+void GrAAConvexTessellator::removeDups(GrVertInfo* start, int* trailingIdx) {
+#if 0
+ GrVertInfo* next;
+ for (GrVertInfo* cur = start->next(); cur != start; cur = next) {
+ next = cur->next();
+
+ if (!duplicate_pt(start->newPt(), cur->newPt())) {
+ return; // found a non-duplicate
+ }
+
+ SkASSERT(SkScalarNearlyEqual(start->depth(), cur->depth(), kClose));
+ this->addTri(*trailingIdx, cur->index1(), start->index1());
+ fPQueue.remove(cur);
+ *trailingIdx = cur->index1();
+ rm_from_dll(cur);
+ }
+#endif
+}
+
+void GrAAConvexTessellator::removeDups2(GrVertInfo* start, int* trailingIdx) {
+#if 0
+ GrVertInfo* prev;
+ for (GrVertInfo* cur = start->next(); cur != start; cur = prev) {
+ prev = cur->prev();
+
+ if (!duplicate_pt(start->newPt(), cur->newPt())) {
+ return; // found a non-duplicate
+ }
+
+ SkASSERT(SkScalarNearlyEqual(start->depth(), cur->depth()));
+ this->addTri(*trailingIdx, cur->index1(), start->index1());
+ fPQueue.remove(cur);
+ *trailingIdx = cur->index1();
+ rm_from_dll(cur);
+ }
+#endif
+}
+
+void GrAAConvexTessellator::createInsetRing() {
+
+#if 0
+ while (fPQueue.count()) {
+ GrCandidateVert* curI = fPQueue.peek();
+
+ int newIdx = this->addPt(curI->pt(), curI->depth());
+ SkDebugf("newIdx %d\n", newIdx);
+
+ fPQueue.pop();
+ }
+
+ return;
+#endif
+
+ int floob = 0;
+ while (fPQueue.count() && floob < 4) {
+ floob++;
+
+ GrCandidateVert* curI = fPQueue.peek();
+ fPQueue.pop();
+ rm_from_dll(curI);
+
+ if (curI->depth() >= fTargetDepth) {
+ // None of the bisectors intersect before reaching the desired depth.
+ // Just step them all to the desired depth
+ this->stepAllIn();
+ return;
+ }
+
+
+ int newIdx = this->addPt(curI->pt(), curI->depth());
+
+ GrVertInfo* prev = curI->prevSubsumed();
+ GrVertInfo* next = curI->nextSubsumed();
+ int newEdgeId = next->edgeId();
+ GrVertInfo* pp = prev->prev();
+ GrVertInfo* nn = next->next();
+
+#if 0
+ SkVector newBisector = prev->bisector();
+ newBisector += next->bisector();
+ newBisector.normalize();
+#endif
+
+ if (!fPQueue.count()) {
+ GrVertInfo* foo = prev;
+ do {
+ this->addTri(foo->originatingPt(), foo->next()->originatingPt(), newIdx);
+ foo = foo->next();
+ } while (foo != next);
+ return;
+ }
+
+ bool nextNeedsRecompute = false, prevNeedsRecompute = false;
+ if (curI->next()->prevSubsumed() == next) {
+ curI->next()->setPrevSubsumed(nn);
+ nextNeedsRecompute = true;
+ }
+ if (curI->prev()->nextSubsumed() == prev) {
+ curI->prev()->setNextSubsumed(pp);
+ prevNeedsRecompute = true;
+ }
+
+ this->addTri(pp->originatingPt(), pp->next()->originatingPt(), newIdx);
+ this->addTri(nn->prev()->originatingPt(), nn->originatingPt(), newIdx);
+
+ GrVertInfo *cur = prev, *next2;
+ do {
+ next2 = cur->next();
+ this->addTri(cur->originatingPt(), next2->originatingPt(), newIdx);
+ rm_from_dllV(cur);
+ cur = next2;
+ } while (cur != nn);
+
+ GrVertInfo* newV = prev; // recycle prev - let next and all the ones in between lie fallow
+ newV->setOriginatingPt(newIdx);
+ newV->setBisector(curI->bisector());
+ newV->setEdgeId(newEdgeId);
+ add_to_dllV(newV, pp, nn);
+
+ {
+ SkScalar tPrev, tNext;
+ intersect2(curI->pt(), curI->bisector(),
+ this->point(pp->originatingPt()), pp->bisector(),
+ &tPrev);
+ intersect2(curI->pt(), curI->bisector(),
+ this->point(nn->originatingPt()), nn->bisector(),
+ &tNext);
+
+ SkScalar t;
+ GrVertInfo* prevSubsumed, *nextSubsumed;
+ if (tPrev < tNext) {
+ t = tPrev;
+ prevSubsumed = pp;
+ nextSubsumed = newV;
+ } else {
+ t = tNext;
+ prevSubsumed = newV;
+ nextSubsumed = nn;
+ }
+
+ SkPoint newPt = newV->bisector();
+ newPt.scale(t);
+ newPt += this->point(newV->originatingPt());
+
+ SkScalar depth = this->computeDepthFromEdge(newV->edgeId(), newPt);
+
+ if (!prevNeedsRecompute &&
+ SkScalarNearlyEqual(depth, curI->prev()->depth(), kClose) &&
+ duplicate_pt(curI->prev()->pt(), newPt)) {
+ curI->prev()->setNextSubsumed(newV);
+ curI->prev()->recomputeBi();
+ continue;
+ }
+
+ if (!nextNeedsRecompute &&
+ SkScalarNearlyEqual(depth, curI->next()->depth(), kClose) &&
+ duplicate_pt(curI->next()->pt(), newPt)) {
+ curI->next()->setPrevSubsumed(newV);
+ curI->next()->recomputeBi();
+ continue;
+ }
+
+ GrCandidateVert* newI = curI; // recycle curI
+
+ newI->setPt(newPt);
+ newI->setBisector(newV->bisector());
+ newI->setDepth(depth);
+ newI->setOriginatingPt(newIdx);
+ newI->setPrevSubsumed(prevSubsumed);
+ newI->setNextSubsumed(nextSubsumed);
+ add_to_dll(newI->prev(), newI, newI->next());
+
+ fPQueue.insert(newI);
+
+ if (nextNeedsRecompute) {
+ this->recompute(newI->next());
+ }
+ if (prevNeedsRecompute) {
+ this->recompute(newI->prev());
+ }
+ }
+
+#if 0
+ fPQueue.remove(curVert);
+ fPQueue.remove(otherVert);
+ rm_from_dll(otherVert);
+#endif
+
+#if 0
+ int dyingCurIdx = curVert->index1();
+ int newIdx = this->addPt(curVert->newPt(), curVert->depth());
+
+ if (curVert->wasNext()) {
+ this->addTri(dyingCurIdx, otherVert->index1(), newIdx); // tri for the dying edge
+ } else {
+ this->addTri(otherVert->index1(), dyingCurIdx, newIdx); // tri for the dying edge
+ }
+
+ curVert->setIndex(newIdx);
+
+ int trailingIdx = curVert->prev()->index1();
+ int leadingIdx = curVert->next()->index1();
+
+ this->removeDups(curVert, &leadingIdx);
+ this->removeDups2(curVert, &trailingIdx);
+
+ if (curVert->wasNext()) {
+ this->addTri(trailingIdx, dyingCurIdx, newIdx);
+ this->addTri(newIdx, otherVert->index1(), leadingIdx);
+ } else {
+ this->addTri(trailingIdx, otherVert->index1(), newIdx);
+ this->addTri(newIdx, dyingCurIdx, leadingIdx);
+ }
+
+ if (fPQueue.count() < 2) {
+ break;
+ }
+
+ curVert->setBisector(newBisector);
+ curVert->setEdgeId(newEdgeId);
+ curVert->computeIntersection(*this);
+
+ fPQueue.insert(curVert);
+#endif
+ }
+
+ while (fPQueue.count()) {
+ GrCandidateVert* curI = fPQueue.peek();
+
+ int newIdx = this->addPt(curI->pt(), curI->depth());
+ SkDebugf("newIdx %d\n", newIdx);
+
+ fPQueue.pop();
+ }
+
+}
+
+void GrAAConvexTessellator::validate() const {
+ SkASSERT(fPts.count() == fDepths.count());
+ SkASSERT(0 == (fIndices.count() % 3));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+void GrVertInfo::updateGeometry(const GrAAConvexTessellator& tess) {
+ SkVector prevV = tess.point(fPrev->originatingPt()) - tess.point(fIndex1);
+ SkVector nextV = tess.point(fNext->originatingPt()) - tess.point(fIndex1);
+
+ nextV.normalize();
+ prevV.normalize();
+ prevV += nextV;
+ fBisector = prevV;
+ fBisector.normalize();
+}
+
+
+void GrCandidateVert::recomputeBi() {
+ SkVector newBi = this->prevSubsumed()->bisector();
+ newBi += this->nextSubsumed()->bisector();
+ newBi.normalize();
+ this->setBisector(newBi);
+}
+
+void GrCandidateVert::computeIntersection() {
+#if 0
+ SkScalar tPrev, tNext;
+ intersect2(fPt, fBisector,
+ fPrev->fPt, fPrev->fBisector,
+ &tPrev);
+ intersect2(fPt, fBisector,
+ fNext->fPt, fNext->fBisector,
+ &tNext);
+ SkScalar t = SkMinScalar(tPrev, tNext);
+ if (tPrev < tNext) {
+ fWasNext = false;
+ } else {
+ fWasNext = true;
+ }
+
+ fNewPt = fBisector;
+ fNewPt.scale(t);
+ fNewPt += tess.point(fIndex1);
+
+ fDepth = tess.computeDepthFromEdge(fEdgeId, fNewPt);
+#endif
+}
+
+#if 0
+void GrVertInfo::computeIntersection(const GrAAConvexTessellator& tess) {
+ SkScalar tPrev, tNext;
+ intersect2(tess.point(fIndex1), fBisector,
+ tess.point(fPrev->index1()), fPrev->bisector(),
+ &tPrev);
+ intersect2(tess.point(fIndex1), fBisector,
+ tess.point(fNext->index1()), fNext->bisector(),
+ &tNext);
+ SkScalar t = SkMinScalar(tPrev, tNext);
+ if (tPrev < tNext) {
+ fWasNext = false;
+ } else {
+ fWasNext = true;
+ }
+
+ fNewPt = fBisector;
+ fNewPt.scale(t);
+ fNewPt += tess.point(fIndex1);
+
+ fDepth = tess.computeDepthFromEdge(fEdgeId, fNewPt);
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+#ifdef SK_DEBUG
+#if 0
+// Is this ring convex?
+bool GrRing::isConvex(const GrAAConvexTessellator& tess) const {
+ if (fIndices.count() < 3) {
+ return false;
+ }
+
+ SkPoint prev = tess.point(fIndices[0]) - tess.point(fIndices[fIndices.count()-1]);
+ SkPoint cur = tess.point(fIndices[1]) - tess.point(fIndices[0]);
+ SkScalar minDot = prev.fX * cur.fY - prev.fY * cur.fX;
+ SkScalar maxDot = minDot;
+
+ prev = cur;
+ for (int i = 1; i < fIndices.count(); ++i) {
+ int next = (i + 1) % fIndices.count();
+
+ cur = tess.point(fIndices[next]) - tess.point(fIndices[i]);
+ SkScalar dot = prev.fX * cur.fY - prev.fY * cur.fX;
+
+ minDot = SkMinScalar(minDot, dot);
+ maxDot = SkMaxScalar(maxDot, dot);
+
+ prev = cur;
+ }
+
+ return (maxDot > 0.0f) == (minDot >= 0.0f);
+}
+#endif
+
+static SkScalar capsule_depth(const SkPoint& p0, const SkPoint& p1,
+ const SkPoint& test, SkPoint::Side side,
+ int* sign) {
+ *sign = -1;
+ SkPoint edge = p1 - p0;
+ SkScalar len = SkPoint::Normalize(&edge);
+
+ SkPoint testVec = test - p0;
+
+ SkScalar d0 = edge.dot(testVec);
+ if (d0 < 0.0f) {
+ return SkPoint::Distance(p0, test);
+ }
+ if (d0 > len) {
+ return SkPoint::Distance(p1, test);
+ }
+
+ SkScalar perpDist = testVec.fY * edge.fX - testVec.fX * edge.fY;
+ if (SkPoint::kRight_Side == side) {
+ perpDist = -perpDist;
+ }
+
+ if (perpDist < 0.0f) {
+ perpDist = -perpDist;
+ } else {
+ *sign = 1;
+ }
+ return perpDist;
+}
+
+SkScalar GrAAConvexTessellator::computeRealDepth(const SkPoint& p) const {
+ SkScalar minDist = SK_ScalarMax;
+ int closestEdge, closestSign, sign;
+
+ for (int edge = 0; edge < fNorms.count(); ++edge) {
+ SkScalar dist = capsule_depth(fPts[edge],
+ fPts[(edge+1) % fNorms.count()],
+ p, fSide, &sign);
+ SkASSERT(dist >= 0.0f);
+
+ if (minDist > dist) {
+ minDist = dist;
+ closestEdge = edge;
+ closestSign = sign;
+ }
+ }
+
+ return closestSign * minDist;
+}
+
+// Verify that the incrementally computed depths are close to the actual depths.
+void GrAAConvexTessellator::checkAllDepths() const {
+ for (int cur = 0; cur < this->numPts(); ++cur) {
+ SkScalar realDepth = this->computeRealDepth(this->point(cur));
+ realDepth++;
+// SkASSERT(SkScalarNearlyEqual(realDepth, this->depth(cur)));
+ }
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+#if GR_AA_CONVEX_TESSELLATOR_VIZ
+static const SkScalar kPointRadius = 0.02f;
+static const SkScalar kArrowStrokeWidth = 0.0f;
+static const SkScalar kArrowLength = 0.1f;
+static const SkScalar kEdgeTextSize = 0.2f;
+static const SkScalar kPointTextSize = 0.02f;
+
+static void draw_point(SkCanvas* canvas, const SkPoint& p, SkScalar paramValue) {
+ SkPaint paint;
+ if (paramValue > 1.0f) {
+ //SkASSERT(0);
+ paramValue = 1.0f;
+ }
+ int gs = int(255*paramValue);
+ paint.setARGB(255, gs, gs, gs);
+
+ canvas->drawCircle(p.fX, p.fY, kPointRadius, paint);
+}
+
+static void draw_line(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, SkColor color) {
+ SkPaint p;
+ p.setColor(color);
+
+ canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, p);
+}
+
+static void draw_arrow(SkCanvas*canvas, const SkPoint& p, const SkPoint &n,
+ SkScalar len, SkColor color) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setStrokeWidth(kArrowStrokeWidth);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ canvas->drawLine(p.fX, p.fY,
+ p.fX + len * n.fX, p.fY + len * n.fY,
+ paint);
+}
+
+void GrAAConvexTessellator::drawInitialPoly(SkCanvas* canvas) const {
+ SkPaint paint;
+ paint.setTextSize(kEdgeTextSize);
+
+ for (int cur = 0; cur < fNorms.count(); ++cur) {
+ int next = (cur + 1) % fNorms.count();
+
+ draw_line(canvas, this->point(cur), this->point(next), SK_ColorGREEN);
+
+ SkPoint mid = this->point(cur) + this->point(next);
+ mid.scale(0.5f);
+
+ draw_arrow(canvas, mid, fNorms[cur], kArrowLength, SK_ColorRED);
+ mid.fX += (kArrowLength/2) * fNorms[cur].fX;
+ mid.fY += (kArrowLength/2) * fNorms[cur].fY;
+
+ SkString num;
+ num.printf("%d", cur);
+ canvas->drawText(num.c_str(), num.size(), mid.fX, mid.fY, paint);
+
+ draw_arrow(canvas, this->point(cur), fBisectors[cur], kArrowLength, SK_ColorBLUE);
+ }
+}
+
+void GrAAConvexTessellator::draw(SkCanvas* canvas) const {
+ for (int i = 0; i < fIndices.count(); i += 3) {
+ SkASSERT(fIndices[i] < this->numPts()) ;
+ SkASSERT(fIndices[i+1] < this->numPts()) ;
+ SkASSERT(fIndices[i+2] < this->numPts()) ;
+
+ draw_line(canvas,
+ this->point(this->fIndices[i]), this->point(this->fIndices[i+1]),
+ SK_ColorBLACK);
+ draw_line(canvas,
+ this->point(this->fIndices[i+1]), this->point(this->fIndices[i+2]),
+ SK_ColorBLACK);
+ draw_line(canvas,
+ this->point(this->fIndices[i+2]), this->point(this->fIndices[i]),
+ SK_ColorBLACK);
+ }
+
+ this->drawInitialPoly(canvas);
+
+ for (int i = 0; i < this->numPts(); ++i) {
+ draw_point(canvas,
+ this->point(i), 0.5f + (this->depth(i)/(2*fTargetDepth)));
+
+ SkPaint paint;
+ paint.setTextSize(kPointTextSize);
+ paint.setTextAlign(SkPaint::kCenter_Align);
+ if (this->depth(i) <= -fTargetDepth) {
+ paint.setColor(SK_ColorWHITE);
+ }
+
+ SkString num;
+ num.printf("%d", i);
+ canvas->drawText(num.c_str(), num.size(),
+ this->point(i).fX, this->point(i).fY+(kPointRadius/2.0f),
+ paint);
+ }
+}
+
+#endif
+
« no previous file with comments | « src/gpu/GrAAConvexTessellator.h ('k') | src/gpu/GrAARectRenderer.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698