shape ops work in progress
first cut at getting binary ops to work (union/intersection/diff/xor) git-svn-id: http://skia.googlecode.com/svn/trunk@6375 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
3fb91a1fe9
commit
31143cf37f
@ -24,9 +24,9 @@ static bool drawPaths(SkCanvas* canvas, const SkPath& path, bool useOld)
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
// paint.setStrokeWidth(6);
|
||||
// paint.setColor(0x1F003f7f);
|
||||
// canvas->drawPath(path, paint);
|
||||
// paint.setStrokeWidth(6);
|
||||
// paint.setColor(0x1F003f7f);
|
||||
// canvas->drawPath(path, paint);
|
||||
paint.setColor(0xFF305F00);
|
||||
paint.setStrokeWidth(1);
|
||||
canvas->drawPath(out, paint);
|
||||
@ -287,7 +287,7 @@ static bool drawLetters(SkCanvas* canvas, int step, bool useOld)
|
||||
textPos[x].fY = height / 2;
|
||||
}
|
||||
paint.setTextSize(40 + step / 100.0f);
|
||||
#if 1
|
||||
#if 0
|
||||
bool oneShot = false;
|
||||
for (int mask = 0; mask < 1 << testStrLen; ++mask) {
|
||||
char maskStr[testStrLen];
|
||||
|
@ -16,7 +16,7 @@ public:
|
||||
};
|
||||
protected:
|
||||
virtual void onDraw(SkCanvas* canvas) {
|
||||
static int step = 17907; // 17907 drawLetters first error
|
||||
static int step = 0; // 17907 drawLetters first error
|
||||
// drawStars triggers error at 33348
|
||||
// drawStars error not easy to debug last time I checked
|
||||
static double seconds;
|
||||
|
@ -22,6 +22,7 @@ extern bool testSimplify(const SkPath& path, bool fill, SkPath& out,
|
||||
extern bool testSimplifyx(SkPath& path, bool useXor, SkPath& out,
|
||||
State4& state, const char* pathStr);
|
||||
extern bool testSimplifyx(const SkPath& path);
|
||||
extern bool testShapeOp(const SkPath& a, const SkPath& b, const ShapeOp );
|
||||
|
||||
struct State4 {
|
||||
State4();
|
||||
|
@ -286,6 +286,24 @@ bool testSimplifyx(const SkPath& path) {
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool testShapeOp(const SkPath& a, const SkPath& b, const ShapeOp shapeOp) {
|
||||
SkPath out;
|
||||
operate(a, b, shapeOp, out);
|
||||
SkPath pathOut;
|
||||
SkRegion rgnA, rgnB, openClip, rgnOut;
|
||||
openClip.setRect(-16000, -16000, 16000, 16000);
|
||||
rgnA.setPath(a, openClip);
|
||||
rgnB.setPath(b, openClip);
|
||||
rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
|
||||
rgnOut.getBoundaryPath(&pathOut);
|
||||
SkBitmap bitmap;
|
||||
int result = comparePaths(pathOut, out, bitmap);
|
||||
if (result && gPathStrAssert) {
|
||||
SkASSERT(0);
|
||||
}
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
const int maxThreadsAllocated = 64;
|
||||
static int maxThreads = 1;
|
||||
static int threadIndex;
|
||||
|
@ -10,37 +10,146 @@ namespace Op {
|
||||
|
||||
#include "Simplify.cpp"
|
||||
|
||||
static bool windingIsActive(int winding, int spanWinding, int oppWinding,
|
||||
const ShapeOp op) {
|
||||
return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
|
||||
&& (!winding || !spanWinding || winding == -spanWinding);
|
||||
// FIXME: this and find chase should be merge together, along with
|
||||
// other code that walks winding in angles
|
||||
// OPTIMIZATION: Probably, the walked winding should be rolled into the angle structure
|
||||
// so it isn't duplicated by walkers like this one
|
||||
static Segment* findChaseOp(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
|
||||
while (chase.count()) {
|
||||
Span* span;
|
||||
chase.pop(&span);
|
||||
const Span& backPtr = span->fOther->span(span->fOtherIndex);
|
||||
Segment* segment = backPtr.fOther;
|
||||
tIndex = backPtr.fOtherIndex;
|
||||
SkTDArray<Angle> angles;
|
||||
int done = 0;
|
||||
if (segment->activeAngle(tIndex, done, angles)) {
|
||||
Angle* last = angles.end() - 1;
|
||||
tIndex = last->start();
|
||||
endIndex = last->end();
|
||||
#if TRY_ROTATE
|
||||
*chase.insert(0) = span;
|
||||
#else
|
||||
*chase.append() = span;
|
||||
#endif
|
||||
return last->segment();
|
||||
}
|
||||
if (done == angles.count()) {
|
||||
continue;
|
||||
}
|
||||
SkTDArray<Angle*> sorted;
|
||||
bool sortable = Segment::SortAngles(angles, sorted);
|
||||
#if DEBUG_SORT
|
||||
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
|
||||
#endif
|
||||
if (!sortable) {
|
||||
continue;
|
||||
}
|
||||
// find first angle, initialize winding to computed fWindSum
|
||||
int firstIndex = -1;
|
||||
const Angle* angle;
|
||||
int winding;
|
||||
do {
|
||||
angle = sorted[++firstIndex];
|
||||
segment = angle->segment();
|
||||
winding = segment->windSum(angle);
|
||||
} while (winding == SK_MinS32);
|
||||
int spanWinding = segment->spanSign(angle->start(), angle->end());
|
||||
#if DEBUG_WINDING
|
||||
SkDebugf("%s winding=%d spanWinding=%d\n",
|
||||
__FUNCTION__, winding, spanWinding);
|
||||
#endif
|
||||
// turn span winding into contour winding
|
||||
if (spanWinding * winding < 0) {
|
||||
winding += spanWinding;
|
||||
}
|
||||
// we care about first sign and whether wind sum indicates this
|
||||
// edge is inside or outside. Maybe need to pass span winding
|
||||
// or first winding or something into this function?
|
||||
// advance to first undone angle, then return it and winding
|
||||
// (to set whether edges are active or not)
|
||||
int nextIndex = firstIndex + 1;
|
||||
int angleCount = sorted.count();
|
||||
int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
|
||||
angle = sorted[firstIndex];
|
||||
segment = angle->segment();
|
||||
int oWinding = segment->oppSum(angle);
|
||||
#if DEBUG_SORT
|
||||
segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
|
||||
#endif
|
||||
winding -= segment->spanSign(angle);
|
||||
bool firstOperand = segment->operand();
|
||||
do {
|
||||
SkASSERT(nextIndex != firstIndex);
|
||||
if (nextIndex == angleCount) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
angle = sorted[nextIndex];
|
||||
segment = angle->segment();
|
||||
int deltaSum = segment->spanSign(angle);
|
||||
bool angleIsOp = segment->operand() ^ firstOperand;
|
||||
int maxWinding;
|
||||
if (angleIsOp) {
|
||||
maxWinding = oWinding;
|
||||
oWinding -= deltaSum;
|
||||
} else {
|
||||
maxWinding = winding;
|
||||
winding -= deltaSum;
|
||||
}
|
||||
#if DEBUG_SORT
|
||||
SkDebugf("%s id=%d maxWinding=%d winding=%d oWinding=%d sign=%d\n", __FUNCTION__,
|
||||
segment->debugID(), maxWinding, winding, oWinding, angle->sign());
|
||||
#endif
|
||||
tIndex = angle->start();
|
||||
endIndex = angle->end();
|
||||
int lesser = SkMin32(tIndex, endIndex);
|
||||
const Span& nextSpan = segment->span(lesser);
|
||||
if (!nextSpan.fDone) {
|
||||
if (angleIsOp) {
|
||||
SkTSwap(winding, oWinding);
|
||||
}
|
||||
if (useInnerWinding(maxWinding, winding)) {
|
||||
maxWinding = winding;
|
||||
}
|
||||
segment->markWinding(lesser, maxWinding, oWinding);
|
||||
break;
|
||||
}
|
||||
} while (++nextIndex != lastIndex);
|
||||
#if TRY_ROTATE
|
||||
*chase.insert(0) = span;
|
||||
#else
|
||||
*chase.append() = span;
|
||||
#endif
|
||||
return segment;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
||||
static bool windingIsActive(int winding, int oppWinding, int spanWinding,
|
||||
bool windingIsOp, ShapeOp op) {
|
||||
bool active = windingIsActive(winding, spanWinding);
|
||||
if (!active) {
|
||||
return false;
|
||||
}
|
||||
bool opActive = oppWinding != 0;
|
||||
return gOpLookup[op][opActive][windingIsOp];
|
||||
}
|
||||
|
||||
static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
||||
const int aXorMask, const int bXorMask, PathWrapper& simple) {
|
||||
bool firstContour = true;
|
||||
bool unsortable = false;
|
||||
bool closable = true;
|
||||
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
|
||||
do {
|
||||
|
||||
#if SORTABLE_CONTOURS // old way
|
||||
Segment* topStart = findTopContour(contourList);
|
||||
if (!topStart) {
|
||||
break;
|
||||
}
|
||||
// Start at the top. Above the top is outside, below is inside.
|
||||
// follow edges to intersection by changing the index by direction.
|
||||
int index, endIndex;
|
||||
Segment* current = topStart->findTop(index, endIndex);
|
||||
#else // new way: iterate while top is unsortable
|
||||
int index, endIndex;
|
||||
Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
int contourWinding;
|
||||
int contourWinding, oppContourWinding;
|
||||
if (firstContour) {
|
||||
contourWinding = 0;
|
||||
contourWinding = oppContourWinding = 0;
|
||||
firstContour = false;
|
||||
} else {
|
||||
int sumWinding = current->windSum(SkMin32(index, endIndex));
|
||||
@ -50,9 +159,16 @@ static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
||||
}
|
||||
if (sumWinding == SK_MinS32) {
|
||||
contourWinding = innerContourCheck(contourList, current,
|
||||
index, endIndex);
|
||||
index, endIndex, false);
|
||||
oppContourWinding = innerContourCheck(contourList, current,
|
||||
index, endIndex, true);
|
||||
} else {
|
||||
contourWinding = sumWinding;
|
||||
oppContourWinding = 0;
|
||||
SkASSERT(0);
|
||||
// FIXME: need to get oppContourWinding by building sort wheel and
|
||||
// retrieving sumWinding of uphill opposite span, calling inner contour check
|
||||
// if need be
|
||||
int spanWinding = current->spanSign(index, endIndex);
|
||||
bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
|
||||
if (inner) {
|
||||
@ -69,79 +185,69 @@ static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
||||
SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
|
||||
#endif
|
||||
}
|
||||
// SkPoint lastPt;
|
||||
int winding = contourWinding;
|
||||
int oppWinding = oppContourWinding;
|
||||
int spanWinding = current->spanSign(index, endIndex);
|
||||
int oppWinding = current->oppSign(index, endIndex);
|
||||
bool active = windingIsActive(winding, spanWinding, oppWinding, op);
|
||||
SkTDArray<Span*> chaseArray;
|
||||
bool unsortable = false;
|
||||
do {
|
||||
bool active = windingIsActive(winding, oppWinding, spanWinding,
|
||||
current->operand(), op);
|
||||
#if DEBUG_WINDING
|
||||
SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
|
||||
SkDebugf("%s active=%s winding=%d oppWinding=%d spanWinding=%d\n",
|
||||
__FUNCTION__, active ? "true" : "false",
|
||||
winding, spanWinding);
|
||||
winding, oppWinding, spanWinding);
|
||||
#endif
|
||||
// const SkPoint* firstPt = NULL;
|
||||
do {
|
||||
SkASSERT(!current->done());
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
if (!unsortable && current->done()) {
|
||||
debugShowActiveSpans(contourList);
|
||||
}
|
||||
#endif
|
||||
SkASSERT(unsortable || !current->done());
|
||||
int nextStart = index;
|
||||
int nextEnd = endIndex;
|
||||
Segment* next = current->findNextOp(chaseArray, active,
|
||||
nextStart, nextEnd, winding, spanWinding, unsortable, op,
|
||||
aXorMask, bXorMask);
|
||||
nextStart, nextEnd, winding, oppWinding, spanWinding,
|
||||
unsortable, op, aXorMask, bXorMask);
|
||||
if (!next) {
|
||||
// FIXME: if unsortable, allow partial paths to be later
|
||||
// assembled
|
||||
SkASSERT(!unsortable);
|
||||
if (active && simple.hasMove()
|
||||
if (active && !unsortable && simple.hasMove()
|
||||
&& current->verb() != SkPath::kLine_Verb
|
||||
&& !simple.isClosed()) {
|
||||
/* lastPt = */ current->addCurveTo(index, endIndex, simple, true);
|
||||
current->addCurveTo(index, endIndex, simple, true);
|
||||
SkASSERT(simple.isClosed());
|
||||
}
|
||||
break;
|
||||
}
|
||||
// if (!firstPt) {
|
||||
// firstPt = ¤t->addMoveTo(index, simple, active);
|
||||
// }
|
||||
/* lastPt = */ current->addCurveTo(index, endIndex, simple, active);
|
||||
current->addCurveTo(index, endIndex, simple, active);
|
||||
current = next;
|
||||
index = nextStart;
|
||||
endIndex = nextEnd;
|
||||
} while (!simple.isClosed() && (active || !current->done()));
|
||||
if (simple.hasMove() && active) {
|
||||
#if DEBUG_PATH_CONSTRUCTION
|
||||
SkDebugf("%s close\n", __FUNCTION__);
|
||||
#endif
|
||||
} while (!simple.isClosed()
|
||||
&& ((active && !unsortable) || !current->done()));
|
||||
if (active) {
|
||||
if (!simple.isClosed()) {
|
||||
SkASSERT(unsortable);
|
||||
int min = SkMin32(index, endIndex);
|
||||
if (!current->done(min)) {
|
||||
current->addCurveTo(index, endIndex, simple, true);
|
||||
current->markDone(SkMin32(index, endIndex), winding ? winding : spanWinding);
|
||||
}
|
||||
closable = false;
|
||||
}
|
||||
simple.close();
|
||||
}
|
||||
current = findChase(chaseArray, index, endIndex, contourWinding);
|
||||
current = findChaseOp(chaseArray, index, endIndex);
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
debugShowActiveSpans(contourList);
|
||||
#endif
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
int lesser = SkMin32(index, endIndex);
|
||||
spanWinding = current->spanSign(index, endIndex);
|
||||
winding = current->windSum(lesser);
|
||||
bool inner = useInnerWinding(winding - spanWinding, winding);
|
||||
#if DEBUG_WINDING
|
||||
SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
|
||||
" inner=%d result=%d\n",
|
||||
__FUNCTION__, current->debugID(), current->t(lesser),
|
||||
spanWinding, winding, SkSign32(index - endIndex),
|
||||
useInnerWinding(winding - spanWinding, winding),
|
||||
inner ? winding - spanWinding : winding);
|
||||
#endif
|
||||
if (inner) {
|
||||
winding -= spanWinding;
|
||||
}
|
||||
int oppWinding = current->oppSign(index, endIndex);
|
||||
active = windingIsActive(winding, spanWinding, oppWinding, op);
|
||||
winding = updateWindings(current, index, endIndex, spanWinding, &oppWinding);
|
||||
} while (true);
|
||||
} while (true);
|
||||
return closable;
|
||||
}
|
||||
|
||||
} // end of Op namespace
|
||||
@ -177,6 +283,10 @@ void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
|
||||
// eat through coincident edges
|
||||
coincidenceCheck(contourList);
|
||||
fixOtherTIndex(contourList);
|
||||
sortSegments(contourList);
|
||||
#if DEBUG_ACTIVE_SPANS
|
||||
debugShowActiveSpans(contourList);
|
||||
#endif
|
||||
// construct closed contours
|
||||
Op::PathWrapper wrapper(result);
|
||||
bridgeOp(contourList, op, aXorMask, bXorMask, wrapper);
|
||||
|
@ -18,7 +18,8 @@ enum ShapeOp {
|
||||
kDifference_Op,
|
||||
kIntersect_Op,
|
||||
kUnion_Op,
|
||||
kXor_Op
|
||||
kXor_Op,
|
||||
kShapeOp_Count
|
||||
};
|
||||
|
||||
enum ShapeOpMask {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2556,55 +2556,59 @@ static void testIntersect1() {
|
||||
one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
|
||||
two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
|
||||
operate(one, two, kIntersect_Op, result);
|
||||
SkASSERT(result.countPoints() == 4);
|
||||
SkASSERT(result.countPoints() == 5);
|
||||
SkASSERT(result.countVerbs() == 6); // move, 4 lines, close
|
||||
SkRect bounds = result.getBounds();
|
||||
SkASSERT(bounds.fLeft == 3);
|
||||
SkASSERT(bounds.fTop == 3);
|
||||
SkASSERT(bounds.fRight == 6);
|
||||
SkASSERT(bounds.fBottom == 6);
|
||||
testShapeOp(one, two, kIntersect_Op);
|
||||
}
|
||||
|
||||
static void testUnion1() {
|
||||
SkPath one, two, result;
|
||||
one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
|
||||
two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
|
||||
operate(one, two, kIntersect_Op, result);
|
||||
SkASSERT(result.countPoints() == 8);
|
||||
operate(one, two, kUnion_Op, result);
|
||||
SkASSERT(result.countPoints() == 9);
|
||||
SkASSERT(result.countVerbs() == 10); // move, 8 lines, close
|
||||
SkRect bounds = result.getBounds();
|
||||
SkASSERT(bounds.fLeft == 0);
|
||||
SkASSERT(bounds.fTop == 0);
|
||||
SkASSERT(bounds.fRight == 9);
|
||||
SkASSERT(bounds.fBottom == 9);
|
||||
testShapeOp(one, two, kUnion_Op);
|
||||
}
|
||||
|
||||
static void testDiff1() {
|
||||
SkPath one, two, result;
|
||||
one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
|
||||
two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
|
||||
operate(one, two, kIntersect_Op, result);
|
||||
SkASSERT(result.countPoints() == 6);
|
||||
SkASSERT(result.countVerbs() == 8); // move, 8 lines, close
|
||||
operate(one, two, kDifference_Op, result);
|
||||
SkASSERT(result.countPoints() == 7);
|
||||
SkASSERT(result.countVerbs() == 8); // move, 6 lines, close
|
||||
SkRect bounds = result.getBounds();
|
||||
SkASSERT(bounds.fLeft == 0);
|
||||
SkASSERT(bounds.fTop == 0);
|
||||
SkASSERT(bounds.fRight == 6);
|
||||
SkASSERT(bounds.fBottom == 6);
|
||||
testShapeOp(one, two, kDifference_Op);
|
||||
}
|
||||
|
||||
static void testXor1() {
|
||||
SkPath one, two, result;
|
||||
one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
|
||||
two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
|
||||
operate(one, two, kIntersect_Op, result);
|
||||
SkASSERT(result.countPoints() == 10);
|
||||
SkASSERT(result.countVerbs() == 12); // move, 8 lines, close
|
||||
operate(one, two, kXor_Op, result);
|
||||
SkASSERT(result.countPoints() == 14);
|
||||
SkASSERT(result.countVerbs() == 16); // move, 6 lines, close, move, 6 lines, close
|
||||
SkRect bounds = result.getBounds();
|
||||
SkASSERT(bounds.fLeft == 0);
|
||||
SkASSERT(bounds.fTop == 0);
|
||||
SkASSERT(bounds.fRight == 12);
|
||||
SkASSERT(bounds.fBottom == 12);
|
||||
SkASSERT(bounds.fRight == 9);
|
||||
SkASSERT(bounds.fBottom == 9);
|
||||
testShapeOp(one, two, kXor_Op);
|
||||
}
|
||||
|
||||
static void testQuadratic22() {
|
||||
@ -3213,15 +3217,15 @@ static struct {
|
||||
} subTests[] = {
|
||||
TEST(testXor1),
|
||||
TEST(testDiff1),
|
||||
TEST(testUnion1),
|
||||
TEST(testIntersect1),
|
||||
TEST(testUnion1),
|
||||
};
|
||||
|
||||
static const size_t subTestCount = sizeof(subTests) / sizeof(subTests[0]);
|
||||
|
||||
static bool skipAll = false;
|
||||
static bool runSubTests = false;
|
||||
static bool runReverse = true;
|
||||
static bool runBinaryTestsFirst = true;
|
||||
static bool runReverse = false;
|
||||
|
||||
void SimplifyNew_Test() {
|
||||
if (skipAll) {
|
||||
@ -3232,7 +3236,7 @@ void SimplifyNew_Test() {
|
||||
gDebugMaxWindValue = 4;
|
||||
size_t index;
|
||||
#endif
|
||||
if (runSubTests) {
|
||||
if (runBinaryTestsFirst) {
|
||||
index = subTestCount - 1;
|
||||
do {
|
||||
SkDebugf(" %s [%s]\n", __FUNCTION__, subTests[index].str);
|
||||
@ -3259,6 +3263,13 @@ void SimplifyNew_Test() {
|
||||
}
|
||||
index += runReverse ? -1 : 1;
|
||||
} while (true);
|
||||
if (!runBinaryTestsFirst) {
|
||||
index = subTestCount - 1;
|
||||
do {
|
||||
SkDebugf(" %s [%s]\n", __FUNCTION__, subTests[index].str);
|
||||
(*subTests[index].fun)();
|
||||
} while (index--);
|
||||
}
|
||||
#ifdef SK_DEBUG
|
||||
gDebugMaxWindSum = SK_MaxS32;
|
||||
gDebugMaxWindValue = SK_MaxS32;
|
||||
|
Loading…
Reference in New Issue
Block a user