Index: src/pathops/SkPathOpsSimplify.cpp |
=================================================================== |
--- src/pathops/SkPathOpsSimplify.cpp (revision 0) |
+++ src/pathops/SkPathOpsSimplify.cpp (revision 0) |
@@ -0,0 +1,195 @@ |
+/* |
+ * Copyright 2012 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+#include "SkAddIntersections.h" |
+#include "SkOpEdgeBuilder.h" |
+#include "SkPathOpsCommon.h" |
+#include "SkPathWriter.h" |
+ |
+static bool bridgeWinding(SkTDArray<SkOpContour*>& contourList, SkPathWriter& simple) { |
+ bool firstContour = true; |
+ bool unsortable = false; |
+ bool topUnsortable = false; |
+ SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; |
+ do { |
+ int index, endIndex; |
+ bool topDone; |
+ SkOpSegment* current = FindSortableTop(contourList, firstContour, index, endIndex, topLeft, |
+ topUnsortable, topDone, false); |
+ if (!current) { |
+ if (topUnsortable || !topDone) { |
+ topUnsortable = false; |
+ SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin); |
+ topLeft.fX = topLeft.fY = SK_ScalarMin; |
+ continue; |
+ } |
+ break; |
+ } |
+ SkTDArray<SkOpSpan*> chaseArray; |
+ do { |
+ if (current->activeWinding(index, endIndex)) { |
+ do { |
+ #if DEBUG_ACTIVE_SPANS |
+ if (!unsortable && current->done()) { |
+ DebugShowActiveSpans(contourList); |
+ } |
+ #endif |
+ SkASSERT(unsortable || !current->done()); |
+ int nextStart = index; |
+ int nextEnd = endIndex; |
+ SkOpSegment* next = current->findNextWinding(chaseArray, nextStart, nextEnd, |
+ unsortable); |
+ if (!next) { |
+ if (!unsortable && simple.hasMove() |
+ && current->verb() != SkPath::kLine_Verb |
+ && !simple.isClosed()) { |
+ current->addCurveTo(index, endIndex, simple, true); |
+ SkASSERT(simple.isClosed()); |
+ } |
+ break; |
+ } |
+ #if DEBUG_FLOW |
+ SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__, |
+ current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY, |
+ current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY); |
+ #endif |
+ current->addCurveTo(index, endIndex, simple, true); |
+ current = next; |
+ index = nextStart; |
+ endIndex = nextEnd; |
+ } while (!simple.isClosed() && (!unsortable |
+ || !current->done(SkMin32(index, endIndex)))); |
+ if (current->activeWinding(index, endIndex) && !simple.isClosed()) { |
+ SkASSERT(unsortable); |
+ int min = SkMin32(index, endIndex); |
+ if (!current->done(min)) { |
+ current->addCurveTo(index, endIndex, simple, true); |
+ current->markDoneUnary(min); |
+ } |
+ } |
+ simple.close(); |
+ } else { |
+ SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex); |
+ if (last && !last->fLoop) { |
+ *chaseArray.append() = last; |
+ } |
+ } |
+ current = FindChase(chaseArray, index, endIndex); |
+ #if DEBUG_ACTIVE_SPANS |
+ DebugShowActiveSpans(contourList); |
+ #endif |
+ if (!current) { |
+ break; |
+ } |
+ } while (true); |
+ } while (true); |
+ return simple.someAssemblyRequired(); |
+} |
+ |
+// returns true if all edges were processed |
+static bool bridgeXor(SkTDArray<SkOpContour*>& contourList, SkPathWriter& simple) { |
+ SkOpSegment* current; |
+ int start, end; |
+ bool unsortable = false; |
+ bool closable = true; |
+ while ((current = FindUndone(contourList, start, end))) { |
+ do { |
+ #if DEBUG_ACTIVE_SPANS |
+ if (!unsortable && current->done()) { |
+ DebugShowActiveSpans(contourList); |
+ } |
+ #endif |
+ SkASSERT(unsortable || !current->done()); |
+ int nextStart = start; |
+ int nextEnd = end; |
+ SkOpSegment* next = current->findNextXor(nextStart, nextEnd, unsortable); |
+ if (!next) { |
+ if (!unsortable && simple.hasMove() |
+ && current->verb() != SkPath::kLine_Verb |
+ && !simple.isClosed()) { |
+ current->addCurveTo(start, end, simple, true); |
+ SkASSERT(simple.isClosed()); |
+ } |
+ break; |
+ } |
+ #if DEBUG_FLOW |
+ SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__, |
+ current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY, |
+ current->xyAtT(end).fX, current->xyAtT(end).fY); |
+ #endif |
+ current->addCurveTo(start, end, simple, true); |
+ current = next; |
+ start = nextStart; |
+ end = nextEnd; |
+ } while (!simple.isClosed() && (!unsortable || !current->done(SkMin32(start, end)))); |
+ if (!simple.isClosed()) { |
+ SkASSERT(unsortable); |
+ int min = SkMin32(start, end); |
+ if (!current->done(min)) { |
+ current->addCurveTo(start, end, simple, true); |
+ current->markDone(min, 1); |
+ } |
+ closable = false; |
+ } |
+ simple.close(); |
+ #if DEBUG_ACTIVE_SPANS |
+ DebugShowActiveSpans(contourList); |
+ #endif |
+ } |
+ return closable; |
+} |
+ |
+// FIXME : add this as a member of SkPath |
+void Simplify(const SkPath& path, SkPath* result) { |
+#if DEBUG_SORT || DEBUG_SWAP_TOP |
+ gDebugSortCount = gDebugSortCountDefault; |
+#endif |
+ // returns 1 for evenodd, -1 for winding, regardless of inverse-ness |
+ result->reset(); |
+ result->setFillType(SkPath::kEvenOdd_FillType); |
+ SkPathWriter simple(*result); |
+ |
+ // turn path into list of segments |
+ SkTArray<SkOpContour> contours; |
+ SkOpEdgeBuilder builder(path, contours); |
+ builder.finish(); |
+ SkTDArray<SkOpContour*> contourList; |
+ MakeContourList(contours, contourList, false, false); |
+ SkOpContour** currentPtr = contourList.begin(); |
+ if (!currentPtr) { |
+ return; |
+ } |
+ SkOpContour** listEnd = contourList.end(); |
+ // find all intersections between segments |
+ do { |
+ SkOpContour** nextPtr = currentPtr; |
+ SkOpContour* current = *currentPtr++; |
+ if (current->containsCubics()) { |
+ AddSelfIntersectTs(current); |
+ } |
+ SkOpContour* next; |
+ do { |
+ next = *nextPtr++; |
+ } while (AddIntersectTs(current, next) && nextPtr != listEnd); |
+ } while (currentPtr != listEnd); |
+ // eat through coincident edges |
+ CoincidenceCheck(contourList, 0); |
+ FixOtherTIndex(contourList); |
+ SortSegments(contourList); |
+#if DEBUG_ACTIVE_SPANS |
+ DebugShowActiveSpans(contourList); |
+#endif |
+ // construct closed contours |
+ if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, simple) |
+ : !bridgeXor(contourList, simple)) |
+ { // if some edges could not be resolved, assemble remaining fragments |
+ SkPath temp; |
+ temp.setFillType(SkPath::kEvenOdd_FillType); |
+ SkPathWriter assembled(temp); |
+ Assemble(simple, assembled); |
+ *result = *assembled.nativePath(); |
+ } |
+} |