Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(430)

Side by Side Diff: src/pathops/SkPathOpsCommon.cpp

Issue 13094010: Add implementation of path ops (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/pathops/SkPathOpsCommon.h ('k') | src/pathops/SkPathOpsOp.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 #include "SkOpEdgeBuilder.h"
8 #include "SkPathOpsCommon.h"
9 #include "SkPathWriter.h"
10 #include "TSearch.h"
11
12 static int contourRangeCheckY(SkTDArray<SkOpContour*>& contourList, SkOpSegment *& current,
13 int& index, int& endIndex, double& bestHit, SkScal ar& bestDx,
14 bool& tryAgain, double& mid, bool opp) {
15 double tAtMid = current->tAtMid(index, endIndex, mid);
16 SkPoint basePt = current->xyAtT(tAtMid);
17 int contourCount = contourList.count();
18 SkScalar bestY = SK_ScalarMin;
19 SkOpSegment* bestSeg = NULL;
20 int bestTIndex;
21 bool bestOpp;
22 bool hitSomething = false;
23 for (int cTest = 0; cTest < contourCount; ++cTest) {
24 SkOpContour* contour = contourList[cTest];
25 bool testOpp = contour->operand() ^ current->operand() ^ opp;
26 if (basePt.fY < contour->bounds().fTop) {
27 continue;
28 }
29 if (bestY > contour->bounds().fBottom) {
30 continue;
31 }
32 int segmentCount = contour->segments().count();
33 for (int test = 0; test < segmentCount; ++test) {
34 SkOpSegment* testSeg = &contour->segments()[test];
35 SkScalar testY = bestY;
36 double testHit;
37 int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, hitSo mething, tAtMid,
38 testOpp, testSeg == current);
39 if (testTIndex < 0) {
40 if (testTIndex == SK_MinS32) {
41 hitSomething = true;
42 bestSeg = NULL;
43 goto abortContours; // vertical encountered, return and try different point
44 }
45 continue;
46 }
47 if (testSeg == current && current->betweenTs(index, testHit, endInde x)) {
48 double baseT = current->t(index);
49 double endT = current->t(endIndex);
50 double newMid = (testHit - baseT) / (endT - baseT);
51 #if DEBUG_WINDING
52 double midT = current->tAtMid(index, endIndex, mid);
53 SkPoint midXY = current->xyAtT(midT);
54 double newMidT = current->tAtMid(index, endIndex, newMid);
55 SkPoint newXY = current->xyAtT(newMidT);
56 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
57 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNC TION__,
58 current->debugID(), mid, newMid,
59 baseT, current->xAtT(index), current->yAtT(index),
60 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
61 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
62 endT, current->xAtT(endIndex), current->yAtT(endIndex));
63 #endif
64 mid = newMid * 2; // calling loop with divide by 2 before contin uing
65 return SK_MinS32;
66 }
67 bestSeg = testSeg;
68 bestHit = testHit;
69 bestOpp = testOpp;
70 bestTIndex = testTIndex;
71 bestY = testY;
72 }
73 }
74 abortContours:
75 int result;
76 if (!bestSeg) {
77 result = hitSomething ? SK_MinS32 : 0;
78 } else {
79 if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
80 current = bestSeg;
81 index = bestTIndex;
82 endIndex = bestSeg->nextSpan(bestTIndex, 1);
83 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
84 tryAgain = true;
85 return 0;
86 }
87 result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, bestDx);
88 SkASSERT(bestDx);
89 }
90 double baseT = current->t(index);
91 double endT = current->t(endIndex);
92 bestHit = baseT + mid * (endT - baseT);
93 return result;
94 }
95
96 SkOpSegment* FindUndone(SkTDArray<SkOpContour*>& contourList, int& start, int& e nd) {
97 int contourCount = contourList.count();
98 SkOpSegment* result;
99 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
100 SkOpContour* contour = contourList[cIndex];
101 result = contour->undoneSegment(start, end);
102 if (result) {
103 return result;
104 }
105 }
106 return NULL;
107 }
108
109 SkOpSegment* FindChase(SkTDArray<SkOpSpan*>& chase, int& tIndex, int& endIndex) {
110 while (chase.count()) {
111 SkOpSpan* span;
112 chase.pop(&span);
113 const SkOpSpan& backPtr = span->fOther->span(span->fOtherIndex);
114 SkOpSegment* segment = backPtr.fOther;
115 tIndex = backPtr.fOtherIndex;
116 SkTDArray<SkOpAngle> angles;
117 int done = 0;
118 if (segment->activeAngle(tIndex, done, angles)) {
119 SkOpAngle* last = angles.end() - 1;
120 tIndex = last->start();
121 endIndex = last->end();
122 #if TRY_ROTATE
123 *chase.insert(0) = span;
124 #else
125 *chase.append() = span;
126 #endif
127 return last->segment();
128 }
129 if (done == angles.count()) {
130 continue;
131 }
132 SkTDArray<SkOpAngle*> sorted;
133 bool sortable = SkOpSegment::SortAngles(angles, sorted);
134 int angleCount = sorted.count();
135 #if DEBUG_SORT
136 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
137 #endif
138 if (!sortable) {
139 continue;
140 }
141 // find first angle, initialize winding to computed fWindSum
142 int firstIndex = -1;
143 const SkOpAngle* angle;
144 int winding;
145 do {
146 angle = sorted[++firstIndex];
147 segment = angle->segment();
148 winding = segment->windSum(angle);
149 } while (winding == SK_MinS32);
150 int spanWinding = segment->spanSign(angle->start(), angle->end());
151 #if DEBUG_WINDING
152 SkDebugf("%s winding=%d spanWinding=%d\n",
153 __FUNCTION__, winding, spanWinding);
154 #endif
155 // turn span winding into contour winding
156 if (spanWinding * winding < 0) {
157 winding += spanWinding;
158 }
159 #if DEBUG_SORT
160 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
161 #endif
162 // we care about first sign and whether wind sum indicates this
163 // edge is inside or outside. Maybe need to pass span winding
164 // or first winding or something into this function?
165 // advance to first undone angle, then return it and winding
166 // (to set whether edges are active or not)
167 int nextIndex = firstIndex + 1;
168 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
169 angle = sorted[firstIndex];
170 winding -= angle->segment()->spanSign(angle);
171 do {
172 SkASSERT(nextIndex != firstIndex);
173 if (nextIndex == angleCount) {
174 nextIndex = 0;
175 }
176 angle = sorted[nextIndex];
177 segment = angle->segment();
178 int maxWinding = winding;
179 winding -= segment->spanSign(angle);
180 #if DEBUG_SORT
181 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__ ,
182 segment->debugID(), maxWinding, winding, angle->sign());
183 #endif
184 tIndex = angle->start();
185 endIndex = angle->end();
186 int lesser = SkMin32(tIndex, endIndex);
187 const SkOpSpan& nextSpan = segment->span(lesser);
188 if (!nextSpan.fDone) {
189 // FIXME: this be wrong? assign startWinding if edge is in
190 // same direction. If the direction is opposite, winding to
191 // assign is flipped sign or +/- 1?
192 if (SkOpSegment::UseInnerWinding(maxWinding, winding)) {
193 maxWinding = winding;
194 }
195 segment->markAndChaseWinding(angle, maxWinding, 0);
196 break;
197 }
198 } while (++nextIndex != lastIndex);
199 *chase.insert(0) = span;
200 return segment;
201 }
202 return NULL;
203 }
204
205 #if DEBUG_ACTIVE_SPANS
206 void DebugShowActiveSpans(SkTDArray<SkOpContour*>& contourList) {
207 int index;
208 for (index = 0; index < contourList.count(); ++ index) {
209 contourList[index]->debugShowActiveSpans();
210 }
211 for (index = 0; index < contourList.count(); ++ index) {
212 contourList[index]->validateActiveSpans();
213 }
214 }
215 #endif
216
217 static SkOpSegment* findSortableTop(SkTDArray<SkOpContour*>& contourList, int& i ndex, int& endIndex,
218 SkPoint& topLeft, bool& unsortable, bool& done, boo l onlySortable) {
219 SkOpSegment* result;
220 do {
221 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
222 int contourCount = contourList.count();
223 SkOpSegment* topStart = NULL;
224 done = true;
225 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
226 SkOpContour* contour = contourList[cIndex];
227 if (contour->done()) {
228 continue;
229 }
230 const SkPathOpsBounds& bounds = contour->bounds();
231 if (bounds.fBottom < topLeft.fY) {
232 done = false;
233 continue;
234 }
235 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
236 done = false;
237 continue;
238 }
239 contour->topSortableSegment(topLeft, bestXY, topStart);
240 if (!contour->done()) {
241 done = false;
242 }
243 }
244 if (!topStart) {
245 return NULL;
246 }
247 topLeft = bestXY;
248 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
249 } while (!result);
250 return result;
251 }
252
253 static int rightAngleWinding(SkTDArray<SkOpContour*>& contourList,
254 SkOpSegment*& current, int& index, int& endIndex, double& tHit, SkScalar & hitDx, bool& tryAgain,
255 bool opp) {
256 double test = 0.9;
257 int contourWinding;
258 do {
259 contourWinding = contourRangeCheckY(contourList, current, index, endInde x, tHit, hitDx,
260 tryAgain, test, opp);
261 if (contourWinding != SK_MinS32 || tryAgain) {
262 return contourWinding;
263 }
264 test /= 2;
265 } while (!approximately_negative(test));
266 SkASSERT(0); // should be OK to comment out, but interested when this hits
267 return contourWinding;
268 }
269
270 static void skipVertical(SkTDArray<SkOpContour*>& contourList,
271 SkOpSegment*& current, int& index, int& endIndex) {
272 if (!current->isVertical(index, endIndex)) {
273 return;
274 }
275 int contourCount = contourList.count();
276 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
277 SkOpContour* contour = contourList[cIndex];
278 if (contour->done()) {
279 continue;
280 }
281 current = contour->nonVerticalSegment(index, endIndex);
282 if (current) {
283 return;
284 }
285 }
286 }
287
288 SkOpSegment* FindSortableTop(SkTDArray<SkOpContour*>& contourList, bool& firstCo ntour, int& index,
289 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done,
290 bool binary) {
291 SkOpSegment* current = findSortableTop(contourList, index, endIndex, topLeft , unsortable, done,
292 true);
293 if (!current) {
294 return NULL;
295 }
296 if (firstContour) {
297 current->initWinding(index, endIndex);
298 firstContour = false;
299 return current;
300 }
301 int minIndex = SkMin32(index, endIndex);
302 int sumWinding = current->windSum(minIndex);
303 if (sumWinding != SK_MinS32) {
304 return current;
305 }
306 sumWinding = current->computeSum(index, endIndex, binary);
307 if (sumWinding != SK_MinS32) {
308 return current;
309 }
310 int contourWinding;
311 int oppContourWinding = 0;
312 // the simple upward projection of the unresolved points hit unsortable angl es
313 // shoot rays at right angles to the segment to find its winding, ignoring a ngle cases
314 bool tryAgain;
315 double tHit;
316 SkScalar hitDx = 0;
317 SkScalar hitOppDx = 0;
318 do {
319 // if current is vertical, find another candidate which is not
320 // if only remaining candidates are vertical, then they can be marked do ne
321 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
322 skipVertical(contourList, current, index, endIndex);
323 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
324 tryAgain = false;
325 contourWinding = rightAngleWinding(contourList, current, index, endIndex , tHit, hitDx,
326 tryAgain, false);
327 if (tryAgain) {
328 continue;
329 }
330 if (!binary) {
331 break;
332 }
333 oppContourWinding = rightAngleWinding(contourList, current, index, endIn dex, tHit, hitOppDx,
334 tryAgain, true);
335 } while (tryAgain);
336
337 current->initWinding(index, endIndex, tHit, contourWinding, hitDx, oppContou rWinding, hitOppDx);
338 return current;
339 }
340
341 void FixOtherTIndex(SkTDArray<SkOpContour*>& contourList) {
342 int contourCount = contourList.count();
343 for (int cTest = 0; cTest < contourCount; ++cTest) {
344 SkOpContour* contour = contourList[cTest];
345 contour->fixOtherTIndex();
346 }
347 }
348
349 void SortSegments(SkTDArray<SkOpContour*>& contourList) {
350 int contourCount = contourList.count();
351 for (int cTest = 0; cTest < contourCount; ++cTest) {
352 SkOpContour* contour = contourList[cTest];
353 contour->sortSegments();
354 }
355 }
356
357 void MakeContourList(SkTArray<SkOpContour>& contours, SkTDArray<SkOpContour*>& l ist,
358 bool evenOdd, bool oppEvenOdd) {
359 int count = contours.count();
360 if (count == 0) {
361 return;
362 }
363 for (int index = 0; index < count; ++index) {
364 SkOpContour& contour = contours[index];
365 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
366 *list.append() = &contour;
367 }
368 QSort<SkOpContour>(list.begin(), list.end() - 1);
369 }
370
371 static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
372 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
373 }
374
375 static bool lessThan(SkTDArray<double>& distances, const int one, const int two) {
376 return distances[one] < distances[two];
377 }
378 /*
379 check start and end of each contour
380 if not the same, record them
381 match them up
382 connect closest
383 reassemble contour pieces into new path
384 */
385 void Assemble(const SkPathWriter& path, SkPathWriter& simple) {
386 #if DEBUG_PATH_CONSTRUCTION
387 SkDebugf("%s\n", __FUNCTION__);
388 #endif
389 SkTArray<SkOpContour> contours;
390 SkOpEdgeBuilder builder(path, contours);
391 builder.finish();
392 int count = contours.count();
393 int outer;
394 SkTDArray<int> runs; // indices of partial contours
395 for (outer = 0; outer < count; ++outer) {
396 const SkOpContour& eContour = contours[outer];
397 const SkPoint& eStart = eContour.start();
398 const SkPoint& eEnd = eContour.end();
399 #if DEBUG_ASSEMBLE
400 SkDebugf("%s contour", __FUNCTION__);
401 if (!approximatelyEqual(eStart, eEnd)) {
402 SkDebugf("[%d]", runs.count());
403 } else {
404 SkDebugf(" ");
405 }
406 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
407 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
408 #endif
409 if (approximatelyEqual(eStart, eEnd)) {
410 eContour.toPath(simple);
411 continue;
412 }
413 *runs.append() = outer;
414 }
415 count = runs.count();
416 if (count == 0) {
417 return;
418 }
419 SkTDArray<int> sLink, eLink;
420 sLink.setCount(count);
421 eLink.setCount(count);
422 int rIndex, iIndex;
423 for (rIndex = 0; rIndex < count; ++rIndex) {
424 sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
425 }
426 SkTDArray<double> distances;
427 const int ends = count * 2; // all starts and ends
428 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
429 distances.setCount(entries);
430 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
431 outer = runs[rIndex >> 1];
432 const SkOpContour& oContour = contours[outer];
433 const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
434 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
435 * ends - rIndex - 1;
436 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
437 int inner = runs[iIndex >> 1];
438 const SkOpContour& iContour = contours[inner];
439 const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
440 double dx = iPt.fX - oPt.fX;
441 double dy = iPt.fY - oPt.fY;
442 double dist = dx * dx + dy * dy;
443 distances[row + iIndex] = dist; // oStart distance from iStart
444 }
445 }
446 SkTDArray<int> sortedDist;
447 sortedDist.setCount(entries);
448 for (rIndex = 0; rIndex < entries; ++rIndex) {
449 sortedDist[rIndex] = rIndex;
450 }
451 QSort<SkTDArray<double>, int>(distances, sortedDist.begin(), sortedDist.end( ) - 1, lessThan);
452 int remaining = count; // number of start/end pairs
453 for (rIndex = 0; rIndex < entries; ++rIndex) {
454 int pair = sortedDist[rIndex];
455 int row = pair / ends;
456 int col = pair - row * ends;
457 int thingOne = row < col ? row : ends - row - 2;
458 int ndxOne = thingOne >> 1;
459 bool endOne = thingOne & 1;
460 int* linkOne = endOne ? eLink.begin() : sLink.begin();
461 if (linkOne[ndxOne] != SK_MaxS32) {
462 continue;
463 }
464 int thingTwo = row < col ? col : ends - row + col - 1;
465 int ndxTwo = thingTwo >> 1;
466 bool endTwo = thingTwo & 1;
467 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
468 if (linkTwo[ndxTwo] != SK_MaxS32) {
469 continue;
470 }
471 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
472 bool flip = endOne == endTwo;
473 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
474 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
475 if (!--remaining) {
476 break;
477 }
478 }
479 SkASSERT(!remaining);
480 #if DEBUG_ASSEMBLE
481 for (rIndex = 0; rIndex < count; ++rIndex) {
482 int s = sLink[rIndex];
483 int e = eLink[rIndex];
484 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : ' e',
485 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
486 }
487 #endif
488 rIndex = 0;
489 do {
490 bool forward = true;
491 bool first = true;
492 int sIndex = sLink[rIndex];
493 SkASSERT(sIndex != SK_MaxS32);
494 sLink[rIndex] = SK_MaxS32;
495 int eIndex;
496 if (sIndex < 0) {
497 eIndex = sLink[~sIndex];
498 sLink[~sIndex] = SK_MaxS32;
499 } else {
500 eIndex = eLink[sIndex];
501 eLink[sIndex] = SK_MaxS32;
502 }
503 SkASSERT(eIndex != SK_MaxS32);
504 #if DEBUG_ASSEMBLE
505 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
506 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
507 eIndex < 0 ? ~eIndex : eIndex);
508 #endif
509 do {
510 outer = runs[rIndex];
511 const SkOpContour& contour = contours[outer];
512 if (first) {
513 first = false;
514 const SkPoint* startPtr = &contour.start();
515 simple.deferredMove(startPtr[0]);
516 }
517 if (forward) {
518 contour.toPartialForward(simple);
519 } else {
520 contour.toPartialBackward(simple);
521 }
522 #if DEBUG_ASSEMBLE
523 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex ,
524 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
525 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
526 #endif
527 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
528 simple.close();
529 break;
530 }
531 if (forward) {
532 eIndex = eLink[rIndex];
533 SkASSERT(eIndex != SK_MaxS32);
534 eLink[rIndex] = SK_MaxS32;
535 if (eIndex >= 0) {
536 SkASSERT(sLink[eIndex] == rIndex);
537 sLink[eIndex] = SK_MaxS32;
538 } else {
539 SkASSERT(eLink[~eIndex] == ~rIndex);
540 eLink[~eIndex] = SK_MaxS32;
541 }
542 } else {
543 eIndex = sLink[rIndex];
544 SkASSERT(eIndex != SK_MaxS32);
545 sLink[rIndex] = SK_MaxS32;
546 if (eIndex >= 0) {
547 SkASSERT(eLink[eIndex] == rIndex);
548 eLink[eIndex] = SK_MaxS32;
549 } else {
550 SkASSERT(sLink[~eIndex] == ~rIndex);
551 sLink[~eIndex] = SK_MaxS32;
552 }
553 }
554 rIndex = eIndex;
555 if (rIndex < 0) {
556 forward ^= 1;
557 rIndex = ~rIndex;
558 }
559 } while (true);
560 for (rIndex = 0; rIndex < count; ++rIndex) {
561 if (sLink[rIndex] != SK_MaxS32) {
562 break;
563 }
564 }
565 } while (rIndex < count);
566 #if DEBUG_ASSEMBLE
567 for (rIndex = 0; rIndex < count; ++rIndex) {
568 SkASSERT(sLink[rIndex] == SK_MaxS32);
569 SkASSERT(eLink[rIndex] == SK_MaxS32);
570 }
571 #endif
572 }
573
OLDNEW
« no previous file with comments | « src/pathops/SkPathOpsCommon.h ('k') | src/pathops/SkPathOpsOp.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698