aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/pathops/SkOpBuilder.cpp75
-rw-r--r--src/pathops/SkOpContour.cpp10
-rw-r--r--src/pathops/SkOpContour.h34
-rw-r--r--src/pathops/SkOpSegment.cpp7
-rw-r--r--src/pathops/SkOpSegment.h1
-rw-r--r--src/pathops/SkPathOpsCommon.h1
-rw-r--r--src/pathops/SkPathOpsSimplify.cpp1
-rw-r--r--src/pathops/SkPathOpsTypes.h13
-rw-r--r--src/pathops/SkPathOpsWinding.cpp8
-rw-r--r--tests/PathOpsBuilderTest.cpp31
10 files changed, 158 insertions, 23 deletions
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 1a446f15b6..39da2efbce 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -6,8 +6,80 @@
*/
#include "SkMatrix.h"
+#include "SkOpEdgeBuilder.h"
#include "SkPath.h"
#include "SkPathOps.h"
+#include "SkPathOpsCommon.h"
+
+static bool one_contour(const SkPath& path) {
+ SkChunkAlloc allocator(256);
+ int verbCount = path.countVerbs();
+ uint8_t* verbs = (uint8_t*) allocator.alloc(sizeof(uint8_t) * verbCount,
+ SkChunkAlloc::kThrow_AllocFailType);
+ (void) path.getVerbs(verbs, verbCount);
+ for (int index = 1; index < verbCount; ++index) {
+ if (verbs[index] == SkPath::kMove_Verb) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FixWinding(SkPath* path) {
+ SkPath::FillType fillType = path->getFillType();
+ if (fillType == SkPath::kInverseEvenOdd_FillType) {
+ fillType = SkPath::kInverseWinding_FillType;
+ } else if (fillType == SkPath::kEvenOdd_FillType) {
+ fillType = SkPath::kWinding_FillType;
+ }
+ SkPath::Direction dir;
+ if (one_contour(*path) && path->cheapComputeDirection(&dir)) {
+ if (dir != SkPath::kCCW_Direction) {
+ SkPath temp;
+ temp.reverseAddPath(*path);
+ *path = temp;
+ }
+ path->setFillType(fillType);
+ return;
+ }
+ SkChunkAlloc allocator(4096);
+ SkOpContourHead contourHead;
+ SkOpGlobalState globalState(NULL, &contourHead);
+ SkOpEdgeBuilder builder(*path, &contourHead, &allocator, &globalState);
+ builder.finish(&allocator);
+ SkASSERT(contourHead.next());
+ contourHead.resetReverse();
+ bool writePath = false;
+ SkOpSpan* topSpan;
+ globalState.setPhase(SkOpGlobalState::kFixWinding);
+ while ((topSpan = FindSortableTop(&contourHead))) {
+ SkOpSegment* topSegment = topSpan->segment();
+ SkOpContour* topContour = topSegment->contour();
+ bool active = topSegment->activeWinding(topSpan, topSpan->next());
+ SkASSERT(topContour->isCcw() >= 0);
+ if (active != SkToBool(topContour->isCcw())) {
+ topContour->setReverse();
+ writePath = true;
+ }
+ topContour->markDone();
+ }
+ if (!writePath) {
+ path->setFillType(fillType);
+ return;
+ }
+ SkPath empty;
+ SkPathWriter woundPath(empty);
+ SkOpContour* test = &contourHead;
+ do {
+ if (test->reversed()) {
+ test->toReversePath(&woundPath);
+ } else {
+ test->toPath(&woundPath);
+ }
+ } while ((test = test->next()));
+ *path = *woundPath.nativePath();
+ path->setFillType(fillType);
+}
void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
if (0 == fOps.count() && op != kUnion_SkPathOp) {
@@ -82,10 +154,11 @@ bool SkOpBuilder::resolve(SkPath* result) {
*result = original;
return false;
}
+ // convert the even odd result back to winding form before accumulating it
+ FixWinding(&fPathRefs[index]);
sum.addPath(fPathRefs[index]);
}
reset();
- sum.setFillType(SkPath::kEvenOdd_FillType);
bool success = Simplify(sum, result);
if (!success) {
*result = original;
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index 178ba3e89c..18b6328a7e 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -47,6 +47,16 @@ void SkOpContour::toPath(SkPathWriter* path) const {
path->close();
}
+void SkOpContour::toReversePath(SkPathWriter* path) const {
+ const SkPoint& pt = fTail->pts()[0];
+ path->deferredMove(pt);
+ const SkOpSegment* segment = fTail;
+ do {
+ segment->addCurveTo(segment->tail(), segment->head(), path, true);
+ } while ((segment = segment->prev()));
+ path->close();
+}
+
SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
SkOpSegment* segment = &fHead;
do {
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index dd5dbb40ca..f8143cf555 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -207,10 +207,21 @@ public:
SkDEBUGCODE(fID = globalState->nextContourID());
}
+ int isCcw() const {
+ return fCcw;
+ }
+
bool isXor() const {
return fXor;
}
+ void markDone() {
+ SkOpSegment* segment = &fHead;
+ do {
+ segment->markAllDone();
+ } while ((segment = segment->next()));
+ }
+
void missingCoincidence(SkOpCoincidence* coincidences, SkChunkAlloc* allocator) {
SkASSERT(fCount > 0);
SkOpSegment* segment = &fHead;
@@ -289,6 +300,18 @@ public:
SkDEBUGCODE(fDebugIndent = 0);
}
+ void resetReverse() {
+ SkOpContour* next = this;
+ do {
+ next->fCcw = -1;
+ next->fReverse = false;
+ } while ((next = next->next()));
+ }
+
+ bool reversed() const {
+ return fReverse;
+ }
+
void setBounds() {
SkASSERT(fCount > 0);
const SkOpSegment* segment = &fHead;
@@ -298,6 +321,10 @@ public:
}
}
+ void setCcw(int ccw) {
+ fCcw = ccw;
+ }
+
void setGlobalState(SkOpGlobalState* state) {
fState = state;
}
@@ -315,6 +342,10 @@ public:
fOppXor = isOppXor;
}
+ void setReverse() {
+ fReverse = true;
+ }
+
void setXor(bool isXor) {
fXor = isXor;
}
@@ -347,6 +378,7 @@ public:
} while ((segment = segment->next()));
}
+ void toReversePath(SkPathWriter* path) const;
void toPath(SkPathWriter* path) const;
SkOpSegment* undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr);
@@ -356,11 +388,13 @@ private:
SkOpSegment* fTail;
SkOpContour* fNext;
SkPathOpsBounds fBounds;
+ int fCcw;
int fCount;
int fFirstSorted;
bool fDone; // set by find top segment
bool fTopsFound;
bool fOperand; // true for the second argument to a binary operator
+ bool fReverse; // true if contour should be reverse written to path (used only by fix winding)
bool fXor; // set if original path had even-odd fill
bool fOppXor; // set if opposite path had even-odd fill
SkDEBUGCODE(int fID);
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 8bd0f03518..462cff60cf 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -852,6 +852,13 @@ bool SkOpSegment::isXor() const {
return fContour->isXor();
}
+void SkOpSegment::markAllDone() {
+ SkOpSpan* span = this->head();
+ do {
+ this->markDone(span);
+ } while ((span = span->next()->upCastable()));
+}
+
SkOpSpanBase* SkOpSegment::markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end) {
int step = start->step(end);
SkOpSpan* minSpan = start->starter(end);
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index 1162c2cfaf..38de40617a 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -231,6 +231,7 @@ public:
return fPts[SkPathOpsVerbToPoints(fVerb)];
}
+ void markAllDone();
SkOpSpanBase* markAndChaseDone(SkOpSpanBase* start, SkOpSpanBase* end);
bool markAndChaseWinding(SkOpSpanBase* start, SkOpSpanBase* end, int winding,
SkOpSpanBase** lastPtr);
diff --git a/src/pathops/SkPathOpsCommon.h b/src/pathops/SkPathOpsCommon.h
index b7fbf38b3d..f76d57c83b 100644
--- a/src/pathops/SkPathOpsCommon.h
+++ b/src/pathops/SkPathOpsCommon.h
@@ -22,6 +22,7 @@ SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
SkOpSpan* FindSortableTop(SkOpContourHead* );
SkOpSegment* FindUndone(SkOpContourHead* , SkOpSpanBase** startPtr,
SkOpSpanBase** endPtr);
+void FixWinding(SkPath* path);
bool SortContourList(SkOpContourHead** , bool evenOdd, bool oppEvenOdd);
bool HandleCoincidence(SkOpContourHead* , SkOpCoincidence* , SkChunkAlloc* );
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result, bool expectSuccess);
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index d37998b448..11b6833a73 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -212,3 +212,4 @@ bool Simplify(const SkPath& path, SkPath* result) {
}
return true;
}
+
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 845288f609..a780a88c6d 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -33,9 +33,7 @@ public:
, fContourHead(head)
, fWindingFailed(false)
, fAngleCoincidence(false)
-#if DEBUG_VALIDATE
, fPhase(kIntersecting)
-#endif
SkDEBUGPARAMS(fAngleID(0))
SkDEBUGPARAMS(fContourID(0))
SkDEBUGPARAMS(fPtTID(0))
@@ -43,12 +41,11 @@ public:
SkDEBUGPARAMS(fSpanID(0)) {
}
-#if DEBUG_VALIDATE
enum Phase {
kIntersecting,
- kWalking
+ kWalking,
+ kFixWinding,
};
-#endif
enum {
kMaxWindingTries = 10
@@ -93,11 +90,9 @@ public:
}
#endif
-#if DEBUG_VALIDATE
Phase phase() const {
return fPhase;
}
-#endif
void setAngleCoincidence() {
fAngleCoincidence = true;
@@ -107,12 +102,10 @@ public:
fContourHead = contourHead;
}
-#if DEBUG_VALIDATE
void setPhase(Phase phase) {
SkASSERT(fPhase != phase);
fPhase = phase;
}
-#endif
// called in very rare cases where angles are sorted incorrectly -- signfies op will fail
void setWindingFailed() {
@@ -128,9 +121,7 @@ private:
SkOpContourHead* fContourHead;
bool fWindingFailed;
bool fAngleCoincidence;
-#if DEBUG_VALIDATE
Phase fPhase;
-#endif
#ifdef SK_DEBUG
int fAngleID;
int fContourID;
diff --git a/src/pathops/SkPathOpsWinding.cpp b/src/pathops/SkPathOpsWinding.cpp
index 53083e484e..4a7b1bc3eb 100644
--- a/src/pathops/SkPathOpsWinding.cpp
+++ b/src/pathops/SkPathOpsWinding.cpp
@@ -342,8 +342,12 @@ bool SkOpSpan::sortableTop(SkOpContour* contourHead) {
#endif
}
if (sumSet) {
- (void) hitSegment->markAndChaseWinding(span, span->next(), windSum, oppSum, NULL);
- (void) hitSegment->markAndChaseWinding(span->next(), span, windSum, oppSum, NULL);
+ if (this->globalState()->phase() == SkOpGlobalState::kFixWinding) {
+ hitSegment->contour()->setCcw(ccw);
+ } else {
+ (void) hitSegment->markAndChaseWinding(span, span->next(), windSum, oppSum, NULL);
+ (void) hitSegment->markAndChaseWinding(span->next(), span, windSum, oppSum, NULL);
+ }
}
if (operand) {
SkTSwap(wind, oppWind);
diff --git a/tests/PathOpsBuilderTest.cpp b/tests/PathOpsBuilderTest.cpp
index 8ab92c3124..ea4c567e62 100644
--- a/tests/PathOpsBuilderTest.cpp
+++ b/tests/PathOpsBuilderTest.cpp
@@ -33,8 +33,10 @@ DEF_TEST(PathOpsBuilder, reporter) {
SkPath::Direction dir;
REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
REPORTER_ASSERT(reporter, closed);
- REPORTER_ASSERT(reporter, dir == SkPath::kCW_Direction);
- REPORTER_ASSERT(reporter, rectPath == result);
+ REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
+ SkBitmap bitmap;
+ int pixelDiff = comparePaths(reporter, __FUNCTION__, rectPath, result, bitmap);
+ REPORTER_ASSERT(reporter, pixelDiff == 0);
rectPath.reset();
rectPath.setFillType(SkPath::kEvenOdd_FillType);
@@ -44,7 +46,7 @@ DEF_TEST(PathOpsBuilder, reporter) {
REPORTER_ASSERT(reporter, result.isRect(NULL, &closed, &dir));
REPORTER_ASSERT(reporter, closed);
REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
- REPORTER_ASSERT(reporter, rectPath == result);
+ REPORTER_ASSERT(reporter, rectPath == result);
builder.add(rectPath, kDifference_SkPathOp);
REPORTER_ASSERT(reporter, builder.resolve(&result));
@@ -74,12 +76,11 @@ DEF_TEST(PathOpsBuilder, reporter) {
builder.add(circle2, kUnion_SkPathOp);
builder.add(circle3, kDifference_SkPathOp);
REPORTER_ASSERT(reporter, builder.resolve(&result));
- SkBitmap bitmap;
- int pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result, bitmap);
+ pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result, bitmap);
REPORTER_ASSERT(reporter, pixelDiff == 0);
}
-DEF_TEST(Issue3838, reporter) {
+DEF_TEST(BuilderIssue3838, reporter) {
SkPath path;
path.moveTo(200, 170);
path.lineTo(220, 170);
@@ -93,9 +94,6 @@ DEF_TEST(Issue3838, reporter) {
path.lineTo(200, 250);
path.lineTo(200, 170);
path.close();
- testSimplify(reporter, path, __FUNCTION__);
- SkPath path3;
- Simplify(path, &path3);
SkPath path2;
SkOpBuilder builder;
builder.add(path, kUnion_SkPathOp);
@@ -104,3 +102,18 @@ DEF_TEST(Issue3838, reporter) {
int pixelDiff = comparePaths(reporter, __FUNCTION__, path, path2, bitmap);
REPORTER_ASSERT(reporter, pixelDiff == 0);
}
+
+DEF_TEST(BuilderIssue3838_2, reporter) {
+ SkPath path;
+ path.addCircle(100, 100, 50);
+
+ SkOpBuilder builder;
+ builder.add(path, kUnion_SkPathOp);
+ builder.add(path, kUnion_SkPathOp);
+
+ SkPath result;
+ SkBitmap bitmap;
+ builder.resolve(&result);
+ int pixelDiff = comparePaths(reporter, __FUNCTION__, path, result, bitmap);
+ REPORTER_ASSERT(reporter, pixelDiff == 0);
+}