shape ops work in progress
git-svn-id: http://skia.googlecode.com/svn/trunk@5959 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
f94dd182e6
commit
c91dfe417a
@ -220,7 +220,7 @@ static void tryRoncoOnce(const SkPath& path, const SkRect& target, bool show) {
|
|||||||
if (!closed) {
|
if (!closed) {
|
||||||
tiny.close();
|
tiny.close();
|
||||||
}
|
}
|
||||||
if (false && show) {
|
if (show) {
|
||||||
showPath(tiny, NULL);
|
showPath(tiny, NULL);
|
||||||
SkDebugf("simplified:\n");
|
SkDebugf("simplified:\n");
|
||||||
}
|
}
|
||||||
@ -229,13 +229,13 @@ static void tryRoncoOnce(const SkPath& path, const SkRect& target, bool show) {
|
|||||||
|
|
||||||
static void tryRonco(const SkPath& path) {
|
static void tryRonco(const SkPath& path) {
|
||||||
const SkRect& overall = path.getBounds();
|
const SkRect& overall = path.getBounds();
|
||||||
const int divs = 4;
|
const int divs = 64;
|
||||||
SkScalar cellWidth = overall.width() / divs * 2;
|
SkScalar cellWidth = overall.width() / divs * 2;
|
||||||
SkScalar cellHeight = overall.height() / divs * 2;
|
SkScalar cellHeight = overall.height() / divs * 2;
|
||||||
SkRect target;
|
SkRect target;
|
||||||
if (true) {
|
if (true) {
|
||||||
int xDiv = 1;
|
int xDiv = 28;
|
||||||
int yDiv = 2;
|
int yDiv = 17;
|
||||||
target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
|
target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
|
||||||
overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
|
overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
|
||||||
cellWidth, cellHeight);
|
cellWidth, cellHeight);
|
||||||
@ -280,13 +280,14 @@ static bool drawLetters(SkCanvas* canvas, int step, bool useOld)
|
|||||||
#if 0
|
#if 0
|
||||||
for (int mask = 0; mask < 1 << testStrLen; ++mask) {
|
for (int mask = 0; mask < 1 << testStrLen; ++mask) {
|
||||||
char maskStr[testStrLen];
|
char maskStr[testStrLen];
|
||||||
mask = 12;
|
mask = 15;
|
||||||
for (int letter = 0; letter < testStrLen; ++letter) {
|
for (int letter = 0; letter < testStrLen; ++letter) {
|
||||||
maskStr[letter] = mask & (1 << letter) ? testStr[letter] : ' ';
|
maskStr[letter] = mask & (1 << letter) ? testStr[letter] : ' ';
|
||||||
}
|
}
|
||||||
paint.getPosTextPath(maskStr, testStrLen, textPos, &path);
|
paint.getPosTextPath(maskStr, testStrLen, textPos, &path);
|
||||||
// showPath(path, NULL);
|
// showPath(path, NULL);
|
||||||
// SkDebugf("%d simplified:\n", mask);
|
// SkDebugf("%d simplified:\n", mask);
|
||||||
|
tryRonco(path);
|
||||||
testSimplifyx(path);
|
testSimplifyx(path);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,7 +16,7 @@ public:
|
|||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
virtual void onDraw(SkCanvas* canvas) {
|
virtual void onDraw(SkCanvas* canvas) {
|
||||||
static int step = 0 ; // 17904; // drawLetters first error
|
static int step = 0; // 12752; // 17908 ; // 17904; // drawLetters first error
|
||||||
// drawStars triggers error at 23275
|
// drawStars triggers error at 23275
|
||||||
// error is not easy to debug in its current state
|
// error is not easy to debug in its current state
|
||||||
static double seconds;
|
static double seconds;
|
||||||
|
@ -14,9 +14,9 @@ void cubecode_test(int test);
|
|||||||
|
|
||||||
void Intersection_Tests() {
|
void Intersection_Tests() {
|
||||||
int testsRun = 0;
|
int testsRun = 0;
|
||||||
|
SimplifyNew_Test();
|
||||||
QuadraticIntersection_Test();
|
QuadraticIntersection_Test();
|
||||||
MiniSimplify_Test();
|
MiniSimplify_Test();
|
||||||
SimplifyNew_Test();
|
|
||||||
SimplifyAngle_Test();
|
SimplifyAngle_Test();
|
||||||
QuarticRoot_Test();
|
QuarticRoot_Test();
|
||||||
// QuadraticIntersection_Test();
|
// QuadraticIntersection_Test();
|
||||||
|
@ -65,6 +65,7 @@ static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
|||||||
int oppWinding = current->oppSign(index, endIndex);
|
int oppWinding = current->oppSign(index, endIndex);
|
||||||
bool active = windingIsActive(winding, spanWinding, oppWinding, op);
|
bool active = windingIsActive(winding, spanWinding, oppWinding, op);
|
||||||
SkTDArray<Span*> chaseArray;
|
SkTDArray<Span*> chaseArray;
|
||||||
|
bool unsortable = false;
|
||||||
do {
|
do {
|
||||||
#if DEBUG_WINDING
|
#if DEBUG_WINDING
|
||||||
SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
|
SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
|
||||||
@ -77,9 +78,12 @@ static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
|||||||
int nextStart = index;
|
int nextStart = index;
|
||||||
int nextEnd = endIndex;
|
int nextEnd = endIndex;
|
||||||
Segment* next = current->findNextOp(chaseArray, active,
|
Segment* next = current->findNextOp(chaseArray, active,
|
||||||
nextStart, nextEnd, winding, spanWinding, op,
|
nextStart, nextEnd, winding, spanWinding, unsortable, op,
|
||||||
aXorMask, bXorMask);
|
aXorMask, bXorMask);
|
||||||
if (!next) {
|
if (!next) {
|
||||||
|
// FIXME: if unsortable, allow partial paths to be later
|
||||||
|
// assembled
|
||||||
|
SkASSERT(!unsortable);
|
||||||
if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
|
if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
|
||||||
lastPt = current->addCurveTo(index, endIndex, simple, true);
|
lastPt = current->addCurveTo(index, endIndex, simple, true);
|
||||||
SkASSERT(*firstPt == lastPt);
|
SkASSERT(*firstPt == lastPt);
|
||||||
|
@ -49,7 +49,7 @@ const bool gRunTestsInOneThread = false;
|
|||||||
|
|
||||||
const bool gRunTestsInOneThread = true;
|
const bool gRunTestsInOneThread = true;
|
||||||
|
|
||||||
#define DEBUG_ACTIVE_SPANS 0
|
#define DEBUG_ACTIVE_SPANS 1
|
||||||
#define DEBUG_ADD_INTERSECTING_TS 1
|
#define DEBUG_ADD_INTERSECTING_TS 1
|
||||||
#define DEBUG_ADD_T_PAIR 1
|
#define DEBUG_ADD_T_PAIR 1
|
||||||
#define DEBUG_ANGLE 1
|
#define DEBUG_ANGLE 1
|
||||||
@ -481,6 +481,8 @@ struct Span {
|
|||||||
int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
|
int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
|
||||||
int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
|
int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
|
||||||
bool fDone; // if set, this span to next higher T has been processed
|
bool fDone; // if set, this span to next higher T has been processed
|
||||||
|
bool fUnsortableStart; // set when start is part of an unsortable pair
|
||||||
|
bool fUnsortableEnd; // set when end is part of an unsortable pair
|
||||||
};
|
};
|
||||||
|
|
||||||
// sorting angles
|
// sorting angles
|
||||||
@ -527,6 +529,14 @@ public:
|
|||||||
&& !approximately_zero_squared(cmp)) {
|
&& !approximately_zero_squared(cmp)) {
|
||||||
return cmp < 0;
|
return cmp < 0;
|
||||||
}
|
}
|
||||||
|
// at this point, the initial tangent line is coincident
|
||||||
|
if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
|
||||||
|
// FIXME: running demo will trigger this assertion
|
||||||
|
// (don't know if commenting out will trigger further assertion or not)
|
||||||
|
// commenting it out allows demo to run in release, though
|
||||||
|
// SkASSERT(fSide != rh.fSide);
|
||||||
|
return fSide < rh.fSide;
|
||||||
|
}
|
||||||
// see if either curve can be lengthened and try the tangent compare again
|
// see if either curve can be lengthened and try the tangent compare again
|
||||||
if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
|
if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
|
||||||
&& (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
|
&& (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
|
||||||
@ -542,14 +552,6 @@ public:
|
|||||||
return longer < rhLonger;
|
return longer < rhLonger;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// at this point, the initial tangent line is coincident
|
|
||||||
if (fSide * rh.fSide <= 0) {
|
|
||||||
// FIXME: running demo will trigger this assertion
|
|
||||||
// (don't know if commenting out will trigger further assertion or not)
|
|
||||||
// commenting it out allows demo to run in release, though
|
|
||||||
// SkASSERT(fSide != rh.fSide);
|
|
||||||
return fSide < rh.fSide;
|
|
||||||
}
|
|
||||||
SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
|
SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
|
||||||
SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
|
SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
|
||||||
// FIXME: until I can think of something better, project a ray from the
|
// FIXME: until I can think of something better, project a ray from the
|
||||||
@ -573,8 +575,14 @@ public:
|
|||||||
roots = QuadRayIntersect(fPts, ray, i);
|
roots = QuadRayIntersect(fPts, ray, i);
|
||||||
rroots = QuadRayIntersect(rh.fPts, ray, ri);
|
rroots = QuadRayIntersect(rh.fPts, ray, ri);
|
||||||
} while ((roots == 0 || rroots == 0) && (flip ^= true));
|
} while ((roots == 0 || rroots == 0) && (flip ^= true));
|
||||||
SkASSERT(roots > 0);
|
if (roots == 0 || rroots == 0) {
|
||||||
SkASSERT(rroots > 0);
|
// FIXME: we don't have a solution in this case. The interim solution
|
||||||
|
// is to mark the edges as unsortable, exclude them from this and
|
||||||
|
// future computations, and allow the returned path to be fragmented
|
||||||
|
fUnsortable = true;
|
||||||
|
rh.fUnsortable = true;
|
||||||
|
return this < &rh; // even with no solution, return a stable sort
|
||||||
|
}
|
||||||
_Point loc;
|
_Point loc;
|
||||||
double best = SK_ScalarInfinity;
|
double best = SK_ScalarInfinity;
|
||||||
double dx, dy, dist;
|
double dx, dy, dist;
|
||||||
@ -649,6 +657,7 @@ public:
|
|||||||
fVerb = verb;
|
fVerb = verb;
|
||||||
fSpans = &spans;
|
fSpans = &spans;
|
||||||
fReversed = false;
|
fReversed = false;
|
||||||
|
fUnsortable = false;
|
||||||
setSpans();
|
setSpans();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,19 +696,23 @@ public:
|
|||||||
return SkSign32(fStart - fEnd);
|
return SkSign32(fStart - fEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SkTDArray<Span>* spans() const {
|
||||||
|
return fSpans;
|
||||||
|
}
|
||||||
|
|
||||||
int start() const {
|
int start() const {
|
||||||
return fStart;
|
return fStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool unsortable() const {
|
||||||
|
return fUnsortable;
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG_ANGLE
|
#if DEBUG_ANGLE
|
||||||
const SkPoint* pts() const {
|
const SkPoint* pts() const {
|
||||||
return fPts;
|
return fPts;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SkTDArray<Span>* spans() const {
|
|
||||||
return fSpans;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkPath::Verb verb() const {
|
SkPath::Verb verb() const {
|
||||||
return fVerb;
|
return fVerb;
|
||||||
}
|
}
|
||||||
@ -720,18 +733,9 @@ private:
|
|||||||
int fStart;
|
int fStart;
|
||||||
int fEnd;
|
int fEnd;
|
||||||
bool fReversed;
|
bool fReversed;
|
||||||
|
mutable bool fUnsortable; // this alone is editable by the less than operator
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
|
|
||||||
int angleCount = angles.count();
|
|
||||||
int angleIndex;
|
|
||||||
angleList.setReserve(angleCount);
|
|
||||||
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
|
|
||||||
*angleList.append() = &angles[angleIndex];
|
|
||||||
}
|
|
||||||
QSort<Angle>(angleList.begin(), angleList.end() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bounds, unlike Rect, does not consider a line to be empty.
|
// Bounds, unlike Rect, does not consider a line to be empty.
|
||||||
struct Bounds : public SkRect {
|
struct Bounds : public SkRect {
|
||||||
static bool Intersects(const Bounds& a, const Bounds& b) {
|
static bool Intersects(const Bounds& a, const Bounds& b) {
|
||||||
@ -1131,6 +1135,8 @@ public:
|
|||||||
if ((span->fDone = newT == 1)) {
|
if ((span->fDone = newT == 1)) {
|
||||||
++fDoneSpans;
|
++fDoneSpans;
|
||||||
}
|
}
|
||||||
|
span->fUnsortableStart = false;
|
||||||
|
span->fUnsortableEnd = false;
|
||||||
return insertedAt;
|
return insertedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1486,10 +1492,13 @@ public:
|
|||||||
// OPTIMIZATION: check all angles to see if any have computed wind sum
|
// OPTIMIZATION: check all angles to see if any have computed wind sum
|
||||||
// before sorting (early exit if none)
|
// before sorting (early exit if none)
|
||||||
SkTDArray<Angle*> sorted;
|
SkTDArray<Angle*> sorted;
|
||||||
sortAngles(angles, sorted);
|
bool sortable = SortAngles(angles, sorted);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
|
if (!sortable) {
|
||||||
|
return SK_MinS32;
|
||||||
|
}
|
||||||
int angleCount = angles.count();
|
int angleCount = angles.count();
|
||||||
const Angle* angle;
|
const Angle* angle;
|
||||||
const Segment* base;
|
const Segment* base;
|
||||||
@ -1651,7 +1660,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
|
Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
|
||||||
int& nextStart, int& nextEnd, int& winding, int& spanWinding, ShapeOp op,
|
int& nextStart, int& nextEnd, int& winding, int& spanWinding,
|
||||||
|
bool& unsortable, ShapeOp op,
|
||||||
const int aXorMask, const int bXorMask) {
|
const int aXorMask, const int bXorMask) {
|
||||||
const int startIndex = nextStart;
|
const int startIndex = nextStart;
|
||||||
const int endIndex = nextEnd;
|
const int endIndex = nextEnd;
|
||||||
@ -1706,13 +1716,17 @@ public:
|
|||||||
addTwoAngles(startIndex, end, angles);
|
addTwoAngles(startIndex, end, angles);
|
||||||
buildAngles(end, angles);
|
buildAngles(end, angles);
|
||||||
SkTDArray<Angle*> sorted;
|
SkTDArray<Angle*> sorted;
|
||||||
sortAngles(angles, sorted);
|
bool sortable = SortAngles(angles, sorted);
|
||||||
int angleCount = angles.count();
|
int angleCount = angles.count();
|
||||||
int firstIndex = findStartingEdge(sorted, startIndex, end);
|
int firstIndex = findStartingEdge(sorted, startIndex, end);
|
||||||
SkASSERT(firstIndex >= 0);
|
SkASSERT(firstIndex >= 0);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
|
debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
|
||||||
#endif
|
#endif
|
||||||
|
if (!sortable) {
|
||||||
|
unsortable = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
SkASSERT(sorted[firstIndex]->segment() == this);
|
SkASSERT(sorted[firstIndex]->segment() == this);
|
||||||
#if DEBUG_WINDING
|
#if DEBUG_WINDING
|
||||||
SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
|
SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
|
||||||
@ -1883,7 +1897,8 @@ public:
|
|||||||
// it is guaranteed to have an end which describes a non-zero length (?)
|
// it is guaranteed to have an end which describes a non-zero length (?)
|
||||||
// winding -1 means ccw, 1 means cw
|
// winding -1 means ccw, 1 means cw
|
||||||
Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
|
Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
|
||||||
int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
|
int& nextStart, int& nextEnd, int& winding, int& spanWinding,
|
||||||
|
bool& unsortable) {
|
||||||
const int startIndex = nextStart;
|
const int startIndex = nextStart;
|
||||||
const int endIndex = nextEnd;
|
const int endIndex = nextEnd;
|
||||||
int outerWinding = winding;
|
int outerWinding = winding;
|
||||||
@ -1937,13 +1952,17 @@ public:
|
|||||||
addTwoAngles(startIndex, end, angles);
|
addTwoAngles(startIndex, end, angles);
|
||||||
buildAngles(end, angles);
|
buildAngles(end, angles);
|
||||||
SkTDArray<Angle*> sorted;
|
SkTDArray<Angle*> sorted;
|
||||||
sortAngles(angles, sorted);
|
bool sortable = SortAngles(angles, sorted);
|
||||||
int angleCount = angles.count();
|
int angleCount = angles.count();
|
||||||
int firstIndex = findStartingEdge(sorted, startIndex, end);
|
int firstIndex = findStartingEdge(sorted, startIndex, end);
|
||||||
SkASSERT(firstIndex >= 0);
|
SkASSERT(firstIndex >= 0);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
|
debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
|
||||||
#endif
|
#endif
|
||||||
|
if (!sortable) {
|
||||||
|
unsortable = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
SkASSERT(sorted[firstIndex]->segment() == this);
|
SkASSERT(sorted[firstIndex]->segment() == this);
|
||||||
#if DEBUG_WINDING
|
#if DEBUG_WINDING
|
||||||
SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
|
SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
|
||||||
@ -2068,7 +2087,7 @@ public:
|
|||||||
return nextSegment;
|
return nextSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
Segment* findNextXor(int& nextStart, int& nextEnd) {
|
Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
|
||||||
const int startIndex = nextStart;
|
const int startIndex = nextStart;
|
||||||
const int endIndex = nextEnd;
|
const int endIndex = nextEnd;
|
||||||
SkASSERT(startIndex != endIndex);
|
SkASSERT(startIndex != endIndex);
|
||||||
@ -2126,13 +2145,17 @@ public:
|
|||||||
addTwoAngles(startIndex, end, angles);
|
addTwoAngles(startIndex, end, angles);
|
||||||
buildAngles(end, angles);
|
buildAngles(end, angles);
|
||||||
SkTDArray<Angle*> sorted;
|
SkTDArray<Angle*> sorted;
|
||||||
sortAngles(angles, sorted);
|
bool sortable = SortAngles(angles, sorted);
|
||||||
int angleCount = angles.count();
|
int angleCount = angles.count();
|
||||||
int firstIndex = findStartingEdge(sorted, startIndex, end);
|
int firstIndex = findStartingEdge(sorted, startIndex, end);
|
||||||
SkASSERT(firstIndex >= 0);
|
SkASSERT(firstIndex >= 0);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
|
debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
|
||||||
#endif
|
#endif
|
||||||
|
if (!sortable) {
|
||||||
|
unsortable = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
SkASSERT(sorted[firstIndex]->segment() == this);
|
SkASSERT(sorted[firstIndex]->segment() == this);
|
||||||
int nextIndex = firstIndex + 1;
|
int nextIndex = firstIndex + 1;
|
||||||
int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
|
int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
|
||||||
@ -2302,6 +2325,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start here;
|
||||||
|
// either:
|
||||||
|
// a) mark spans with either end unsortable as done, or
|
||||||
|
// b) rewrite findTop / findTopSegment / findTopContour to iterate further
|
||||||
|
// when encountering an unsortable span
|
||||||
|
|
||||||
// OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
|
// OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
|
||||||
// and use more concise logic like the old edge walker code?
|
// and use more concise logic like the old edge walker code?
|
||||||
// FIXME: this needs to deal with coincident edges
|
// FIXME: this needs to deal with coincident edges
|
||||||
@ -2316,9 +2345,10 @@ public:
|
|||||||
int count = fTs.count();
|
int count = fTs.count();
|
||||||
// see if either end is not done since we want smaller Y of the pair
|
// see if either end is not done since we want smaller Y of the pair
|
||||||
bool lastDone = true;
|
bool lastDone = true;
|
||||||
|
bool lastUnsortableEnd;
|
||||||
for (int index = 0; index < count; ++index) {
|
for (int index = 0; index < count; ++index) {
|
||||||
const Span& span = fTs[index];
|
const Span& span = fTs[index];
|
||||||
if (!span.fDone || !lastDone) {
|
if ((!span.fDone && !span.fUnsortableStart) || (!lastDone && !lastUnsortableEnd)) {
|
||||||
const SkPoint& intercept = xyAtT(&span);
|
const SkPoint& intercept = xyAtT(&span);
|
||||||
if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
|
if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
|
||||||
&& topPt.fX > intercept.fX)) {
|
&& topPt.fX > intercept.fX)) {
|
||||||
@ -2329,6 +2359,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastDone = span.fDone;
|
lastDone = span.fDone;
|
||||||
|
lastUnsortableEnd = span.fUnsortableEnd;
|
||||||
}
|
}
|
||||||
// sort the edges to find the leftmost
|
// sort the edges to find the leftmost
|
||||||
int step = 1;
|
int step = 1;
|
||||||
@ -2345,7 +2376,7 @@ public:
|
|||||||
addTwoAngles(end, firstT, angles);
|
addTwoAngles(end, firstT, angles);
|
||||||
buildAngles(firstT, angles);
|
buildAngles(firstT, angles);
|
||||||
SkTDArray<Angle*> sorted;
|
SkTDArray<Angle*> sorted;
|
||||||
sortAngles(angles, sorted);
|
(void) SortAngles(angles, sorted);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
@ -2354,6 +2385,11 @@ public:
|
|||||||
Segment* leftSegment;
|
Segment* leftSegment;
|
||||||
do {
|
do {
|
||||||
const Angle* angle = sorted[++firstT];
|
const Angle* angle = sorted[++firstT];
|
||||||
|
if (angle->unsortable()) {
|
||||||
|
// FIXME: if all angles are unsortable, find next topmost
|
||||||
|
SkASSERT(firstT < angles.count() - 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
leftSegment = angle->segment();
|
leftSegment = angle->segment();
|
||||||
tIndex = angle->end();
|
tIndex = angle->end();
|
||||||
endIndex = angle->start();
|
endIndex = angle->start();
|
||||||
@ -2687,6 +2723,33 @@ public:
|
|||||||
fTs.reset();
|
fTs.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
|
||||||
|
int angleCount = angles.count();
|
||||||
|
int angleIndex;
|
||||||
|
angleList.setReserve(angleCount);
|
||||||
|
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
|
||||||
|
*angleList.append() = &angles[angleIndex];
|
||||||
|
}
|
||||||
|
QSort<Angle>(angleList.begin(), angleList.end() - 1);
|
||||||
|
bool result = true;
|
||||||
|
for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
|
||||||
|
Angle& angle = angles[angleIndex];
|
||||||
|
if (angle.unsortable()) {
|
||||||
|
// so that it is available for early exclusion in findTop and others
|
||||||
|
const SkTDArray<Span>* spans = angle.spans();
|
||||||
|
Span* span = const_cast<Span*>(&(*spans)[angle.start()]);
|
||||||
|
if (angle.start() < angle.end()) {
|
||||||
|
span->fUnsortableStart = true;
|
||||||
|
} else {
|
||||||
|
--span;
|
||||||
|
span->fUnsortableEnd = true;
|
||||||
|
}
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// OPTIMIZATION: mark as debugging only if used solely by tests
|
// OPTIMIZATION: mark as debugging only if used solely by tests
|
||||||
const Span& span(int tIndex) const {
|
const Span& span(int tIndex) const {
|
||||||
return fTs[tIndex];
|
return fTs[tIndex];
|
||||||
@ -2968,9 +3031,10 @@ public:
|
|||||||
lastSum = windSum;
|
lastSum = windSum;
|
||||||
windSum -= segment.spanSign(&angle);
|
windSum -= segment.spanSign(&angle);
|
||||||
}
|
}
|
||||||
SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
|
SkDebugf("%s [%d] %s id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
|
||||||
" sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
|
" sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
|
||||||
__FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
|
__FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE ***" : "",
|
||||||
|
segment.fID, kLVerbStr[segment.fVerb],
|
||||||
start, segment.xAtT(&sSpan),
|
start, segment.xAtT(&sSpan),
|
||||||
segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
|
segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
|
||||||
segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
|
segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
|
||||||
@ -4075,7 +4139,7 @@ static int innerContourCheck(SkTDArray<Contour*>& contourList,
|
|||||||
// returns the first counterclockwise hour before 6 o'clock,
|
// returns the first counterclockwise hour before 6 o'clock,
|
||||||
// or if the base point is rightmost, returns the first clockwise
|
// or if the base point is rightmost, returns the first clockwise
|
||||||
// hour after 6 o'clock
|
// hour after 6 o'clock
|
||||||
sortAngles(angles, sorted);
|
(void) Segment::SortAngles(angles, sorted);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
@ -4089,6 +4153,9 @@ static int innerContourCheck(SkTDArray<Contour*>& contourList,
|
|||||||
bool baseMatches = test->yAtT(tIndex) == basePt.fY;
|
bool baseMatches = test->yAtT(tIndex) == basePt.fY;
|
||||||
for (int index = 0; index < count; ++index) {
|
for (int index = 0; index < count; ++index) {
|
||||||
angle = sorted[index];
|
angle = sorted[index];
|
||||||
|
if (angle->unsortable()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (baseMatches && angle->isHorizontal()) {
|
if (baseMatches && angle->isHorizontal()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -4235,10 +4302,14 @@ static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SkTDArray<Angle*> sorted;
|
SkTDArray<Angle*> sorted;
|
||||||
sortAngles(angles, sorted);
|
bool sortable = Segment::SortAngles(angles, sorted);
|
||||||
#if DEBUG_SORT
|
#if DEBUG_SORT
|
||||||
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
|
||||||
#endif
|
#endif
|
||||||
|
if (!sortable) {
|
||||||
|
chase.pop(&span);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// find first angle, initialize winding to computed fWindSum
|
// find first angle, initialize winding to computed fWindSum
|
||||||
int firstIndex = -1;
|
int firstIndex = -1;
|
||||||
const Angle* angle;
|
const Angle* angle;
|
||||||
@ -4331,8 +4402,10 @@ static bool windingIsActive(int winding, int spanWinding) {
|
|||||||
// is an option, choose first edge that continues the inside.
|
// is an option, choose first edge that continues the inside.
|
||||||
// since we start with leftmost top edge, we'll traverse through a
|
// since we start with leftmost top edge, we'll traverse through a
|
||||||
// smaller angle counterclockwise to get to the next edge.
|
// smaller angle counterclockwise to get to the next edge.
|
||||||
static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
// returns true if all edges were processed
|
||||||
|
static bool bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
||||||
bool firstContour = true;
|
bool firstContour = true;
|
||||||
|
bool unsortable = false;
|
||||||
do {
|
do {
|
||||||
Segment* topStart = findTopContour(contourList);
|
Segment* topStart = findTopContour(contourList);
|
||||||
if (!topStart) {
|
if (!topStart) {
|
||||||
@ -4392,11 +4465,11 @@ static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
|||||||
#endif
|
#endif
|
||||||
const SkPoint* firstPt = NULL;
|
const SkPoint* firstPt = NULL;
|
||||||
do {
|
do {
|
||||||
SkASSERT(!current->done());
|
SkASSERT(unsortable || !current->done());
|
||||||
int nextStart = index;
|
int nextStart = index;
|
||||||
int nextEnd = endIndex;
|
int nextEnd = endIndex;
|
||||||
Segment* next = current->findNextWinding(chaseArray, active,
|
Segment* next = current->findNextWinding(chaseArray, active,
|
||||||
nextStart, nextEnd, winding, spanWinding);
|
nextStart, nextEnd, winding, spanWinding, unsortable);
|
||||||
if (!next) {
|
if (!next) {
|
||||||
if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
|
if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
|
||||||
lastPt = current->addCurveTo(index, endIndex, simple, true);
|
lastPt = current->addCurveTo(index, endIndex, simple, true);
|
||||||
@ -4443,19 +4516,22 @@ static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
|||||||
active = windingIsActive(winding, spanWinding);
|
active = windingIsActive(winding, spanWinding);
|
||||||
} while (true);
|
} while (true);
|
||||||
} while (true);
|
} while (true);
|
||||||
|
return !unsortable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
// returns true if all edges were processed
|
||||||
|
static bool bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
||||||
Segment* current;
|
Segment* current;
|
||||||
int start, end;
|
int start, end;
|
||||||
|
bool unsortable = false;
|
||||||
while ((current = findUndone(contourList, start, end))) {
|
while ((current = findUndone(contourList, start, end))) {
|
||||||
const SkPoint* firstPt = NULL;
|
const SkPoint* firstPt = NULL;
|
||||||
SkPoint lastPt;
|
SkPoint lastPt;
|
||||||
do {
|
do {
|
||||||
SkASSERT(!current->done());
|
SkASSERT(unsortable || !current->done());
|
||||||
int nextStart = start;
|
int nextStart = start;
|
||||||
int nextEnd = end;
|
int nextEnd = end;
|
||||||
Segment* next = current->findNextXor(nextStart, nextEnd);
|
Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
|
||||||
if (!next) {
|
if (!next) {
|
||||||
if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
|
if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
|
||||||
lastPt = current->addCurveTo(start, end, simple, true);
|
lastPt = current->addCurveTo(start, end, simple, true);
|
||||||
@ -4481,6 +4557,7 @@ static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
|
|||||||
debugShowActiveSpans(contourList);
|
debugShowActiveSpans(contourList);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
return !unsortable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
|
static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
|
||||||
@ -4503,6 +4580,11 @@ static void makeContourList(SkTArray<Contour>& contours,
|
|||||||
QSort<Contour>(list.begin(), list.end() - 1);
|
QSort<Contour>(list.begin(), list.end() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void assemble(SkPath& simple) {
|
||||||
|
// TODO: find the non-closed paths and connect them together
|
||||||
|
SkASSERT(0);
|
||||||
|
}
|
||||||
|
|
||||||
void simplifyx(const SkPath& path, SkPath& simple) {
|
void simplifyx(const SkPath& path, SkPath& simple) {
|
||||||
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
|
// returns 1 for evenodd, -1 for winding, regardless of inverse-ness
|
||||||
simple.reset();
|
simple.reset();
|
||||||
@ -4533,10 +4615,11 @@ void simplifyx(const SkPath& path, SkPath& simple) {
|
|||||||
coincidenceCheck(contourList);
|
coincidenceCheck(contourList);
|
||||||
fixOtherTIndex(contourList);
|
fixOtherTIndex(contourList);
|
||||||
// construct closed contours
|
// construct closed contours
|
||||||
if (builder.xorMask() == kWinding_Mask) {
|
if (builder.xorMask() == kWinding_Mask
|
||||||
bridgeWinding(contourList, simple);
|
? !bridgeWinding(contourList, simple)
|
||||||
} else {
|
: !bridgeXor(contourList, simple))
|
||||||
bridgeXor(contourList, simple);
|
{ // if some edges could not be resolved, assemble remaining fragments
|
||||||
|
assemble(simple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,10 @@ static const SimplifyFindNextTest::Segment* testCommon(
|
|||||||
int nextStart = startIndex;
|
int nextStart = startIndex;
|
||||||
int nextEnd = endIndex;
|
int nextEnd = endIndex;
|
||||||
SkTDArray<SimplifyFindNextTest::Span*> chaseArray;
|
SkTDArray<SimplifyFindNextTest::Span*> chaseArray;
|
||||||
|
bool unsortable = false;
|
||||||
SimplifyFindNextTest::Segment* next = segment.findNextWinding(chaseArray,
|
SimplifyFindNextTest::Segment* next = segment.findNextWinding(chaseArray,
|
||||||
true, nextStart, nextEnd, contourWinding, spanWinding);
|
true, nextStart, nextEnd, contourWinding, spanWinding,
|
||||||
|
unsortable);
|
||||||
pts[1] = next->xyAtT(&next->span(nextStart));
|
pts[1] = next->xyAtT(&next->span(nextStart));
|
||||||
SkASSERT(pts[0] == pts[1]);
|
SkASSERT(pts[0] == pts[1]);
|
||||||
return next;
|
return next;
|
||||||
|
@ -2828,7 +2828,7 @@ static void testQuadratic38() {
|
|||||||
testSimplifyx(path);
|
testSimplifyx(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void (*firstTest)() = testLine73x;
|
static void (*firstTest)() = testQuadratic7;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
void (*fun)();
|
void (*fun)();
|
||||||
|
@ -2274,11 +2274,56 @@ path.lineTo(398.164948,136.674606);
|
|||||||
path.quadTo(388.299255,136.674606, 380.294495,140.44487);
|
path.quadTo(388.299255,136.674606, 380.294495,140.44487);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="testQuadratic47o">
|
||||||
|
path.moveTo(343.939362, 212.598053);
|
||||||
|
path.lineTo(378.457642, 118.940636);
|
||||||
|
path.lineTo(383.692657, 141.516571);
|
||||||
|
path.lineTo(350.319519, 231.902115);
|
||||||
|
path.lineTo(343.939362, 212.598053);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(325.429016, 162.047577);
|
||||||
|
path.quadTo(336.348907, 149.123688, 353.36264, 149.123688);
|
||||||
|
path.quadTo(369.476624, 149.123688, 378.269806, 160.575241);
|
||||||
|
path.lineTo(325.429016, 162.047577);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(370.867188, 186.014069);
|
||||||
|
path.quadTo(370.867188, 161.229614, 352.381104, 161.229614);
|
||||||
|
path.quadTo(333.813202, 161.229614, 331.686493, 186.014069);
|
||||||
|
path.lineTo(370.867188, 186.014069);
|
||||||
|
path.close();
|
||||||
|
path.moveTo(353.161499, 195.011719);
|
||||||
|
path.quadTo(353.161499, 174.726105, 363.876862, 161.96579);
|
||||||
|
path.lineTo(353.161499, 195.011719);
|
||||||
|
path.close();
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="testQuadratic47s">
|
||||||
|
path.moveTo(366.466309, 151.476364);
|
||||||
|
path.lineTo(378.457642,118.940636);
|
||||||
|
path.lineTo(383.692657,141.516571);
|
||||||
|
path.lineTo(377.159943,159.209305);
|
||||||
|
path.quadTo(377.728729,159.87059, 378.269806,160.575241);
|
||||||
|
path.lineTo(376.638824,160.620682);
|
||||||
|
path.lineTo(370.26593,177.8806);
|
||||||
|
path.quadTo(368.708496,168.390671, 363.116943,164.309357);
|
||||||
|
path.lineTo(356.079041,186.014069);
|
||||||
|
path.lineTo(367.262817,186.014069);
|
||||||
|
path.lineTo(350.319519,231.902115);
|
||||||
|
path.lineTo(343.939362,212.598053);
|
||||||
|
path.lineTo(353.736816,186.014923);
|
||||||
|
path.lineTo(353.737122,186.014069);
|
||||||
|
path.lineTo(353.736938,186.014069);
|
||||||
|
path.quadTo(353.736877,186.014496, 353.736816,186.014923);
|
||||||
|
path.quadTo(353.161499,190.31131, 353.161499,195.011719);
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
var testDivs = [
|
var testDivs = [
|
||||||
|
testQuadratic47o,
|
||||||
|
testQuadratic47s,
|
||||||
testQuadratic46o,
|
testQuadratic46o,
|
||||||
testQuadratic46s,
|
testQuadratic46s,
|
||||||
testQuadratic45o,
|
testQuadratic45o,
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
'pdf.gyp:pdf',
|
'pdf.gyp:pdf',
|
||||||
],
|
],
|
||||||
'conditions' : [
|
'conditions' : [
|
||||||
[ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
|
[ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
|
||||||
}],
|
}],
|
||||||
[ 'skia_os == "win"', {
|
[ 'skia_os == "win"', {
|
||||||
}],
|
}],
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
'../experimental/Intersection/LineQuadraticIntersection.cpp',
|
'../experimental/Intersection/LineQuadraticIntersection.cpp',
|
||||||
'../experimental/Intersection/LineQuadraticIntersection_Test.cpp',
|
'../experimental/Intersection/LineQuadraticIntersection_Test.cpp',
|
||||||
'../experimental/Intersection/LineUtilities.cpp',
|
'../experimental/Intersection/LineUtilities.cpp',
|
||||||
|
'../experimental/Intersection/MiniSimplify_Test.cpp',
|
||||||
'../experimental/Intersection/QuadraticBezierClip.cpp',
|
'../experimental/Intersection/QuadraticBezierClip.cpp',
|
||||||
'../experimental/Intersection/QuadraticBezierClip_Test.cpp',
|
'../experimental/Intersection/QuadraticBezierClip_Test.cpp',
|
||||||
'../experimental/Intersection/QuadraticBounds.cpp',
|
'../experimental/Intersection/QuadraticBounds.cpp',
|
||||||
|
Loading…
Reference in New Issue
Block a user