| Index: src/pathops/SkPathOpsOp.cpp
|
| ===================================================================
|
| --- src/pathops/SkPathOpsOp.cpp (revision 0)
|
| +++ src/pathops/SkPathOpsOp.cpp (revision 0)
|
| @@ -0,0 +1,272 @@
|
| +/*
|
| + * 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"
|
| +
|
| +// 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 SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int& nextStart, int& nextEnd) {
|
| + while (chase.count()) {
|
| + SkOpSpan* span;
|
| + chase.pop(&span);
|
| + const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
|
| + SkOpSegment* segment = backPtr.fOther;
|
| + nextStart = backPtr.fOtherIndex;
|
| + SkTDArray<SkOpAngle> angles;
|
| + int done = 0;
|
| + if (segment->activeAngle(nextStart, done, angles)) {
|
| + SkOpAngle* last = angles.end() - 1;
|
| + nextStart = last->start();
|
| + nextEnd = last->end();
|
| + #if TRY_ROTATE
|
| + *chase.insert(0) = span;
|
| + #else
|
| + *chase.append() = span;
|
| + #endif
|
| + return last->segment();
|
| + }
|
| + if (done == angles.count()) {
|
| + continue;
|
| + }
|
| + SkTDArray<SkOpAngle*> sorted;
|
| + bool sortable = SkOpSegment::SortAngles(angles, sorted);
|
| + int angleCount = sorted.count();
|
| +#if DEBUG_SORT
|
| + sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0);
|
| +#endif
|
| + if (!sortable) {
|
| + continue;
|
| + }
|
| + // find first angle, initialize winding to computed fWindSum
|
| + int firstIndex = -1;
|
| + const SkOpAngle* angle;
|
| + do {
|
| + angle = sorted[++firstIndex];
|
| + segment = angle->segment();
|
| + } while (segment->windSum(angle) == SK_MinS32);
|
| + #if DEBUG_SORT
|
| + segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
|
| + #endif
|
| + int sumMiWinding = segment->updateWindingReverse(angle);
|
| + int sumSuWinding = segment->updateOppWindingReverse(angle);
|
| + if (segment->operand()) {
|
| + SkTSwap<int>(sumMiWinding, sumSuWinding);
|
| + }
|
| + int nextIndex = firstIndex + 1;
|
| + int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
|
| + SkOpSegment* first = NULL;
|
| + do {
|
| + SkASSERT(nextIndex != firstIndex);
|
| + if (nextIndex == angleCount) {
|
| + nextIndex = 0;
|
| + }
|
| + angle = sorted[nextIndex];
|
| + segment = angle->segment();
|
| + int start = angle->start();
|
| + int end = angle->end();
|
| + int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
|
| + segment->setUpWindings(start, end, sumMiWinding, sumSuWinding,
|
| + maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
|
| + if (!segment->done(angle)) {
|
| + if (!first) {
|
| + first = segment;
|
| + nextStart = start;
|
| + nextEnd = end;
|
| + }
|
| + (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
|
| + oppSumWinding, true, angle);
|
| + }
|
| + } while (++nextIndex != lastIndex);
|
| + if (first) {
|
| + #if TRY_ROTATE
|
| + *chase.insert(0) = span;
|
| + #else
|
| + *chase.append() = span;
|
| + #endif
|
| + return first;
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +/*
|
| +static bool windingIsActive(int winding, int oppWinding, int spanWinding, int oppSpanWinding,
|
| + bool windingIsOp, PathOp op) {
|
| + bool active = windingIsActive(winding, spanWinding);
|
| + if (!active) {
|
| + return false;
|
| + }
|
| + if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) {
|
| + 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);
|
| + }
|
| + }
|
| + bool opActive = oppWinding != 0;
|
| + return gOpLookup[op][opActive][windingIsOp];
|
| +}
|
| +*/
|
| +
|
| +static bool bridgeOp(SkTDArray<SkOpContour*>& contourList, const SkPathOp op,
|
| + const int xorMask, const int xorOpMask, SkPathWriter& simple) {
|
| + bool firstContour = true;
|
| + bool unsortable = false;
|
| + bool topUnsortable = false;
|
| + SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
|
| + do {
|
| + int index, endIndex;
|
| + bool done;
|
| + SkOpSegment* current = FindSortableTop(contourList, firstContour, index, endIndex, topLeft,
|
| + topUnsortable, done, true);
|
| + if (!current) {
|
| + if (topUnsortable || !done) {
|
| + 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->activeOp(index, endIndex, xorMask, xorOpMask, op)) {
|
| + 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->findNextOp(chaseArray, nextStart, nextEnd,
|
| + unsortable, op, xorMask, xorOpMask);
|
| + 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->markDoneBinary(min);
|
| + }
|
| + }
|
| + simple.close();
|
| + } else {
|
| + SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex);
|
| + if (last && !last->fLoop) {
|
| + *chaseArray.append() = last;
|
| + }
|
| + }
|
| + current = findChaseOp(chaseArray, index, endIndex);
|
| + #if DEBUG_ACTIVE_SPANS
|
| + DebugShowActiveSpans(contourList);
|
| + #endif
|
| + if (!current) {
|
| + break;
|
| + }
|
| + } while (true);
|
| + } while (true);
|
| + return simple.someAssemblyRequired();
|
| +}
|
| +
|
| +void Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
|
| +#if DEBUG_SORT || DEBUG_SWAP_TOP
|
| + gDebugSortCount = gDebugSortCountDefault;
|
| +#endif
|
| + result->reset();
|
| + result->setFillType(SkPath::kEvenOdd_FillType);
|
| + // turn path into list of segments
|
| + SkTArray<SkOpContour> contours;
|
| + // FIXME: add self-intersecting cubics' T values to segment
|
| + SkOpEdgeBuilder builder(one, contours);
|
| + const int xorMask = builder.xorMask();
|
| + builder.addOperand(two);
|
| + builder.finish();
|
| + const int xorOpMask = builder.xorMask();
|
| + SkTDArray<SkOpContour*> contourList;
|
| + MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask,
|
| + xorOpMask == kEvenOdd_PathOpsMask);
|
| + 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
|
| +
|
| + int total = 0;
|
| + int index;
|
| + for (index = 0; index < contourList.count(); ++index) {
|
| + total += contourList[index]->segments().count();
|
| + }
|
| +#if DEBUG_SHOW_WINDING
|
| + SkOpContour::debugShowWindingValues(contourList);
|
| +#endif
|
| + CoincidenceCheck(contourList, total);
|
| +#if DEBUG_SHOW_WINDING
|
| + SkOpContour::debugShowWindingValues(contourList);
|
| +#endif
|
| + FixOtherTIndex(contourList);
|
| + SortSegments(contourList);
|
| +#if DEBUG_ACTIVE_SPANS
|
| + DebugShowActiveSpans(contourList);
|
| +#endif
|
| + // construct closed contours
|
| + SkPathWriter wrapper(*result);
|
| + bridgeOp(contourList, op, xorMask, xorOpMask, wrapper);
|
| + { // if some edges could not be resolved, assemble remaining fragments
|
| + SkPath temp;
|
| + temp.setFillType(SkPath::kEvenOdd_FillType);
|
| + SkPathWriter assembled(temp);
|
| + Assemble(wrapper, assembled);
|
| + *result = *assembled.nativePath();
|
| + }
|
| +}
|
|
|