work in progress
git-svn-id: http://skia.googlecode.com/svn/trunk@3276 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
301968088a
commit
cef7e3fc4b
experimental/Intersection
@ -14,9 +14,9 @@
|
||||
#include "SkTDArray.h"
|
||||
#include "TSearch.h"
|
||||
|
||||
static bool fShowDebugf = true; // FIXME: remove once debugging is complete
|
||||
|
||||
const int kOpenerVerbBitShift = 3; // leaves 3 bits for SkPath::Verb
|
||||
static bool gShowDebugf = true; // FIXME: remove once debugging is complete
|
||||
static bool gShowPath = false;
|
||||
static bool gDebugLessThan = false;
|
||||
|
||||
static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
|
||||
double aRange[2], double bRange[2]) {
|
||||
@ -30,6 +30,13 @@ static int LineIntersect(const SkPoint a[2], SkScalar y, double aRange[2]) {
|
||||
return horizontalIntersect(aLine, y, aRange);
|
||||
}
|
||||
|
||||
static SkScalar LineXAtT(const SkPoint a[2], double t) {
|
||||
_Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
|
||||
double x;
|
||||
xy_at_t(aLine, t, x, *(double*) 0);
|
||||
return SkDoubleToScalar(x);
|
||||
}
|
||||
|
||||
static SkScalar LineYAtT(const SkPoint a[2], double t) {
|
||||
_Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
|
||||
double y;
|
||||
@ -119,17 +126,15 @@ static bool extendLine(const SkPoint line[2], const SkPoint& add) {
|
||||
|
||||
struct OutEdge {
|
||||
bool operator<(const OutEdge& rh) const {
|
||||
const SkPoint& first = fPts.begin()[0];
|
||||
const SkPoint& rhFirst = rh.fPts.begin()[0];
|
||||
const SkPoint& first = fPts[0];
|
||||
const SkPoint& rhFirst = rh.fPts[0];
|
||||
return first.fY == rhFirst.fY
|
||||
? first.fX < rhFirst.fX
|
||||
: first.fY < rhFirst.fY;
|
||||
}
|
||||
|
||||
SkTDArray<SkPoint> fPts;
|
||||
// contains the SkPath verb, plus 1<<kOpenerVerbBitShift if edge opens span
|
||||
SkTDArray<uint8_t> fVerbs; // FIXME: not read from everywhere
|
||||
bool fOpener;
|
||||
SkPoint fPts[4];
|
||||
uint8_t fVerb; // FIXME: not read from everywhere
|
||||
};
|
||||
|
||||
class OutEdgeBuilder {
|
||||
@ -138,34 +143,11 @@ public:
|
||||
: fFill(fill) {
|
||||
}
|
||||
|
||||
void addLine(const SkPoint line[2], bool opener) {
|
||||
size_t count = fEdges.count();
|
||||
for (size_t index = 0; index < count; ++index) {
|
||||
OutEdge& edge = fEdges[index];
|
||||
if (opener != edge.fOpener) {
|
||||
continue;
|
||||
}
|
||||
SkTDArray<SkPoint>& pts = edge.fPts;
|
||||
SkPoint& last = pts.top();
|
||||
if (last == line[0]) {
|
||||
SkTDArray<uint8_t>& verbs = edge.fVerbs;
|
||||
uint8_t lastVerb = verbs.top();
|
||||
if (lastVerb == SkPath::kLine_Verb
|
||||
&& extendLine(&last - 1, line[1])) {
|
||||
last = line[1];
|
||||
} else {
|
||||
*pts.append() = line[1];
|
||||
*verbs.append() = SkPath::kLine_Verb;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
void addLine(const SkPoint line[2]) {
|
||||
OutEdge& newEdge = fEdges.push_back();
|
||||
*newEdge.fPts.append() = line[0];
|
||||
*newEdge.fVerbs.append() = SkPath::kMove_Verb;
|
||||
*newEdge.fPts.append() = line[1];
|
||||
*newEdge.fVerbs.append() = SkPath::kLine_Verb;
|
||||
newEdge.fOpener = opener;
|
||||
newEdge.fPts[0] = line[0];
|
||||
newEdge.fPts[1] = line[1];
|
||||
newEdge.fVerb = SkPath::kLine_Verb;
|
||||
}
|
||||
|
||||
void assemble(SkPath& simple) {
|
||||
@ -182,46 +164,70 @@ public:
|
||||
if (listIndex >= listCount) {
|
||||
break;
|
||||
}
|
||||
SkPoint firstPt;
|
||||
SkPoint firstPt, lastLine[2];
|
||||
bool doMove = true;
|
||||
bool closed = false;
|
||||
int edgeIndex;
|
||||
do {
|
||||
SkTDArray<SkPoint>& ptArray = fEdges[listIndex].fPts;
|
||||
SkASSERT(ptArray.count() > 0);
|
||||
SkPoint* pts, * end;
|
||||
SkPoint* ptArray = fEdges[listIndex].fPts;
|
||||
uint8_t verb = fEdges[listIndex].fVerb;
|
||||
SkPoint* start, * end;
|
||||
if (advance < 0) {
|
||||
pts = ptArray.end() - 1;
|
||||
end = ptArray.begin();
|
||||
start = &ptArray[verb];
|
||||
end = &ptArray[0];
|
||||
} else {
|
||||
pts = ptArray.begin();
|
||||
end = ptArray.end() - 1;
|
||||
start = &ptArray[0];
|
||||
end = &ptArray[verb];
|
||||
}
|
||||
if (doMove) {
|
||||
firstPt = pts[0];
|
||||
simple.moveTo(pts[0].fX, pts[0].fY);
|
||||
if (fShowDebugf) {
|
||||
SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
|
||||
}
|
||||
doMove = false;
|
||||
} else {
|
||||
simple.lineTo(pts[0].fX, pts[0].fY);
|
||||
if (fShowDebugf) {
|
||||
SkDebugf("%s 1 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
|
||||
}
|
||||
if (firstPt == pts[0]) {
|
||||
simple.close();
|
||||
if (fShowDebugf) {
|
||||
SkDebugf("%s close\n", __FUNCTION__);
|
||||
switch (verb) {
|
||||
case SkPath::kLine_Verb:
|
||||
bool gap;
|
||||
if (doMove) {
|
||||
firstPt = *start;
|
||||
simple.moveTo(start->fX, start->fY);
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
|
||||
start->fX, start->fY);
|
||||
}
|
||||
lastLine[0] = *start;
|
||||
lastLine[1] = *end;
|
||||
doMove = false;
|
||||
closed = false;
|
||||
break;
|
||||
}
|
||||
gap = lastLine[1] != *start;
|
||||
if (gap) {
|
||||
SkASSERT(fFill && lastLine[1].fY == start->fY);
|
||||
simple.lineTo(lastLine[1].fX, lastLine[1].fY);
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s lineTo x (%g,%g)\n", __FUNCTION__,
|
||||
lastLine[1].fX, lastLine[1].fY);
|
||||
}
|
||||
}
|
||||
if (gap || !extendLine(lastLine, *end)) {
|
||||
SkASSERT(lastLine[1] == *start ||
|
||||
(fFill && lastLine[1].fY == start->fY));
|
||||
simple.lineTo(start->fX, start->fY);
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
|
||||
start->fX, start->fY);
|
||||
}
|
||||
lastLine[0] = *start;
|
||||
}
|
||||
lastLine[1] = *end;
|
||||
if (firstPt == *end) {
|
||||
simple.lineTo(end->fX, end->fY);
|
||||
simple.close();
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s close 1 (%g, %g)\n", __FUNCTION__,
|
||||
end->fX, end->fY);
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (pts != end) {
|
||||
pts += advance;
|
||||
simple.lineTo(pts->fX, pts->fY);
|
||||
if (fShowDebugf) {
|
||||
SkDebugf("%s 2 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
|
||||
}
|
||||
default:
|
||||
// FIXME: add other curve types
|
||||
;
|
||||
}
|
||||
if (advance < 0) {
|
||||
edgeIndex = fTops[listIndex];
|
||||
@ -230,6 +236,15 @@ public:
|
||||
edgeIndex = fBottoms[listIndex];
|
||||
fBottoms[listIndex] = 0;
|
||||
}
|
||||
if (!edgeIndex) {
|
||||
simple.lineTo(firstPt.fX, firstPt.fY);
|
||||
simple.close();
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s close 2 (%g,%g)\n", __FUNCTION__,
|
||||
firstPt.fX, firstPt.fY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
listIndex = abs(edgeIndex) - 1;
|
||||
if (edgeIndex < 0) {
|
||||
fTops[listIndex] = 0;
|
||||
@ -240,21 +255,63 @@ public:
|
||||
if (advance > 0 ^ edgeIndex < 0) {
|
||||
advance = -advance;
|
||||
}
|
||||
} while (edgeIndex);
|
||||
} while (edgeIndex && !closed);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
static bool lessThan(SkTArray<OutEdge>& edges, const int* onePtr,
|
||||
const int* twoPtr) {
|
||||
int one = *onePtr;
|
||||
const OutEdge& oneEdge = edges[(one < 0 ? -one : one) - 1];
|
||||
const SkPoint* onePt = one < 0 ? oneEdge.fPts.begin()
|
||||
: oneEdge.fPts.end() - 1;
|
||||
int two = *twoPtr;
|
||||
const OutEdge& twoEdge = edges[(two < 0 ? -two : two) - 1];
|
||||
const SkPoint* twoPt = two < 0 ? twoEdge.fPts.begin()
|
||||
: twoEdge.fPts.end() - 1;
|
||||
return onePt->fY == twoPt->fY ? onePt->fX < twoPt->fX : onePt->fY < twoPt->fY;
|
||||
// sort points by y, then x
|
||||
// if x/y is identical, sort bottoms before tops
|
||||
// if identical and both tops/bottoms, sort by angle
|
||||
static bool lessThan(SkTArray<OutEdge>& edges, const int one,
|
||||
const int two) {
|
||||
const OutEdge& oneEdge = edges[abs(one) - 1];
|
||||
int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
|
||||
const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
|
||||
const OutEdge& twoEdge = edges[abs(two) - 1];
|
||||
int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
|
||||
const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
|
||||
if (startPt1.fY != startPt2.fY) {
|
||||
if (gDebugLessThan) {
|
||||
SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
|
||||
one, two, startPt1.fY, startPt2.fY,
|
||||
startPt1.fY < startPt2.fY ? "true" : "false");
|
||||
}
|
||||
return startPt1.fY < startPt2.fY;
|
||||
}
|
||||
if (startPt1.fX != startPt2.fX) {
|
||||
if (gDebugLessThan) {
|
||||
SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
|
||||
one, two, startPt1.fX, startPt2.fX,
|
||||
startPt1.fX < startPt2.fX ? "true" : "false");
|
||||
}
|
||||
return startPt1.fX < startPt2.fX;
|
||||
}
|
||||
const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
|
||||
const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
|
||||
SkScalar dy1 = startPt1.fY - endPt1.fY;
|
||||
SkScalar dy2 = startPt2.fY - endPt2.fY;
|
||||
SkScalar dy1y2 = dy1 * dy2;
|
||||
if (dy1y2 < 0) { // different signs
|
||||
if (gDebugLessThan) {
|
||||
SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
|
||||
dy1 > 0 ? "true" : "false");
|
||||
}
|
||||
return dy1 > 0; // one < two if one goes up and two goes down
|
||||
}
|
||||
if (dy1y2 == 0) {
|
||||
if (gDebugLessThan) {
|
||||
SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
|
||||
one, two, endPt1.fX < endPt2.fX ? "true" : "false");
|
||||
}
|
||||
return endPt1.fX < endPt2.fX;
|
||||
}
|
||||
SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
|
||||
SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
|
||||
if (gDebugLessThan) {
|
||||
SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
|
||||
one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
|
||||
}
|
||||
return dy2 > 0 ^ dx1y2 < dx2y1;
|
||||
}
|
||||
|
||||
// Sort the indices of paired points and then create more indices so
|
||||
@ -265,17 +322,19 @@ public:
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(!fFill || (count & 1) == 0);
|
||||
SkASSERT(!fFill || count > 1);
|
||||
fTops.setCount(count);
|
||||
sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
|
||||
fBottoms.setCount(count);
|
||||
sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
|
||||
SkTDArray<int> order;
|
||||
for (index = 1; index <= count; ++index) {
|
||||
*order.append() = index;
|
||||
*order.append() = -index;
|
||||
}
|
||||
QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), count * 2, lessThan);
|
||||
for (index = 1; index <= count; ++index) {
|
||||
*order.append() = index;
|
||||
}
|
||||
QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
|
||||
int* lastPtr = order.end() - 1;
|
||||
int* leftPtr = order.begin();
|
||||
while (leftPtr < lastPtr) {
|
||||
@ -286,13 +345,18 @@ public:
|
||||
int rightIndex = *rightPtr;
|
||||
int rightOutIndex = abs(rightIndex) - 1;
|
||||
const OutEdge& right = fEdges[rightOutIndex];
|
||||
// OPTIMIZATION: if fFill is true, we don't need leftMatch, rightMatch
|
||||
SkPoint& leftMatch = left.fPts[leftIndex < 0 ? 0
|
||||
: left.fPts.count() - 1];
|
||||
SkPoint& rightMatch = right.fPts[rightIndex < 0 ? 0
|
||||
: right.fPts.count() - 1];
|
||||
SkASSERT(!fFill || leftMatch.fY == rightMatch.fY);
|
||||
if (fFill || leftMatch == rightMatch) {
|
||||
bool pairUp = fFill;
|
||||
if (!pairUp) {
|
||||
const SkPoint& leftMatch =
|
||||
left.fPts[leftIndex < 0 ? 0 : left.fVerb];
|
||||
const SkPoint& rightMatch =
|
||||
right.fPts[rightIndex < 0 ? 0 : right.fVerb];
|
||||
pairUp = leftMatch == rightMatch;
|
||||
} else {
|
||||
SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
|
||||
== right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
|
||||
}
|
||||
if (pairUp) {
|
||||
if (leftIndex < 0) {
|
||||
fTops[leftOutIndex] = rightIndex;
|
||||
} else {
|
||||
@ -492,18 +556,36 @@ void walk() {
|
||||
while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
|
||||
switch (fVerb) {
|
||||
case SkPath::kMove_Verb:
|
||||
if (gShowPath) {
|
||||
SkDebugf("path.moveTo(%g, %g);\n", fPts[0].fX, fPts[0].fY);
|
||||
}
|
||||
startEdge();
|
||||
continue;
|
||||
case SkPath::kLine_Verb:
|
||||
if (gShowPath) {
|
||||
SkDebugf("path.lineTo(%g, %g);\n", fPts[1].fX, fPts[1].fY);
|
||||
}
|
||||
winding = direction(2);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
if (gShowPath) {
|
||||
SkDebugf("path.quadTo(%g, %g, %g, %g);\n",
|
||||
fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY);
|
||||
}
|
||||
winding = direction(3);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
if (gShowPath) {
|
||||
SkDebugf("path.cubicTo(%g, %g, %g, %g);\n",
|
||||
fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY,
|
||||
fPts[3].fX, fPts[3].fY);
|
||||
}
|
||||
winding = direction(4);
|
||||
break;
|
||||
case SkPath::kClose_Verb:
|
||||
if (gShowPath) {
|
||||
SkDebugf("path.close();\n");
|
||||
}
|
||||
SkASSERT(fCurrentEdge);
|
||||
complete();
|
||||
continue;
|
||||
@ -521,8 +603,9 @@ void walk() {
|
||||
// FIXME: if prior verb or this verb is a horizontal line, reverse
|
||||
// it instead of starting a new edge
|
||||
SkASSERT(fCurrentEdge);
|
||||
fCurrentEdge->complete(fWinding);
|
||||
startEdge();
|
||||
if (complete()) {
|
||||
startEdge();
|
||||
}
|
||||
}
|
||||
fWinding = winding;
|
||||
addEdge();
|
||||
@ -532,6 +615,9 @@ void walk() {
|
||||
fEdges.pop_back();
|
||||
}
|
||||
}
|
||||
if (gShowPath) {
|
||||
SkDebugf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -558,7 +644,7 @@ struct WorkEdge {
|
||||
fVerb = edge->fVerbs.begin();
|
||||
}
|
||||
|
||||
bool next() {
|
||||
bool advance() {
|
||||
SkASSERT(fVerb < fEdge->fVerbs.end());
|
||||
fPts += *fVerb++;
|
||||
return fVerb != fEdge->fVerbs.end();
|
||||
@ -586,16 +672,38 @@ struct WorkEdge {
|
||||
// ActiveEdge* may be faster to insert and search
|
||||
struct ActiveEdge {
|
||||
bool operator<(const ActiveEdge& rh) const {
|
||||
return fX < rh.fX;
|
||||
return fXAbove != rh.fXAbove ? fXAbove < rh.fXAbove
|
||||
: fXBelow < rh.fXBelow;
|
||||
}
|
||||
|
||||
void calcLeft() {
|
||||
fX = fWorkEdge.fPts[fWorkEdge.verb()].fX;
|
||||
// OPTIMIZE: put a kDone_Verb at the end of the verb list?
|
||||
if (fDone)
|
||||
return; // nothing to do; use last
|
||||
switch (fWorkEdge.verb()) {
|
||||
case SkPath::kLine_Verb: {
|
||||
// OPTIMIZATION: if fXAbove, fXBelow have already been computed
|
||||
// for the fTIndex, don't do it again
|
||||
// For identical x, this lets us know which edge is first.
|
||||
// If both edges have T values < 1, check x at next T (fXBelow).
|
||||
int add = (fTIndex <= fTs->count()) - 1;
|
||||
double tAbove = t(fTIndex + add);
|
||||
fXAbove = LineXAtT(fWorkEdge.fPts, tAbove);
|
||||
double tBelow = t(fTIndex - ~add);
|
||||
fXBelow = LineXAtT(fWorkEdge.fPts, tBelow);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// FIXME: add support for all curve types
|
||||
SkASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
void init(const InEdge* edge) {
|
||||
fWorkEdge.init(edge);
|
||||
initT();
|
||||
fDone = false;
|
||||
fYBottom = SK_ScalarMin;
|
||||
}
|
||||
|
||||
void initT() {
|
||||
@ -608,19 +716,48 @@ struct ActiveEdge {
|
||||
// but templated arrays don't allow returning a pointer to the end() element
|
||||
fTIndex = 0;
|
||||
}
|
||||
|
||||
bool isCoincidentWith(const ActiveEdge* edge) const {
|
||||
if (fXAbove != edge->fXAbove || fXBelow != edge->fXBelow) {
|
||||
return false;
|
||||
}
|
||||
switch (fWorkEdge.verb()) {
|
||||
case SkPath::kLine_Verb: {
|
||||
return (fWorkEdge.fPts[0].fX - fWorkEdge.fPts[1].fX) *
|
||||
(edge->fWorkEdge.fPts[0].fY - edge->fWorkEdge.fPts[1].fY) ==
|
||||
(fWorkEdge.fPts[0].fY - fWorkEdge.fPts[1].fY) *
|
||||
(edge->fWorkEdge.fPts[0].fX - edge->fWorkEdge.fPts[1].fX);
|
||||
}
|
||||
default:
|
||||
// FIXME: add support for all curve types
|
||||
SkASSERT(0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nextT() {
|
||||
bool advanceT() {
|
||||
SkASSERT(fTIndex <= fTs->count());
|
||||
return ++fTIndex == fTs->count() + 1;
|
||||
return ++fTIndex <= fTs->count();
|
||||
}
|
||||
|
||||
bool next() {
|
||||
bool result = fWorkEdge.next();
|
||||
bool advance() {
|
||||
// FIXME: flip sense of next
|
||||
bool result = fWorkEdge.advance();
|
||||
fDone = !result;
|
||||
initT();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool done(SkScalar y) {
|
||||
return fDone || fYBottom > y;
|
||||
}
|
||||
|
||||
double nextT() {
|
||||
SkASSERT(fTIndex <= fTs->count());
|
||||
return t(fTIndex + 1);
|
||||
}
|
||||
|
||||
double t() {
|
||||
double t() const {
|
||||
if (fTIndex == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -630,11 +767,24 @@ struct ActiveEdge {
|
||||
return (*fTs)[fTIndex - 1];
|
||||
}
|
||||
|
||||
double t(int tIndex) const {
|
||||
if (tIndex == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (tIndex > fTs->count()) {
|
||||
return 1;
|
||||
}
|
||||
return (*fTs)[tIndex - 1];
|
||||
}
|
||||
|
||||
WorkEdge fWorkEdge;
|
||||
const SkTDArray<double>* fTs;
|
||||
SkScalar fX;
|
||||
SkScalar fXAbove;
|
||||
SkScalar fXBelow;
|
||||
SkScalar fYBottom;
|
||||
int fTIndex;
|
||||
bool fSkip;
|
||||
bool fDone;
|
||||
};
|
||||
|
||||
static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
|
||||
@ -657,7 +807,6 @@ static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
|
||||
WorkEdge wt;
|
||||
wt.init(test);
|
||||
do {
|
||||
// FIXME: add all curve types
|
||||
// OPTIMIZATION: if bottom intersection does not change
|
||||
// the winding on either side of the split, don't intersect
|
||||
if (wt.verb() == SkPath::kLine_Verb) {
|
||||
@ -667,25 +816,33 @@ static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
|
||||
test->fContainsIntercepts |= test->add(wtTs, pts,
|
||||
wt.verbIndex());
|
||||
}
|
||||
} else {
|
||||
// FIXME: add all curve types
|
||||
SkASSERT(0);
|
||||
}
|
||||
} while (wt.next());
|
||||
} while (wt.advance());
|
||||
}
|
||||
test = *++testPtr;
|
||||
}
|
||||
}
|
||||
|
||||
static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
|
||||
InEdge** testPtr = currentPtr;
|
||||
InEdge* test = *testPtr;
|
||||
while (testPtr != lastPtr - 1) {
|
||||
InEdge* next = *++testPtr;
|
||||
if (!test->cached(next)
|
||||
&& Bounds::Intersects(test->fBounds, next->fBounds)) {
|
||||
InEdge** testPtr = currentPtr - 1;
|
||||
while (++testPtr != lastPtr - 1) {
|
||||
InEdge* test = *testPtr;
|
||||
InEdge** nextPtr = testPtr;
|
||||
do {
|
||||
InEdge* next = *++nextPtr;
|
||||
if (test->cached(next)) {
|
||||
continue;
|
||||
}
|
||||
if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
|
||||
continue;
|
||||
}
|
||||
WorkEdge wt, wn;
|
||||
wt.init(test);
|
||||
wn.init(next);
|
||||
do {
|
||||
// FIXME: add all combinations of curve types
|
||||
if (wt.verb() == SkPath::kLine_Verb
|
||||
&& wn.verb() == SkPath::kLine_Verb) {
|
||||
double wtTs[2], wnTs[2];
|
||||
@ -696,10 +853,12 @@ static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
|
||||
next->fContainsIntercepts |= next->add(wnTs, pts,
|
||||
wn.verbIndex());
|
||||
}
|
||||
} else {
|
||||
// FIXME: add all combinations of curve types
|
||||
SkASSERT(0);
|
||||
}
|
||||
} while (wt.bottom() <= wn.bottom() ? wt.next() : wn.next());
|
||||
}
|
||||
test = next;
|
||||
} while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
|
||||
} while (nextPtr != lastPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -739,7 +898,6 @@ static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
|
||||
WorkEdge wt;
|
||||
wt.init(test);
|
||||
do {
|
||||
// FIXME: add all curve types
|
||||
const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
|
||||
const SkTDArray<double>& fTs = intercepts.fTs;
|
||||
size_t count = fTs.count();
|
||||
@ -749,9 +907,12 @@ static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
|
||||
if (yIntercept > y && bottom > yIntercept) {
|
||||
bottom = yIntercept;
|
||||
}
|
||||
} else {
|
||||
// FIXME: add all curve types
|
||||
SkASSERT(0);
|
||||
}
|
||||
}
|
||||
} while (wt.next());
|
||||
} while (wt.advance());
|
||||
}
|
||||
}
|
||||
|
||||
@ -842,7 +1003,7 @@ static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
|
||||
for (index = 1; index < edgeCount; ++index) {
|
||||
winding += activePtr->fWorkEdge.winding();
|
||||
ActiveEdge* nextPtr = edgeList[index];
|
||||
if (activePtr->fX == nextPtr->fX) {
|
||||
if (activePtr->isCoincidentWith(nextPtr)) {
|
||||
if (!firstCoincident) {
|
||||
firstCoincident = activePtr;
|
||||
}
|
||||
@ -871,7 +1032,7 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
||||
int winding = 0;
|
||||
ActiveEdge** activeHandle = edgeList.begin() - 1;
|
||||
ActiveEdge** lastActive = edgeList.end();
|
||||
if (fShowDebugf) {
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
|
||||
}
|
||||
while (++activeHandle != lastActive) {
|
||||
@ -879,23 +1040,27 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
||||
const WorkEdge& wt = activePtr->fWorkEdge;
|
||||
int lastWinding = winding;
|
||||
winding += wt.winding();
|
||||
if (activePtr->done(y)) {
|
||||
continue;
|
||||
}
|
||||
int opener = (lastWinding & windingMask) == 0;
|
||||
bool closer = (winding & windingMask) == 0;
|
||||
SkASSERT(!opener | !closer);
|
||||
bool inWinding = opener | closer;
|
||||
opener <<= kOpenerVerbBitShift;
|
||||
const SkPoint* clipped;
|
||||
uint8_t verb = wt.verb();
|
||||
bool moreToDo, aboveBottom;
|
||||
do {
|
||||
double currentT = activePtr->t();
|
||||
SkASSERT(currentT < 1);
|
||||
const SkPoint* points = wt.fPts;
|
||||
bool last;
|
||||
double nextT;
|
||||
do {
|
||||
last = activePtr->nextT();
|
||||
double nextT = activePtr->t();
|
||||
// FIXME: add all combinations of curve types
|
||||
if (wt.verb() == SkPath::kLine_Verb) {
|
||||
nextT = activePtr->nextT();
|
||||
if (verb == SkPath::kLine_Verb) {
|
||||
SkPoint clippedPts[2];
|
||||
const SkPoint* clipped;
|
||||
// FIXME: obtuse: want efficient way to say
|
||||
// !currentT && currentT != 1 || !nextT && nextT != 1
|
||||
if (currentT * nextT != 0 || currentT + nextT != 1) {
|
||||
// OPTIMIZATION: if !inWinding, we only need
|
||||
// clipped[1].fY
|
||||
@ -905,25 +1070,23 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
|
||||
clipped = points;
|
||||
}
|
||||
if (inWinding && !activePtr->fSkip) {
|
||||
if (fShowDebugf) {
|
||||
if (gShowDebugf) {
|
||||
SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
|
||||
clipped[0].fX, clipped[0].fY,
|
||||
clipped[1].fX, clipped[1].fY);
|
||||
}
|
||||
outBuilder.addLine(clipped, opener);
|
||||
}
|
||||
if (clipped[1].fY >= bottom) {
|
||||
if (last) {
|
||||
activePtr->next();
|
||||
}
|
||||
goto nextEdge;
|
||||
outBuilder.addLine(clipped);
|
||||
}
|
||||
} else {
|
||||
// FIXME: add all curve types
|
||||
SkASSERT(0);
|
||||
}
|
||||
currentT = nextT;
|
||||
} while (!last);
|
||||
} while (activePtr->next());
|
||||
nextEdge:
|
||||
;
|
||||
moreToDo = activePtr->advanceT();
|
||||
activePtr->fYBottom = clipped[verb].fY;
|
||||
aboveBottom = activePtr->fYBottom < bottom;
|
||||
} while (moreToDo & aboveBottom);
|
||||
} while ((moreToDo || activePtr->advance()) & aboveBottom);
|
||||
}
|
||||
}
|
||||
|
||||
@ -933,9 +1096,10 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
|
||||
simple.reset();
|
||||
simple.setFillType(SkPath::kEvenOdd_FillType);
|
||||
// turn path into list of edges increasing in y
|
||||
// if an edge is a quad or a cubic with a y extrema, note it, but leave it unbroken
|
||||
// once we have a list, sort it, then walk the list (walk edges twice that have y extrema's on top)
|
||||
// and detect crossings -- look for raw bounds that cross over, then tight bounds that cross
|
||||
// if an edge is a quad or a cubic with a y extrema, note it, but leave it
|
||||
// unbroken. Once we have a list, sort it, then walk the list (walk edges
|
||||
// twice that have y extrema's on top) and detect crossings -- look for raw
|
||||
// bounds that cross over, then tight bounds that cross
|
||||
SkTArray<InEdge> edges;
|
||||
InEdgeBuilder builder(path, asFill, edges);
|
||||
SkTDArray<InEdge*> edgeList;
|
||||
@ -965,276 +1129,3 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
|
||||
outBuilder.bridge();
|
||||
outBuilder.assemble(simple);
|
||||
}
|
||||
|
||||
static void testSimplifyCoincidentVertical() {
|
||||
SkPath path, out;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.addRect(10, 10, 30, 30);
|
||||
path.addRect(10, 30, 30, 40);
|
||||
simplify(path, true, out);
|
||||
SkRect rect;
|
||||
if (!out.isRect(&rect)) {
|
||||
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||
}
|
||||
if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
|
||||
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSimplifyCoincidentHorizontal() {
|
||||
SkPath path, out;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.addRect(10, 10, 30, 30);
|
||||
path.addRect(30, 10, 40, 30);
|
||||
simplify(path, true, out);
|
||||
SkRect rect;
|
||||
if (!out.isRect(&rect)) {
|
||||
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||
}
|
||||
if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
|
||||
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSimplifyMulti() {
|
||||
SkPath path, out;
|
||||
path.setFillType(SkPath::kWinding_FillType);
|
||||
path.addRect(10, 10, 30, 30);
|
||||
path.addRect(20, 20, 40, 40);
|
||||
simplify(path, true, out);
|
||||
SkPath expected;
|
||||
expected.setFillType(SkPath::kEvenOdd_FillType);
|
||||
expected.moveTo(10,10); // two cutout corners
|
||||
expected.lineTo(10,30);
|
||||
expected.lineTo(20,30);
|
||||
expected.lineTo(20,40);
|
||||
expected.lineTo(40,40);
|
||||
expected.lineTo(40,20);
|
||||
expected.lineTo(30,20);
|
||||
expected.lineTo(30,10);
|
||||
expected.lineTo(10,10);
|
||||
expected.close();
|
||||
if (out != expected) {
|
||||
SkDebugf("%s expected equal\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
path = out;
|
||||
path.addRect(30, 10, 40, 20);
|
||||
path.addRect(10, 30, 20, 40);
|
||||
simplify(path, true, out);
|
||||
SkRect rect;
|
||||
if (!out.isRect(&rect)) {
|
||||
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||
}
|
||||
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
path = out;
|
||||
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||
simplify(path, true, out);
|
||||
if (!out.isEmpty()) {
|
||||
SkDebugf("%s expected empty\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSimplifyAddL() {
|
||||
SkPath path, out;
|
||||
path.moveTo(10,10); // 'L' shape
|
||||
path.lineTo(10,40);
|
||||
path.lineTo(40,40);
|
||||
path.lineTo(40,20);
|
||||
path.lineTo(30,20);
|
||||
path.lineTo(30,10);
|
||||
path.lineTo(10,10);
|
||||
path.close();
|
||||
path.addRect(30, 10, 40, 20); // missing notch of 'L'
|
||||
simplify(path, true, out);
|
||||
SkRect rect;
|
||||
if (!out.isRect(&rect)) {
|
||||
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||
}
|
||||
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSimplifyCoincidentCCW() {
|
||||
SkPath path, out;
|
||||
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||
simplify(path, true, out);
|
||||
SkRect rect;
|
||||
if (!out.isRect(&rect)) {
|
||||
SkDebugf("%s expected rect\n", __FUNCTION__);
|
||||
}
|
||||
if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
|
||||
SkDebugf("%s expected union\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSimplifyCoincidentCW() {
|
||||
SkPath path, out;
|
||||
path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
|
||||
path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
|
||||
simplify(path, true, out);
|
||||
if (!out.isEmpty()) {
|
||||
SkDebugf("%s expected empty\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
static void testSimplifyCorner() {
|
||||
SkPath path, out;
|
||||
path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
|
||||
path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
|
||||
simplify(path, true, out);
|
||||
SkTDArray<SkRect> boundsArray;
|
||||
contourBounds(out, boundsArray);
|
||||
if (boundsArray.count() != 2) {
|
||||
SkDebugf("%s expected 2 contours\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
|
||||
SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
|
||||
if (boundsArray[0] != one && boundsArray[0] != two
|
||||
|| boundsArray[1] != one && boundsArray[1] != two) {
|
||||
SkDebugf("%s expected match\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
// non-intersecting test points, two equal sized rectangles
|
||||
static void lookForFailingTests(const SkPoint* pts, size_t ptsSize, int width,
|
||||
int height, const SkRect& center) {
|
||||
size_t index = 0;
|
||||
for ( ; index < ptsSize; ++index) {
|
||||
SkPath path, out;
|
||||
path.addRect(center);
|
||||
SkRect test = SkRect::MakeXYWH(pts[index].fX,
|
||||
pts[index].fY, width, height);
|
||||
path.addRect(test);
|
||||
simplify(path, true, out);
|
||||
SkPath::Iter iter(out, false);
|
||||
SkPoint pts[2];
|
||||
SkRect bounds[2];
|
||||
bounds[0].setEmpty();
|
||||
bounds[1].setEmpty();
|
||||
SkRect* boundsPtr = bounds;
|
||||
int count = 0;
|
||||
SkPath::Verb verb;
|
||||
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
||||
switch (verb) {
|
||||
case SkPath::kMove_Verb:
|
||||
if (!boundsPtr->isEmpty()) {
|
||||
SkASSERT(boundsPtr == bounds);
|
||||
++boundsPtr;
|
||||
}
|
||||
boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
|
||||
count = 0;
|
||||
break;
|
||||
case SkPath::kLine_Verb:
|
||||
count = 1;
|
||||
break;
|
||||
case SkPath::kClose_Verb:
|
||||
count = 0;
|
||||
break;
|
||||
default:
|
||||
SkDEBUGFAIL("bad verb");
|
||||
return;
|
||||
}
|
||||
for (int i = 1; i <= count; ++i) {
|
||||
boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
|
||||
}
|
||||
}
|
||||
SkASSERT(bounds[0] == center && bounds[1] == test
|
||||
|| bounds[1] == center && bounds[0] == test);
|
||||
}
|
||||
}
|
||||
|
||||
static void twoEqualRects() {
|
||||
SkPoint pts[] = {
|
||||
{ 0, 0}, {10, 0}, {20, 0}, {30, 0}, {40, 0}, {50, 0}, {60, 0}, // above
|
||||
{ 0, 10}, { 0, 20}, { 0, 30}, { 0, 40}, { 0, 50}, { 0, 60}, // left
|
||||
{10, 60}, {20, 60}, {30, 60}, {40, 60}, {50, 60}, // below
|
||||
{60, 10}, {60, 20}, {60, 30}, {60, 40}, {60, 50}, {60, 60}, // right
|
||||
};
|
||||
size_t ptsCount = sizeof(pts) / sizeof(pts[0]);
|
||||
SkRect center = SkRect::MakeLTRB(30, 30, 50, 50);
|
||||
lookForFailingTests(pts, ptsCount, 20, 20, center);
|
||||
}
|
||||
|
||||
static void largerOuter() {
|
||||
SkRect center = SkRect::MakeLTRB(50, 50, 70, 70);
|
||||
const size_t count = 9;
|
||||
SkPoint pts[count];
|
||||
size_t index;
|
||||
for (index = 0; index < count; ++index) { // above
|
||||
pts[index].fX = index * 10;
|
||||
pts[index].fY = 0;
|
||||
}
|
||||
lookForFailingTests(pts, count, 40, 20, center);
|
||||
for (index = 0; index < count; ++index) { // left
|
||||
pts[index].fX = 0;
|
||||
pts[index].fY = index * 10;
|
||||
}
|
||||
lookForFailingTests(pts, count, 20, 40, center);
|
||||
for (index = 0; index < count; ++index) { // below
|
||||
pts[index].fX = index * 10;
|
||||
pts[index].fY = 80;
|
||||
}
|
||||
lookForFailingTests(pts, count, 40, 20, center);
|
||||
for (index = 0; index < count; ++index) { // right
|
||||
pts[index].fX = 80;
|
||||
pts[index].fY = index * 10;
|
||||
}
|
||||
lookForFailingTests(pts, count, 20, 40, center);
|
||||
}
|
||||
|
||||
static void smallerOuter() {
|
||||
SkPoint pts[] = {
|
||||
{ 0, 0}, {10, 0}, {20, 0}, {30, 0}, {40, 0}, {50, 0}, {60, 0}, // above
|
||||
{ 0, 10}, { 0, 20}, { 0, 30}, { 0, 40}, { 0, 50}, { 0, 60}, // left
|
||||
{10, 60}, {20, 60}, {30, 60}, {40, 60}, {50, 60}, // below
|
||||
{60, 10}, {60, 20}, {60, 30}, {60, 40}, {60, 50}, {60, 60}, // right
|
||||
};
|
||||
size_t ptsCount = sizeof(pts) / sizeof(pts[0]);
|
||||
SkRect center = SkRect::MakeLTRB(20, 20, 50, 50);
|
||||
lookForFailingTests(pts, ptsCount, 10, 10, center);
|
||||
}
|
||||
|
||||
void testSimplify();
|
||||
|
||||
void (*simplifyTests[])() = {
|
||||
testSimplifyCorner,
|
||||
testSimplifyCoincidentCW,
|
||||
testSimplifyCoincidentCCW,
|
||||
testSimplifyCoincidentVertical,
|
||||
testSimplifyCoincidentHorizontal,
|
||||
testSimplifyAddL,
|
||||
testSimplifyMulti,
|
||||
};
|
||||
|
||||
size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
|
||||
|
||||
static void (*firstTest)() = 0;
|
||||
static bool lookForFailing = false;
|
||||
|
||||
void testSimplify() {
|
||||
/* look for failing test cases */
|
||||
if (lookForFailing) {
|
||||
// rects do not touch
|
||||
twoEqualRects();
|
||||
largerOuter();
|
||||
smallerOuter();
|
||||
}
|
||||
size_t index = 0;
|
||||
if (firstTest) {
|
||||
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
|
||||
++index;
|
||||
}
|
||||
}
|
||||
for ( ; index < simplifyTestsCount; ++index) {
|
||||
(*simplifyTests[index])();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,7 +17,8 @@ void Intersection_Tests() {
|
||||
LineQuadraticIntersection_Test();
|
||||
LineCubicIntersection_Test();
|
||||
|
||||
testSimplify();
|
||||
SimplifyPolygonPaths_Test();
|
||||
SimplifyRectangularPaths_Test();
|
||||
|
||||
QuadraticCoincidence_Test();
|
||||
QuadraticReduceOrder_Test();
|
||||
|
@ -11,6 +11,8 @@ void LineCubicIntersection_Test();
|
||||
void LineIntersection_Test();
|
||||
void LineParameter_Test();
|
||||
void LineQuadraticIntersection_Test();
|
||||
void SimplifyPolygonPaths_Test();
|
||||
void SimplifyRectangularPaths_Test();
|
||||
void QuadraticBezierClip_Test();
|
||||
void QuadraticCoincidence_Test();
|
||||
void QuadraticIntersection_Test();
|
||||
|
@ -40,41 +40,32 @@ void QSort(T** base, size_t count)
|
||||
}
|
||||
|
||||
template <typename S, typename T>
|
||||
static void QSort_Partition(S& context, T* first, T* last,
|
||||
bool (*lessThan)(S&, const T*, const T*))
|
||||
static T* QSort_Partition(S& context, T* left, T* right, T* pivot,
|
||||
bool (*lessThan)(S&, const T, const T))
|
||||
{
|
||||
T* left = first;
|
||||
T* rite = last;
|
||||
T* pivot = left;
|
||||
|
||||
while (left <= rite) {
|
||||
while (left < last && lessThan(context, left, pivot))
|
||||
left += 1;
|
||||
while (first < rite && lessThan(context, pivot, rite))
|
||||
rite -= 1;
|
||||
if (left <= rite) {
|
||||
if (left < rite) {
|
||||
SkTSwap(*left, *rite);
|
||||
}
|
||||
left += 1;
|
||||
rite -= 1;
|
||||
T pivotValue = *pivot;
|
||||
SkTSwap(*pivot, *right);
|
||||
T* newPivot = left;
|
||||
while (left < right) {
|
||||
if (lessThan(context, *left, pivotValue)) {
|
||||
SkTSwap(*left, *newPivot);
|
||||
newPivot += 1;
|
||||
}
|
||||
left += 1;
|
||||
}
|
||||
if (first < rite)
|
||||
QSort_Partition(context, first, rite, lessThan);
|
||||
if (left < last)
|
||||
QSort_Partition(context, left, last, lessThan);
|
||||
SkTSwap(*newPivot, *right);
|
||||
return newPivot;
|
||||
}
|
||||
|
||||
template <typename S, typename T>
|
||||
void QSort(S& context, T* base, size_t count,
|
||||
bool (*lessThan)(S& , const T*, const T*))
|
||||
void QSort(S& context, T* left, T* right,
|
||||
bool (*lessThan)(S& , const T, const T))
|
||||
{
|
||||
SkASSERT(base);
|
||||
SkASSERT(lessThan);
|
||||
|
||||
if (count <= 1) {
|
||||
if (left >= right) {
|
||||
return;
|
||||
}
|
||||
QSort_Partition(context, base, base + (count - 1), lessThan);
|
||||
T* pivot = left + (right - left >> 1);
|
||||
pivot = QSort_Partition(context, left, right, pivot, lessThan);
|
||||
QSort(context, left, pivot - 1, lessThan);
|
||||
QSort(context, pivot + 1, right, lessThan);
|
||||
}
|
||||
|
@ -22,7 +22,12 @@
|
||||
FE7134F514D1E7C70008E392 /* LineParameterization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7134F414D1E7C70008E392 /* LineParameterization.cpp */; };
|
||||
FE71351314D2E9F50008E392 /* RectUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE71351214D2E9F50008E392 /* RectUtilities.cpp */; };
|
||||
FE71358614D309E90008E392 /* EdgeWalker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE71358514D309E90008E392 /* EdgeWalker.cpp */; };
|
||||
FE7413AE14F689E700056D7B /* libopts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C2C13E0410900335C58 /* libopts.a */; };
|
||||
FE7413D414F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7413D314F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp */; };
|
||||
FE7413D814F691C200056D7B /* EdgeWalker_TestUtility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7413D714F691C200056D7B /* EdgeWalker_TestUtility.cpp */; };
|
||||
FEA5F4E21498000C005052F9 /* libports.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA5F4E11497FFF6005052F9 /* libports.a */; };
|
||||
FEA61B0014EF589900B736CB /* libanimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7268144DD3EA0059E97B /* libanimator.a */; };
|
||||
FEA61B2C14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEA61B2B14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp */; };
|
||||
FEA671D013C4A21600FE6FC1 /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA671CF13C4A21600FE6FC1 /* AGL.framework */; };
|
||||
FEA671D813C4A21600FE6FC1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA671D713C4A21600FE6FC1 /* Foundation.framework */; };
|
||||
FEA671DA13C4A21600FE6FC1 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEA671D913C4A21600FE6FC1 /* OpenGL.framework */; };
|
||||
@ -54,7 +59,6 @@
|
||||
FECAA6E114BDDF2D00B35E2C /* QuadraticReduceOrder_Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FECAA6E014BDDF2D00B35E2C /* QuadraticReduceOrder_Test.cpp */; };
|
||||
FED53C391483CB9400F6359E /* Inline_Tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED53C381483CB9400F6359E /* Inline_Tests.cpp */; };
|
||||
FEED7245144DD2250059E97B /* SkEventNotifier.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEED723E144DD2250059E97B /* SkEventNotifier.mm */; };
|
||||
FEED7291144DD45E0059E97B /* libanimator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7268144DD3EA0059E97B /* libanimator.a */; };
|
||||
FEED7292144DD4610059E97B /* libexperimental.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED726E144DD4050059E97B /* libexperimental.a */; };
|
||||
FEED7293144DD4620059E97B /* libskgr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7276144DD4140059E97B /* libskgr.a */; };
|
||||
FEED7294144DD4630059E97B /* libgr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED7278144DD4140059E97B /* libgr.a */; };
|
||||
@ -78,11 +82,17 @@
|
||||
FEED76EE144F66E90059E97B /* Intersection_Tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEED76ED144F66E90059E97B /* Intersection_Tests.cpp */; };
|
||||
FEF87C3C13E0413500335C58 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C1A13E040E000335C58 /* libcore.a */; };
|
||||
FEF87C3D13E0413A00335C58 /* libeffects.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C2313E040F100335C58 /* libeffects.a */; };
|
||||
FEF87C3E13E0413F00335C58 /* libopts.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C2C13E0410900335C58 /* libopts.a */; };
|
||||
FEF87C3F13E0414400335C58 /* libutils.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FEF87C3B13E0412600335C58 /* libutils.a */; };
|
||||
/* 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 */;
|
||||
@ -240,7 +250,11 @@
|
||||
FE71351214D2E9F50008E392 /* RectUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RectUtilities.cpp; sourceTree = "<group>"; };
|
||||
FE71358514D309E90008E392 /* EdgeWalker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalker.cpp; sourceTree = "<group>"; };
|
||||
FE713C6114D9879B0008E392 /* TSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSearch.h; sourceTree = "<group>"; };
|
||||
FE7413D314F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalkerPolygons_Test.cpp; sourceTree = "<group>"; };
|
||||
FE7413D714F691C200056D7B /* EdgeWalker_TestUtility.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalker_TestUtility.cpp; sourceTree = "<group>"; };
|
||||
FE7413DB14F6926D00056D7B /* EdgeWalker_Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EdgeWalker_Test.h; sourceTree = "<group>"; };
|
||||
FEA5F4D91497FFF6005052F9 /* ports.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ports.xcodeproj; path = ../../out/gyp/ports.xcodeproj; sourceTree = SOURCE_ROOT; };
|
||||
FEA61B2B14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EdgeWalkerRectangles_Test.cpp; sourceTree = "<group>"; };
|
||||
FEA670F013C49E2200FE6FC1 /* SkAntiEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAntiEdge.cpp; sourceTree = "<group>"; };
|
||||
FEA670F113C49E2200FE6FC1 /* SkAntiEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAntiEdge.h; sourceTree = "<group>"; };
|
||||
FEA6710713C4A13900FE6FC1 /* gyp */ = {isa = PBXFileReference; lastKnownFileType = folder; name = gyp; path = ../../out/gyp; sourceTree = SOURCE_ROOT; };
|
||||
@ -328,12 +342,10 @@
|
||||
files = (
|
||||
FEF87C3C13E0413500335C58 /* libcore.a in Frameworks */,
|
||||
FEF87C3D13E0413A00335C58 /* libeffects.a in Frameworks */,
|
||||
FEF87C3E13E0413F00335C58 /* libopts.a in Frameworks */,
|
||||
FEF87C3F13E0414400335C58 /* libutils.a in Frameworks */,
|
||||
FEA671D813C4A21600FE6FC1 /* Foundation.framework in Frameworks */,
|
||||
FEA671D013C4A21600FE6FC1 /* AGL.framework in Frameworks */,
|
||||
FEA671DA13C4A21600FE6FC1 /* OpenGL.framework in Frameworks */,
|
||||
FEED7291144DD45E0059E97B /* libanimator.a in Frameworks */,
|
||||
FEED7292144DD4610059E97B /* libexperimental.a in Frameworks */,
|
||||
FEED7293144DD4620059E97B /* libskgr.a in Frameworks */,
|
||||
FEED7294144DD4630059E97B /* libgr.a in Frameworks */,
|
||||
@ -348,6 +360,8 @@
|
||||
FEED75DD144DD6590059E97B /* QuartzCore.framework in Frameworks */,
|
||||
FEED75DF144DD6840059E97B /* libz.dylib in Frameworks */,
|
||||
FEA5F4E21498000C005052F9 /* libports.a in Frameworks */,
|
||||
FEA61B0014EF589900B736CB /* libanimator.a in Frameworks */,
|
||||
FE7413AE14F689E700056D7B /* libopts.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -501,6 +515,9 @@
|
||||
FECA9A5914B3B09100B35E2C /* CubicParameterization_Test.cpp */,
|
||||
FECAAB7F14BDFAFD00B35E2C /* CubicParameterization_TestUtility.cpp */,
|
||||
FEED7625144F22E20059E97B /* CubicReduceOrder_Test.cpp */,
|
||||
FE7413D714F691C200056D7B /* EdgeWalker_TestUtility.cpp */,
|
||||
FE7413D314F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp */,
|
||||
FEA61B2B14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp */,
|
||||
FED53C381483CB9400F6359E /* Inline_Tests.cpp */,
|
||||
FEED7680144F2E480059E97B /* Intersection_Tests.h */,
|
||||
FEED76ED144F66E90059E97B /* Intersection_Tests.cpp */,
|
||||
@ -518,6 +535,7 @@
|
||||
FECAA6E014BDDF2D00B35E2C /* QuadraticReduceOrder_Test.cpp */,
|
||||
FEED7673144F2D770059E97B /* TestUtilities.h */,
|
||||
FEED764B144F29BD0059E97B /* TestUtilities.cpp */,
|
||||
FE7413DB14F6926D00056D7B /* EdgeWalker_Test.h */,
|
||||
);
|
||||
name = Tests;
|
||||
sourceTree = "<group>";
|
||||
@ -626,6 +644,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FEF87C2C13E0410900335C58 /* libopts.a */,
|
||||
FE74136114F6866000056D7B /* libopts_ssse3.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@ -746,6 +765,13 @@
|
||||
/* 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;
|
||||
@ -919,6 +945,9 @@
|
||||
FE7134F514D1E7C70008E392 /* LineParameterization.cpp in Sources */,
|
||||
FE71351314D2E9F50008E392 /* RectUtilities.cpp in Sources */,
|
||||
FE71358614D309E90008E392 /* EdgeWalker.cpp in Sources */,
|
||||
FEA61B2C14F2AF6600B736CB /* EdgeWalkerRectangles_Test.cpp in Sources */,
|
||||
FE7413D414F6915A00056D7B /* EdgeWalkerPolygons_Test.cpp in Sources */,
|
||||
FE7413D814F691C200056D7B /* EdgeWalker_TestUtility.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -981,11 +1010,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = "edge-Info.plist";
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../../../xcode/core/build/Debug\"",
|
||||
"\"$(SRCROOT)/../../../xcode/maccore/build/Debug\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = edge;
|
||||
SKIP_INSTALL = YES;
|
||||
@ -1004,11 +1029,7 @@
|
||||
GCC_PREFIX_HEADER = edge_Prefix.pch;
|
||||
INFOPLIST_FILE = "edge-Info.plist";
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../../../xcode/core/build/Debug\"",
|
||||
"\"$(SRCROOT)/../../../xcode/maccore/build/Debug\"",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
PRODUCT_NAME = edge;
|
||||
};
|
||||
name = Release;
|
||||
|
Loading…
Reference in New Issue
Block a user