work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@3702 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2012-04-17 11:40:34 +00:00
parent 95bfdedb37
commit 78e17130f3
13 changed files with 729 additions and 217 deletions

View File

@ -47,6 +47,26 @@ SkPoint leftRight[][4] = {
size_t leftRightCount = sizeof(leftRight) / sizeof(leftRight[0]);
// older code that worked mostly
static bool operator_less_than(const UnitTest::ActiveEdge& lh,
const UnitTest::ActiveEdge& rh) {
if (rh.fAbove.fY - lh.fAbove.fY > lh.fBelow.fY - rh.fAbove.fY
&& lh.fBelow.fY < rh.fBelow.fY
|| lh.fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - lh.fAbove.fY
&& rh.fBelow.fY < lh.fBelow.fY) {
const SkPoint& check = rh.fBelow.fY <= lh.fBelow.fY
&& lh.fBelow != rh.fBelow ? rh.fBelow :
rh.fAbove;
return (check.fY - lh.fAbove.fY) * (lh.fBelow.fX - lh.fAbove.fX)
< (lh.fBelow.fY - lh.fAbove.fY) * (check.fX - lh.fAbove.fX);
}
const SkPoint& check = lh.fBelow.fY <= rh.fBelow.fY
&& lh.fBelow != rh.fBelow ? lh.fBelow : lh.fAbove;
return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
< (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
}
void ActiveEdge_Test() {
UnitTest::InEdge leftIn, rightIn;
UnitTest::ActiveEdge left, right;
@ -58,9 +78,9 @@ void ActiveEdge_Test() {
right.fAbove = leftRight[x][2];
right.fBelow = leftRight[x][3];
SkASSERT(left < right);
SkASSERT(left.operator_less_than(right));
SkASSERT(operator_less_than(left, right));
SkASSERT(!(right < left));
SkASSERT(!right.operator_less_than(left));
SkASSERT(!operator_less_than(right, left));
}
}

View File

@ -14,6 +14,7 @@ int convex_hull(const Cubic& cubic, char order[4]);
bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]);
bool implicit_matches(const Cubic& cubic1, const Cubic& cubic2);
bool implicit_matches(const _Line& line1, const _Line& line2);
bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps);
bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2);
void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst);
void sub_divide(const _Line& src, double t1, double t2, _Line& dst);

View File

