caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2012 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | #include "Simplify.h" |
| 8 | |
| 9 | namespace Op { |
| 10 | |
| 11 | #include "Simplify.cpp" |
| 12 | |
| 13 | static bool windingIsActive(int winding, int spanWinding, int oppWinding, |
| 14 | const ShapeOp op) { |
| 15 | return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding) |
| 16 | && (!winding || !spanWinding || winding == -spanWinding); |
| 17 | } |
| 18 | |
skia.committer@gmail.com | 055c7c2 | 2012-09-15 02:01:41 +0000 | [diff] [blame] | 19 | static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, |
caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 20 | const int aXorMask, const int bXorMask, SkPath& simple) { |
| 21 | bool firstContour = true; |
| 22 | do { |
| 23 | Segment* topStart = findTopContour(contourList); |
| 24 | if (!topStart) { |
| 25 | break; |
| 26 | } |
| 27 | // Start at the top. Above the top is outside, below is inside. |
| 28 | // follow edges to intersection by changing the index by direction. |
| 29 | int index, endIndex; |
| 30 | Segment* current = topStart->findTop(index, endIndex); |
| 31 | int contourWinding; |
| 32 | if (firstContour) { |
| 33 | contourWinding = 0; |
| 34 | firstContour = false; |
| 35 | } else { |
| 36 | int sumWinding = current->windSum(SkMin32(index, endIndex)); |
| 37 | // FIXME: don't I have to adjust windSum to get contourWinding? |
| 38 | if (sumWinding == SK_MinS32) { |
| 39 | sumWinding = current->computeSum(index, endIndex); |
| 40 | } |
| 41 | if (sumWinding == SK_MinS32) { |
| 42 | contourWinding = innerContourCheck(contourList, current, |
| 43 | index, endIndex); |
| 44 | } else { |
| 45 | contourWinding = sumWinding; |
| 46 | int spanWinding = current->spanSign(index, endIndex); |
| 47 | bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding); |
| 48 | if (inner) { |
| 49 | contourWinding -= spanWinding; |
| 50 | } |
| 51 | #if DEBUG_WINDING |
| 52 | SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__, |
| 53 | sumWinding, spanWinding, SkSign32(index - endIndex), |
| 54 | inner, contourWinding); |
| 55 | #endif |
| 56 | } |
| 57 | #if DEBUG_WINDING |
| 58 | // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding)); |
| 59 | SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding); |
| 60 | #endif |
| 61 | } |
| 62 | SkPoint lastPt; |
| 63 | int winding = contourWinding; |
| 64 | int spanWinding = current->spanSign(index, endIndex); |
| 65 | int oppWinding = current->oppSign(index, endIndex); |
| 66 | bool active = windingIsActive(winding, spanWinding, oppWinding, op); |
| 67 | SkTDArray<Span*> chaseArray; |
caryclark@google.com | c91dfe4 | 2012-10-16 12:06:27 +0000 | [diff] [blame^] | 68 | bool unsortable = false; |
caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 69 | do { |
| 70 | #if DEBUG_WINDING |
| 71 | SkDebugf("%s active=%s winding=%d spanWinding=%d\n", |
| 72 | __FUNCTION__, active ? "true" : "false", |
| 73 | winding, spanWinding); |
| 74 | #endif |
| 75 | const SkPoint* firstPt = NULL; |
| 76 | do { |
| 77 | SkASSERT(!current->done()); |
| 78 | int nextStart = index; |
| 79 | int nextEnd = endIndex; |
| 80 | Segment* next = current->findNextOp(chaseArray, active, |
caryclark@google.com | c91dfe4 | 2012-10-16 12:06:27 +0000 | [diff] [blame^] | 81 | nextStart, nextEnd, winding, spanWinding, unsortable, op, |
caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 82 | aXorMask, bXorMask); |
| 83 | if (!next) { |
caryclark@google.com | c91dfe4 | 2012-10-16 12:06:27 +0000 | [diff] [blame^] | 84 | // FIXME: if unsortable, allow partial paths to be later |
| 85 | // assembled |
| 86 | SkASSERT(!unsortable); |
caryclark@google.com | 235f56a | 2012-09-14 14:19:30 +0000 | [diff] [blame] | 87 | if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) { |
| 88 | lastPt = current->addCurveTo(index, endIndex, simple, true); |
| 89 | SkASSERT(*firstPt == lastPt); |
| 90 | } |
| 91 | break; |
| 92 | } |
| 93 | if (!firstPt) { |
| 94 | firstPt = ¤t->addMoveTo(index, simple, active); |
| 95 | } |
| 96 | lastPt = current->addCurveTo(index, endIndex, simple, active); |
| 97 | current = next; |
| 98 | index = nextStart; |
| 99 | endIndex = nextEnd; |
| 100 | } while (*firstPt != lastPt && (active || !current->done())); |
| 101 | if (firstPt && active) { |
| 102 | #if DEBUG_PATH_CONSTRUCTION |
| 103 | SkDebugf("%s close\n", __FUNCTION__); |
| 104 | #endif |
| 105 | simple.close(); |
| 106 | } |
| 107 | current = findChase(chaseArray, index, endIndex, contourWinding); |
| 108 | #if DEBUG_ACTIVE_SPANS |
| 109 | debugShowActiveSpans(contourList); |
| 110 | #endif |
| 111 | if (!current) { |
| 112 | break; |
| 113 | } |
| 114 | int lesser = SkMin32(index, endIndex); |
| 115 | spanWinding = current->spanSign(index, endIndex); |
| 116 | winding = current->windSum(lesser); |
| 117 | bool inner = useInnerWinding(winding - spanWinding, winding); |
| 118 | #if DEBUG_WINDING |
| 119 | SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d" |
| 120 | " inner=%d result=%d\n", |
| 121 | __FUNCTION__, current->debugID(), current->t(lesser), |
| 122 | spanWinding, winding, SkSign32(index - endIndex), |
| 123 | useInnerWinding(winding - spanWinding, winding), |
| 124 | inner ? winding - spanWinding : winding); |
| 125 | #endif |
| 126 | if (inner) { |
| 127 | winding -= spanWinding; |
| 128 | } |
| 129 | int oppWinding = current->oppSign(index, endIndex); |
| 130 | active = windingIsActive(winding, spanWinding, oppWinding, op); |
| 131 | } while (true); |
| 132 | } while (true); |
| 133 | } |
| 134 | |
| 135 | } // end of Op namespace |
| 136 | |
| 137 | |
| 138 | void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) { |
| 139 | result.reset(); |
| 140 | result.setFillType(SkPath::kEvenOdd_FillType); |
| 141 | // turn path into list of segments |
| 142 | SkTArray<Op::Contour> contours; |
| 143 | // FIXME: add self-intersecting cubics' T values to segment |
| 144 | Op::EdgeBuilder builder(one, contours); |
| 145 | const int aXorMask = builder.xorMask(); |
| 146 | builder.addOperand(two); |
| 147 | const int bXorMask = builder.xorMask(); |
| 148 | builder.finish(); |
| 149 | SkTDArray<Op::Contour*> contourList; |
| 150 | makeContourList(contours, contourList); |
| 151 | Op::Contour** currentPtr = contourList.begin(); |
| 152 | if (!currentPtr) { |
| 153 | return; |
| 154 | } |
| 155 | Op::Contour** listEnd = contourList.end(); |
| 156 | // find all intersections between segments |
| 157 | do { |
| 158 | Op::Contour** nextPtr = currentPtr; |
| 159 | Op::Contour* current = *currentPtr++; |
| 160 | Op::Contour* next; |
| 161 | do { |
| 162 | next = *nextPtr++; |
| 163 | } while (addIntersectTs(current, next) && nextPtr != listEnd); |
| 164 | } while (currentPtr != listEnd); |
| 165 | // eat through coincident edges |
| 166 | coincidenceCheck(contourList); |
| 167 | fixOtherTIndex(contourList); |
| 168 | // construct closed contours |
| 169 | bridgeOp(contourList, op, aXorMask, bXorMask, result); |
| 170 | } |