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