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:
caryclark@google.com 2012-11-09 22:14:19 +00:00
parent 3fb91a1fe9
commit 31143cf37f
8 changed files with 571 additions and 469 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = &current->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);

View File

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

View File

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