diff options
author | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-04-10 18:28:55 +0000 |
---|---|---|
committer | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-04-10 18:28:55 +0000 |
commit | fb173424e915e696a73067d616ce4f39a407261a (patch) | |
tree | 2f2bb2ca93d0993649f6afac76b0c87137834893 /experimental | |
parent | 75589257c6ac7fc55a66502b74b8bc09c0212fea (diff) |
shape ops work in progress
more quadratics work
git-svn-id: http://skia.googlecode.com/svn/trunk@3643 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'experimental')
-rw-r--r-- | experimental/Intersection/EdgeWalker.cpp | 424 | ||||
-rw-r--r-- | experimental/Intersection/EdgeWalkerQuadratics_Test.cpp | 6 | ||||
-rw-r--r-- | experimental/Intersection/EdgeWalker_TestUtility.cpp | 17 | ||||
-rw-r--r-- | experimental/Intersection/edge.xcodeproj/project.pbxproj | 28 | ||||
-rw-r--r-- | experimental/Intersection/op.htm | 135 |
5 files changed, 393 insertions, 217 deletions
diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp index 70fa8e679c..f3dff48f97 100644 --- a/experimental/Intersection/EdgeWalker.cpp +++ b/experimental/Intersection/EdgeWalker.cpp @@ -16,7 +16,7 @@ #include "ShapeOps.h" #include "TSearch.h" -#if 0 // set to 1 for no debugging whatsoever +#if 01 // set to 1 for no debugging whatsoever const bool gShowDebugf = false; // FIXME: remove once debugging is complete #define DEBUG_DUMP 0 @@ -25,13 +25,15 @@ const bool gShowDebugf = false; // FIXME: remove once debugging is complete #define DEBUG_ADD_BOTTOM_TS 0 #define DEBUG_ABOVE_BELOW 0 #define DEBUG_ACTIVE_LESS_THAN 0 +#define DEBUG_ASSEMBLE 0 +#define DEBUG_BRIDGE 0 #define DEBUG_SORT_HORIZONTAL 0 #define DEBUG_OUT 0 #define DEBUG_OUT_LESS_THAN 0 #define DEBUG_ADJUST_COINCIDENT 0 #define DEBUG_BOTTOM 0 #define DEBUG_SPLIT 0 - +#define DEBUG_STITCH_EDGE 0 #else const bool gShowDebugf = true; // FIXME: remove once debugging is complete @@ -41,15 +43,25 @@ const bool gShowDebugf = true; // FIXME: remove once debugging is complete #define DEBUG_ADD_BOTTOM_TS 0 #define DEBUG_ABOVE_BELOW 01 #define DEBUG_ACTIVE_LESS_THAN 0 +#define DEBUG_ASSEMBLE 1 +#define DEBUG_BRIDGE 1 #define DEBUG_SORT_HORIZONTAL 01 #define DEBUG_OUT 01 #define DEBUG_OUT_LESS_THAN 0 #define DEBUG_ADJUST_COINCIDENT 1 #define DEBUG_BOTTOM 0 #define DEBUG_SPLIT 1 +#define DEBUG_STITCH_EDGE 1 #endif +#if DEBUG_ASSEMBLE || DEBUG_BRIDGE +static const char* kLVerbStr[] = {"", "line", "quad", "cubic"}; +#endif +#if DEBUG_STITCH_EDGE +static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"}; +#endif + static int LineIntersect(const SkPoint a[2], const SkPoint b[2], Intersections& intersections) { const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}}; @@ -196,7 +208,29 @@ static void CubicSubDivide(const SkPoint a[4], double startT, double endT, sub[3].fX = SkDoubleToScalar(dst[3].x); sub[3].fY = SkDoubleToScalar(dst[3].y); } - + +static void QuadSubBounds(const SkPoint a[3], double startT, double endT, + SkRect& bounds) { + SkPoint dst[3]; + QuadSubDivide(a, startT, endT, dst); + bounds.fLeft = bounds.fRight = dst[0].fX; + bounds.fTop = bounds.fBottom = dst[0].fY; + for (int index = 1; index < 3; ++index) { + bounds.growToInclude(dst[index].fX, dst[index].fY); + } +} + +static void CubicSubBounds(const SkPoint a[4], double startT, double endT, + SkRect& bounds) { + SkPoint dst[4]; + CubicSubDivide(a, startT, endT, dst); + bounds.fLeft = bounds.fRight = dst[0].fX; + bounds.fTop = bounds.fBottom = dst[0].fY; + for (int index = 1; index < 4; ++index) { + bounds.growToInclude(dst[index].fX, dst[index].fY); + } +} + /* list of edges bounds for edge @@ -336,49 +370,57 @@ public: break; } int closeEdgeIndex = -listIndex - 1; + // the curve is deferred and not added right away because the + // following edge may extend the first curve. SkPoint firstPt, lastCurve[4]; uint8_t lastVerb; +#if DEBUG_ASSEMBLE + int firstIndex, lastIndex; + const int tab = 8; +#endif bool doMove = true; int edgeIndex; do { SkPoint* ptArray = fEdges[listIndex].fPts; uint8_t verb = fEdges[listIndex].fVerb; - SkPoint* start, * end; + SkPoint* curve[4]; if (advance < 0) { - start = &ptArray[verb]; - end = &ptArray[0]; + curve[0] = &ptArray[verb]; + if (verb == SkPath::kCubic_Verb) { + curve[1] = &ptArray[2]; + curve[2] = &ptArray[1]; + } + curve[verb] = &ptArray[0]; } else { - start = &ptArray[0]; - end = &ptArray[verb]; + curve[0] = &ptArray[0]; + if (verb == SkPath::kCubic_Verb) { + curve[1] = &ptArray[1]; + curve[2] = &ptArray[2]; + } + curve[verb] = &ptArray[verb]; + } + if (verb == SkPath::kQuad_Verb) { + curve[1] = &ptArray[1]; } if (doMove) { - firstPt = *start; - simple.moveTo(start->fX, start->fY); - if (gShowDebugf) { - SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__, - start->fX, start->fY); - } - lastCurve[0] = *start; - if (verb == SkPath::kQuad_Verb) { - lastCurve[1] = ptArray[1]; - } else if (verb == SkPath::kCubic_Verb) { - if (advance < 0) { - lastCurve[1] = ptArray[2]; - lastCurve[2] = ptArray[1]; - } else { - lastCurve[1] = ptArray[1]; - lastCurve[2] = ptArray[2]; - } + firstPt = *curve[0]; + simple.moveTo(curve[0]->fX, curve[0]->fY); +#if DEBUG_ASSEMBLE + SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__, + listIndex + 1, curve[0]->fX, curve[0]->fY); + firstIndex = listIndex; +#endif + for (int index = 0; index <= verb; ++index) { + lastCurve[index] = *curve[index]; } - lastCurve[verb] = *end; - lastVerb = verb; doMove = false; } else { - bool gap = lastCurve[verb] != *start; - if (gap) { + bool gap = lastCurve[lastVerb] != *curve[0]; + if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap // FIXME: see comment in bridge -- this probably // conceals errors - SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10); + SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, + curve[0]->fY) <= 10); switch (lastVerb) { case SkPath::kLine_Verb: simple.lineTo(lastCurve[1].fX, lastCurve[1].fY); @@ -393,43 +435,41 @@ public: lastCurve[3].fX, lastCurve[3].fY); break; } - if (gShowDebugf) { - const char* verbStr[] = {"", "line", "quad", "cubic"}; - SkDebugf("%s %sTo-1 (%g,%g)\n", __FUNCTION__, - verbStr[lastVerb], lastCurve[lastVerb].fX, - lastCurve[lastVerb].fY); - } +#if DEBUG_ASSEMBLE + SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1, + kLVerbStr[lastVerb], lastCurve[lastVerb].fX, + lastCurve[lastVerb].fY); +#endif } - if (gap || lastVerb != SkPath::kLine_Verb || !extendLine(lastCurve, *end)) { + int firstCopy = 1; + if (gap || (lastVerb == SkPath::kLine_Verb && + !extendLine(lastCurve, *curve[verb]))) { // FIXME: see comment in bridge -- this probably // conceals errors - SkASSERT(lastCurve[lastVerb] == *start || - (fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10)); - simple.lineTo(start->fX, start->fY); - if (gShowDebugf) { - SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__, - start->fX, start->fY); - } - lastCurve[0] = *start; + SkASSERT(lastCurve[lastVerb] == *curve[0] || + (fFill && UlpsDiff(lastCurve[lastVerb].fY, + curve[0]->fY) <= 10)); + simple.lineTo(curve[0]->fX, curve[0]->fY); +#if DEBUG_ASSEMBLE + SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "", + lastIndex + 1, curve[0]->fX, curve[0]->fY); +#endif + firstCopy = 0; + } else if (lastVerb != SkPath::kLine_Verb) { + firstCopy = 0; } - if (verb == SkPath::kQuad_Verb) { - lastCurve[1] = ptArray[1]; - } else if (verb == SkPath::kCubic_Verb) { - if (advance < 0) { - lastCurve[1] = ptArray[2]; - lastCurve[2] = ptArray[1]; - } else { - lastCurve[1] = ptArray[1]; - lastCurve[2] = ptArray[2]; - } + for (int index = firstCopy; index <= verb; ++index) { + lastCurve[index] = *curve[index]; } - lastCurve[verb] = *end; - lastVerb = verb; } + lastVerb = verb; +#if DEBUG_ASSEMBLE + lastIndex = listIndex; +#endif if (advance < 0) { edgeIndex = fTops[listIndex]; fTops[listIndex] = 0; - } else { + } else { edgeIndex = fBottoms[listIndex]; fBottoms[listIndex] = 0; } @@ -442,37 +482,44 @@ public: } } if (edgeIndex == closeEdgeIndex || edgeIndex == 0) { + switch (lastVerb) { + case SkPath::kLine_Verb: + simple.lineTo(lastCurve[1].fX, lastCurve[1].fY); + break; + case SkPath::kQuad_Verb: + simple.quadTo(lastCurve[1].fX, lastCurve[1].fY, + lastCurve[2].fX, lastCurve[2].fY); + break; + case SkPath::kCubic_Verb: + simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY, + lastCurve[2].fX, lastCurve[2].fY, + lastCurve[3].fX, lastCurve[3].fY); + break; + } +#if DEBUG_ASSEMBLE + SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "", + lastIndex + 1, kLVerbStr[lastVerb], + lastCurve[lastVerb].fX, lastCurve[lastVerb].fY); +#endif if (lastCurve[lastVerb] != firstPt) { - switch (lastVerb) { - case SkPath::kLine_Verb: - simple.lineTo(lastCurve[1].fX, lastCurve[1].fY); - break; - case SkPath::kQuad_Verb: - simple.quadTo(lastCurve[1].fX, lastCurve[1].fY, - lastCurve[2].fX, lastCurve[2].fY); - break; - case SkPath::kCubic_Verb: - simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY, - lastCurve[2].fX, lastCurve[2].fY, - lastCurve[3].fX, lastCurve[3].fY); - break; - } - if (gShowDebugf) { - const char* verbStr[] = {"", "line", "quad", "cubic"}; - SkDebugf("%s %sTo last (%g, %g)\n", __FUNCTION__, - verbStr[lastVerb], - lastCurve[lastVerb].fX, lastCurve[lastVerb].fY); - } + simple.lineTo(firstPt.fX, firstPt.fY); +#if DEBUG_ASSEMBLE + SkDebugf("%*s %d final line (%g, %g)\n", tab, "", + firstIndex + 1, firstPt.fX, firstPt.fY); +#endif } - simple.lineTo(firstPt.fX, firstPt.fY); simple.close(); - if (gShowDebugf) { - SkDebugf("%s close (%g, %g)\n", __FUNCTION__, - firstPt.fX, firstPt.fY); - } +#if DEBUG_ASSEMBLE + SkDebugf("%*s close\n", tab, ""); +#endif break; } - // if this and next edge go different directions + // if this and next edge go different directions +#if DEBUG_ASSEMBLE + SkDebugf("%*s advance=%d edgeIndex=%d flip=%s\n", tab, "", + advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ? + "true" : "false"); +#endif if (advance > 0 ^ edgeIndex < 0) { advance = -advance; } @@ -618,6 +665,22 @@ public: } SkASSERT(fMismatches.count() == 0); #endif +#if DEBUG_BRIDGE + for (index = 0; index < count; ++index) { + const OutEdge& edge = fEdges[index]; + uint8_t verb = edge.fVerb; + SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n", + index == 0 ? __FUNCTION__ : " ", + index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX, + edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY); + } + for (index = 0; index < count; ++index) { + SkDebugf(" top of % 2d connects to %s of % 2d\n", index + 1, + fTops[index] < 0 ? "top " : "bottom", abs(fTops[index])); + SkDebugf(" bottom of % 2d connects to %s of % 2d\n", index + 1, + fBottoms[index] < 0 ? "top " : "bottom", abs(fBottoms[index])); + } +#endif } protected: @@ -696,6 +759,8 @@ public: className, fTopIntercepts); SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className), className, fBottomIntercepts); + SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className), + className, fExplicit); } #endif @@ -781,7 +846,7 @@ struct InEdge { return insertedAt; } - void addPartial(SkTArray<InEdge>& edges, int id, int ptStart, int ptEnd, + void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd, int verbStart, int verbEnd) { InEdge* edge = edges.push_back_n(1); int verbCount = verbEnd - verbStart; @@ -793,25 +858,35 @@ struct InEdge { edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]); edge->fVerbs.append(verbCount, &fVerbs[verbStart]); edge->setBounds(); - edge->fID = id + edges.count(); edge->fWinding = fWinding; edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know? } - void addSplit(SkTArray<InEdge>& edges, int id, SkPoint* pts, uint8_t verb, - double* ts, int tCount, bool flipped) { + void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb, + Intercepts& intercepts, int firstT, int lastT, bool flipped) { InEdge* edge = edges.push_back_n(1); edge->fIntercepts.push_back_n(1); - edge->fIntercepts[0].fTs.append(tCount, ts); + if (firstT == 0) { + *edge->fIntercepts[0].fTs.append() = 0; + } else { + *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1]; + } + bool add1 = lastT == intercepts.fTs.count(); + edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]); + if (add1) { + *edge->fIntercepts[0].fTs.append() = 1; + } edge->fIntercepts[0].fExplicit = true; - edge->fPts.append(verb, pts); + edge->fPts.append(verb + 1, pts); edge->fVerbs.append(1, &verb); - edge->setBounds(); - edge->fID = id + edges.count(); - edge->fWinding = fWinding; + // FIXME: bounds could be better for partial Ts + edge->setSubBounds(); edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know? if (flipped) { - flip(); + edge->flipTs(); + edge->fWinding = -fWinding; + } else { + edge->fWinding = fWinding; } } @@ -831,17 +906,16 @@ struct InEdge { return false; } - void complete(signed char winding, int id) { + void complete(signed char winding) { setBounds(); fIntercepts.push_back_n(fVerbs.count()); if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top flip(); } fContainsIntercepts = fIntersected = false; - fID = id; } - void flip() { + void flip() { size_t index; size_t last = fPts.count() - 1; for (index = 0; index < last; ++index, --last) { @@ -852,6 +926,18 @@ struct InEdge { SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]); } } + + void flipTs() { + SkASSERT(fIntercepts.count() == 1); + Intercepts& intercepts = fIntercepts[0]; + SkASSERT(intercepts.fExplicit); + SkTDArray<double>& ts = intercepts.fTs; + size_t index; + size_t last = ts.count() - 1; + for (index = 0; index < last; ++index, --last) { + SkTSwap<double>(ts[index], ts[last]); + } + } void reset() { fCached.reset(); @@ -859,7 +945,6 @@ struct InEdge { fPts.reset(); fVerbs.reset(); fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax); - fID = -1; fWinding = 0; fContainsIntercepts = false; fIntersected = false; @@ -881,8 +966,25 @@ struct InEdge { ++ptPtr; } } + + // recompute bounds based on subrange of T values + void setSubBounds() { + SkASSERT(fIntercepts.count() == 1); + Intercepts& intercepts = fIntercepts[0]; + SkASSERT(intercepts.fExplicit); + SkASSERT(fVerbs.count() == 1); + SkTDArray<double>& ts = intercepts.fTs; + if (fVerbs[0] == SkPath::kQuad_Verb) { + SkASSERT(fPts.count() == 3); + QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds); + } else { + SkASSERT(fVerbs[0] == SkPath::kCubic_Verb); + SkASSERT(fPts.count() == 4); + CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds); + } + } - void splitInflectionPts(SkTArray<InEdge>& edges, int idStart) { + void splitInflectionPts(SkTArray<InEdge>& edges) { if (!fIntersected) { return; } @@ -918,11 +1020,9 @@ struct InEdge { int nextPt = pts - fPts.begin(); int nextVerb = verbs - 1 - fVerbs.begin(); if (lastVerb < nextVerb) { - addPartial(edges, idStart, lastPt, nextPt, - lastVerb, nextVerb); + addPartial(edges, lastPt, nextPt, lastVerb, nextVerb); #if DEBUG_SPLIT - SkDebugf("%s addPartial 1 edge=%d\n", __FUNCTION__, - fID); + SkDebugf("%s addPartial 1\n", __FUNCTION__); #endif } lastPt = nextPt; @@ -931,20 +1031,18 @@ struct InEdge { } else { if (firstSplit >= 0) { if (lastSplit < firstSplit) { - addSplit(edges, idStart, pts, verb, - &intercepts.fTs[lastSplit], - firstSplit - lastSplit, false); + addSplit(edges, pts, verb, intercepts, + lastSplit, firstSplit, false); #if DEBUG_SPLIT - SkDebugf("%s addSplit 1 edge=%d tIndex=%d,%d\n", - __FUNCTION__, fID, lastSplit, firstSplit); + SkDebugf("%s addSplit 1 tIndex=%d,%d\n", + __FUNCTION__, lastSplit, firstSplit); #endif } - addSplit(edges, idStart, pts, verb, - &intercepts.fTs[firstSplit], - tIndex - firstSplit, true); + addSplit(edges, pts, verb, intercepts, + firstSplit, tIndex, true); #if DEBUG_SPLIT - SkDebugf("%s addSplit 2 edge=%d tIndex=%d,%d flip\n", - __FUNCTION__, fID, firstSplit, tIndex); + SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n", + __FUNCTION__, firstSplit, tIndex); #endif lastSplit = tIndex; firstSplit = -1; @@ -956,20 +1054,18 @@ struct InEdge { if (firstSplit < 0) { firstSplit = lastSplit; } else { - addSplit(edges, idStart, pts, verb, - &intercepts.fTs[lastSplit], firstSplit - lastSplit, - false); + addSplit(edges, pts, verb, intercepts, lastSplit, + firstSplit, false); #if DEBUG_SPLIT - SkDebugf("%s addSplit 3 edge=%d tIndex=%d,%d\n", __FUNCTION__, - fID, lastSplit, firstSplit); + SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__, + lastSplit, firstSplit); #endif } - addSplit(edges, idStart, pts, verb, - &intercepts.fTs[firstSplit], tIndex - firstSplit, - pts[verb].fY < y); + addSplit(edges, pts, verb, intercepts, firstSplit, + tIndex, pts[verb].fY < y); #if DEBUG_SPLIT - SkDebugf("%s addSplit 4 edge=%d tIndex=%d,%d %s\n", __FUNCTION__, - fID, firstSplit, tIndex, pts[verb].fY < y ? "flip" : ""); + SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__, + firstSplit, tIndex, pts[verb].fY < y ? "flip" : ""); #endif } } @@ -978,10 +1074,9 @@ struct InEdge { int nextVerb = verbs - 1 - fVerbs.begin(); if (lastVerb < nextVerb) { int nextPt = pts - fPts.begin(); - addPartial(edges, idStart, lastPt, nextPt, - lastVerb, nextVerb); + addPartial(edges, lastPt, nextPt, lastVerb, nextVerb); #if DEBUG_SPLIT - SkDebugf("%s addPartial 2 edge=%d\n", __FUNCTION__, fID); + SkDebugf("%s addPartial 2\n", __FUNCTION__); #endif } // OPTIMIZATION: reuse the edge instead of marking it empty @@ -1015,7 +1110,7 @@ struct InEdge { SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className), className, i, fVerbs[i]); } - SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className), + SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className), className, fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom); SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className, @@ -1049,7 +1144,6 @@ InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges : fPath(path) , fCurrentEdge(NULL) , fEdges(edges) - , fID(0) , fHorizontalEdges(horizontalEdges) , fIgnoreHorizontal(ignoreHorizontal) , fContainsCurves(false) @@ -1061,10 +1155,6 @@ bool containsCurves() const { return fContainsCurves; } -int nextID() { - return ++fID; -} - protected: void addEdge() { @@ -1076,7 +1166,7 @@ void addEdge() { bool complete() { if (fCurrentEdge && fCurrentEdge->fVerbs.count()) { - fCurrentEdge->complete(fWinding, nextID()); + fCurrentEdge->complete(fWinding); fCurrentEdge = NULL; return true; } @@ -1180,7 +1270,6 @@ private: SkPath::Verb fVerb; int fPtCount; int fPtOffset; - int fID; int8_t fWinding; bool fIgnoreHorizontal; bool fContainsCurves; @@ -1729,19 +1818,17 @@ static void debugShowLineIntersection(int pts, const WorkEdge& wt, SkPoint wtOutPt, wnOutPt; LineXYAtT(wt.fPts, wtTs[0], &wtOutPt); LineXYAtT(wn.fPts, wnTs[0], &wnOutPt); - SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n", + SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n", __FUNCTION__, wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY, - wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY, - test->fID, next->fID); + wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY); if (pts == 2) { SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]); } - SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n", + SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n", __FUNCTION__, wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY, - wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY, - test->fID, next->fID); + wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY); if (pts == 2) { SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]); } @@ -1979,10 +2066,15 @@ static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel, if (edgeCount == 0) { return; } + int id = 0; for (size_t index = 0; index < edgeCount; ++index) { - *edgeList.append() = &edges[index]; + InEdge& edge = edges[index]; + if (!edge.fWinding) { + continue; + } + edge.fID = ++id; + *edgeList.append() = &edge; } - edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax); *edgeList.append() = &edgeSentinel; QSort<InEdge>(edgeList.begin(), edgeList.end() - 1); } @@ -2240,7 +2332,6 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y, bool moreToDo, aboveBottom; do { double currentT = activePtr->t(); - SkASSERT(currentT < 1); const SkPoint* points = wt.fPts; double nextT; do { @@ -2270,32 +2361,30 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y, } if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY != clipped[verb].fY : clipped[0] != clipped[verb])) { - if (gShowDebugf) { - const char* verbStr[] = {"", "Line", "Quad", "Cubic"}; - SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d" - " v=%d t=(%1.9g,%1.9g)\n", tab, "", - verbStr[verb], clipped[0].fX, clipped[0].fY, - clipped[verb].fX, clipped[verb].fY, - activePtr->ID(), - activePtr->fWorkEdge.fVerb - - activePtr->fWorkEdge.fEdge->fVerbs.begin(), - currentT, nextT); - } +#if DEBUG_STITCH_EDGE + SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d" + " v=%d t=(%1.9g,%1.9g)\n", tab, "", + kUVerbStr[verb], clipped[0].fX, clipped[0].fY, + clipped[verb].fX, clipped[verb].fY, + activePtr->ID(), + activePtr->fWorkEdge.fVerb + - activePtr->fWorkEdge.fEdge->fVerbs.begin(), + currentT, nextT); +#endif outBuilder.addCurve(clipped, (SkPath::Verb) verb, activePtr->fWorkEdge.fEdge->fID, activePtr->fCloseCall); } else { - if (gShowDebugf ) { - const char* verbStr[] = {"", "Line", "Quad", "Cubic"}; - SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g" - " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "", - verbStr[verb], clipped[0].fX, clipped[0].fY, - clipped[verb].fX, clipped[verb].fY, - activePtr->ID(), - activePtr->fWorkEdge.fVerb - - activePtr->fWorkEdge.fEdge->fVerbs.begin(), - currentT, nextT); - } +#if DEBUG_STITCH_EDGE + SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g" + " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "", + kUVerbStr[verb], clipped[0].fX, clipped[0].fY, + clipped[verb].fX, clipped[verb].fY, + activePtr->ID(), + activePtr->fWorkEdge.fVerb + - activePtr->fWorkEdge.fEdge->fVerbs.begin(), + currentT, nextT); +#endif } // by advancing fAbove/fBelow, the next call to sortHorizontal // will use these values if they're still valid instead of @@ -2369,6 +2458,7 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) { InEdgeBuilder builder(path, asFill, edges, horizontalEdges); SkTDArray<InEdge*> edgeList; InEdge edgeSentinel; + edgeSentinel.reset(); makeEdgeList(edges, edgeSentinel, edgeList); SkTDArray<HorizontalEdge*> horizontalList; HorizontalEdge horizontalSentinel; @@ -2407,13 +2497,13 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) { currentPtr = edgeList.begin(); SkTArray<InEdge> splits; do { - (*currentPtr)->splitInflectionPts(splits, builder.nextID()); + (*currentPtr)->splitInflectionPts(splits); } while (*++currentPtr != &edgeSentinel); if (splits.count()) { - edges.pop_back(); // pop the sentinel for (int index = 0; index < splits.count(); ++index) { edges.push_back(splits[index]); } + edgeList.reset(); makeEdgeList(edges, edgeSentinel, edgeList); } } diff --git a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp index 584ecdabb3..31a5a23778 100644 --- a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp +++ b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp @@ -58,9 +58,13 @@ static void (*simplifyTests[])() = { static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]); -static void (*firstTest)() = 0; +static void (*firstTest)() = testSimplifyQuadratic3; +static bool skipAll = false; void SimplifyQuadraticPaths_Test() { + if (skipAll) { + return; + } size_t index = 0; if (firstTest) { while (index < simplifyTestsCount && simplifyTests[index] != firstTest) { diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp index 0b2fd06fbf..0844458ba3 100644 --- a/experimental/Intersection/EdgeWalker_TestUtility.cpp +++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp @@ -3,6 +3,7 @@ #include "SkBitmap.h" #include "SkCanvas.h" #include "SkPaint.h" +#include <algorithm> static bool gShowPath = false; static bool gComparePaths = true; @@ -124,7 +125,7 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two, canvas.drawPath(one, paint); canvas.restore(); canvas.save(); - canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1); + canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1); canvas.drawPath(two, paint); canvas.restore(); for (int y = 0; y < bitHeight; ++y) { @@ -184,13 +185,19 @@ static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap, } } if (!gDrawAllAsciiPaths) { - errors = scaledDrawTheSame(one, two, 9, 7, false, bitmap, canvas); - if (errors > 4) { - scaledDrawTheSame(one, two, 9, 7, true, bitmap, canvas); + const SkRect& bounds1 = one.getBounds(); + const SkRect& bounds2 = two.getBounds(); + SkRect larger = bounds1; + larger.join(bounds2); + SkScalar xScale = std::max(80.0f / larger.width(), 1.0f); + SkScalar yScale = std::max(60.0f / larger.height(), 1.0f); + errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas); + if (errors > 8) { + scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas); } } if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); - if (errors > 4 && gComparePathsAssert) { + if (errors > 31 && gComparePathsAssert) { showPath(one); showPath(two, "simplified:"); SkASSERT(0); diff --git a/experimental/Intersection/edge.xcodeproj/project.pbxproj b/experimental/Intersection/edge.xcodeproj/project.pbxproj index c91ac45f78..6a6ea320b0 100644 --- a/experimental/Intersection/edge.xcodeproj/project.pbxproj +++ b/experimental/Intersection/edge.xcodeproj/project.pbxproj @@ -94,13 +94,6 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - FE74136014F6866000056D7B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = ECDC7853EF9A45553165AE98; - remoteInfo = opts_ssse3; - }; FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = FEA5F4D91497FFF6005052F9 /* ports.xcodeproj */; @@ -108,6 +101,13 @@ remoteGlobalIDString = CDE03B47AA5CD6CE32E53995; remoteInfo = ports; }; + FEE70DA6153487F200814606 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = ECDC7853EF9A45553165AE98 /* libopts_ssse3.a */; + remoteInfo = opts_ssse3; + }; FEED7261144DD38D0059E97B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = FEED725D144DD38D0059E97B /* views.xcodeproj */; @@ -670,7 +670,7 @@ isa = PBXGroup; children = ( FEF87C2C13E0410900335C58 /* libopts.a */, - FE74136114F6866000056D7B /* libopts_ssse3.a */, + FEE70DA7153487F200814606 /* libopts_ssse3.a */, ); name = Products; sourceTree = "<group>"; @@ -791,18 +791,18 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - FE74136114F6866000056D7B /* libopts_ssse3.a */ = { + FEA5F4E11497FFF6005052F9 /* libports.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libopts_ssse3.a; - remoteRef = FE74136014F6866000056D7B /* PBXContainerItemProxy */; + path = libports.a; + remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - FEA5F4E11497FFF6005052F9 /* libports.a */ = { + FEE70DA7153487F200814606 /* libopts_ssse3.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libports.a; - remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */; + path = libopts_ssse3.a; + remoteRef = FEE70DA6153487F200814606 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; FEED7262144DD38D0059E97B /* libviews.a */ = { diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm index b2c91237f2..2078396bad 100644 --- a/experimental/Intersection/op.htm +++ b/experimental/Intersection/op.htm @@ -69,11 +69,55 @@ path.lineTo(460.782654, -112.96492); path.lineTo(326.610535, 34.0393639); path.close(); </div> +<div id="test_5div"> +original: +path.moveTo(0, 0); +path.quadTo(20, 0, 20, 20); +path.lineTo(0, 0); +path.close(); +path.moveTo(0, 20); +path.quadTo(0, 0, 20, 0); +path.lineTo(0, 20); +path.close(); +simplified: +path.moveTo(0, 0); +path.lineTo(5, 5); +path.quadTo(0, 10, 0, 20); +path.lineTo(20, 20); +path.quadTo(20, 10, 15, 5); +path.lineTo(20, 0); +path.quadTo(14.1421356, 0, 10, 1.71572876); +path.quadTo(5.85786438, 0, 0, 0); +path.close(); +</div> +<div id="test_6div"> +original: +path.moveTo(0, 20); +path.quadTo(20, 0, 40, 20); +path.lineTo(0, 20); +path.close(); +path.moveTo(40, 10); +path.quadTo(20, 30, 0, 10); +path.lineTo(40, 10); +path.close(); +simplified: +path.moveTo(0, 10); +path.quadTo(2.92893219, 12.9289322, 5.85786438, 15); +path.quadTo(2.92893219, 17.0710678, 0, 20); +path.lineTo(40, 20); +path.quadTo(37.0710678, 17.0710678, 34.1421356, 15); +path.quadTo(37.0710678, 12.9289322, 40, 10); +path.lineTo(0, 10); +path.close(); +</div> + </div> <script type="text/javascript"> var testDivs = [ + test_5div, + test_6div, test_4div, test_3div, test_2div, @@ -96,16 +140,26 @@ function parse(test) { var contourStrs = test.split("path.close();"); var pattern = /-?\d+\.*\d*/g; for (var c in contourStrs) { - var points = contourStrs[c].match(pattern); - var pts = []; - for (var wd in points) { - var num = parseFloat(points[wd]); - if (isNaN(num)) continue; - pts.push(num ); + var contour = contourStrs[c]; + var verbStrs = contour.split("path"); + var verbs = []; + for (var v in verbStrs) { + var verbStr = verbStrs[v]; + var points = verbStr.match(pattern); + var pts = []; + for (var wd in points) { + var num = parseFloat(points[wd]); + if (isNaN(num)) continue; + pts.push(num); + } + if (pts.length > 0) + verbs.push(pts); } - contours.push(pts); + if (verbs.length > 0) + contours.push(verbs); } - tests.push(contours); + if (contours.length > 0) + tests.push(contours); } function init(test) { @@ -116,13 +170,15 @@ function init(test) { var xmax = -Infinity; var ymin = Infinity; var ymax = -Infinity; - for (pts in test) { - var pt = test[pts]; - for (i = 0; i < pt.length; i += 2) { - xmin = Math.min(xmin, pt[i]); - ymin = Math.min(ymin, pt[i + 1]); - xmax = Math.max(xmax, pt[i]); - ymax = Math.max(ymax, pt[i + 1]); + for (var contours in test) { + var contour = test[contours]; + for (var verbs in contour) { + var verb = contour[verbs]; + var last = verb.length; + xmin = Math.min(xmin, verb[0]); + ymin = Math.min(ymin, verb[1]); + xmax = Math.max(xmax, verb[last - 2]); + ymax = Math.max(ymax, verb[last - 1]); } } var subscale = 1; @@ -183,27 +239,46 @@ function draw(test, _at_x, _at_y, scale) { ctx.fillText(num.toFixed(0), 0, i * unit + _at_y + 0); } ctx.strokeStyle = "red"; - for (pts in test) { - var pt = test[pts]; - var x = pt[0]; - var y = pt[1]; + var contours, verbs, pts; + for (contours in test) { + var contour = test[contours]; + if (contours == 2) ctx.strokeStyle = "blue"; ctx.beginPath(); - ctx.moveTo(xoffset + x * unit, yoffset + y * unit); - for (i = 2; i < pt.length; i += 2) { - x = pt[i]; - y = pt[i + 1]; - ctx.lineTo(xoffset + x * unit, yoffset + y * unit); + var first = true; + for (verbs in contour) { + var verb = contour[verbs]; + switch (verb.length) { + case 2: + if (first) { + ctx.moveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit); + first = false; + } else + ctx.lineTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit); + break; + case 4: + ctx.quadraticCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit, + xoffset + verb[2] * unit, yoffset + verb[3] * unit); + break; + case 6: + ctx.bezierCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit, + xoffset + verb[2] * unit, yoffset + verb[3] * unit, + xoffset + verb[4] * unit, yoffset + verb[5] * unit); + break; + } } ctx.stroke(); } ctx.fillStyle="blue"; - for (pts in test) { - var pt = test[pts]; - for (i = 0; i < pt.length; i += 2) { - x = pt[i]; - y = pt[i + 1]; - drawPoint(x, y, xoffset, yoffset, unit); + for (contours in test) { + var contour = test[contours]; + for (verbs in contour) { + var verb = contour[verbs]; + for (i = 0; i < verb.length; i += 2) { + x = verb[i]; + y = verb[i + 1]; + drawPoint(x, y, xoffset, yoffset, unit); + } } } } |