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