shape ops work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@6961 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2012-12-28 22:10:41 +00:00
parent 6c406146ee
commit 10227bf467
3 changed files with 294 additions and 482 deletions

View File

@ -1927,127 +1927,94 @@ public:
} }
int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool& hitSomething, int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool& hitSomething,
bool opp) const { double mid, bool opp, bool current) const {
SkScalar bottom = fBounds.fBottom; SkScalar bottom = fBounds.fBottom;
int bestT = -1; int bestTIndex = -1;
if (bottom <= bestY) { if (bottom <= bestY) {
return bestT; return bestTIndex;
} }
SkScalar top = fBounds.fTop; SkScalar top = fBounds.fTop;
if (top >= basePt.fY) { if (top >= basePt.fY) {
return bestT; return bestTIndex;
} }
if (fBounds.fLeft > basePt.fX) { if (fBounds.fLeft > basePt.fX) {
return bestT; return bestTIndex;
} }
if (fBounds.fRight < basePt.fX) { if (fBounds.fRight < basePt.fX) {
return bestT; return bestTIndex;
} }
if (fBounds.fLeft == fBounds.fRight) { if (fBounds.fLeft == fBounds.fRight) {
return bestT; return bestTIndex;
} }
int end = 0; // intersect ray starting at basePt with edge
int xbestT = -1; Intersections intersections;
double xhitT; // OPTIMIZE: use specialty function that intersects ray with curve,
bool xhitSomething = false; // returning t values only for curve (we don't care about t on ray)
SkScalar xbestY = bestY; int pts = (*VSegmentIntersect[fVerb])(fPts, top, bottom, basePt.fX, false, intersections);
bool expectNoDx = false; if (pts == 0 || (current && pts == 1)) {
do { return bestTIndex;
int start = end; }
end = nextSpan(start, 1); if (current) {
if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) { SkASSERT(pts > 1);
int closestIdx = 0;
double closest = fabs(intersections.fT[0][0] - mid);
for (int idx = 1; idx < pts; ++idx) {
double test = fabs(intersections.fT[0][idx] - mid);
if (closest > test) {
closestIdx = idx;
closest = test;
}
}
if (closestIdx < pts - 1) {
intersections.fT[0][closestIdx] = intersections.fT[0][pts - 1];
}
--pts;
}
double bestT = -1;
for (int index = 0; index < pts; ++index) {
double foundT = intersections.fT[0][index];
SkScalar testY = (*SegmentYAtT[fVerb])(fPts, foundT);
if (approximately_negative(testY - bestY)
|| approximately_negative(basePt.fY - testY)) {
continue; continue;
} }
SkPoint edge[4]; if (pts > 1 && fVerb == SkPath::kLine_Verb) {
double startT = fTs[start].fT; // if the intersection is edge on, wait for another one
double endT = fTs[end].fT; hitSomething = true;
(*SegmentSubDivide[fVerb])(fPts, startT, endT, edge); return bestTIndex;
// intersect ray starting at basePt with edge }
Intersections intersections, intersectionsX; if (fVerb > SkPath::kLine_Verb && !approximately_less_than_zero(foundT)
// FIXME: always use original and limit results to T values within && !approximately_greater_than_one(foundT)) {
// start t and end t. SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, foundT);
// OPTIMIZE: use specialty function that intersects ray with curve, if (approximately_zero(dx)) {
// returning t values only for curve (we don't care about t on ray)
int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
false, intersections);
int ptsX = (*VSegmentIntersect[fVerb])(fPts, top, bottom, basePt.fX,
false, intersectionsX);
int index;
for (index = 0; index < ptsX; ++index) {
double xfoundT = intersectionsX.fT[0][index];
SkScalar xtestY = (*SegmentYAtT[fVerb])(fPts, xfoundT);
if (approximately_negative(xtestY - bestY)
|| approximately_negative(basePt.fY - xtestY)) {
continue; continue;
} }
xhitSomething = true;
if (pts > 1 && fVerb == SkPath::kLine_Verb) {
// if the intersection is edge on, wait for another one
expectNoDx = true;
break;
}
if (fVerb > SkPath::kLine_Verb
&& !approximately_negative(xfoundT - startT)
&& !approximately_negative(endT - xfoundT)) {
SkScalar xdx = (*SegmentDXAtT[fVerb])(fPts, xfoundT);
if (approximately_zero(xdx)) {
continue;
}
}
xbestY = xtestY;
while (start + 1 < end && fTs[start].fDone) {
++start;
}
xbestT = xfoundT < 1 ? start : end;
xhitT = xfoundT;
} }
for (index = 0; index < pts; ++index) { bestY = testY;
double foundT = intersections.fT[0][index]; bestT = foundT;
SkScalar testY = (*SegmentYAtT[fVerb])(edge, foundT);
if (bestY < testY && testY < basePt.fY) {
hitSomething = true;
if (pts > 1 && fVerb == SkPath::kLine_Verb) {
// if the intersection is edge on, wait for another one
SkASSERT(expectNoDx);
return -1;
}
if (fVerb > SkPath::kLine_Verb
&& !approximately_less_than_zero(foundT)
&& !approximately_greater_than_one(foundT)) {
SkScalar dx = (*SegmentDXAtT[fVerb])(edge, foundT);
if (approximately_zero(dx)) {
continue;
}
}
bestY = testY;
while (start + 1 < end && fTs[start].fDone) {
++start;
}
bestT = foundT < 1 ? start : end;
hitT = startT + (endT - startT) * foundT;
}
}
} while (fTs[end].fT != 1);
SkASSERT(!expectNoDx);
if (bestT != xbestT) {
SkDebugf("%s mismatch bestT=%d xbestT=%d\n", __FUNCTION__, bestT, xbestT);
bestT = xbestT;
} }
if (bestY != xbestY) { if (bestT < 0) {
SkDebugf("%s mismatch bestY=%1.9g xbestY=%1.9g\n", __FUNCTION__, bestY, xbestY); return bestTIndex;
bestY = xbestY;
} }
if (hitT != xhitT) { SkASSERT(bestT >= 0);
SkDebugf("%s mismatch hitT=%1.9g xhitT=%1.9g\n", __FUNCTION__, hitT, xhitT); SkASSERT(bestT <= 1);
hitT = xhitT; int start;
int end = 0;
do {
start = end;
end = nextSpan(start, 1);
} while (fTs[end].fT < bestT);
// FIXME: see next candidate for a better pattern to find the next start/end pair
while (start + 1 < end && fTs[start].fDone) {
++start;
} }
if (hitSomething != xhitSomething) { int testTIndex = bestT < 1 ? start : end;
SkDebugf("%s mismatch hitSomething=%d xhitSomething=%d\n", __FUNCTION__, hitSomething, if (!isCanceled(testTIndex)) {
xhitSomething); hitT = bestT;
hitSomething = xhitSomething; bestTIndex = testTIndex;
hitSomething = true;
} }
return bestTIndex;
return bestT;
} }
void decrementSpan(Span* span) { void decrementSpan(Span* span) {
@ -2965,6 +2932,10 @@ the same winding is shared by both.
return fTs.count() > 0; return fTs.count() > 0;
} }
bool isCanceled(int tIndex) const {
return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
}
bool isConnected(int startIndex, int endIndex) const { bool isConnected(int startIndex, int endIndex) const {
return fTs[startIndex].fWindSum != SK_MinS32 return fTs[startIndex].fWindSum != SK_MinS32
|| fTs[endIndex].fWindSum != SK_MinS32; || fTs[endIndex].fWindSum != SK_MinS32;
@ -3452,13 +3423,14 @@ the same winding is shared by both.
} }
bool nextCandidate(int& start, int& end) const { bool nextCandidate(int& start, int& end) const {
do { while (fTs[end].fDone) {
start = end; if (fTs[end].fT == 1) {
if (fTs[start].fT == 1) {
return false; return false;
} }
end = nextExactSpan(start, 1); ++end;
} while (fTs[start].fDone); }
start = end;
end = nextExactSpan(start, 1);
return true; return true;
} }
@ -3646,6 +3618,10 @@ the same winding is shared by both.
return fTs[tIndex].fT; return fTs[tIndex].fT;
} }
double tAtMid(int start, int end, double mid) const {
return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
}
bool tiny(const Angle* angle) const { bool tiny(const Angle* angle) const {
int start = angle->start(); int start = angle->start();
int end = angle->end(); int end = angle->end();
@ -3735,17 +3711,6 @@ the same winding is shared by both.
return fVerb; return fVerb;
} }
int windingAtTX(double tHit, int tIndex, bool crossOpp) const {
if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
return SK_MinS32;
}
int endIndex = nextSpan(tIndex, 1);
if (crossOpp) {
return updateOppWinding(tIndex, endIndex);
}
return updateWinding(tIndex, endIndex);
}
int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar& dx) const { int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar& dx) const {
if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
return SK_MinS32; return SK_MinS32;
@ -3825,9 +3790,8 @@ the same winding is shared by both.
} }
// used only by right angle winding finding // used only by right angle winding finding
void xyAtT(int start, int end, double mid, SkPoint& pt) const { void xyAtT(double mid, SkPoint& pt) const {
double t = fTs[start].fT * (1 - mid) + fTs[end].fT * mid; (*SegmentXYAtT[fVerb])(fPts, mid, &pt);
(*SegmentXYAtT[fVerb])(fPts, t, &pt);
} }
SkScalar yAtT(int index) const { SkScalar yAtT(int index) const {
@ -4316,25 +4280,6 @@ public:
fContainsIntercepts = true; fContainsIntercepts = true;
} }
#if 0
const Segment* crossedSegmentY(const SkPoint& basePt, SkScalar& bestY,
int &tIndex, double& hitT, bool opp) {
int segmentCount = fSegments.count();
const Segment* bestSegment = NULL;
for (int test = 0; test < segmentCount; ++test) {
Segment* testSegment = &fSegments[test];
double testHitT;
int testT = testSegment->crossedSpanY(basePt, bestY, testHitT, opp);
if (testT >= 0) {
bestSegment = testSegment;
tIndex = testT;
hitT = testHitT;
}
}
return bestSegment;
}
#endif
bool crosses(const Contour* crosser) const { bool crosses(const Contour* crosser) const {
for (int index = 0; index < fCrosses.count(); ++index) { for (int index = 0; index < fCrosses.count(); ++index) {
if (fCrosses[index] == crosser) { if (fCrosses[index] == crosser) {
@ -4522,48 +4467,6 @@ public:
} }
} }
#if 0 // FIXME: obsolete, remove
// OPTIMIZATION: feel pretty uneasy about this. It seems like once again
// we need to sort and walk edges in y, but that on the surface opens the
// same can of worms as before. But then, this is a rough sort based on
// segments' top, and not a true sort, so it could be ameniable to regular
// sorting instead of linear searching. Still feel like I'm missing something
Segment* topSegment(SkScalar& bestY) {
int segmentCount = fSegments.count();
SkASSERT(segmentCount > 0);
int best = -1;
Segment* bestSegment = NULL;
while (++best < segmentCount) {
Segment* testSegment = &fSegments[best];
if (testSegment->done()) {
continue;
}
bestSegment = testSegment;
break;
}
if (!bestSegment) {
return NULL;
}
SkScalar bestTop = bestSegment->activeTop();
for (int test = best + 1; test < segmentCount; ++test) {
Segment* testSegment = &fSegments[test];
if (testSegment->done()) {
continue;
}
if (testSegment->bounds().fTop > bestTop) {
continue;
}
SkScalar testTop = testSegment->activeTop();
if (bestTop > testTop) {
bestTop = testTop;
bestSegment = testSegment;
}
}
bestY = bestTop;
return bestSegment;
}
#endif
void topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, Segment*& topStart) { void topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, Segment*& topStart) {
int segmentCount = fSortedSegments.count(); int segmentCount = fSortedSegments.count();
SkASSERT(segmentCount > 0); SkASSERT(segmentCount > 0);
@ -5377,7 +5280,8 @@ static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& current, int& index, static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& current, int& index,
int& endIndex, double& bestHit, SkScalar& bestDx, bool& tryAgain, double& mid, bool opp) { int& endIndex, double& bestHit, SkScalar& bestDx, bool& tryAgain, double& mid, bool opp) {
SkPoint basePt; SkPoint basePt;
current->xyAtT(index, endIndex, mid, basePt); double tAtMid = current->tAtMid(index, endIndex, mid);
current->xyAtT(tAtMid, basePt);
int contourCount = contourList.count(); int contourCount = contourList.count();
SkScalar bestY = SK_ScalarMin; SkScalar bestY = SK_ScalarMin;
Segment* bestSeg = NULL; Segment* bestSeg = NULL;
@ -5398,7 +5302,8 @@ static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& curre
Segment* testSeg = &contour->segments()[test]; Segment* testSeg = &contour->segments()[test];
SkScalar testY = bestY; SkScalar testY = bestY;
double testHit; double testHit;
int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, hitSomething, testOpp); int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, hitSomething, tAtMid,
testOpp, testSeg == current);
if (testTIndex < 0) { if (testTIndex < 0) {
continue; continue;
} }
@ -5408,8 +5313,10 @@ static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& curre
double newMid = (testHit - baseT) / (endT - baseT); double newMid = (testHit - baseT) / (endT - baseT);
#if DEBUG_WINDING #if DEBUG_WINDING
SkPoint midXY, newXY; SkPoint midXY, newXY;
current->xyAtT(index, endIndex, mid, midXY); double midT = current->tAtMid(index, endIndex, mid);
current->xyAtT(index, endIndex, newMid, newXY); current->xyAtT(midT, midXY);
double newMidT = current->tAtMid(index, endIndex, newMid);
current->xyAtT(newMidT, newXY);
SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)" SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
" n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
current->debugID(), mid, newMid, current->debugID(), mid, newMid,
@ -5428,214 +5335,27 @@ static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& curre
bestY = testY; bestY = testY;
} }
} }
int result;
if (!bestSeg) { if (!bestSeg) {
return hitSomething ? SK_MinS32 : 0; result = hitSomething ? SK_MinS32 : 0;
} } else {
if (bestSeg->windSum(bestTIndex) == SK_MinS32) { if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
current = bestSeg; current = bestSeg;
index = bestTIndex; index = bestTIndex;
endIndex = bestSeg->nextSpan(bestTIndex, 1); endIndex = bestSeg->nextSpan(bestTIndex, 1);
tryAgain = true; SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
return 0; tryAgain = true;
} return 0;
int tryAnother = bestSeg->windingAtTX(bestHit, bestTIndex, bestOpp); }
int result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, bestDx); result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, bestDx);
if (result != tryAnother) { SkASSERT(bestDx);
SkDebugf("%s result=%d tryAnother=%d\n", __FUNCTION__, result,
tryAnother);
} }
double baseT = current->t(index); double baseT = current->t(index);
double endT = current->t(endIndex); double endT = current->t(endIndex);
bestHit = baseT + mid * (endT - baseT); bestHit = baseT + mid * (endT - baseT);
SkASSERT(bestDx);
return result; return result;
} }
// project a ray from the top of the contour up and see if it hits anything
// note: when we compute line intersections, we keep track of whether
// two contours touch, so we need only look at contours not touching this one.
// OPTIMIZATION: sort contourList vertically to avoid linear walk
#if 0
static int innerContourCheck(SkTDArray<Contour*>& contourList,
const Segment* current, int index, int endIndex, bool opp) {
const SkPoint& basePt = current->xyAtT(endIndex);
int contourCount = contourList.count();
SkScalar bestY = SK_ScalarMin;
const Segment* test = NULL;
int tIndex;
double tHit;
bool crossOpp;
for (int cTest = 0; cTest < contourCount; ++cTest) {
Contour* contour = contourList[cTest];
bool testOpp = contour->operand() ^ current->operand() ^ opp;
if (basePt.fY < contour->bounds().fTop) {
continue;
}
if (bestY > contour->bounds().fBottom) {
continue;
}
const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
if (next) {
test = next;
crossOpp = testOpp;
}
}
if (!test) {
return 0;
}
int winding, windValue;
// If the ray hit the end of a span, we need to construct the wheel of
// angles to find the span closest to the ray -- even if there are just
// two spokes on the wheel.
const Angle* angle = NULL;
if (approximately_zero(tHit - test->t(tIndex))) {
SkTDArray<Angle> angles;
int end = test->nextSpan(tIndex, 1);
if (end < 0) {
end = test->nextSpan(tIndex, -1);
}
test->addTwoAngles(end, tIndex, angles);
SkASSERT(angles.count() > 0);
if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
#if DEBUG_SORT
SkDebugf("%s early return\n", __FUNCTION__);
#endif
return SK_MinS32;
}
test->buildAngles(tIndex, angles, false);
SkTDArray<Angle*> sorted;
// OPTIMIZATION: call a sort that, if base point is the leftmost,
// returns the first counterclockwise hour before 6 o'clock,
// or if the base point is rightmost, returns the first clockwise
// hour after 6 o'clock
bool sortable = Segment::SortAngles(angles, sorted);
if (!sortable) {
return SK_MinS32;
}
#if DEBUG_SORT
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
#endif
// walk the sorted angle fan to find the lowest angle
// above the base point. Currently, the first angle in the sorted array
// is 12 noon or an earlier hour (the next counterclockwise)
int count = sorted.count();
int left = -1;
int mid = -1;
int right = -1;
bool baseMatches = test->yAtT(tIndex) == basePt.fY;
for (int index = 0; index < count; ++index) {
angle = sorted[index];
if (angle->unsortable()) {
continue;
}
if (baseMatches && angle->isHorizontal()) {
continue;
}
double indexDx = angle->dx();
test = angle->segment();
if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
const SkPoint* pts = test->pts();
indexDx = pts[2].fX - pts[1].fX - indexDx;
}
if (indexDx < 0) {
left = index;
} else if (indexDx > 0) {
right = index;
int previous = index - 1;
if (previous < 0) {
previous = count - 1;
}
const Angle* prev = sorted[previous];
if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
#if DEBUG_SORT
SkDebugf("%s use prev\n", __FUNCTION__);
#endif
right = previous;
}
break;
} else {
mid = index;
}
}
if ((left < 0 || right < 0) && mid >= 0) {
angle = sorted[mid];
Segment* midSeg = angle->segment();
int end = angle->end();
if (midSeg->unsortable(end)) {
return SK_MinS32;
}
}
if (left < 0 && right < 0) {
left = mid;
}
if (left < 0 && right < 0) {
SkASSERT(0);
return SK_MinS32; // unsortable
}
if (left < 0) {
left = right;
} else if (left >= 0 && mid >= 0 && right >= 0
&& sorted[mid]->sign() == sorted[right]->sign()) {
left = right;
}
angle = sorted[left];
test = angle->segment();
winding = crossOpp ? test->oppSum(angle) : test->windSum(angle);
SkASSERT(winding != SK_MinS32);
windValue = crossOpp ? test->oppValue(angle) : test->windValue(angle);
#if DEBUG_WINDING
SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
windValue, angle->sign());
#endif
} else {
// start here;
/*
FIXME: fails because the span found by findTop is not the span closest to vertical
intersecting the found point. Because the most vertical span could be done, and the
span found undone, findTop should not be changed. Instead, the spans at the found
point need to be sorted again, and the winding found below needs to be adjusted by
the span signs of the spans walking from vertical to the findTop span
findTop could but probably shouldn't compute this adjustment, since if the found span
already has computed winding, we won't get to innerContourCheck --
best solution -- findTop could check to see if found span already has computed winding
before returning adjustment
*/
#if 0
int windingTx = test->windingAtTX(tHit, tIndex, crossOpp);
SkScalar dx;
int windingT = test->windingAtT(tHit, tIndex, crossOpp, dx);
SkDebugf("%s windingTx=%d windingT=%d\n", __FUNCTION__, windingTx, windingT);
#endif
winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
if (winding == SK_MinS32) {
return SK_MinS32; // unsortable
}
windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
#if DEBUG_WINDING
SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
windValue);
#endif
}
// see if a + change in T results in a +/- change in X (compute x'(T))
SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
const SkPoint* pts = test->pts();
dx = pts[2].fX - pts[1].fX - dx;
}
#if DEBUG_WINDING
SkDebugf("%s dx=%1.9g winding=%d\n", __FUNCTION__, dx, winding);
#endif
SkASSERT(dx != 0);
if (winding * dx > 0) { // if same signs, result is negative
winding += dx > 0 ? -windValue : windValue;
#if DEBUG_WINDING
SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
#endif
}
return winding;
}
#endif
static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) { static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
int contourCount = contourList.count(); int contourCount = contourList.count();
Segment* result; Segment* result;
@ -5794,14 +5514,6 @@ static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
} }
#endif #endif
#if 0
static bool windingIsActive(int winding, int spanWinding) {
// FIXME: !spanWinding test must be superflorous, true?
return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
&& (!winding || !spanWinding || winding == -spanWinding);
}
#endif
static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index, static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool onlySortable) { int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool onlySortable) {
Segment* result; Segment* result;
@ -5838,27 +5550,6 @@ static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
return result; return result;
} }
#if 0
static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
int lesser = SkMin32(index, endIndex);
spanWinding = current->spanSign(index, endIndex);
int winding = current->windSum(lesser);
bool inner = useInnerWinding(winding - spanWinding, winding);
#if DEBUG_WINDING
SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
" inner=%d result=%d\n",
__FUNCTION__, current->debugID(), current->t(lesser),
spanWinding, winding, SkSign32(index - endIndex),
useInnerWinding(winding - spanWinding, winding),
inner ? winding - spanWinding : winding);
#endif
if (inner) {
winding -= spanWinding;
}
return winding;
}
#endif
static int rightAngleWinding(SkTDArray<Contour*>& contourList, static int rightAngleWinding(SkTDArray<Contour*>& contourList,
Segment*& current, int& index, int& endIndex, double& tHit, SkScalar& hitDx, bool& tryAgain, Segment*& current, int& index, int& endIndex, double& tHit, SkScalar& hitDx, bool& tryAgain,
bool opp) { bool opp) {
@ -5917,18 +5608,6 @@ static Segment* findSortableTop(SkTDArray<Contour*>& contourList, bool& firstCon
} }
int contourWinding; int contourWinding;
int oppContourWinding = 0; int oppContourWinding = 0;
#if 0
contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
if (contourWinding != SK_MinS32) {
if (binary) {
oppContourWinding = innerContourCheck(contourList, current, index, endIndex, true);
}
if (!binary || oppContourWinding != SK_MinS32) {
current->initWinding(index, endIndex, contourWinding, oppContourWinding);
return current;
}
}
#endif
// the simple upward projection of the unresolved points hit unsortable angles // the simple upward projection of the unresolved points hit unsortable angles
// shoot rays at right angles to the segment to find its winding, ignoring angle cases // shoot rays at right angles to the segment to find its winding, ignoring angle cases
bool tryAgain; bool tryAgain;
@ -5938,7 +5617,9 @@ static Segment* findSortableTop(SkTDArray<Contour*>& contourList, bool& firstCon
do { do {
// if current is vertical, find another candidate which is not // if current is vertical, find another candidate which is not
// if only remaining candidates are vertical, then they can be marked done // if only remaining candidates are vertical, then they can be marked done
SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
skipVertical(contourList, current, index, endIndex); skipVertical(contourList, current, index, endIndex);
SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
tryAgain = false; tryAgain = false;
contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitDx, contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitDx,
tryAgain, false); tryAgain, false);
@ -6003,7 +5684,8 @@ static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple)
current = next; current = next;
index = nextStart; index = nextStart;
endIndex = nextEnd; endIndex = nextEnd;
} while (!simple.isClosed() && ((!unsortable) || !current->done())); } while (!simple.isClosed() && ((!unsortable)
|| !current->done(SkMin32(index, endIndex))));
if (current->activeWinding(index, endIndex) && !simple.isClosed()) { if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
SkASSERT(unsortable); SkASSERT(unsortable);
int min = SkMin32(index, endIndex); int min = SkMin32(index, endIndex);
@ -6113,6 +5795,9 @@ static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY); return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
} }
static bool lessThan(SkTDArray<double>& distances, const int one, const int two) {
return distances[one] < distances[two];
}
/* /*
check start and end of each contour check start and end of each contour
if not the same, record them if not the same, record them
@ -6157,67 +5842,64 @@ static void assemble(const PathWrapper& path, PathWrapper& simple) {
SkTDArray<int> sLink, eLink; SkTDArray<int> sLink, eLink;
sLink.setCount(count); sLink.setCount(count);
eLink.setCount(count); eLink.setCount(count);
SkTDArray<double> sBest, eBest; int rIndex, iIndex;
sBest.setCount(count);
eBest.setCount(count);
int rIndex;
for (rIndex = 0; rIndex < count; ++rIndex) { for (rIndex = 0; rIndex < count; ++rIndex) {
outer = runs[rIndex]; sLink[rIndex] = eLink[rIndex] = INT_MAX;
const Contour& oContour = contours[outer];
const SkPoint& oStart = oContour.start();
const SkPoint& oEnd = oContour.end();
double dx = oEnd.fX - oStart.fX;
double dy = oEnd.fY - oStart.fY;
double dist = dx * dx + dy * dy;
sBest[rIndex] = eBest[rIndex] = dist;
sLink[rIndex] = eLink[rIndex] = rIndex;
} }
for (rIndex = 0; rIndex < count - 1; ++rIndex) { SkTDArray<double> distances;
outer = runs[rIndex]; const int ends = count * 2; // all starts and ends
const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
distances.setCount(entries);
for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
outer = runs[rIndex >> 1];
const Contour& oContour = contours[outer]; const Contour& oContour = contours[outer];
const SkPoint& oStart = oContour.start(); const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
const SkPoint& oEnd = oContour.end(); const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
double bestStartDist = sBest[rIndex]; * ends - rIndex - 1;
double bestEndDist = eBest[rIndex]; for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) { int inner = runs[iIndex >> 1];
int inner = runs[iIndex];
const Contour& iContour = contours[inner]; const Contour& iContour = contours[inner];
const SkPoint& iStart = iContour.start(); const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
const SkPoint& iEnd = iContour.end(); double dx = iPt.fX - oPt.fX;
double dx = iStart.fX - oStart.fX; double dy = iPt.fY - oPt.fY;
double dy = iStart.fY - oStart.fY;
double dist = dx * dx + dy * dy; double dist = dx * dx + dy * dy;
if (bestStartDist > dist && sBest[iIndex] > dist) { distances[row + iIndex] = dist; // oStart distance from iStart
sBest[iIndex] = bestStartDist = dist; }
sLink[rIndex] = ~iIndex;
sLink[iIndex] = ~rIndex;
}
dx = iEnd.fX - oStart.fX;
dy = iEnd.fY - oStart.fY;
dist = dx * dx + dy * dy;
if (bestStartDist > dist && eBest[iIndex] > dist) {
eBest[iIndex] = bestStartDist = dist;
sLink[rIndex] = iIndex;
eLink[iIndex] = rIndex;
}
dx = iStart.fX - oEnd.fX;
dy = iStart.fY - oEnd.fY;
dist = dx * dx + dy * dy;
if (bestEndDist > dist && sBest[iIndex] > dist) {
sBest[iIndex] = bestEndDist = dist;
sLink[iIndex] = rIndex;
eLink[rIndex] = iIndex;
}
dx = iEnd.fX - oEnd.fX;
dy = iEnd.fY - oEnd.fY;
dist = dx * dx + dy * dy;
if (bestEndDist > dist && eBest[iIndex] > dist) {
eBest[iIndex] = bestEndDist = dist;
eLink[iIndex] = ~rIndex;
eLink[rIndex] = ~iIndex;
}
}
} }
SkTDArray<int> sortedDist;
sortedDist.setCount(entries);
for (rIndex = 0; rIndex < entries; ++rIndex) {
sortedDist[rIndex] = rIndex;
}
QSort<SkTDArray<double>, int>(distances, sortedDist.begin(), sortedDist.end() - 1, lessThan);
int remaining = count; // number of start/end pairs
for (rIndex = 0; rIndex < entries; ++rIndex) {
int pair = sortedDist[rIndex];
int row = pair / ends;
int col = pair - row * ends;
int thingOne = row < col ? row : ends - row - 2;
int ndxOne = thingOne >> 1;
bool endOne = thingOne & 1;
int* linkOne = endOne ? eLink.begin() : sLink.begin();
if (linkOne[ndxOne] != INT_MAX) {
continue;
}
int thingTwo = row < col ? col : ends - row + col - 1;
int ndxTwo = thingTwo >> 1;
bool endTwo = thingTwo & 1;
int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
if (linkTwo[ndxTwo] != INT_MAX) {
continue;
}
SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
bool flip = endOne == endTwo;
linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
if (!--remaining) {
break;
}
}
SkASSERT(!remaining);
#if DEBUG_ASSEMBLE #if DEBUG_ASSEMBLE
for (rIndex = 0; rIndex < count; ++rIndex) { for (rIndex = 0; rIndex < count; ++rIndex) {
int s = sLink[rIndex]; int s = sLink[rIndex];

View File

@ -3282,12 +3282,82 @@ static void testQuadratic83() {
testSimplifyx(path); testSimplifyx(path);
} }
static void (*firstTest)() = testQuadratic83; static void testQuadratic84() {
SkPath path, pathB;
path.moveTo(0, 0);
path.quadTo(2, 0, 1, 1);
path.lineTo(2, 1);
path.close();
path.moveTo(1, 0);
path.lineTo(2, 0);
path.quadTo(0, 1, 2, 2);
path.close();
testSimplifyx(path);
}
static void testQuadratic85() {
SkPath path, pathB;
path.moveTo(0, 0);
path.quadTo(3, 0, 1, 1);
path.lineTo(1, 1);
path.close();
path.moveTo(1, 0);
path.lineTo(3, 0);
path.quadTo(0, 1, 1, 2);
path.close();
testSimplifyx(path);
}
static void testQuadratic86() {
SkPath path, pathB;
path.moveTo(0, 0);
path.quadTo(0, 1, 1, 1);
path.lineTo(2, 3);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 1, 1, 3);
path.close();
testSimplifyx(path);
}
static void testQuadratic87() {
SkPath path, pathB;
path.moveTo(0, 0);
path.quadTo(2, 1, 0, 2);
path.lineTo(2, 3);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 1);
path.quadTo(0, 2, 3, 2);
path.close();
testSimplifyx(path);
}
static void testQuadratic88() {
SkPath path, pathB;
path.moveTo(0, 0);
path.quadTo(2, 1, 0, 2);
path.lineTo(2, 2);
path.close();
path.moveTo(1, 0);
path.lineTo(1, 1);
path.quadTo(0, 2, 2, 2);
path.close();
testSimplifyx(path);
}
static void (*firstTest)() = testQuadratic88;
static struct { static struct {
void (*fun)(); void (*fun)();
const char* str; const char* str;
} tests[] = { } tests[] = {
TEST(testQuadratic88),
TEST(testQuadratic87),
TEST(testQuadratic86),
TEST(testQuadratic85),
TEST(testQuadratic84),
TEST(testQuadratic83), TEST(testQuadratic83),
TEST(testQuadratic82), TEST(testQuadratic82),
TEST(testQuadratic81), TEST(testQuadratic81),

View File

@ -3101,11 +3101,71 @@ path.close();
path.close(); path.close();
</div> </div>
<div id="testQuadratic84">
path.moveTo(0, 0);
path.quadTo(2, 0, 1, 1);
path.lineTo(2, 1);
path.close();
path.moveTo(1, 0);
path.lineTo(2, 0);
path.quadTo(0, 1, 2, 2);
path.close();
</div>
<div id="testQuadratic85">
path.moveTo(0, 0);
path.quadTo(3, 0, 1, 1);
path.lineTo(1, 1);
path.close();
path.moveTo(1, 0);
path.lineTo(3, 0);
path.quadTo(0, 1, 1, 2);
path.close();
</div>
<div id="testQuadratic86">
path.moveTo(0, 0);
path.quadTo(0, 1, 1, 1);
path.lineTo(2, 3);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 1, 1, 3);
path.close();
</div>
<div id="testQuadratic87">
path.moveTo(0, 0);
path.quadTo(2, 1, 0, 2);
path.lineTo(2, 3);
path.close();
path.moveTo(0, 0);
path.lineTo(1, 1);
path.quadTo(0, 2, 3, 2);
path.close();
</div>
<div id="testQuadratic88">
path.moveTo(0, 0);
path.quadTo(2, 1, 0, 2);
path.lineTo(2, 2);
path.close();
path.moveTo(1, 0);
path.lineTo(1, 1);
path.quadTo(0, 2, 2, 2);
path.close();
</div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var testDivs = [ var testDivs = [
testQuadratic88,
testQuadratic87,
testQuadratic86,
testQuadratic85,
testQuadratic84,
testQuadratic83, testQuadratic83,
testQuadratic82, testQuadratic82,
testQuadratic81, testQuadratic81,