@ -16,42 +16,49 @@
#include "ShapeOps.h"
#include "TSearch.h"
#if 01 // set to 1 for no debugging whatsoever
const bool gShowDebugf = false; // FIXME: remove once debugging is complete
#undef SkASSERT
#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
// FIXME: remove once debugging is complete
#if 0 // set to 1 for no debugging whatsoever
const bool gRunTestsInOneThread = true;
#define DEBUG_DUMP 0
#define DEBUG_ADD 0
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_ADD_BOTTOM_TS 0
#define DEBUG_ABOVE_BELOW 0
#define DEBUG_ACTIVE_LESS_THAN 0
#define DEBUG_ADD 0
#define DEBUG_ADD_BOTTOM_TS 0
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_ADJUST_COINCIDENT 0
#define DEBUG_ASSEMBLE 0
#define DEBUG_BOTTOM 0
#define DEBUG_BRIDGE 0
#define DEBUG_DUMP 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
#define DEBUG_TRIM_LINE 0
#else
const bool gRunTestsInOneThread = true;
#define DEBUG_DUMP 01
#define DEBUG_ADD 01
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_ADD_BOTTOM_TS 0
#define DEBUG_ABOVE_BELOW 01
#define DEBUG_ACTIVE_LESS_THAN 0
#define DEBUG_ADD 01
#define DEBUG_ADD_BOTTOM_TS 0
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_ADJUST_COINCIDENT 1
#define DEBUG_ASSEMBLE 1
#define DEBUG_BOTTOM 0
#define DEBUG_BRIDGE 1
#define DEBUG_DUMP 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
#define DEBUG_TRIM_LINE 1
#endif
@ -182,7 +189,8 @@ static void LineSubDivide(const SkPoint a[2], double startT, double endT,
static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
SkPoint sub[3]) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}};
Quadratic dst;
sub_divide(aQuad, startT, endT, dst);
sub[0].fX = SkDoubleToScalar(dst[0].x);
@ -195,8 +203,8 @@ static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
SkPoint sub[4]) {
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
{a[3].fX, a[3].fY}};
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
Cubic dst;
sub_divide(aCubic, startT, endT, dst);
sub[0].fX = SkDoubleToScalar(dst[0].x);
@ -231,6 +239,45 @@ static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
}
}
static SkPath::Verb QuadReduceOrder(SkPoint a[4]) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}};
Quadratic dst;
int order = reduceOrder(aQuad, dst);
for (int index = 0; index < order; ++index) {
a[index].fX = SkDoubleToScalar(dst[index].x);
a[index].fY = SkDoubleToScalar(dst[index].y);
}
if (order == 1) { // FIXME: allow returning points, caller should discard
a[1] = a[0];
return (SkPath::Verb) order;
}
return (SkPath::Verb) (order - 1);
}
static SkPath::Verb CubicReduceOrder(SkPoint a[4]) {
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
{a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
Cubic dst;
int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
for (int index = 0; index < order; ++index) {
a[index].fX = SkDoubleToScalar(dst[index].x);
a[index].fY = SkDoubleToScalar(dst[index].y);
}
if (order == 1) { // FIXME: allow returning points, caller should discard
a[1] = a[0];
return (SkPath::Verb) order;
}
return (SkPath::Verb) (order - 1);
}
static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
const SkPoint& below) {
const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
return implicit_matches_ulps(aLine, bLine, 32);
}
/*
list of edges
bounds for edge
@ -346,10 +393,10 @@ public:
* (edge.fPts[1].fX - edge.fPts[0].fX)
/ (edge.fPts[1].fY - edge.fPts[0].fY);
edge.fPts[1].fY = y;
if (gShowDebugf) {
SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
edge.fPts[1].fX, y);
}
#if DEBUG_TRIM_LINE
SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
edge.fPts[1].fX, y);
#endif
return true;
}
return false;
@ -442,8 +489,9 @@ public:
#endif
}
int firstCopy = 1;
if (gap || (lastVerb == SkPath::kLine_Verb &&
!extendLine(lastCurve, *curve[verb]))) {
if (gap || (lastVerb == SkPath::kLine_Verb
&& (verb != SkPath::kLine_Verb
|| !extendLine(lastCurve, *curve[verb])))) {
// FIXME: see comment in bridge -- this probably
// conceals errors
SkASSERT(lastCurve[lastVerb] == *curve[0] ||
@ -1173,15 +1221,14 @@ bool complete() {
return false;
}
int direction(int count) {
fPtCount = count;
int direction(SkPath::Verb verb) {
fPtCount = verb + 1;
if (fIgnoreHorizontal && isHorizontal()) {
return 0;
}
int last = count - 1;
return fPts[0].fY == fPts[last].fY
? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
return fPts[0].fY == fPts[verb].fY
? fPts[0].fX == fPts[verb].fX ? 0 : fPts[0].fX < fPts[verb].fX
? 1 : -1 : fPts[0].fY < fPts[verb].fY ? 1 : -1;
}
bool isHorizontal() {
@ -1211,15 +1258,17 @@ void walk() {
startEdge();
continue;
case SkPath::kLine_Verb:
winding = direction(2);
winding = direction(SkPath::kLine_Verb);
break;
case SkPath::kQuad_Verb:
winding = direction(3);
fContainsCurves = true;
fVerb = QuadReduceOrder(fPts);
winding = direction(fVerb);
fContainsCurves |= fVerb == SkPath::kQuad_Verb;
break;
case SkPath::kCubic_Verb:
winding = direction(4);
fContainsCurves = true;
fVerb = CubicReduceOrder(fPts);
winding = direction(fVerb);
fContainsCurves |= fVerb >= SkPath::kQuad_Verb;
break;
case SkPath::kClose_Verb:
SkASSERT(fCurrentEdge);
@ -1291,12 +1340,20 @@ struct WorkEdge {
fPts += *fVerb++;
return fVerb != fEdge->fVerbs.end();
}
const SkPoint* lastPoints() const {
SkASSERT(fPts >= fEdge->fPts.begin() + lastVerb());
return &fPts[-lastVerb()];
}
SkPath::Verb lastVerb() const {
SkASSERT(fVerb > fEdge->fVerbs.begin());
return (SkPath::Verb) fVerb[-1];
}
const SkPoint* points() const {
return fPts;
}
SkPath::Verb verb() const {
return (SkPath::Verb) *fVerb;
@ -1323,6 +1380,8 @@ struct WorkEdge {
// as active edges are introduced, only local sorting should be required
class ActiveEdge {
public:
// this logic must be kept in sync with tooCloseToCall
// callers expect this to only read fAbove, fBelow
bool operator<(const ActiveEdge& rh) const {
double topD = fAbove.fX - rh.fAbove.fX;
if (rh.fAbove.fY < fAbove.fY) {
@ -1344,49 +1403,6 @@ public:
return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
}
// OPTIMIZATION: fold return statements into one
bool operator_less_than(const ActiveEdge& rh) const {
if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
&& fBelow.fY < rh.fBelow.fY
|| fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
&& rh.fBelow.fY < fBelow.fY) {
// FIXME: need to compute distance, not check for points equal
const SkPoint& check = rh.fBelow.fY <= fBelow.fY
&& fBelow != rh.fBelow ? rh.fBelow :
rh.fAbove;
#if DEBUG_ACTIVE_LESS_THAN
SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}"
" < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__,
rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
(check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
< (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' '
: '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
(fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)));
#endif
return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
< (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
}
// FIXME: need to compute distance, not check for points equal
const SkPoint& check = fBelow.fY <= rh.fBelow.fY
&& fBelow != rh.fBelow ? fBelow : fAbove;
#if DEBUG_ACTIVE_LESS_THAN
SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}"
" < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__,
fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
(rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
< (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY,
UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX),
(check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)),
UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY));
#endif
return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
< (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
}
// If a pair of edges are nearly coincident for some span, add a T in the
// edge so it can be shortened to match the other edge. Note that another
// approach is to trim the edge after it is added to the OutBuilder list --
@ -1417,6 +1433,7 @@ public:
SkTSwap(left, right);
}
double ts[2];
SkASSERT(fWorkEdge.fVerb[0] == SkPath::kLine_Verb);
int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
SkASSERT(pts == 1);
// An ActiveEdge or WorkEdge has no need to modify the T values computed
@ -1514,16 +1531,14 @@ public:
(*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow);
}
}
#if DEBUG_ABOVE_BELOW
fTAbove = tAbove;
fTBelow = tBelow;
#endif
}
bool done(SkScalar bottom) const {
return fDone || fYBottom >= bottom;
}
void fixBelow() {
if (fFixBelow) {
switch (fWorkEdge.verb()) {
@ -1567,27 +1582,39 @@ public:
// It's unclear how to do this -- seems more complicated than recording the
// t values, since the same t values could exist intersecting non-coincident
// edges.
bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
bool isCoincidentWith(const ActiveEdge* edge) const {
if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
return false;
}
uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
SkPath::Verb verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
SkPath::Verb edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
: edge->fWorkEdge.verb();
if (verb != edgeVerb) {
return false;
}
switch (verb) {
case SkPath::kLine_Verb: {
case SkPath::kLine_Verb:
return true;
}
default:
// FIXME: add support for all curve types
// FIXME: add support for quads, cubics
SkASSERT(0);
return false;
}
return false;
}
bool isUnordered(const ActiveEdge* edge) const {
return fAbove == edge->fAbove && fBelow == edge->fBelow;
}
SkPath::Verb lastVerb() const {
return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
}
const SkPoint* lastPoints() const {
return fDone ? fWorkEdge.lastPoints() : fWorkEdge.points();
}
// The shortest close call edge should be moved into a position where
// it contributes if the winding is transitioning to or from zero.
bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
@ -1625,35 +1652,23 @@ public:
}
return false;
}
bool swapUnordered(const ActiveEdge* edge, SkScalar bottom) const {
SkASSERT(lastVerb() != SkPath::kLine_Verb
|| edge->lastVerb() != SkPath::kLine_Verb);
if (fDone || edge->fDone) {
return false;
}
ActiveEdge thisWork, edgeWork;
extractAboveBelow(thisWork);
edge->extractAboveBelow(edgeWork);
return edgeWork < thisWork;
}
bool tooCloseToCall(const ActiveEdge* edge) const {
int ulps;
// FIXME: the first variation works better (or at least causes fewer tests
// to fail than the second, although the second's logic better matches the
// current sort criteria. Need to track down the cause of the crash, and
// see if the second case isn't somehow buggy.
#if 01
// FIXME: don't compare points for equality
// OPTIMIZATION: refactor to make one call to UlpsDiff
if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY
&& fBelow.fY < edge->fBelow.fY
|| fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY
&& edge->fBelow.fY < fBelow.fY) {
const SkPoint& check = edge->fBelow.fY <= fBelow.fY
&& fBelow != edge->fBelow ? edge->fBelow :
edge->fAbove;
ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX),
(fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX));
} else {
const SkPoint& check = fBelow.fY <= edge->fBelow.fY
&& fBelow != edge->fBelow ? fBelow : fAbove;
ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY)
* (check.fX - edge->fAbove.fX),
(check.fY - edge->fAbove.fY)
* (edge->fBelow.fX - edge->fAbove.fX));
}
#else
double t1, t2, b1, b2;
// This logic must be kept in sync with operator <
if (edge->fAbove.fY < fAbove.fY) {
t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX);
@ -1683,8 +1698,49 @@ public:
SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
ulps);
#endif
#endif
return ulps >= 0 && ulps <= 32;
if (ulps < 0 || ulps > 32) {
return false;
}
SkPath::Verb verb = lastVerb();
SkPath::Verb edgeVerb = edge->lastVerb();
if (verb == SkPath::kLine_Verb && edgeVerb == SkPath::kLine_Verb) {
return true;
}
if (verb != SkPath::kLine_Verb && edgeVerb != SkPath::kLine_Verb) {
return false;
}
double ts[2];
bool isLine = true;
bool curveQuad = true;
if (verb == SkPath::kCubic_Verb) {
ts[0] = (fTAbove * 2 + fTBelow) / 3;
ts[1] = (fTAbove + fTBelow * 2) / 3;
curveQuad = isLine = false;
} else if (edgeVerb == SkPath::kCubic_Verb) {
ts[0] = (edge->fTAbove * 2 + edge->fTBelow) / 3;
ts[1] = (edge->fTAbove + edge->fTBelow * 2) / 3;
curveQuad = false;
} else if (verb == SkPath::kQuad_Verb) {
ts[0] = fTAbove;
ts[1] = (fTAbove + fTBelow) / 2;
isLine = false;
} else {
SkASSERT(edgeVerb == SkPath::kQuad_Verb);
ts[0] = edge->fTAbove;
ts[1] = (edge->fTAbove + edge->fTBelow) / 2;
}
const SkPoint* curvePts = isLine ? edge->lastPoints() : lastPoints();
const ActiveEdge* lineEdge = isLine ? this : edge;
SkPoint curveSample[2];
for (int index = 0; index < 2; ++index) {
if (curveQuad) {
QuadXYAtT(curvePts, ts[index], &curveSample[index]);
} else {
CubicXYAtT(curvePts, ts[index], &curveSample[index]);
}
}
return IsCoincident(curveSample, lineEdge->fAbove, lineEdge->fBelow);
}
double nextT() const {
@ -1715,15 +1771,35 @@ public:
return fWorkEdge.fEdge->fID;
}
private:
// utility used only by swapUnordered
void extractAboveBelow(ActiveEdge& extracted) const {
SkPoint curve[4];
switch (lastVerb()) {
case SkPath::kLine_Verb:
extracted.fAbove = fAbove;
extracted.fBelow = fBelow;
return;
case SkPath::kQuad_Verb:
QuadSubDivide(lastPoints(), fTAbove, fTBelow, curve);
break;
case SkPath::kCubic_Verb:
CubicSubDivide(lastPoints(), fTAbove, fTBelow, curve);
break;
default:
SkASSERT(0);
}
extracted.fAbove = curve[0];
extracted.fBelow = curve[1];
}
public:
WorkEdge fWorkEdge;
const SkTDArray<double>* fTs;
SkPoint fAbove;
SkPoint fBelow;
#if DEBUG_ABOVE_BELOW
double fTAbove;
double fTAbove; // OPTIMIZATION: only required if edge has quads or cubics
double fTBelow;
#endif
SkScalar fYBottom;
int fCoincident;
int fTIndex;
@ -2170,49 +2246,91 @@ static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
if (edgeCount == 0) {
return bottom;
}
ActiveEdge* activePtr = edgeList[0];
ActiveEdge* activePtr, * nextPtr = edgeList[0];
size_t index;
bool foundCoincident = false;
int firstIndex = 0;
for (index = 1; index < edgeCount; ++index) {
ActiveEdge* nextPtr = edgeList[index];
activePtr = nextPtr;
nextPtr = edgeList[index];
if (firstIndex != index - 1 && !activePtr->fSkip) {
if (nextPtr->fWorkEdge.verb() == SkPath::kLine_Verb
&& activePtr->isUnordered(nextPtr)) {
start here;
// swap the line with the curve
// back up to the previous edge and retest
SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
SkASSERT(index > 1);
index -= 2;
continue;
}
}
bool closeCall = false;
activePtr->fCoincident = firstIndex;
if (activePtr->isCoincidentWith(nextPtr, y)
if (activePtr->isCoincidentWith(nextPtr)
|| (closeCall = activePtr->tooCloseToCall(nextPtr))) {
activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
} else if (activePtr->isUnordered(nextPtr)) {
foundCoincident = true;
} else {
firstIndex = index;
}
activePtr = nextPtr;
}
activePtr->fCoincident = firstIndex;
nextPtr->fCoincident = firstIndex;
if (!foundCoincident) {
return bottom;
}
int winding = 0;
activePtr = edgeList[0];
nextPtr = edgeList[0];
for (index = 1; index < edgeCount; ++index) {
int priorWinding = winding;
winding += activePtr->fWorkEdge.winding();
ActiveEdge* nextPtr = edgeList[index];
if (activePtr->fCoincident == nextPtr->fCoincident) {
// the coincident edges may not have been sorted above -- advance
// the edges and resort if needed
// OPTIMIZE: if sorting is done incrementally as new edges are added
// and not all at once as is done here, fold this test into the
// current less than test.
activePtr = nextPtr;
nextPtr = edgeList[index];
SkASSERT(activePtr == edgeList[index - 1]);
SkASSERT(nextPtr == edgeList[index]);
if (activePtr->fCoincident != nextPtr->fCoincident) {
continue;
}
// the coincident edges may not have been sorted above -- advance
// the edges and resort if needed
// OPTIMIZE: if sorting is done incrementally as new edges are added
// and not all at once as is done here, fold this test into the
// current less than test.
while ((!activePtr->fSkip || !nextPtr->fSkip)
&& activePtr->fCoincident == nextPtr->fCoincident) {
if (activePtr->swapUnordered(nextPtr, bottom)) {
winding -= activePtr->fWorkEdge.winding();
SkASSERT(activePtr == edgeList[index - 1]);
SkASSERT(nextPtr == edgeList[index]);
SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
if (--index == 0) {
winding += activePtr->fWorkEdge.winding();
break;
}
// back up one
activePtr = edgeList[index - 1];
continue;
}
SkASSERT(activePtr == edgeList[index - 1]);
SkASSERT(nextPtr == edgeList[index]);
break;
}
if (activePtr->fSkip && nextPtr->fSkip) {
if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
priorWinding, winding, windingMask)
: activePtr->swapCoincident(nextPtr, bottom)) {
winding -= activePtr->fWorkEdge.winding();
SkASSERT(activePtr == edgeList[index - 1]);
SkASSERT(nextPtr == edgeList[index]);
SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
SkTSwap<ActiveEdge*>(activePtr, nextPtr);
winding += activePtr->fWorkEdge.winding();
SkASSERT(activePtr == edgeList[index - 1]);
SkASSERT(nextPtr == edgeList[index]);
}
}
activePtr = nextPtr;
}
int firstCoincidentWinding = 0;
ActiveEdge* firstCoincident = NULL;
@ -2221,8 +2339,9 @@ static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
for (index = 1; index < edgeCount; ++index) {
int priorWinding = winding;
winding += activePtr->fWorkEdge.winding();
ActiveEdge* nextPtr = edgeList[index];
if (activePtr->fCoincident == nextPtr->fCoincident) {
nextPtr = edgeList[index];
if (activePtr->fSkip && nextPtr->fSkip
&& activePtr->fCoincident == nextPtr->fCoincident) {
if (!firstCoincident) {
firstCoincident = activePtr;
firstCoincidentWinding = priorWinding;
@ -2294,32 +2413,26 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
ActiveEdge** activeHandle = edgeList.begin() - 1;
ActiveEdge** lastActive = edgeList.end();
const int tab = 7; // FIXME: debugging only
if (gShowDebugf) {
SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
}
#if DEBUG_STITCH_EDGE
SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
#endif
while (++activeHandle != lastActive) {
ActiveEdge* activePtr = *activeHandle;
const WorkEdge& wt = activePtr->fWorkEdge;
int lastWinding = winding;
winding += wt.winding();
if (gShowDebugf) {
SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
#if DEBUG_ABOVE_BELOW
" above=%1.9g below=%1.9g"
#if DEBUG_STITCH_EDGE
SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
" above=%1.9g below=%1.9g\n",
tab-4, "", activePtr->ID(), lastWinding,
winding, activePtr->fSkip, activePtr->fCloseCall,
activePtr->fTAbove, activePtr->fTBelow);
#endif
"\n",
tab-4, "", activePtr->ID(), lastWinding,
winding, activePtr->fSkip, activePtr->fCloseCall
#if DEBUG_ABOVE_BELOW
, activePtr->fTAbove, activePtr->fTBelow
#endif
);
}
if (activePtr->done(bottom)) {
if (gShowDebugf) {
SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
activePtr->fDone, activePtr->fYBottom);
}
#if DEBUG_STITCH_EDGE
SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
activePtr->fDone, activePtr->fYBottom);
#endif
continue;
}
int opener = (lastWinding & windingMask) == 0;
@ -2328,10 +2441,10 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
bool inWinding = opener | closer;
SkPoint clippedPts[4];
const SkPoint* clipped = NULL;
uint8_t verb = wt.verb();
bool moreToDo, aboveBottom;
do {
double currentT = activePtr->t();
uint8_t verb = wt.verb();
const SkPoint* points = wt.fPts;
double nextT;
do {
@ -2389,14 +2502,13 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
// by advancing fAbove/fBelow, the next call to sortHorizontal
// will use these values if they're still valid instead of
// recomputing
if (clipped[1].fY > activePtr->fBelow.fY
if (clipped[verb].fY > activePtr->fBelow.fY
&& bottom >= activePtr->fBelow.fY ) {
activePtr->fAbove = activePtr->fBelow;
activePtr->fBelow = clipped[1];
#if DEBUG_ABOVE_BELOW
activePtr->fTAbove = activePtr->fTBelow;
activePtr->fBelow = clipped[verb];
activePtr->fTAbove = activePtr->fTBelow < 1
? activePtr->fTBelow : 0;
activePtr->fTBelow = nextT;
#endif
}
currentT = nextT;
moreToDo = activePtr->advanceT();
@ -2423,11 +2535,15 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
activePtr->fSkip = false;
activePtr->fCloseCall = false;
aboveBottom &= !activePtr->fCloseCall;
} else {
}
#if DEBUG_STITCH_EDGE
else {
if (activePtr->fSkip || activePtr->fCloseCall) {
if (gShowDebugf) SkDebugf("== %1.9g\n", clippedPts[0].fY);
SkDebugf("%s skip or close == %1.9g\n", __FUNCTION__,
clippedPts[0].fY);
}
}
#endif
} while (moreToDo & aboveBottom);
} while ((moreToDo || activePtr->advance()) & aboveBottom);
}

View File

@ -5,43 +5,12 @@
#include <assert.h>
#include <pthread.h>
struct State {
State() {
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
bitmap.allocPixels();
canvas = new SkCanvas(bitmap);
}
int a;
int b;
int c;
int d;
pthread_t threadID;
SkCanvas* canvas;
SkBitmap bitmap;
bool abcIsATriangle;
};
void createThread(State* statePtr, void* (*test)(void* )) {
int threadError = pthread_create(&statePtr->threadID, NULL, test,
(void*) statePtr);
SkASSERT(!threadError);
}
void waitForCompletion(State threadState[], int& threadIndex) {
for (int index = 0; index < threadIndex; ++index) {
pthread_join(threadState[index].threadID, NULL);
}
SkDebugf(".");
threadIndex = 0;
}
static void* testSimplify4x4QuadralateralsMain(void* data)
{
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
SkASSERT(data);
State& state = *(State*) data;
State4& state = *(State4*) data;
int ax = state.a & 0x03;
int ay = state.a >> 2;
int bx = state.b & 0x03;
@ -105,17 +74,17 @@ static void* testSimplify4x4QuadralateralsMain(void* data)
return NULL;
}
const int maxThreads = gShowDebugf ? 1 : 24;
const int maxThreads = gRunTestsInOneThread ? 1 : 24;
void Simplify4x4QuadralateralsThreaded_Test()
{
State threadState[maxThreads];
State4 threadState[maxThreads];
int threadIndex = 0;
for (int a = 0; a < 16; ++a) {
for (int b = a ; b < 16; ++b) {
for (int c = b ; c < 16; ++c) {
for (int d = c; d < 16; ++d) {
State* statePtr = &threadState[threadIndex];
State4* statePtr = &threadState[threadIndex];
statePtr->a = a;
statePtr->b = b;
statePtr->c = c;
@ -140,7 +109,7 @@ static void* testSimplify4x4NondegeneratesMain(void* data) {
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
SkASSERT(data);
State& state = *(State*) data;
State4& state = *(State4*) data;
int ax = state.a & 0x03;
int ay = state.a >> 2;
int bx = state.b & 0x03;
@ -193,7 +162,7 @@ static void* testSimplify4x4NondegeneratesMain(void* data) {
}
void SimplifyNondegenerate4x4TrianglesThreaded_Test() {
State threadState[maxThreads];
State4 threadState[maxThreads];
int threadIndex = 0;
for (int a = 0; a < 15; ++a) {
int ax = a & 0x03;
@ -210,7 +179,7 @@ void SimplifyNondegenerate4x4TrianglesThreaded_Test() {
if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) {
continue;
}
State* statePtr = &threadState[threadIndex];
State4* statePtr = &threadState[threadIndex];
statePtr->a = a;
statePtr->b = b;
statePtr->c = c;
@ -232,7 +201,7 @@ static void* testSimplify4x4DegeneratesMain(void* data) {
char pathStr[1024];
bzero(pathStr, sizeof(pathStr));
SkASSERT(data);
State& state = *(State*) data;
State4& state = *(State4*) data;
int ax = state.a & 0x03;
int ay = state.a >> 2;
int bx = state.b & 0x03;
@ -283,7 +252,7 @@ static void* testSimplify4x4DegeneratesMain(void* data) {
}
void SimplifyDegenerate4x4TrianglesThreaded_Test() {
State threadState[maxThreads];
State4 threadState[maxThreads];
int threadIndex = 0;
for (int a = 0; a < 16; ++a) {
int ax = a & 0x03;
@ -294,7 +263,7 @@ void SimplifyDegenerate4x4TrianglesThreaded_Test() {
for (int c = a ; c < 16; ++c) {
int cx = c & 0x03;
int cy = c >> 2;
State* statePtr = &threadState[threadIndex];
State4* statePtr = &threadState[threadIndex];
statePtr->abcIsATriangle = (bx - ax) * (cy - ay)
!= (by - ay) * (cx - ax);
statePtr->a = a;

View File

@ -49,7 +49,188 @@ static void testSimplifyQuadratic4() {
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic5() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(0, 0, 0, 1);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic6() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(1, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 0, 0, 1);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic7() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 0, 0, 2);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic8() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 0, 0, 2);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic9() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(1, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 0, 2, 2);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic10() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 1);
path.quadTo(1, 1, 1, 2);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic11() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 2);
path.close();
path.moveTo(0, 0);
path.lineTo(2, 1);
path.quadTo(2, 2, 3, 3);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic12() {
SkPath path, out;
path.moveTo(0, 0);
path.lineTo(0, 2);
path.lineTo(0, 0);
path.close();
path.moveTo(3, 0);
path.quadTo(1, 1, 0, 2);
path.lineTo(3, 0);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic13() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 1, 0);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.quadTo(3, 0, 1, 1);
path.lineTo(0, 0);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic14() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(1, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(0, 1, 2, 1);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic15() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 1, 3);
path.lineTo(3, 3);
path.close();
path.moveTo(0, 1);
path.lineTo(1, 1);
path.quadTo(0, 3, 3, 3);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void testSimplifyQuadratic16() {
SkPath path, out;
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 1);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(1, 0, 0, 1);
path.close();
testSimplify(path, true, out, bitmap);
drawAsciiPaths(path, out, true);
}
static void (*simplifyTests[])() = {
testSimplifyQuadratic16,
testSimplifyQuadratic15,
testSimplifyQuadratic14,
testSimplifyQuadratic13,
testSimplifyQuadratic12,
testSimplifyQuadratic11,
testSimplifyQuadratic10,
testSimplifyQuadratic9,
testSimplifyQuadratic8,
testSimplifyQuadratic7,
testSimplifyQuadratic6,
testSimplifyQuadratic5,
testSimplifyQuadratic4,
testSimplifyQuadratic3,
testSimplifyQuadratic2,
@ -58,7 +239,7 @@ static void (*simplifyTests[])() = {
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
static void (*firstTest)() = testSimplifyQuadratic3;
static void (*firstTest)() = 0;
static bool skipAll = false;
void SimplifyQuadraticPaths_Test() {

View File

@ -1,8 +1,9 @@
#include "ShapeOps.h"
#include "SkBitmap.h"
#include <pthread.h>
class SkBitmap;
class SkCanvas;
//extern int comparePaths(const SkPath& one, const SkPath& two);
@ -12,3 +13,19 @@ extern bool drawAsciiPaths(const SkPath& one, const SkPath& two,
extern void showPath(const SkPath& path, const char* str = NULL);
extern bool testSimplify(const SkPath& path, bool fill, SkPath& out,
SkBitmap& bitmap, SkCanvas* canvas = 0);
struct State4 {
State4();
int a;
int b;
int c;
int d;
pthread_t threadID;
SkCanvas* canvas;
SkBitmap bitmap;
bool abcIsATriangle;
};
void createThread(State4* statePtr, void* (*test)(void* ));
void waitForCompletion(State4 threadState[], int& threadIndex);

View File

@ -5,6 +5,9 @@
#include "SkPaint.h"
#include <algorithm>
#undef SkASSERT
#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
static bool gShowPath = false;
static bool gComparePaths = true;
static bool gDrawLastAsciiPaths = true;
@ -170,6 +173,8 @@ static int scaledDrawTheSame(const SkPath& one, const SkPath& two,
return errors;
}
static int max = 0;
static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
SkCanvas* canvas) {
int errors = pathsDrawTheSame(one, two, bitmap, canvas);
@ -192,17 +197,22 @@ static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
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) {
if (errors > 50) {
scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
}
}
if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
if (errors > 31 && gComparePathsAssert) {
if (errors > max) {
SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
max = errors;
}
const int MAX_ERRORS = 100;
if (errors > MAX_ERRORS) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
if (errors > MAX_ERRORS && gComparePathsAssert) {
showPath(one);
showPath(two, "simplified:");
SkASSERT(0);
}
return errors;
return errors > MAX_ERRORS ? errors : 0;
}
// doesn't work yet
@ -247,3 +257,23 @@ bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap,
}
return comparePaths(path, out, bitmap, canvas) == 0;
}
State4::State4() {
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
bitmap.allocPixels();
canvas = new SkCanvas(bitmap);
}
void createThread(State4* statePtr, void* (*test)(void* )) {
int threadError = pthread_create(&statePtr->threadID, NULL, test,
(void*) statePtr);
SkASSERT(!threadError);
}
void waitForCompletion(State4 threadState[], int& threadIndex) {
for (int index = 0; index < threadIndex; ++index) {
pthread_join(threadState[index].threadID, NULL);
}
SkDebugf(".");
threadIndex = 0;
}

View File

@ -4,8 +4,12 @@
void cubecode_test(int test);
void testSimplify();
#define TEST_QUADS_FIRST 01
void Intersection_Tests() {
#if !TEST_QUADS_FIRST
ActiveEdge_Test();
#endif
cubecode_test(1);
convert_testx();
@ -24,10 +28,17 @@ void Intersection_Tests() {
SimplifyPolygonPaths_Test();
SimplifyRectangularPaths_Test();
SimplifyQuadralateralPaths_Test();
#if TEST_QUADS_FIRST
ActiveEdge_Test();
Simplify4x4QuadraticsThreaded_Test();
#endif
SimplifyDegenerate4x4TrianglesThreaded_Test();
SimplifyNondegenerate4x4TrianglesThreaded_Test();
Simplify4x4QuadralateralsThreaded_Test();
#if !TEST_QUADS_FIRST
Simplify4x4QuadraticsThreaded_Test();
#endif
QuadraticCoincidence_Test();
QuadraticReduceOrder_Test();

View File

@ -18,6 +18,7 @@ void SimplifyPolygonPaths_Test();
void SimplifyQuadralateralPaths_Test();
void SimplifyQuadraticPaths_Test();
void Simplify4x4QuadralateralsThreaded_Test();
void Simplify4x4QuadraticsThreaded_Test();
void SimplifyRectangularPaths_Test();
void QuadraticBezierClip_Test();
void QuadraticCoincidence_Test();

View File

@ -28,6 +28,29 @@ bool implicit_matches(const _Line& one, const _Line& two) {
return true;
}
bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps) {
_Point oneD, twoD;
tangent(one, oneD);
tangent(two, twoD);
/* See if the slopes match, i.e.
dx1 / dy1 == dx2 / dy2
(dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2
dy2 * dx1 == dy1 * dx2
*/
int diff = UlpsDiff(oneD.x * twoD.y, twoD.x * oneD.y);
if (diff < 0 || diff > ulps) {
return false;
}
/* See if the axis intercepts match, i.e.
y0 - x0 * dy / dx == y1 - x1 * dy / dx
dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx)
dx * y0 - x0 * dy == dx * y1 - x1 * dy
*/
diff = UlpsDiff(oneD.x * one[0].y - oneD.y * one[0].x,
oneD.x * two[0].y - oneD.y * two[0].x);
return diff >= 0 && diff <= ulps;
}
void tangent(const _Line& line, _Point& result) {
result.x = line[0].x - line[1].x;
result.y = line[0].y - line[1].y;

View File

@ -9,12 +9,19 @@ int quadraticRoots(double A, double B, double C, double t[2]) {
}
double squareRt = sqrt(square);
double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2;
double ratio;
int foundRoots = 0;
if ((Q <= A) ^ (Q < 0)) {
t[foundRoots++] = Q / A;
ratio = Q / A;
if (!isnan(ratio)) {
t[foundRoots++] = ratio;
}
}
if ((C <= Q) ^ (C < 0)) {
t[foundRoots++] = C / Q;
ratio = C / Q;
if (!isnan(ratio)) {
t[foundRoots++] = ratio;
}
}
return foundRoots;
}

