more quadratics work

git-svn-id: http://skia.googlecode.com/svn/trunk@3643 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2012-04-10 18:28:55 +00:00
Родитель 75589257c6
Коммит fb173424e9
5 изменённых файлов: 394 добавлений и 218 удалений

Просмотреть файл

@ -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);
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[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];
}
}
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;
}
@ -617,6 +664,22 @@ public:
leftPt.fX, leftPt.fY);
}
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
}
@ -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);
}
}

Просмотреть файл

@ -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) {

Просмотреть файл

@ -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);

Просмотреть файл

@ -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,13 +791,6 @@
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
FE74136114F6866000056D7B /* libopts_ssse3.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libopts_ssse3.a;
remoteRef = FE74136014F6866000056D7B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
FEA5F4E11497FFF6005052F9 /* libports.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@ -805,6 +798,13 @@
remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
FEE70DA7153487F200814606 /* libopts_ssse3.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libopts_ssse3.a;
remoteRef = FEE70DA6153487F200814606 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
FEED7262144DD38D0059E97B /* libviews.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;

Просмотреть файл

@ -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);
}
}
}
}