| OLD | NEW |
| (Empty) |
| 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 | |
| 8 #include "Simplify.h" | |
| 9 | |
| 10 namespace Op { | |
| 11 | |
| 12 #define INCLUDED_BY_SHAPE_OPS 1 | |
| 13 | |
| 14 #include "Simplify.cpp" | |
| 15 | |
| 16 // FIXME: this and find chase should be merge together, along with | |
| 17 // other code that walks winding in angles | |
| 18 // OPTIMIZATION: Probably, the walked winding should be rolled into the angle st
ructure | |
| 19 // so it isn't duplicated by walkers like this one | |
| 20 static Segment* findChaseOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEn
d) { | |
| 21 while (chase.count()) { | |
| 22 Span* span; | |
| 23 chase.pop(&span); | |
| 24 const Span& backPtr = span->fOther->span(span->fOtherIndex); | |
| 25 Segment* segment = backPtr.fOther; | |
| 26 nextStart = backPtr.fOtherIndex; | |
| 27 SkTDArray<Angle> angles; | |
| 28 int done = 0; | |
| 29 if (segment->activeAngle(nextStart, done, angles)) { | |
| 30 Angle* last = angles.end() - 1; | |
| 31 nextStart = last->start(); | |
| 32 nextEnd = last->end(); | |
| 33 #if TRY_ROTATE | |
| 34 *chase.insert(0) = span; | |
| 35 #else | |
| 36 *chase.append() = span; | |
| 37 #endif | |
| 38 return last->segment(); | |
| 39 } | |
| 40 if (done == angles.count()) { | |
| 41 continue; | |
| 42 } | |
| 43 SkTDArray<Angle*> sorted; | |
| 44 bool sortable = Segment::SortAngles(angles, sorted); | |
| 45 int angleCount = sorted.count(); | |
| 46 #if DEBUG_SORT | |
| 47 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0); | |
| 48 #endif | |
| 49 if (!sortable) { | |
| 50 continue; | |
| 51 } | |
| 52 // find first angle, initialize winding to computed fWindSum | |
| 53 int firstIndex = -1; | |
| 54 const Angle* angle; | |
| 55 do { | |
| 56 angle = sorted[++firstIndex]; | |
| 57 segment = angle->segment(); | |
| 58 } while (segment->windSum(angle) == SK_MinS32); | |
| 59 #if DEBUG_SORT | |
| 60 segment->debugShowSort(__FUNCTION__, sorted, firstIndex); | |
| 61 #endif | |
| 62 int sumMiWinding = segment->updateWindingReverse(angle); | |
| 63 int sumSuWinding = segment->updateOppWindingReverse(angle); | |
| 64 if (segment->operand()) { | |
| 65 SkTSwap<int>(sumMiWinding, sumSuWinding); | |
| 66 } | |
| 67 int nextIndex = firstIndex + 1; | |
| 68 int lastIndex = firstIndex != 0 ? firstIndex : angleCount; | |
| 69 Segment* first = NULL; | |
| 70 do { | |
| 71 SkASSERT(nextIndex != firstIndex); | |
| 72 if (nextIndex == angleCount) { | |
| 73 nextIndex = 0; | |
| 74 } | |
| 75 angle = sorted[nextIndex]; | |
| 76 segment = angle->segment(); | |
| 77 int start = angle->start(); | |
| 78 int end = angle->end(); | |
| 79 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; | |
| 80 segment->setUpWindings(start, end, sumMiWinding, sumSuWinding, | |
| 81 maxWinding, sumWinding, oppMaxWinding, oppSumWinding); | |
| 82 if (!segment->done(angle)) { | |
| 83 if (!first) { | |
| 84 first = segment; | |
| 85 nextStart = start; | |
| 86 nextEnd = end; | |
| 87 } | |
| 88 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding, | |
| 89 oppSumWinding, true, angle); | |
| 90 } | |
| 91 } while (++nextIndex != lastIndex); | |
| 92 if (first) { | |
| 93 #if TRY_ROTATE | |
| 94 *chase.insert(0) = span; | |
| 95 #else | |
| 96 *chase.append() = span; | |
| 97 #endif | |
| 98 return first; | |
| 99 } | |
| 100 } | |
| 101 return NULL; | |
| 102 } | |
| 103 | |
| 104 /* | |
| 105 static bool windingIsActive(int winding, int oppWinding, int spanWinding, int op
pSpanWinding, | |
| 106 bool windingIsOp, ShapeOp op) { | |
| 107 bool active = windingIsActive(winding, spanWinding); | |
| 108 if (!active) { | |
| 109 return false; | |
| 110 } | |
| 111 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) { | |
| 112 switch (op) { | |
| 113 case kIntersect_Op: | |
| 114 case kUnion_Op: | |
| 115 return true; | |
| 116 case kDifference_Op: { | |
| 117 int absSpan = abs(spanWinding); | |
| 118 int absOpp = abs(oppSpanWinding); | |
| 119 return windingIsOp ? absSpan < absOpp : absSpan > absOpp; | |
| 120 } | |
| 121 case kXor_Op: | |
| 122 return spanWinding != oppSpanWinding; | |
| 123 default: | |
| 124 SkASSERT(0); | |
| 125 } | |
| 126 } | |
| 127 bool opActive = oppWinding != 0; | |
| 128 return gOpLookup[op][opActive][windingIsOp]; | |
| 129 } | |
| 130 */ | |
| 131 | |
| 132 static bool bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op, | |
| 133 const int xorMask, const int xorOpMask, PathWrapper& simple) { | |
| 134 bool firstContour = true; | |
| 135 bool unsortable = false; | |
| 136 bool topUnsortable = false; | |
| 137 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; | |
| 138 do { | |
| 139 int index, endIndex; | |
| 140 bool done; | |
| 141 Segment* current = findSortableTop(contourList, firstContour, index, end
Index, topLeft, | |
| 142 topUnsortable, done, true); | |
| 143 if (!current) { | |
| 144 if (topUnsortable || !done) { | |
| 145 topUnsortable = false; | |
| 146 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMi
n); | |
| 147 topLeft.fX = topLeft.fY = SK_ScalarMin; | |
| 148 continue; | |
| 149 } | |
| 150 break; | |
| 151 } | |
| 152 SkTDArray<Span*> chaseArray; | |
| 153 do { | |
| 154 if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) { | |
| 155 do { | |
| 156 #if DEBUG_ACTIVE_SPANS | |
| 157 if (!unsortable && current->done()) { | |
| 158 debugShowActiveSpans(contourList); | |
| 159 } | |
| 160 #endif | |
| 161 SkASSERT(unsortable || !current->done()); | |
| 162 int nextStart = index; | |
| 163 int nextEnd = endIndex; | |
| 164 Segment* next = current->findNextOp(chaseArray, nextStart, n
extEnd, | |
| 165 unsortable, op, xorMask, xorOpMask); | |
| 166 if (!next) { | |
| 167 if (!unsortable && simple.hasMove() | |
| 168 && current->verb() != SkPath::kLine_Verb | |
| 169 && !simple.isClosed()) { | |
| 170 current->addCurveTo(index, endIndex, simple, true); | |
| 171 SkASSERT(simple.isClosed()); | |
| 172 } | |
| 173 break; | |
| 174 } | |
| 175 #if DEBUG_FLOW | |
| 176 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", _
_FUNCTION__, | |
| 177 current->debugID(), current->xyAtT(index).fX, current->xyAtT
(index).fY, | |
| 178 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY); | |
| 179 #endif | |
| 180 current->addCurveTo(index, endIndex, simple, true); | |
| 181 current = next; | |
| 182 index = nextStart; | |
| 183 endIndex = nextEnd; | |
| 184 } while (!simple.isClosed() && ((!unsortable) | |
| 185 || !current->done(SkMin32(index, endIndex)))); | |
| 186 if (current->activeWinding(index, endIndex) && !simple.isClosed(
)) { | |
| 187 SkASSERT(unsortable); | |
| 188 int min = SkMin32(index, endIndex); | |
| 189 if (!current->done(min)) { | |
| 190 current->addCurveTo(index, endIndex, simple, true); | |
| 191 current->markDoneBinary(min); | |
| 192 } | |
| 193 } | |
| 194 simple.close(); | |
| 195 } else { | |
| 196 Span* last = current->markAndChaseDoneBinary(index, endIndex); | |
| 197 if (last && !last->fLoop) { | |
| 198 *chaseArray.append() = last; | |
| 199 } | |
| 200 } | |
| 201 current = findChaseOp(chaseArray, index, endIndex); | |
| 202 #if DEBUG_ACTIVE_SPANS | |
| 203 debugShowActiveSpans(contourList); | |
| 204 #endif | |
| 205 if (!current) { | |
| 206 break; | |
| 207 } | |
| 208 } while (true); | |
| 209 } while (true); | |
| 210 return simple.someAssemblyRequired(); | |
| 211 } | |
| 212 | |
| 213 } // end of Op namespace | |
| 214 | |
| 215 | |
| 216 void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) { | |
| 217 #if DEBUG_SORT || DEBUG_SWAP_TOP | |
| 218 Op::gDebugSortCount = Op::gDebugSortCountDefault; | |
| 219 #endif | |
| 220 result.reset(); | |
| 221 result.setFillType(SkPath::kEvenOdd_FillType); | |
| 222 // turn path into list of segments | |
| 223 SkTArray<Op::Contour> contours; | |
| 224 // FIXME: add self-intersecting cubics' T values to segment | |
| 225 Op::EdgeBuilder builder(one, contours); | |
| 226 const int xorMask = builder.xorMask(); | |
| 227 builder.addOperand(two); | |
| 228 builder.finish(); | |
| 229 const int xorOpMask = builder.xorMask(); | |
| 230 SkTDArray<Op::Contour*> contourList; | |
| 231 makeContourList(contours, contourList, xorMask == kEvenOdd_Mask, | |
| 232 xorOpMask == kEvenOdd_Mask); | |
| 233 Op::Contour** currentPtr = contourList.begin(); | |
| 234 if (!currentPtr) { | |
| 235 return; | |
| 236 } | |
| 237 Op::Contour** listEnd = contourList.end(); | |
| 238 // find all intersections between segments | |
| 239 do { | |
| 240 Op::Contour** nextPtr = currentPtr; | |
| 241 Op::Contour* current = *currentPtr++; | |
| 242 if (current->containsCubics()) { | |
| 243 addSelfIntersectTs(current); | |
| 244 } | |
| 245 Op::Contour* next; | |
| 246 do { | |
| 247 next = *nextPtr++; | |
| 248 } while (addIntersectTs(current, next) && nextPtr != listEnd); | |
| 249 } while (currentPtr != listEnd); | |
| 250 // eat through coincident edges | |
| 251 | |
| 252 int total = 0; | |
| 253 int index; | |
| 254 for (index = 0; index < contourList.count(); ++index) { | |
| 255 total += contourList[index]->segments().count(); | |
| 256 } | |
| 257 #if DEBUG_SHOW_WINDING | |
| 258 Op::Contour::debugShowWindingValues(contourList); | |
| 259 #endif | |
| 260 coincidenceCheck(contourList, total); | |
| 261 #if DEBUG_SHOW_WINDING | |
| 262 Op::Contour::debugShowWindingValues(contourList); | |
| 263 #endif | |
| 264 fixOtherTIndex(contourList); | |
| 265 sortSegments(contourList); | |
| 266 #if DEBUG_ACTIVE_SPANS | |
| 267 debugShowActiveSpans(contourList); | |
| 268 #endif | |
| 269 // construct closed contours | |
| 270 Op::PathWrapper wrapper(result); | |
| 271 bridgeOp(contourList, op, xorMask, xorOpMask, wrapper); | |
| 272 { // if some edges could not be resolved, assemble remaining fragments | |
| 273 SkPath temp; | |
| 274 temp.setFillType(SkPath::kEvenOdd_FillType); | |
| 275 Op::PathWrapper assembled(temp); | |
| 276 assemble(wrapper, assembled); | |
| 277 result = *assembled.nativePath(); | |
| 278 } | |
| 279 } | |
| OLD | NEW |