/* * 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(SkTArray& contourList, SkPathWriter* simple) { bool firstContour = true; bool unsortable = false; bool topUnsortable = false; bool firstPass = true; SkPoint lastTopLeft; SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; do { int index, endIndex; bool topDone; bool onlyVertical = false; lastTopLeft = topLeft; SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour, &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass); if (!current) { if ((!topUnsortable || firstPass) && !topDone) { SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin); topLeft.fX = topLeft.fY = SK_ScalarMin; continue; } break; } else if (onlyVertical) { break; } firstPass = !topUnsortable || lastTopLeft != topLeft; SkTDArray chase; do { if (current->activeWinding(index, endIndex)) { do { if (!unsortable && current->done()) { break; } SkASSERT(unsortable || !current->done()); int nextStart = index; int nextEnd = endIndex; SkOpSegment* next = current->findNextWinding(&chase, &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 || simple->isEmpty()); 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->fChased && !last->fLoop) { last->fChased = true; SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); // assert that last isn't already in array *chase.append() = last; #if DEBUG_WINDING SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__, last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum, last->fSmall); #endif } } current = FindChase(&chase, &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(SkTArray& 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 bool Simplify(const SkPath& path, SkPath* result) { #if DEBUG_SORT || DEBUG_SWAP_TOP SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; #endif // returns 1 for evenodd, -1 for winding, regardless of inverse-ness SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType; // turn path into list of segments SkTArray contours; SkOpEdgeBuilder builder(path, contours); if (!builder.finish()) { return false; } SkTArray contourList; MakeContourList(contours, contourList, false, false); SkOpContour** currentPtr = contourList.begin(); result->reset(); result->setFillType(fillType); if (!currentPtr) { return true; } 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); if (!HandleCoincidence(&contourList, 0)) { return false; } // construct closed contours SkPathWriter simple(*result); 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(fillType); SkPathWriter assembled(temp); Assemble(simple, &assembled); *result = *assembled.nativePath(); result->setFillType(fillType); } return true; }