OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "SkAddIntersections.h" | 7 #include "SkAddIntersections.h" |
| 8 #include "SkOpCoincidence.h" |
8 #include "SkOpEdgeBuilder.h" | 9 #include "SkOpEdgeBuilder.h" |
9 #include "SkPathOpsCommon.h" | 10 #include "SkPathOpsCommon.h" |
10 #include "SkPathWriter.h" | 11 #include "SkPathWriter.h" |
11 | 12 |
12 static SkOpSegment* findChaseOp(SkTDArray<SkOpSpan*>& chase, int* tIndex, int* e
ndIndex) { | 13 static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase**
startPtr, |
| 14 SkOpSpanBase** endPtr) { |
13 while (chase.count()) { | 15 while (chase.count()) { |
14 SkOpSpan* span; | 16 SkOpSpanBase* span; |
15 chase.pop(&span); | 17 chase.pop(&span); |
16 const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex); | 18 // OPTIMIZE: prev makes this compatible with old code -- but is it neces
sary? |
17 SkOpSegment* segment = backPtr.fOther; | 19 *startPtr = span->ptT()->prev()->span(); |
18 *tIndex = backPtr.fOtherIndex; | 20 SkOpSegment* segment = (*startPtr)->segment(); |
19 bool sortable = true; | 21 bool sortable = true; |
20 bool done = true; | 22 bool done = true; |
21 *endIndex = -1; | 23 *endPtr = NULL; |
22 if (const SkOpAngle* last = segment->activeAngle(*tIndex, tIndex, endInd
ex, &done, | 24 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr,
&done, |
23 &sortable)) { | 25 &sortable)) { |
24 if (last->unorderable()) { | 26 if (last->unorderable()) { |
25 continue; | 27 continue; |
26 } | 28 } |
27 *tIndex = last->start(); | 29 *startPtr = last->start(); |
28 *endIndex = last->end(); | 30 *endPtr = last->end(); |
29 #if TRY_ROTATE | 31 #if TRY_ROTATE |
30 *chase.insert(0) = span; | 32 *chase.insert(0) = span; |
31 #else | 33 #else |
32 *chase.append() = span; | 34 *chase.append() = span; |
33 #endif | 35 #endif |
34 return last->segment(); | 36 return last->segment(); |
35 } | 37 } |
36 if (done) { | 38 if (done) { |
37 continue; | 39 continue; |
38 } | 40 } |
39 if (!sortable) { | 41 if (!sortable) { |
40 continue; | 42 continue; |
41 } | 43 } |
42 // find first angle, initialize winding to computed fWindSum | 44 // find first angle, initialize winding to computed fWindSum |
43 const SkOpAngle* angle = segment->spanToAngle(*tIndex, *endIndex); | 45 const SkOpAngle* angle = segment->spanToAngle(*startPtr, *endPtr); |
44 if (!angle) { | 46 if (!angle) { |
45 continue; | 47 continue; |
46 } | 48 } |
47 const SkOpAngle* firstAngle = angle; | 49 const SkOpAngle* firstAngle = angle; |
48 bool loop = false; | 50 bool loop = false; |
49 int winding = SK_MinS32; | 51 int winding = SK_MinS32; |
50 do { | 52 do { |
51 angle = angle->next(); | 53 angle = angle->next(); |
52 if (angle == firstAngle && loop) { | 54 if (angle == firstAngle && loop) { |
53 break; // if we get here, there's no winding, loop is unorder
able | 55 break; // if we get here, there's no winding, loop is unorder
able |
54 } | 56 } |
55 loop |= angle == firstAngle; | 57 loop |= angle == firstAngle; |
56 segment = angle->segment(); | 58 segment = angle->segment(); |
57 winding = segment->windSum(angle); | 59 winding = segment->windSum(angle); |
58 } while (winding == SK_MinS32); | 60 } while (winding == SK_MinS32); |
59 if (winding == SK_MinS32) { | 61 if (winding == SK_MinS32) { |
60 continue; | 62 continue; |
61 } | 63 } |
62 int sumMiWinding = segment->updateWindingReverse(angle); | 64 int sumMiWinding = segment->updateWindingReverse(angle); |
63 int sumSuWinding = segment->updateOppWindingReverse(angle); | 65 int sumSuWinding = segment->updateOppWindingReverse(angle); |
64 if (segment->operand()) { | 66 if (segment->operand()) { |
65 SkTSwap<int>(sumMiWinding, sumSuWinding); | 67 SkTSwap<int>(sumMiWinding, sumSuWinding); |
66 } | 68 } |
67 SkOpSegment* first = NULL; | 69 SkOpSegment* first = NULL; |
68 bool badData = false; | 70 firstAngle = angle; |
69 while ((angle = angle->next()) != firstAngle && !badData) { | 71 while ((angle = angle->next()) != firstAngle) { |
70 segment = angle->segment(); | 72 segment = angle->segment(); |
71 int start = angle->start(); | 73 SkOpSpanBase* start = angle->start(); |
72 int end = angle->end(); | 74 SkOpSpanBase* end = angle->end(); |
73 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; | 75 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding; |
74 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding, | 76 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding, |
75 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); | 77 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding); |
76 if (!segment->done(angle)) { | 78 if (!segment->done(angle)) { |
77 if (!first) { | 79 if (!first) { |
78 first = segment; | 80 first = segment; |
79 *tIndex = start; | 81 *startPtr = start; |
80 *endIndex = end; | 82 *endPtr = end; |
81 } | |
82 if (segment->inconsistentAngle(maxWinding, sumWinding, oppMaxWin
ding, | |
83 oppSumWinding, angle)) { | |
84 badData = true; | |
85 break; | |
86 } | 83 } |
87 // OPTIMIZATION: should this also add to the chase? | 84 // OPTIMIZATION: should this also add to the chase? |
88 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding, | 85 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding, |
89 oppSumWinding, angle); | 86 oppSumWinding, angle); |
90 } | 87 } |
91 } | 88 } |
92 if (badData) { | |
93 continue; | |
94 } | |
95 if (first) { | 89 if (first) { |
96 #if TRY_ROTATE | 90 #if TRY_ROTATE |
97 *chase.insert(0) = span; | 91 *chase.insert(0) = span; |
98 #else | 92 #else |
99 *chase.append() = span; | 93 *chase.append() = span; |
100 #endif | 94 #endif |
101 return first; | 95 return first; |
102 } | 96 } |
103 } | 97 } |
104 return NULL; | 98 return NULL; |
105 } | 99 } |
106 | 100 |
107 /* | 101 static bool bridgeOp(SkTDArray<SkOpContour* >& contourList, const SkPathOp op, |
108 static bool windingIsActive(int winding, int oppWinding, int spanWinding, int op
pSpanWinding, | 102 const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAll
oc* allocator) { |
109 bool windingIsOp, PathOp op) { | |
110 bool active = windingIsActive(winding, spanWinding); | |
111 if (!active) { | |
112 return false; | |
113 } | |
114 if (oppSpanWinding && windingIsActive(oppWinding, oppSpanWinding)) { | |
115 switch (op) { | |
116 case kIntersect_Op: | |
117 case kUnion_Op: | |
118 return true; | |
119 case kDifference_Op: { | |
120 int absSpan = abs(spanWinding); | |
121 int absOpp = abs(oppSpanWinding); | |
122 return windingIsOp ? absSpan < absOpp : absSpan > absOpp; | |
123 } | |
124 case kXor_Op: | |
125 return spanWinding != oppSpanWinding; | |
126 default: | |
127 SkASSERT(0); | |
128 } | |
129 } | |
130 bool opActive = oppWinding != 0; | |
131 return gOpLookup[op][opActive][windingIsOp]; | |
132 } | |
133 */ | |
134 | |
135 static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
p, | |
136 const int xorMask, const int xorOpMask, SkPathWriter* simple) { | |
137 bool firstContour = true; | 103 bool firstContour = true; |
138 bool unsortable = false; | 104 bool unsortable = false; |
139 bool topUnsortable = false; | 105 bool topUnsortable = false; |
140 bool firstPass = true; | 106 bool firstPass = true; |
141 SkPoint lastTopLeft; | 107 SkPoint lastTopLeft; |
142 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; | 108 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin}; |
143 do { | 109 do { |
144 int index, endIndex; | 110 SkOpSpanBase* start; |
| 111 SkOpSpanBase* end; |
145 bool topDone; | 112 bool topDone; |
146 bool onlyVertical = false; | 113 bool onlyVertical = false; |
147 lastTopLeft = topLeft; | 114 lastTopLeft = topLeft; |
148 SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kBinarySi
ngle, &firstContour, | 115 SkOpSegment* current = FindSortableTop(contourList, firstPass, SkOpAngle
::kBinarySingle, |
149 &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVert
ical, firstPass); | 116 &firstContour, &start, &end, &topLeft, &topUnsortable, &topDone,
&onlyVertical, |
| 117 allocator); |
150 if (!current) { | 118 if (!current) { |
151 if ((!topUnsortable || firstPass) && !topDone) { | 119 if ((!topUnsortable || firstPass) && !topDone) { |
152 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMi
n); | 120 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMi
n); |
153 if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_Scala
rMin) { | 121 if (lastTopLeft.fX == SK_ScalarMin && lastTopLeft.fY == SK_Scala
rMin) { |
154 if (firstPass) { | 122 if (firstPass) { |
155 firstPass = false; | 123 firstPass = false; |
156 } else { | 124 } else { |
157 break; | 125 break; |
158 } | 126 } |
159 } | 127 } |
160 topLeft.fX = topLeft.fY = SK_ScalarMin; | 128 topLeft.fX = topLeft.fY = SK_ScalarMin; |
161 continue; | 129 continue; |
162 } | 130 } |
163 break; | 131 break; |
164 } else if (onlyVertical) { | 132 } else if (onlyVertical) { |
165 break; | 133 break; |
166 } | 134 } |
167 firstPass = !topUnsortable || lastTopLeft != topLeft; | 135 firstPass = !topUnsortable || lastTopLeft != topLeft; |
168 SkTDArray<SkOpSpan*> chase; | 136 SkTDArray<SkOpSpanBase*> chase; |
169 do { | 137 do { |
170 if (current->activeOp(index, endIndex, xorMask, xorOpMask, op)) { | 138 if (current->activeOp(start, end, xorMask, xorOpMask, op)) { |
171 do { | 139 do { |
172 if (!unsortable && current->done()) { | 140 if (!unsortable && current->done()) { |
173 break; | 141 break; |
174 } | 142 } |
175 SkASSERT(unsortable || !current->done()); | 143 SkASSERT(unsortable || !current->done()); |
176 int nextStart = index; | 144 SkOpSpanBase* nextStart = start; |
177 int nextEnd = endIndex; | 145 SkOpSpanBase* nextEnd = end; |
178 SkOpSegment* next = current->findNextOp(&chase, &nextStart,
&nextEnd, | 146 SkOpSegment* next = current->findNextOp(&chase, &nextStart,
&nextEnd, |
179 &unsortable, op, xorMask, xorOpMask); | 147 &unsortable, op, xorMask, xorOpMask); |
180 if (!next) { | 148 if (!next) { |
181 if (!unsortable && simple->hasMove() | 149 if (!unsortable && simple->hasMove() |
182 && current->verb() != SkPath::kLine_Verb | 150 && current->verb() != SkPath::kLine_Verb |
183 && !simple->isClosed()) { | 151 && !simple->isClosed()) { |
184 current->addCurveTo(index, endIndex, simple, true); | 152 current->addCurveTo(start, end, simple, true); |
185 #if DEBUG_ACTIVE_SPANS | 153 #if DEBUG_ACTIVE_SPANS |
186 if (!simple->isClosed()) { | 154 if (!simple->isClosed()) { |
187 DebugShowActiveSpans(contourList); | 155 DebugShowActiveSpans(contourList); |
188 } | 156 } |
189 #endif | 157 #endif |
190 // SkASSERT(simple->isClosed()); | |
191 } | 158 } |
192 break; | 159 break; |
193 } | 160 } |
194 #if DEBUG_FLOW | 161 #if DEBUG_FLOW |
195 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", _
_FUNCTION__, | 162 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9
g)\n", __FUNCTION__, |
196 current->debugID(), current->xyAtT(index).fX, current->xyAtT
(index).fY, | 163 current->debugID(), start->pt().fX, start->pt().fY, |
197 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY); | 164 end->pt().fX, end->pt().fY); |
198 #endif | 165 #endif |
199 current->addCurveTo(index, endIndex, simple, true); | 166 current->addCurveTo(start, end, simple, true); |
200 current = next; | 167 current = next; |
201 index = nextStart; | 168 start = nextStart; |
202 endIndex = nextEnd; | 169 end = nextEnd; |
203 } while (!simple->isClosed() && (!unsortable | 170 } while (!simple->isClosed() && (!unsortable || !start->starter(
end)->done())); |
204 || !current->done(SkMin32(index, endIndex)))); | 171 if (current->activeWinding(start, end) && !simple->isClosed()) { |
205 if (current->activeWinding(index, endIndex) && !simple->isClosed
()) { | 172 SkOpSpan* spanStart = start->starter(end); |
206 // FIXME : add to simplify, xor cpaths | 173 if (!spanStart->done()) { |
207 int min = SkMin32(index, endIndex); | 174 current->addCurveTo(start, end, simple, true); |
208 if (!unsortable && !simple->isEmpty()) { | 175 current->markDone(spanStart); |
209 unsortable = current->checkSmall(min); | |
210 } | |
211 if (!current->done(min)) { | |
212 current->addCurveTo(index, endIndex, simple, true); | |
213 current->markDoneBinary(min); | |
214 } | 176 } |
215 } | 177 } |
216 simple->close(); | 178 simple->close(); |
217 } else { | 179 } else { |
218 SkOpSpan* last = current->markAndChaseDoneBinary(index, endIndex
); | 180 SkOpSpanBase* last = current->markAndChaseDone(start, end); |
219 if (last && !last->fChased && !last->fLoop) { | 181 if (last && !last->chased()) { |
220 last->fChased = true; | 182 last->setChased(true); |
221 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); | 183 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last)); |
222 *chase.append() = last; | 184 *chase.append() = last; |
223 #if DEBUG_WINDING | 185 #if DEBUG_WINDING |
224 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FU
NCTION__, | 186 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segmen
t()->debugID()); |
225 last->fOther->span(last->fOtherIndex).fOther->debugI
D(), last->fWindSum, | 187 if (!last->final()) { |
226 last->fSmall); | 188 SkDebugf(" windSum=%d", last->upCast()->windSum()); |
| 189 } |
| 190 SkDebugf("\n"); |
227 #endif | 191 #endif |
228 } | 192 } |
229 } | 193 } |
230 current = findChaseOp(chase, &index, &endIndex); | 194 current = findChaseOp(chase, &start, &end); |
231 #if DEBUG_ACTIVE_SPANS | 195 #if DEBUG_ACTIVE_SPANS |
232 DebugShowActiveSpans(contourList); | 196 DebugShowActiveSpans(contourList); |
233 #endif | 197 #endif |
234 if (!current) { | 198 if (!current) { |
235 break; | 199 break; |
236 } | 200 } |
237 } while (true); | 201 } while (true); |
238 } while (true); | 202 } while (true); |
239 return simple->someAssemblyRequired(); | 203 return simple->someAssemblyRequired(); |
240 } | 204 } |
241 | 205 |
242 // pretty picture: | 206 // pretty picture: |
243 // https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b
-7vD9JP2V-kn9Ss/edit?usp=sharing | 207 // https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b
-7vD9JP2V-kn9Ss/edit?usp=sharing |
244 static const SkPathOp gOpInverse[kReverseDifference_PathOp + 1][2][2] = { | 208 static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = { |
245 // inside minuend outside minuend | 209 // inside minuend outside minuend |
246 // inside subtrahend outside subtrahend inside subtrahend outsi
de subtrahend | 210 // inside subtrahend outside subtrahend inside subtrahend outsi
de subtrahend |
247 {{ kDifference_PathOp, kIntersect_PathOp }, { kUnion_PathOp, kReverseDiff
erence_PathOp }}, | 211 {{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDif
ference_SkPathOp }}, |
248 {{ kIntersect_PathOp, kDifference_PathOp }, { kReverseDifference_PathOp,
kUnion_PathOp }}, | 212 {{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp,
kUnion_SkPathOp }}, |
249 {{ kUnion_PathOp, kReverseDifference_PathOp }, { kDifference_PathOp, kInt
ersect_PathOp }}, | 213 {{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIn
tersect_SkPathOp }}, |
250 {{ kXOR_PathOp, kXOR_PathOp }, { kXOR_PathOp,
kXOR_PathOp }}, | 214 {{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp,
kXOR_SkPathOp }}, |
251 {{ kReverseDifference_PathOp, kUnion_PathOp }, { kIntersect_PathOp, kDiff
erence_PathOp }}, | 215 {{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDif
ference_SkPathOp }}, |
252 }; | 216 }; |
253 | 217 |
254 static const bool gOutInverse[kReverseDifference_PathOp + 1][2][2] = { | 218 static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = { |
255 {{ false, false }, { true, false }}, // diff | 219 {{ false, false }, { true, false }}, // diff |
256 {{ false, false }, { false, true }}, // sect | 220 {{ false, false }, { false, true }}, // sect |
257 {{ false, true }, { true, true }}, // union | 221 {{ false, true }, { true, true }}, // union |
258 {{ false, true }, { true, false }}, // xor | 222 {{ false, true }, { true, false }}, // xor |
259 {{ false, true }, { false, false }}, // rev diff | 223 {{ false, true }, { false, false }}, // rev diff |
260 }; | 224 }; |
261 | 225 |
262 #define DEBUGGING_PATHOPS_FROM_HOST 0 // enable to debug svg in chrome -- note
path hardcoded below | 226 #define DEBUGGING_PATHOPS_FROM_HOST 0 // enable to debug svg in chrome -- note
path hardcoded below |
263 #if DEBUGGING_PATHOPS_FROM_HOST | 227 #if DEBUGGING_PATHOPS_FROM_HOST |
264 #include "SkData.h" | 228 #include "SkData.h" |
(...skipping 19 matching lines...) Expand all Loading... |
284 ++dumpID); | 248 ++dumpID); |
285 fprintf(file, " SkPath path;\n"); | 249 fprintf(file, " SkPath path;\n"); |
286 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillT
ype()); | 250 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillT
ype()); |
287 dump_path(file, one, false, true); | 251 dump_path(file, one, false, true); |
288 fprintf(file, " SkPath path1(path);\n"); | 252 fprintf(file, " SkPath path1(path);\n"); |
289 fprintf(file, " path.reset();\n"); | 253 fprintf(file, " path.reset();\n"); |
290 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillT
ype()); | 254 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillT
ype()); |
291 dump_path(file, two, false, true); | 255 dump_path(file, two, false, true); |
292 fprintf(file, " SkPath path2(path);\n"); | 256 fprintf(file, " SkPath path2(path);\n"); |
293 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filenam
e);\n", op); | 257 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filenam
e);\n", op); |
294 fprintf(file, "}\n");» | 258 fprintf(file, "}\n"); |
295 fclose(file); | 259 fclose(file); |
296 } | 260 } |
297 #endif | 261 #endif |
298 | 262 |
299 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { | 263 bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { |
| 264 SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tun
e |
| 265 SkOpContour contour; |
| 266 SkOpCoincidence coincidence; |
| 267 SkOpGlobalState globalState(&coincidence PATH_OPS_DEBUG_PARAMS(&contour)); |
300 #if DEBUGGING_PATHOPS_FROM_HOST | 268 #if DEBUGGING_PATHOPS_FROM_HOST |
301 dump_op(one, two, op); | 269 dump_op(one, two, op); |
302 #endif» | 270 #endif |
303 #if DEBUG_SHOW_TEST_NAME | 271 #if 0 && DEBUG_SHOW_TEST_NAME |
304 char* debugName = DEBUG_FILENAME_STRING; | 272 char* debugName = DEBUG_FILENAME_STRING; |
305 if (debugName && debugName[0]) { | 273 if (debugName && debugName[0]) { |
306 SkPathOpsDebug::BumpTestName(debugName); | 274 SkPathOpsDebug::BumpTestName(debugName); |
307 SkPathOpsDebug::ShowPath(one, two, op, debugName); | 275 SkPathOpsDebug::ShowPath(one, two, op, debugName); |
308 } | 276 } |
309 #endif | 277 #endif |
310 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; | 278 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()]; |
311 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isI
nverseFillType()] | 279 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isI
nverseFillType()] |
312 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType; | 280 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType; |
313 const SkPath* minuend = &one; | 281 const SkPath* minuend = &one; |
314 const SkPath* subtrahend = &two; | 282 const SkPath* subtrahend = &two; |
315 if (op == kReverseDifference_PathOp) { | 283 if (op == kReverseDifference_SkPathOp) { |
316 minuend = &two; | 284 minuend = &two; |
317 subtrahend = &one; | 285 subtrahend = &one; |
318 op = kDifference_PathOp; | 286 op = kDifference_SkPathOp; |
319 } | 287 } |
320 #if DEBUG_SORT || DEBUG_SWAP_TOP | 288 #if DEBUG_SORT || DEBUG_SWAP_TOP |
321 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; | 289 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault; |
322 #endif | 290 #endif |
323 // turn path into list of segments | 291 // turn path into list of segments |
324 SkTArray<SkOpContour> contours; | 292 SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState); |
325 // FIXME: add self-intersecting cubics' T values to segment | |
326 SkOpEdgeBuilder builder(*minuend, contours); | |
327 if (builder.unparseable()) { | 293 if (builder.unparseable()) { |
328 return false; | 294 return false; |
329 } | 295 } |
330 const int xorMask = builder.xorMask(); | 296 const int xorMask = builder.xorMask(); |
331 builder.addOperand(*subtrahend); | 297 builder.addOperand(*subtrahend); |
332 if (!builder.finish()) { | 298 if (!builder.finish(&allocator)) { |
333 return false; | 299 return false; |
334 } | 300 } |
| 301 #if !FORCE_RELEASE |
| 302 contour.dumpSegments(op); |
| 303 #endif |
| 304 |
335 result->reset(); | 305 result->reset(); |
336 result->setFillType(fillType); | 306 result->setFillType(fillType); |
337 const int xorOpMask = builder.xorMask(); | 307 const int xorOpMask = builder.xorMask(); |
338 SkTArray<SkOpContour*, true> contourList; | 308 SkTDArray<SkOpContour* > contourList; |
339 MakeContourList(contours, contourList, xorMask == kEvenOdd_PathOpsMask, | 309 MakeContourList(&contour, contourList, xorMask == kEvenOdd_PathOpsMask, |
340 xorOpMask == kEvenOdd_PathOpsMask); | 310 xorOpMask == kEvenOdd_PathOpsMask); |
341 SkOpContour** currentPtr = contourList.begin(); | 311 SkOpContour** currentPtr = contourList.begin(); |
342 if (!currentPtr) { | 312 if (!currentPtr) { |
343 return true; | 313 return true; |
344 } | 314 } |
| 315 if ((*currentPtr)->count() == 0) { |
| 316 SkASSERT((*currentPtr)->next() == NULL); |
| 317 return true; |
| 318 } |
345 SkOpContour** listEnd = contourList.end(); | 319 SkOpContour** listEnd = contourList.end(); |
346 // find all intersections between segments | 320 // find all intersections between segments |
347 do { | 321 do { |
348 SkOpContour** nextPtr = currentPtr; | 322 SkOpContour** nextPtr = currentPtr; |
349 SkOpContour* current = *currentPtr++; | 323 SkOpContour* current = *currentPtr++; |
350 if (current->containsCubics()) { | |
351 AddSelfIntersectTs(current); | |
352 } | |
353 SkOpContour* next; | 324 SkOpContour* next; |
354 do { | 325 do { |
355 next = *nextPtr++; | 326 next = *nextPtr++; |
356 } while (AddIntersectTs(current, next) && nextPtr != listEnd); | 327 } while (AddIntersectTs(current, next, &coincidence, &allocator) && next
Ptr != listEnd); |
357 } while (currentPtr != listEnd); | 328 } while (currentPtr != listEnd); |
| 329 #if DEBUG_VALIDATE |
| 330 globalState.setPhase(SkOpGlobalState::kWalking); |
| 331 #endif |
358 // eat through coincident edges | 332 // eat through coincident edges |
359 | 333 if (!HandleCoincidence(&contourList, &coincidence, &allocator, &globalState)
) { |
360 int total = 0; | |
361 int index; | |
362 for (index = 0; index < contourList.count(); ++index) { | |
363 total += contourList[index]->segments().count(); | |
364 } | |
365 if (!HandleCoincidence(&contourList, total)) { | |
366 return false; | 334 return false; |
367 } | 335 } |
368 // construct closed contours | 336 // construct closed contours |
369 SkPathWriter wrapper(*result); | 337 SkPathWriter wrapper(*result); |
370 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper); | 338 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator); |
371 { // if some edges could not be resolved, assemble remaining fragments | 339 { // if some edges could not be resolved, assemble remaining fragments |
372 SkPath temp; | 340 SkPath temp; |
373 temp.setFillType(fillType); | 341 temp.setFillType(fillType); |
374 SkPathWriter assembled(temp); | 342 SkPathWriter assembled(temp); |
375 Assemble(wrapper, &assembled); | 343 Assemble(wrapper, &assembled); |
376 *result = *assembled.nativePath(); | 344 *result = *assembled.nativePath(); |
377 result->setFillType(fillType); | 345 result->setFillType(fillType); |
378 } | 346 } |
379 return true; | 347 return true; |
380 } | 348 } |
OLD | NEW |