Index: src/pathops/SkOpSegment.cpp |
=================================================================== |
--- src/pathops/SkOpSegment.cpp (revision 9425) |
+++ src/pathops/SkOpSegment.cpp (working copy) |
@@ -209,19 +209,22 @@ |
SkOpAngle* angle = anglesPtr->append(); |
#if DEBUG_ANGLE |
SkTDArray<SkOpAngle>& angles = *anglesPtr; |
- if (angles.count() > 1 && !fTs[start].fTiny) { |
- SkPoint angle0Pt = (*CurvePointAtT[SkPathOpsVerbToPoints(angles[0].verb())])(angles[0].pts(), |
- (*angles[0].spans())[angles[0].start()].fT); |
- SkPoint newPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[start].fT); |
- bool match = AlmostEqualUlps(angle0Pt.fX, newPt.fX); |
- match &= AlmostEqualUlps(angle0Pt.fY, newPt.fY); |
- if (!match) { |
- SkDebugf("%s no match\n", __FUNCTION__); |
- SkASSERT(0); |
+ if (angles.count() > 1) { |
+ const SkOpSegment* aSeg = angles[0].segment(); |
+ int aStart = angles[0].start(); |
+ if (!aSeg->fTs[aStart].fTiny) { |
+ const SkPoint& angle0Pt = aSeg->xyAtT(aStart); |
+ SkDPoint newPt = (*CurveDPointAtT[SkPathOpsVerbToPoints(aSeg->fVerb)])(aSeg->fPts, |
+ aSeg->fTs[aStart].fT); |
+#if ONE_OFF_DEBUG |
+ SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g)\n", __FUNCTION__, |
+ aSeg->fTs[aStart].fT, newPt.fX, newPt.fY, angle0Pt.fX, angle0Pt.fY); |
+#endif |
+ SkASSERT(newPt.approximatelyEqual(angle0Pt)); |
} |
} |
#endif |
- angle->set(fPts, fVerb, this, start, end, fTs); |
+ angle->set(this, start, end); |
} |
void SkOpSegment::addCancelOutsides(double tStart, double oStart, SkOpSegment* other, double oEnd) { |
@@ -853,7 +856,8 @@ |
// OPTIMIZATION: check all angles to see if any have computed wind sum |
// before sorting (early exit if none) |
SkTDArray<SkOpAngle*> sorted; |
- bool sortable = SortAngles(angles, &sorted); |
+ // FIXME?: Not sure if this sort must be ordered or if the relaxed ordering is OK ... |
+ bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
#if DEBUG_SORT |
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0); |
#endif |
@@ -979,7 +983,8 @@ |
SkIntersections intersections; |
// OPTIMIZE: use specialty function that intersects ray with curve, |
// returning t values only for curve (we don't care about t on ray) |
- int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, top, bottom, basePt.fX, false); |
+ int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)]) |
+ (fPts, top, bottom, basePt.fX, false); |
if (pts == 0 || (current && pts == 1)) { |
return bestTIndex; |
} |
@@ -1126,6 +1131,9 @@ |
} |
while (precisely_zero(startT - other->fTs[*nextEnd].fT)); |
SkASSERT(step < 0 ? *nextEnd >= 0 : *nextEnd < other->fTs.count()); |
+ if (other->isTiny(SkMin32(*nextStart, *nextEnd))) { |
+ return NULL; |
+ } |
return other; |
} |
// more than one viable candidate -- measure angles to find best |
@@ -1135,7 +1143,7 @@ |
addTwoAngles(startIndex, end, &angles); |
buildAngles(end, &angles, true); |
SkTDArray<SkOpAngle*> sorted; |
- bool sortable = SortAngles(angles, &sorted); |
+ bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
int angleCount = angles.count(); |
int firstIndex = findStartingEdge(sorted, startIndex, end); |
SkASSERT(firstIndex >= 0); |
@@ -1177,12 +1185,12 @@ |
if (activeAngle) { |
++activeCount; |
if (!foundAngle || (foundDone && activeCount & 1)) { |
- if (nextSegment->tiny(nextAngle)) { |
+ if (nextSegment->isTiny(nextAngle)) { |
*unsortable = true; |
return NULL; |
} |
foundAngle = nextAngle; |
- foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle); |
+ foundDone = nextSegment->done(nextAngle) && !nextSegment->isTiny(nextAngle); |
} |
} |
if (nextSegment->done()) { |
@@ -1257,7 +1265,7 @@ |
addTwoAngles(startIndex, end, &angles); |
buildAngles(end, &angles, true); |
SkTDArray<SkOpAngle*> sorted; |
- bool sortable = SortAngles(angles, &sorted); |
+ bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
int angleCount = angles.count(); |
int firstIndex = findStartingEdge(sorted, startIndex, end); |
SkASSERT(firstIndex >= 0); |
@@ -1294,7 +1302,7 @@ |
if (activeAngle) { |
++activeCount; |
if (!foundAngle || (foundDone && activeCount & 1)) { |
- if (nextSegment->tiny(nextAngle)) { |
+ if (nextSegment->isTiny(nextAngle)) { |
*unsortable = true; |
return NULL; |
} |
@@ -1386,7 +1394,7 @@ |
addTwoAngles(startIndex, end, &angles); |
buildAngles(end, &angles, false); |
SkTDArray<SkOpAngle*> sorted; |
- bool sortable = SortAngles(angles, &sorted); |
+ bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMustBeOrdered_SortAngleKind); |
if (!sortable) { |
*unsortable = true; |
#if DEBUG_SORT |
@@ -1416,7 +1424,7 @@ |
nextSegment = nextAngle->segment(); |
++activeCount; |
if (!foundAngle || (foundDone && activeCount & 1)) { |
- if (nextSegment->tiny(nextAngle)) { |
+ if (nextSegment->isTiny(nextAngle)) { |
*unsortable = true; |
return NULL; |
} |
@@ -1628,7 +1636,7 @@ |
addTwoAngles(end, firstT, &angles); |
buildAngles(firstT, &angles, true); |
SkTDArray<SkOpAngle*> sorted; |
- bool sortable = SortAngles(angles, &sorted); |
+ bool sortable = SortAngles(angles, &sorted, SkOpSegment::kMayBeUnordered_SortAngleKind); |
int first = SK_MaxS32; |
SkScalar top = SK_ScalarMax; |
int count = sorted.count(); |
@@ -2081,7 +2089,8 @@ |
SkASSERT(fVerb != SkPath::kLine_Verb); |
SkPoint edge[4]; |
subDivide(tStart, tEnd, edge); |
- double sum = (edge[0].fX - edge[SkPathOpsVerbToPoints(fVerb)].fX) * (edge[0].fY + edge[SkPathOpsVerbToPoints(fVerb)].fY); |
+ int points = SkPathOpsVerbToPoints(fVerb); |
+ double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY); |
if (fVerb == SkPath::kCubic_Verb) { |
SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY); |
if (edge[1].fY < lesser && edge[2].fY < lesser) { |
@@ -2095,7 +2104,7 @@ |
} |
} |
} |
- for (int idx = 0; idx < SkPathOpsVerbToPoints(fVerb); ++idx){ |
+ for (int idx = 0; idx < points; ++idx){ |
sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY); |
} |
return sum <= 0; |
@@ -2334,8 +2343,8 @@ |
// exclusion in find top and others. This could be optimized to only mark |
// adjacent spans that unsortable. However, this makes it difficult to later |
// determine starting points for edge detection in find top and the like. |
-bool SkOpSegment::SortAngles(const SkTDArray<SkOpAngle>& angles, |
- SkTDArray<SkOpAngle*>* angleList) { |
+bool SkOpSegment::SortAngles(const SkTDArray<SkOpAngle>& angles, SkTDArray<SkOpAngle*>* angleList, |
+ SortAngleKind orderKind) { |
bool sortable = true; |
int angleCount = angles.count(); |
int angleIndex; |
@@ -2343,12 +2352,17 @@ |
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
const SkOpAngle& angle = angles[angleIndex]; |
*angleList->append() = const_cast<SkOpAngle*>(&angle); |
- sortable &= !angle.unsortable(); |
+#if DEBUG_ANGLE |
+ (*(angleList->end() - 1))->setID(angleIndex); |
+#endif |
+ sortable &= !(angle.unsortable() || (orderKind == kMustBeOrdered_SortAngleKind |
+ && angle.unorderable())); |
} |
if (sortable) { |
SkTQSort<SkOpAngle>(angleList->begin(), angleList->end() - 1); |
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) { |
- if (angles[angleIndex].unsortable()) { |
+ if (angles[angleIndex].unsortable() || (orderKind == kMustBeOrdered_SortAngleKind |
+ && angles[angleIndex].unorderable())) { |
sortable = false; |
break; |
} |
@@ -2363,35 +2377,99 @@ |
return sortable; |
} |
-void SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const { |
+// return true if midpoints were computed |
+bool SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const { |
+ SkASSERT(start != end); |
edge[0] = fTs[start].fPt; |
- edge[SkPathOpsVerbToPoints(fVerb)] = fTs[end].fPt; |
- if (fVerb == SkPath::kQuad_Verb || fVerb == SkPath::kCubic_Verb) { |
- SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[SkPathOpsVerbToPoints(fVerb)].fX, edge[SkPathOpsVerbToPoints(fVerb)].fY }}; |
+ int points = SkPathOpsVerbToPoints(fVerb); |
+ edge[points] = fTs[end].fPt; |
+ if (fVerb == SkPath::kLine_Verb) { |
+ return false; |
+ } |
+ double startT = fTs[start].fT; |
+ double endT = fTs[end].fT; |
+ if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) { |
+ // don't compute midpoints if we already have them |
if (fVerb == SkPath::kQuad_Verb) { |
- edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], fTs[start].fT, |
- fTs[end].fT).asSkPoint(); |
- } else { |
- SkDCubic::SubDivide(fPts, sub[0], sub[1], fTs[start].fT, fTs[end].fT, sub); |
- edge[1] = sub[0].asSkPoint(); |
- edge[2] = sub[1].asSkPoint(); |
+ edge[1] = fPts[1]; |
+ return false; |
} |
+ SkASSERT(fVerb == SkPath::kCubic_Verb); |
+ if (start < end) { |
+ edge[1] = fPts[1]; |
+ edge[2] = fPts[2]; |
+ return false; |
+ } |
+ edge[1] = fPts[2]; |
+ edge[2] = fPts[1]; |
+ return false; |
} |
+ const SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[points].fX, edge[points].fY }}; |
+ if (fVerb == SkPath::kQuad_Verb) { |
+ edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], startT, endT).asSkPoint(); |
+ } else { |
+ SkASSERT(fVerb == SkPath::kCubic_Verb); |
+ SkDPoint ctrl[2]; |
+ SkDCubic::SubDivide(fPts, sub[0], sub[1], startT, endT, ctrl); |
+ edge[1] = ctrl[0].asSkPoint(); |
+ edge[2] = ctrl[1].asSkPoint(); |
+ } |
+ return true; |
} |
+// return true if midpoints were computed |
+bool SkOpSegment::subDivide(int start, int end, SkDCubic* result) const { |
+ SkASSERT(start != end); |
+ (*result)[0].set(fTs[start].fPt); |
+ int points = SkPathOpsVerbToPoints(fVerb); |
+ (*result)[points].set(fTs[end].fPt); |
+ if (fVerb == SkPath::kLine_Verb) { |
+ return false; |
+ } |
+ double startT = fTs[start].fT; |
+ double endT = fTs[end].fT; |
+ if ((startT == 0 || endT == 0) && (startT == 1 || endT == 1)) { |
+ // don't compute midpoints if we already have them |
+ if (fVerb == SkPath::kQuad_Verb) { |
+ (*result)[1].set(fPts[1]); |
+ return false; |
+ } |
+ SkASSERT(fVerb == SkPath::kCubic_Verb); |
+ if (start < end) { |
+ (*result)[1].set(fPts[1]); |
+ (*result)[2].set(fPts[2]); |
+ return false; |
+ } |
+ (*result)[1].set(fPts[2]); |
+ (*result)[2].set(fPts[1]); |
+ return false; |
+ } |
+ if (fVerb == SkPath::kQuad_Verb) { |
+ (*result)[1] = SkDQuad::SubDivide(fPts, (*result)[0], (*result)[2], startT, endT); |
+ } else { |
+ SkASSERT(fVerb == SkPath::kCubic_Verb); |
+ SkDCubic::SubDivide(fPts, (*result)[0], (*result)[3], startT, endT, &(*result)[1]); |
+ } |
+ return true; |
+} |
+ |
void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const { |
SkPoint edge[4]; |
subDivide(start, end, edge); |
(bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge); |
} |
-bool SkOpSegment::tiny(const SkOpAngle* angle) const { |
+bool SkOpSegment::isTiny(const SkOpAngle* angle) const { |
int start = angle->start(); |
int end = angle->end(); |
const SkOpSpan& mSpan = fTs[SkMin32(start, end)]; |
return mSpan.fTiny; |
} |
+bool SkOpSegment::isTiny(int index) const { |
+ return fTs[index].fTiny; |
+} |
+ |
void SkOpSegment::TrackOutside(SkTDArray<double>* outsideTs, double end, double start) { |
int outCount = outsideTs->count(); |
if (outCount == 0 || !approximately_negative(end - (*outsideTs)[outCount - 2])) { |
@@ -2735,8 +2813,8 @@ |
} |
SkDebugf("%s [%d] %s", __FUNCTION__, index, |
angle.unsortable() ? "*** UNSORTABLE *** " : ""); |
- #if COMPACT_DEBUG_SORT |
- SkDebugf("id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)", |
+ #if DEBUG_SORT_COMPACT |
+ SkDebugf("id=%d %s start=%d (%1.9g,%1.9g) end=%d (%1.9g,%1.9g)", |
segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)], |
start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end, |
segment.xAtT(&eSpan), segment.yAtT(&eSpan)); |