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 "src/core/SkTSort.h" |
8 | #include "src/pathops/SkOpSegment.h" |
9 | #include "src/pathops/SkOpSpan.h" |
10 | #include "src/pathops/SkPathOpsPoint.h" |
11 | #include "src/pathops/SkPathWriter.h" |
12 | |
13 | // wrap path to keep track of whether the contour is initialized and non-empty |
14 | SkPathWriter::SkPathWriter(SkPath& path) |
15 | : fPathPtr(&path) |
16 | { |
17 | init(); |
18 | } |
19 | |
20 | void SkPathWriter::close() { |
21 | if (fCurrent.isEmpty()) { |
22 | return; |
23 | } |
24 | SkASSERT(this->isClosed()); |
25 | #if DEBUG_PATH_CONSTRUCTION |
26 | SkDebugf("path.close();\n" ); |
27 | #endif |
28 | fCurrent.close(); |
29 | fPathPtr->addPath(fCurrent); |
30 | fCurrent.reset(); |
31 | init(); |
32 | } |
33 | |
34 | void SkPathWriter::conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight) { |
35 | SkPoint pt2pt = this->update(pt2); |
36 | #if DEBUG_PATH_CONSTRUCTION |
37 | SkDebugf("path.conicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g);\n" , |
38 | pt1.fX, pt1.fY, pt2pt.fX, pt2pt.fY, weight); |
39 | #endif |
40 | fCurrent.conicTo(pt1, pt2pt, weight); |
41 | } |
42 | |
43 | void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3) { |
44 | SkPoint pt3pt = this->update(pt3); |
45 | #if DEBUG_PATH_CONSTRUCTION |
46 | SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n" , |
47 | pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3pt.fX, pt3pt.fY); |
48 | #endif |
49 | fCurrent.cubicTo(pt1, pt2, pt3pt); |
50 | } |
51 | |
52 | bool SkPathWriter::deferredLine(const SkOpPtT* pt) { |
53 | SkASSERT(fFirstPtT); |
54 | SkASSERT(fDefer[0]); |
55 | if (fDefer[0] == pt) { |
56 | // FIXME: why we're adding a degenerate line? Caller should have preflighted this. |
57 | return true; |
58 | } |
59 | if (pt->contains(fDefer[0])) { |
60 | // FIXME: why we're adding a degenerate line? |
61 | return true; |
62 | } |
63 | if (this->matchedLast(pt)) { |
64 | return false; |
65 | } |
66 | if (fDefer[1] && this->changedSlopes(pt)) { |
67 | this->lineTo(); |
68 | fDefer[0] = fDefer[1]; |
69 | } |
70 | fDefer[1] = pt; |
71 | return true; |
72 | } |
73 | |
74 | void SkPathWriter::deferredMove(const SkOpPtT* pt) { |
75 | if (!fDefer[1]) { |
76 | fFirstPtT = fDefer[0] = pt; |
77 | return; |
78 | } |
79 | SkASSERT(fDefer[0]); |
80 | if (!this->matchedLast(pt)) { |
81 | this->finishContour(); |
82 | fFirstPtT = fDefer[0] = pt; |
83 | } |
84 | } |
85 | |
86 | void SkPathWriter::finishContour() { |
87 | if (!this->matchedLast(fDefer[0])) { |
88 | if (!fDefer[1]) { |
89 | return; |
90 | } |
91 | this->lineTo(); |
92 | } |
93 | if (fCurrent.isEmpty()) { |
94 | return; |
95 | } |
96 | if (this->isClosed()) { |
97 | this->close(); |
98 | } else { |
99 | SkASSERT(fDefer[1]); |
100 | fEndPtTs.push_back(fFirstPtT); |
101 | fEndPtTs.push_back(fDefer[1]); |
102 | fPartials.push_back(fCurrent); |
103 | this->init(); |
104 | } |
105 | } |
106 | |
107 | void SkPathWriter::init() { |
108 | fCurrent.reset(); |
109 | fFirstPtT = fDefer[0] = fDefer[1] = nullptr; |
110 | } |
111 | |
112 | bool SkPathWriter::isClosed() const { |
113 | return this->matchedLast(fFirstPtT); |
114 | } |
115 | |
116 | void SkPathWriter::lineTo() { |
117 | if (fCurrent.isEmpty()) { |
118 | this->moveTo(); |
119 | } |
120 | #if DEBUG_PATH_CONSTRUCTION |
121 | SkDebugf("path.lineTo(%1.9g,%1.9g);\n" , fDefer[1]->fPt.fX, fDefer[1]->fPt.fY); |
122 | #endif |
123 | fCurrent.lineTo(fDefer[1]->fPt); |
124 | } |
125 | |
126 | bool SkPathWriter::matchedLast(const SkOpPtT* test) const { |
127 | if (test == fDefer[1]) { |
128 | return true; |
129 | } |
130 | if (!test) { |
131 | return false; |
132 | } |
133 | if (!fDefer[1]) { |
134 | return false; |
135 | } |
136 | return test->contains(fDefer[1]); |
137 | } |
138 | |
139 | void SkPathWriter::moveTo() { |
140 | #if DEBUG_PATH_CONSTRUCTION |
141 | SkDebugf("path.moveTo(%1.9g,%1.9g);\n" , fFirstPtT->fPt.fX, fFirstPtT->fPt.fY); |
142 | #endif |
143 | fCurrent.moveTo(fFirstPtT->fPt); |
144 | } |
145 | |
146 | void SkPathWriter::quadTo(const SkPoint& pt1, const SkOpPtT* pt2) { |
147 | SkPoint pt2pt = this->update(pt2); |
148 | #if DEBUG_PATH_CONSTRUCTION |
149 | SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n" , |
150 | pt1.fX, pt1.fY, pt2pt.fX, pt2pt.fY); |
151 | #endif |
152 | fCurrent.quadTo(pt1, pt2pt); |
153 | } |
154 | |
155 | // if last point to be written matches the current path's first point, alter the |
156 | // last to avoid writing a degenerate lineTo when the path is closed |
157 | SkPoint SkPathWriter::update(const SkOpPtT* pt) { |
158 | if (!fDefer[1]) { |
159 | this->moveTo(); |
160 | } else if (!this->matchedLast(fDefer[0])) { |
161 | this->lineTo(); |
162 | } |
163 | SkPoint result = pt->fPt; |
164 | if (fFirstPtT && result != fFirstPtT->fPt && fFirstPtT->contains(pt)) { |
165 | result = fFirstPtT->fPt; |
166 | } |
167 | fDefer[0] = fDefer[1] = pt; // set both to know that there is not a pending deferred line |
168 | return result; |
169 | } |
170 | |
171 | bool SkPathWriter::someAssemblyRequired() { |
172 | this->finishContour(); |
173 | return fEndPtTs.count() > 0; |
174 | } |
175 | |
176 | bool SkPathWriter::changedSlopes(const SkOpPtT* ptT) const { |
177 | if (matchedLast(fDefer[0])) { |
178 | return false; |
179 | } |
180 | SkVector deferDxdy = fDefer[1]->fPt - fDefer[0]->fPt; |
181 | SkVector lineDxdy = ptT->fPt - fDefer[1]->fPt; |
182 | return deferDxdy.fX * lineDxdy.fY != deferDxdy.fY * lineDxdy.fX; |
183 | } |
184 | |
185 | class DistanceLessThan { |
186 | public: |
187 | DistanceLessThan(double* distances) : fDistances(distances) { } |
188 | double* fDistances; |
189 | bool operator()(const int one, const int two) { |
190 | return fDistances[one] < fDistances[two]; |
191 | } |
192 | }; |
193 | |
194 | /* |
195 | check start and end of each contour |
196 | if not the same, record them |
197 | match them up |
198 | connect closest |
199 | reassemble contour pieces into new path |
200 | */ |
201 | void SkPathWriter::assemble() { |
202 | if (!this->someAssemblyRequired()) { |
203 | return; |
204 | } |
205 | #if DEBUG_PATH_CONSTRUCTION |
206 | SkDebugf("%s\n" , __FUNCTION__); |
207 | #endif |
208 | SkOpPtT const* const* runs = fEndPtTs.begin(); // starts, ends of partial contours |
209 | int endCount = fEndPtTs.count(); // all starts and ends |
210 | SkASSERT(endCount > 0); |
211 | SkASSERT(endCount == fPartials.count() * 2); |
212 | #if DEBUG_ASSEMBLE |
213 | for (int index = 0; index < endCount; index += 2) { |
214 | const SkOpPtT* eStart = runs[index]; |
215 | const SkOpPtT* eEnd = runs[index + 1]; |
216 | SkASSERT(eStart != eEnd); |
217 | SkASSERT(!eStart->contains(eEnd)); |
218 | SkDebugf("%s contour start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n" , __FUNCTION__, |
219 | eStart->fPt.fX, eStart->fPt.fY, eEnd->fPt.fX, eEnd->fPt.fY); |
220 | } |
221 | #endif |
222 | // lengthen any partial contour adjacent to a simple segment |
223 | for (int pIndex = 0; pIndex < endCount; pIndex++) { |
224 | SkOpPtT* opPtT = const_cast<SkOpPtT*>(runs[pIndex]); |
225 | SkPath dummy; |
226 | SkPathWriter partWriter(dummy); |
227 | do { |
228 | if (!zero_or_one(opPtT->fT)) { |
229 | break; |
230 | } |
231 | SkOpSpanBase* opSpanBase = opPtT->span(); |
232 | SkOpSpanBase* start = opPtT->fT ? opSpanBase->prev() : opSpanBase->upCast()->next(); |
233 | int step = opPtT->fT ? 1 : -1; |
234 | const SkOpSegment* opSegment = opSpanBase->segment(); |
235 | const SkOpSegment* nextSegment = opSegment->isSimple(&start, &step); |
236 | if (!nextSegment) { |
237 | break; |
238 | } |
239 | SkOpSpanBase* opSpanEnd = start->t() ? start->prev() : start->upCast()->next(); |
240 | if (start->starter(opSpanEnd)->alreadyAdded()) { |
241 | break; |
242 | } |
243 | nextSegment->addCurveTo(start, opSpanEnd, &partWriter); |
244 | opPtT = opSpanEnd->ptT(); |
245 | SkOpPtT** runsPtr = const_cast<SkOpPtT**>(&runs[pIndex]); |
246 | *runsPtr = opPtT; |
247 | } while (true); |
248 | partWriter.finishContour(); |
249 | const SkTArray<SkPath>& partPartials = partWriter.partials(); |
250 | if (!partPartials.count()) { |
251 | continue; |
252 | } |
253 | // if pIndex is even, reverse and prepend to fPartials; otherwise, append |
254 | SkPath& partial = const_cast<SkPath&>(fPartials[pIndex >> 1]); |
255 | const SkPath& part = partPartials[0]; |
256 | if (pIndex & 1) { |
257 | partial.addPath(part, SkPath::kExtend_AddPathMode); |
258 | } else { |
259 | SkPath reverse; |
260 | reverse.reverseAddPath(part); |
261 | reverse.addPath(partial, SkPath::kExtend_AddPathMode); |
262 | partial = reverse; |
263 | } |
264 | } |
265 | SkTDArray<int> sLink, eLink; |
266 | int linkCount = endCount / 2; // number of partial contours |
267 | sLink.append(linkCount); |
268 | eLink.append(linkCount); |
269 | int rIndex, iIndex; |
270 | for (rIndex = 0; rIndex < linkCount; ++rIndex) { |
271 | sLink[rIndex] = eLink[rIndex] = SK_MaxS32; |
272 | } |
273 | const int entries = endCount * (endCount - 1) / 2; // folded triangle |
274 | SkSTArray<8, double, true> distances(entries); |
275 | SkSTArray<8, int, true> sortedDist(entries); |
276 | SkSTArray<8, int, true> distLookup(entries); |
277 | int rRow = 0; |
278 | int dIndex = 0; |
279 | for (rIndex = 0; rIndex < endCount - 1; ++rIndex) { |
280 | const SkOpPtT* oPtT = runs[rIndex]; |
281 | for (iIndex = rIndex + 1; iIndex < endCount; ++iIndex) { |
282 | const SkOpPtT* iPtT = runs[iIndex]; |
283 | double dx = iPtT->fPt.fX - oPtT->fPt.fX; |
284 | double dy = iPtT->fPt.fY - oPtT->fPt.fY; |
285 | double dist = dx * dx + dy * dy; |
286 | distLookup.push_back(rRow + iIndex); |
287 | distances.push_back(dist); // oStart distance from iStart |
288 | sortedDist.push_back(dIndex++); |
289 | } |
290 | rRow += endCount; |
291 | } |
292 | SkASSERT(dIndex == entries); |
293 | SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin())); |
294 | int remaining = linkCount; // number of start/end pairs |
295 | for (rIndex = 0; rIndex < entries; ++rIndex) { |
296 | int pair = sortedDist[rIndex]; |
297 | pair = distLookup[pair]; |
298 | int row = pair / endCount; |
299 | int col = pair - row * endCount; |
300 | int ndxOne = row >> 1; |
301 | bool endOne = row & 1; |
302 | int* linkOne = endOne ? eLink.begin() : sLink.begin(); |
303 | if (linkOne[ndxOne] != SK_MaxS32) { |
304 | continue; |
305 | } |
306 | int ndxTwo = col >> 1; |
307 | bool endTwo = col & 1; |
308 | int* linkTwo = endTwo ? eLink.begin() : sLink.begin(); |
309 | if (linkTwo[ndxTwo] != SK_MaxS32) { |
310 | continue; |
311 | } |
312 | SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]); |
313 | bool flip = endOne == endTwo; |
314 | linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo; |
315 | linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne; |
316 | if (!--remaining) { |
317 | break; |
318 | } |
319 | } |
320 | SkASSERT(!remaining); |
321 | #if DEBUG_ASSEMBLE |
322 | for (rIndex = 0; rIndex < linkCount; ++rIndex) { |
323 | int s = sLink[rIndex]; |
324 | int e = eLink[rIndex]; |
325 | SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n" , __FUNCTION__, s < 0 ? 's' : 'e', |
326 | s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e); |
327 | } |
328 | #endif |
329 | rIndex = 0; |
330 | do { |
331 | bool forward = true; |
332 | bool first = true; |
333 | int sIndex = sLink[rIndex]; |
334 | SkASSERT(sIndex != SK_MaxS32); |
335 | sLink[rIndex] = SK_MaxS32; |
336 | int eIndex; |
337 | if (sIndex < 0) { |
338 | eIndex = sLink[~sIndex]; |
339 | sLink[~sIndex] = SK_MaxS32; |
340 | } else { |
341 | eIndex = eLink[sIndex]; |
342 | eLink[sIndex] = SK_MaxS32; |
343 | } |
344 | SkASSERT(eIndex != SK_MaxS32); |
345 | #if DEBUG_ASSEMBLE |
346 | SkDebugf("%s sIndex=%c%d eIndex=%c%d\n" , __FUNCTION__, sIndex < 0 ? 's' : 'e', |
347 | sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e', |
348 | eIndex < 0 ? ~eIndex : eIndex); |
349 | #endif |
350 | do { |
351 | const SkPath& contour = fPartials[rIndex]; |
352 | if (!first) { |
353 | SkPoint prior, next; |
354 | if (!fPathPtr->getLastPt(&prior)) { |
355 | return; |
356 | } |
357 | if (forward) { |
358 | next = contour.getPoint(0); |
359 | } else { |
360 | SkAssertResult(contour.getLastPt(&next)); |
361 | } |
362 | if (prior != next) { |
363 | /* TODO: if there is a gap between open path written so far and path to come, |
364 | connect by following segments from one to the other, rather than introducing |
365 | a diagonal to connect the two. |
366 | */ |
367 | SkDebugf("" ); |
368 | } |
369 | } |
370 | if (forward) { |
371 | fPathPtr->addPath(contour, |
372 | first ? SkPath::kAppend_AddPathMode : SkPath::kExtend_AddPathMode); |
373 | } else { |
374 | SkASSERT(!first); |
375 | fPathPtr->reversePathTo(contour); |
376 | } |
377 | if (first) { |
378 | first = false; |
379 | } |
380 | #if DEBUG_ASSEMBLE |
381 | SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n" , __FUNCTION__, rIndex, |
382 | eIndex < 0 ? "~" : "" , eIndex < 0 ? ~eIndex : eIndex, |
383 | sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)); |
384 | #endif |
385 | if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) { |
386 | fPathPtr->close(); |
387 | break; |
388 | } |
389 | if (forward) { |
390 | eIndex = eLink[rIndex]; |
391 | SkASSERT(eIndex != SK_MaxS32); |
392 | eLink[rIndex] = SK_MaxS32; |
393 | if (eIndex >= 0) { |
394 | SkASSERT(sLink[eIndex] == rIndex); |
395 | sLink[eIndex] = SK_MaxS32; |
396 | } else { |
397 | SkASSERT(eLink[~eIndex] == ~rIndex); |
398 | eLink[~eIndex] = SK_MaxS32; |
399 | } |
400 | } else { |
401 | eIndex = sLink[rIndex]; |
402 | SkASSERT(eIndex != SK_MaxS32); |
403 | sLink[rIndex] = SK_MaxS32; |
404 | if (eIndex >= 0) { |
405 | SkASSERT(eLink[eIndex] == rIndex); |
406 | eLink[eIndex] = SK_MaxS32; |
407 | } else { |
408 | SkASSERT(sLink[~eIndex] == ~rIndex); |
409 | sLink[~eIndex] = SK_MaxS32; |
410 | } |
411 | } |
412 | rIndex = eIndex; |
413 | if (rIndex < 0) { |
414 | forward ^= 1; |
415 | rIndex = ~rIndex; |
416 | } |
417 | } while (true); |
418 | for (rIndex = 0; rIndex < linkCount; ++rIndex) { |
419 | if (sLink[rIndex] != SK_MaxS32) { |
420 | break; |
421 | } |
422 | } |
423 | } while (rIndex < linkCount); |
424 | #if DEBUG_ASSEMBLE |
425 | for (rIndex = 0; rIndex < linkCount; ++rIndex) { |
426 | SkASSERT(sLink[rIndex] == SK_MaxS32); |
427 | SkASSERT(eLink[rIndex] == SK_MaxS32); |
428 | } |
429 | #endif |
430 | return; |
431 | } |
432 | |