View File

@ -3,4 +3,4 @@
void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
void simplify(const SkPath& path, bool asFill, SkPath& simple);
extern const bool gShowDebugf; // FIXME: remove once debugging is complete
extern const bool gRunTestsInOneThread; // FIXME: remove once debugging is complete

View File

@ -110,14 +110,150 @@ path.quadTo(37.0710678, 12.9289322, 40, 10);
path.lineTo(0, 10);
path.close();
</div>
<div id="test_7div">
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 1);
path.quadTo(1, 1, 1, 2);
path.lineTo(0, 0);
path.close();
</div>
<div id="test_8div">
original:
path.moveTo(0, 0);
path.lineTo(3, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(1, 0);
path.lineTo(0, 1);
path.quadTo(2, 1, 3, 3);
path.lineTo(1, 0);
path.close();
simplified:
path.moveTo(1, 0);
path.lineTo(0, 1);
path.quadTo(1.42857146, 1, 2.34693885, 2.02040815);
path.lineTo(1.28571427, 0.428571433);
path.lineTo(1, 0);
path.close();
path.moveTo(2.34693885, 2.02040815);
path.quadTo(2.71428561, 2.42857146, 3, 3);
path.lineTo(2.34693885, 2.02040815);
path.close();
</div>
<div id="test_9div">
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(0, 2);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(2, 1);
path.quadTo(2, 2, 3, 3);
path.lineTo(0, 0);
path.close();
</div>
<div id="test_10div">
original:
path.moveTo(0, 0);
path.lineTo(0, 2);
path.lineTo(0, 0);
path.close();
path.moveTo(3, 0);
path.quadTo(1, 1, 0, 2);
path.lineTo(3, 0);
path.close();
simplified:
path.moveTo(0, 0);
path.lineTo(0, 2);
path.quadTo(1, 1, 3, 0);
path.lineTo(0, 0);
path.close();
</div>
<div id="test_11div">
original:
path.moveTo(0, 0);
path.quadTo(0, 0, 1, 0);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.quadTo(3, 0, 1, 1);
path.lineTo(0, 0);
path.close();
simplified:
path.moveTo(0, 0);
path.lineTo(1, 1);
path.lineTo(1, 0);
path.lineTo(0, 0);
</div>
<div id="test_12div">
path.moveTo(0, 0);
path.quadTo(0, 0, 0, 0);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 0);
path.lineTo(0, 0);
path.quadTo(0, 1, 2, 1);
path.lineTo(0, 0);
path.close();
</div>
<div id="test_13div">
original:
path.moveTo(0, 0);
path.quadTo(0, 0, 1, 3);
path.lineTo(3, 3);
path.lineTo(0, 0);
path.close();
path.moveTo(0, 1);
path.lineTo(1, 1);
path.quadTo(0, 3, 3, 3);
path.lineTo(0, 1);
path.close();
simplified:
path.moveTo(0, 0);
path.lineTo(0.333333343, 1);
path.lineTo(0, 1);
path.lineTo(0.428571433, 1.28571427);
path.lineTo(0.333333343, 1);
path.lineTo(1, 1);
path.lineTo(0, 0);
path.close();
path.moveTo(1, 1);
path.quadTo(0.857142866, 1.28571427, 0.795918345, 1.53061223);
path.lineTo(0.428571433, 1.28571427);
path.lineTo(1, 3);
path.lineTo(3, 3);
path.lineTo(0.795918345, 1.53061223);
path.quadTo(0.428571433, 3, 3, 3);
path.lineTo(1, 1);
path.close();
</div>
</div>
<script type="text/javascript">
var testDivs = [
test_5div,
test_13div,
test_12div,
test_11div,
test_10div,
test_9div,
test_8div,
test_7div,
test_6div,
test_5div,
test_4div,
test_3div,
test_2div,