2012-09-14 14:19:30 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2012 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
2012-11-21 02:02:25 +00:00
|
|
|
|
2012-09-14 14:19:30 +00:00
|
|
|
#include "Simplify.h"
|
|
|
|
|
|
|
|
namespace Op {
|
|
|
|
|
2012-11-20 14:21:54 +00:00
|
|
|
#define INCLUDED_BY_SHAPE_OPS 1
|
|
|
|
|
2012-09-14 14:19:30 +00:00
|
|
|
#include "Simplify.cpp"
|
|
|
|
|
2012-11-09 22:14:19 +00:00
|
|
|
// 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
|
2012-11-29 14:31:50 +00:00
|
|
|
static Segment* findChaseOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd) {
|
2012-11-09 22:14:19 +00:00
|
|
|
while (chase.count()) {
|
|
|
|
Span* span;
|
|
|
|
chase.pop(&span);
|
|
|
|
const Span& backPtr = span->fOther->span(span->fOtherIndex);
|
|
|
|
Segment* segment = backPtr.fOther;
|
2012-11-29 14:31:50 +00:00
|
|
|
nextStart = backPtr.fOtherIndex;
|
2012-11-09 22:14:19 +00:00
|
|
|
SkTDArray<Angle> angles;
|
|
|
|
int done = 0;
|
2012-11-29 14:31:50 +00:00
|
|
|
if (segment->activeAngle(nextStart, done, angles)) {
|
2012-11-09 22:14:19 +00:00
|
|
|
Angle* last = angles.end() - 1;
|
2012-11-29 14:31:50 +00:00
|
|
|
nextStart = last->start();
|
|
|
|
nextEnd = last->end();
|
2012-11-09 22:14:19 +00:00
|
|
|
#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);
|
2012-11-29 14:31:50 +00:00
|
|
|
int angleCount = sorted.count();
|
2012-11-09 22:14:19 +00:00
|
|
|
#if DEBUG_SORT
|
2012-11-29 14:31:50 +00:00
|
|
|
sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0);
|
2012-11-09 22:14:19 +00:00
|
|
|
#endif
|
|
|
|
if (!sortable) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// find first angle, initialize winding to computed fWindSum
|
|
|
|
int firstIndex = -1;
|
|
|
|
const Angle* angle;
|
|
|
|
do {
|
|
|
|
angle = sorted[++firstIndex];
|
|
|
|
segment = angle->segment();
|
2012-11-29 14:31:50 +00:00
|
|
|
} while (segment->windSum(angle) == SK_MinS32);
|
|
|
|
#if DEBUG_SORT
|
|
|
|
segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
|
2012-11-09 22:14:19 +00:00
|
|
|
#endif
|
2012-11-29 14:31:50 +00:00
|
|
|
int sumMiWinding = segment->updateWindingReverse(angle);
|
|
|
|
int sumSuWinding = segment->updateOppWindingReverse(angle);
|
|
|
|
if (segment->operand()) {
|
|
|
|
SkTSwap<int>(sumMiWinding, sumSuWinding);
|
2012-11-09 22:14:19 +00:00
|
|
|
}
|
|
|
|
int nextIndex = firstIndex + 1;
|
|
|
|
int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
|
2012-11-29 14:31:50 +00:00
|
|
|
Segment* first = NULL;
|
2012-11-09 22:14:19 +00:00
|
|
|
do {
|
|
|
|
SkASSERT(nextIndex != firstIndex);
|
|
|
|
if (nextIndex == angleCount) {
|
|
|
|
nextIndex = 0;
|
|
|
|
}
|
2012-11-29 14:31:50 +00:00
|
|
|
int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
|
2012-11-09 22:14:19 +00:00
|
|
|
angle = sorted[nextIndex];
|
|
|
|
segment = angle->segment();
|
2012-11-29 14:31:50 +00:00
|
|
|
int start = angle->start();
|
|
|
|
int end = angle->end();
|
|
|
|
segment->setUpWindings(start, end, sumMiWinding, sumSuWinding,
|
|
|
|
maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
|
|
|
|
if (!segment->done(angle)) {
|
|
|
|
if (!first) {
|
|
|
|
first = segment;
|
|
|
|
nextStart = start;
|
|
|
|
nextEnd = end;
|
2012-11-09 22:14:19 +00:00
|
|
|
}
|
2012-11-29 14:31:50 +00:00
|
|
|
(void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
|
|
|
|
oppSumWinding, true, angle);
|
2012-11-09 22:14:19 +00:00
|
|
|
}
|
|
|
|
} while (++nextIndex != lastIndex);
|
2012-11-29 14:31:50 +00:00
|
|
|
if (first) {
|
|
|
|
#if TRY_ROTATE
|
|
|
|
*chase.insert(0) = span;
|
|
|
|
#else
|
|
|
|
*chase.append() = span;
|
|
|
|
#endif
|
|
|
|
return first;
|
|
|
|
}
|
2012-11-09 22:14:19 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|
|
|
|
|
2012-11-29 14:31:50 +00:00
|
|
|
/*
|
2012-11-14 21:14:56 +00:00
|
|
|
static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
|
2012-11-09 22:14:19 +00:00
|
|
|
bool windingIsOp, ShapeOp op) {
|
|
|
|
bool active = windingIsActive(winding, spanWinding);
|
|
|
|
if (!active) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-11-14 21:14:56 +00:00
|
|
|
if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
|
2012-11-29 14:31:50 +00:00
|
|
|
switch (op) {
|
|
|
|
case kIntersect_Op:
|
|
|
|
case kUnion_Op:
|
|
|
|
return true;
|
|
|
|
case kDifference_Op: {
|
|
|
|
int absSpan = abs(spanWinding);
|
|
|
|
int absOpp = abs(oppSpanWinding);
|
|
|
|
return windingIsOp ? absSpan < absOpp : absSpan > absOpp;
|
|
|
|
}
|
|
|
|
case kXor_Op:
|
|
|
|
return spanWinding != oppSpanWinding;
|
|
|
|
default:
|
|
|
|
SkASSERT(0);
|
|
|
|
}
|
2012-11-14 21:14:56 +00:00
|
|
|
}
|
2012-11-09 22:14:19 +00:00
|
|
|
bool opActive = oppWinding != 0;
|
|
|
|
return gOpLookup[op][opActive][windingIsOp];
|
|
|
|
}
|
2012-11-29 14:31:50 +00:00
|
|
|
*/
|
2012-11-14 21:14:56 +00:00
|
|
|
|
2012-11-09 22:14:19 +00:00
|
|
|
static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
|
2012-11-29 14:31:50 +00:00
|
|
|
const int xorMask, const int xorOpMask, PathWrapper& simple) {
|
2012-09-14 14:19:30 +00:00
|
|
|
bool firstContour = true;
|
2012-11-09 22:14:19 +00:00
|
|
|
bool unsortable = false;
|
2012-12-13 19:47:53 +00:00
|
|
|
bool topUnsortable = false;
|
|
|
|
bool firstRetry = false;
|
2012-11-09 22:14:19 +00:00
|
|
|
bool closable = true;
|
2012-10-26 21:03:50 +00:00
|
|
|
SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
|
2012-09-14 14:19:30 +00:00
|
|
|
do {
|
2012-10-19 15:54:16 +00:00
|
|
|
int index, endIndex;
|
2012-12-13 19:47:53 +00:00
|
|
|
Segment* current = findSortableTopNew(contourList, firstContour, index, endIndex, topLeft,
|
|
|
|
topUnsortable);
|
2012-10-19 15:54:16 +00:00
|
|
|
if (!current) {
|
2012-12-13 19:47:53 +00:00
|
|
|
if (topUnsortable) {
|
|
|
|
topUnsortable = false;
|
|
|
|
SkASSERT(!firstRetry);
|
|
|
|
firstRetry = true;
|
|
|
|
topLeft.fX = topLeft.fY = SK_ScalarMin;
|
|
|
|
continue;
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|
2012-12-13 19:47:53 +00:00
|
|
|
break;
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|
|
|
|
SkTDArray<Span*> chaseArray;
|
|
|
|
do {
|
2012-11-29 14:31:50 +00:00
|
|
|
if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
|
|
|
|
bool active = true;
|
|
|
|
do {
|
|
|
|
#if DEBUG_ACTIVE_SPANS
|
|
|
|
if (!unsortable && current->done()) {
|
|
|
|
debugShowActiveSpans(contourList);
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|
2012-11-29 14:31:50 +00:00
|
|
|
#endif
|
|
|
|
SkASSERT(unsortable || !current->done());
|
|
|
|
int nextStart = index;
|
|
|
|
int nextEnd = endIndex;
|
|
|
|
Segment* next = current->findNextOp(chaseArray, nextStart, nextEnd,
|
|
|
|
unsortable, op, xorMask, xorOpMask);
|
|
|
|
if (!next) {
|
|
|
|
SkASSERT(!unsortable);
|
|
|
|
if (!unsortable && simple.hasMove()
|
|
|
|
&& current->verb() != SkPath::kLine_Verb
|
|
|
|
&& !simple.isClosed()) {
|
|
|
|
current->addCurveTo(index, endIndex, simple, true);
|
|
|
|
SkASSERT(simple.isClosed());
|
|
|
|
}
|
|
|
|
active = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
current->addCurveTo(index, endIndex, simple, true);
|
|
|
|
current = next;
|
|
|
|
index = nextStart;
|
|
|
|
endIndex = nextEnd;
|
|
|
|
} while (!simple.isClosed() && ((!unsortable) || !current->done()));
|
|
|
|
if (active && !simple.isClosed()) {
|
2012-11-09 22:14:19 +00:00
|
|
|
SkASSERT(unsortable);
|
|
|
|
int min = SkMin32(index, endIndex);
|
|
|
|
if (!current->done(min)) {
|
|
|
|
current->addCurveTo(index, endIndex, simple, true);
|
2012-11-29 14:31:50 +00:00
|
|
|
current->markDoneBinary(min);
|
2012-11-09 22:14:19 +00:00
|
|
|
}
|
|
|
|
closable = false;
|
|
|
|
}
|
2012-09-14 14:19:30 +00:00
|
|
|
simple.close();
|
2012-11-29 14:31:50 +00:00
|
|
|
} else {
|
|
|
|
Span* last = current->markAndChaseDoneBinary(index, endIndex);
|
|
|
|
if (last) {
|
|
|
|
*chaseArray.append() = last;
|
|
|
|
}
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|
2012-11-09 22:14:19 +00:00
|
|
|
current = findChaseOp(chaseArray, index, endIndex);
|
2012-09-14 14:19:30 +00:00
|
|
|
#if DEBUG_ACTIVE_SPANS
|
|
|
|
debugShowActiveSpans(contourList);
|
|
|
|
#endif
|
|
|
|
if (!current) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (true);
|
|
|
|
} while (true);
|
2012-11-09 22:14:19 +00:00
|
|
|
return closable;
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // end of Op namespace
|
|
|
|
|
|
|
|
|
|
|
|
void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
|
|
|
|
result.reset();
|
|
|
|
result.setFillType(SkPath::kEvenOdd_FillType);
|
|
|
|
// turn path into list of segments
|
|
|
|
SkTArray<Op::Contour> contours;
|
|
|
|
// FIXME: add self-intersecting cubics' T values to segment
|
|
|
|
Op::EdgeBuilder builder(one, contours);
|
2012-11-29 14:31:50 +00:00
|
|
|
const int xorMask = builder.xorMask();
|
2012-09-14 14:19:30 +00:00
|
|
|
builder.addOperand(two);
|
|
|
|
builder.finish();
|
2012-11-29 14:31:50 +00:00
|
|
|
const int xorOpMask = builder.xorMask();
|
2012-09-14 14:19:30 +00:00
|
|
|
SkTDArray<Op::Contour*> contourList;
|
2012-12-06 21:47:48 +00:00
|
|
|
makeContourList(contours, contourList, xorMask == kEvenOdd_Mask,
|
|
|
|
xorOpMask == kEvenOdd_Mask);
|
2012-09-14 14:19:30 +00:00
|
|
|
Op::Contour** currentPtr = contourList.begin();
|
|
|
|
if (!currentPtr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Op::Contour** listEnd = contourList.end();
|
|
|
|
// find all intersections between segments
|
|
|
|
do {
|
|
|
|
Op::Contour** nextPtr = currentPtr;
|
|
|
|
Op::Contour* current = *currentPtr++;
|
|
|
|
Op::Contour* next;
|
|
|
|
do {
|
|
|
|
next = *nextPtr++;
|
|
|
|
} while (addIntersectTs(current, next) && nextPtr != listEnd);
|
|
|
|
} while (currentPtr != listEnd);
|
|
|
|
// eat through coincident edges
|
2012-11-21 02:02:25 +00:00
|
|
|
|
2012-11-20 14:21:54 +00:00
|
|
|
int total = 0;
|
|
|
|
int index;
|
|
|
|
for (index = 0; index < contourList.count(); ++index) {
|
|
|
|
total += contourList[index]->segments().count();
|
|
|
|
}
|
2012-11-21 21:36:34 +00:00
|
|
|
#if DEBUG_SHOW_WINDING
|
2012-11-20 14:21:54 +00:00
|
|
|
Op::Contour::debugShowWindingValues(contourList);
|
|
|
|
#endif
|
2012-12-06 21:47:48 +00:00
|
|
|
coincidenceCheck(contourList, total);
|
2012-11-21 21:36:34 +00:00
|
|
|
#if DEBUG_SHOW_WINDING
|
2012-11-20 14:21:54 +00:00
|
|
|
Op::Contour::debugShowWindingValues(contourList);
|
|
|
|
#endif
|
2012-09-14 14:19:30 +00:00
|
|
|
fixOtherTIndex(contourList);
|
2012-11-09 22:14:19 +00:00
|
|
|
sortSegments(contourList);
|
|
|
|
#if DEBUG_ACTIVE_SPANS
|
|
|
|
debugShowActiveSpans(contourList);
|
|
|
|
#endif
|
2012-09-14 14:19:30 +00:00
|
|
|
// construct closed contours
|
2012-10-26 21:03:50 +00:00
|
|
|
Op::PathWrapper wrapper(result);
|
2012-11-29 14:31:50 +00:00
|
|
|
bridgeOp(contourList, op, xorMask, xorOpMask, wrapper);
|
2012-09-14 14:19:30 +00:00
|
|
|
}
|