blob: fc53f63b2e806f43a91d63cc68a6cca368b35e8f [file] [log] [blame]
caryclark@google.comc6825902012-02-03 22:07:47 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
caryclark@google.com6680fb12012-02-07 22:10:51 +00009#include "CurveIntersection.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000010#include "LineIntersection.h"
11#include "SkPath.h"
12#include "SkRect.h"
13#include "SkTArray.h"
14#include "SkTDArray.h"
15#include "TSearch.h"
16
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000017static bool gShowDebugf = true; // FIXME: remove once debugging is complete
18static bool gShowPath = false;
19static bool gDebugLessThan = false;
caryclark@google.comc17972e2012-02-20 21:33:22 +000020
caryclark@google.com6680fb12012-02-07 22:10:51 +000021static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.comc6825902012-02-03 22:07:47 +000022 double aRange[2], double bRange[2]) {
23 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
24 _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
25 return intersect(aLine, bLine, aRange, bRange);
26}
27
caryclark@google.com6680fb12012-02-07 22:10:51 +000028static int LineIntersect(const SkPoint a[2], SkScalar y, double aRange[2]) {
caryclark@google.comc6825902012-02-03 22:07:47 +000029 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
30 return horizontalIntersect(aLine, y, aRange);
31}
32
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000033static SkScalar LineXAtT(const SkPoint a[2], double t) {
34 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
35 double x;
36 xy_at_t(aLine, t, x, *(double*) 0);
37 return SkDoubleToScalar(x);
38}
39
caryclark@google.com6680fb12012-02-07 22:10:51 +000040static SkScalar LineYAtT(const SkPoint a[2], double t) {
41 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
42 double y;
43 xy_at_t(aLine, t, *(double*) 0, y);
44 return SkDoubleToScalar(y);
45}
46
47static void LineSubDivide(const SkPoint a[2], double startT, double endT,
48 SkPoint sub[2]) {
49 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
50 _Line dst;
51 sub_divide(aLine, startT, endT, dst);
52 sub[0].fX = SkDoubleToScalar(dst[0].x);
53 sub[0].fY = SkDoubleToScalar(dst[0].y);
54 sub[1].fX = SkDoubleToScalar(dst[1].x);
55 sub[1].fY = SkDoubleToScalar(dst[1].y);
56}
57
58
caryclark@google.comc6825902012-02-03 22:07:47 +000059// functions
60void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
61void simplify(const SkPath& path, bool asFill, SkPath& simple);
62/*
63list of edges
64bounds for edge
65sort
66active T
67
68if a contour's bounds is outside of the active area, no need to create edges
69*/
70
71/* given one or more paths,
72 find the bounds of each contour, select the active contours
73 for each active contour, compute a set of edges
74 each edge corresponds to one or more lines and curves
75 leave edges unbroken as long as possible
76 when breaking edges, compute the t at the break but leave the control points alone
77
78 */
79
80void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
81 SkPath::Iter iter(path, false);
82 SkPoint pts[4];
83 SkPath::Verb verb;
84 SkRect bounds;
85 bounds.setEmpty();
86 int count = 0;
87 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
88 switch (verb) {
89 case SkPath::kMove_Verb:
90 if (!bounds.isEmpty()) {
91 *boundsArray.append() = bounds;
92 }
93 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
94 count = 0;
95 break;
96 case SkPath::kLine_Verb:
97 count = 1;
98 break;
99 case SkPath::kQuad_Verb:
100 count = 2;
101 break;
102 case SkPath::kCubic_Verb:
103 count = 3;
104 break;
105 case SkPath::kClose_Verb:
106 count = 0;
107 break;
108 default:
109 SkDEBUGFAIL("bad verb");
110 return;
111 }
112 for (int i = 1; i <= count; ++i) {
113 bounds.growToInclude(pts[i].fX, pts[i].fY);
114 }
115 }
116}
117
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000118static bool extendLine(const SkPoint line[2], const SkPoint& add) {
119 // FIXME: allow this to extend lines that have slopes that are nearly equal
120 SkScalar dx1 = line[1].fX - line[0].fX;
121 SkScalar dy1 = line[1].fY - line[0].fY;
122 SkScalar dx2 = add.fX - line[0].fX;
123 SkScalar dy2 = add.fY - line[0].fY;
124 return dx1 * dy2 == dx2 * dy1;
125}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000126
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000127struct OutEdge {
128 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000129 const SkPoint& first = fPts[0];
130 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000131 return first.fY == rhFirst.fY
132 ? first.fX < rhFirst.fX
133 : first.fY < rhFirst.fY;
134 }
135
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000136 SkPoint fPts[4];
137 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000138};
139
caryclark@google.com6680fb12012-02-07 22:10:51 +0000140class OutEdgeBuilder {
141public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000142 OutEdgeBuilder(bool fill)
143 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000144 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000145
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000146 void addLine(const SkPoint line[2]) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000147 OutEdge& newEdge = fEdges.push_back();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000148 newEdge.fPts[0] = line[0];
149 newEdge.fPts[1] = line[1];
150 newEdge.fVerb = SkPath::kLine_Verb;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000151 }
152
153 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000154 size_t listCount = fEdges.count();
155 if (listCount == 0) {
156 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000157 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000158 do {
159 size_t listIndex = 0;
160 int advance = 1;
161 while (listIndex < listCount && fTops[listIndex] == 0) {
162 ++listIndex;
163 }
164 if (listIndex >= listCount) {
165 break;
166 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000167 SkPoint firstPt, lastLine[2];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000168 bool doMove = true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000169 bool closed = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000170 int edgeIndex;
171 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000172 SkPoint* ptArray = fEdges[listIndex].fPts;
173 uint8_t verb = fEdges[listIndex].fVerb;
174 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000175 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000176 start = &ptArray[verb];
177 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000178 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000179 start = &ptArray[0];
180 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000181 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000182 switch (verb) {
183 case SkPath::kLine_Verb:
184 bool gap;
185 if (doMove) {
186 firstPt = *start;
187 simple.moveTo(start->fX, start->fY);
188 if (gShowDebugf) {
189 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
190 start->fX, start->fY);
191 }
192 lastLine[0] = *start;
193 lastLine[1] = *end;
194 doMove = false;
195 closed = false;
196 break;
197 }
198 gap = lastLine[1] != *start;
199 if (gap) {
200 SkASSERT(fFill && lastLine[1].fY == start->fY);
201 simple.lineTo(lastLine[1].fX, lastLine[1].fY);
202 if (gShowDebugf) {
203 SkDebugf("%s lineTo x (%g,%g)\n", __FUNCTION__,
204 lastLine[1].fX, lastLine[1].fY);
205 }
206 }
207 if (gap || !extendLine(lastLine, *end)) {
208 SkASSERT(lastLine[1] == *start ||
209 (fFill && lastLine[1].fY == start->fY));
210 simple.lineTo(start->fX, start->fY);
211 if (gShowDebugf) {
212 SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
213 start->fX, start->fY);
214 }
215 lastLine[0] = *start;
216 }
217 lastLine[1] = *end;
218 if (firstPt == *end) {
219 simple.lineTo(end->fX, end->fY);
220 simple.close();
221 if (gShowDebugf) {
222 SkDebugf("%s close 1 (%g, %g)\n", __FUNCTION__,
223 end->fX, end->fY);
224 }
225 closed = true;
caryclark@google.comc17972e2012-02-20 21:33:22 +0000226 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000227 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000228 default:
229 // FIXME: add other curve types
230 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000231 }
232 if (advance < 0) {
233 edgeIndex = fTops[listIndex];
234 fTops[listIndex] = 0;
235 } else {
236 edgeIndex = fBottoms[listIndex];
237 fBottoms[listIndex] = 0;
238 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000239 if (!edgeIndex) {
240 simple.lineTo(firstPt.fX, firstPt.fY);
241 simple.close();
242 if (gShowDebugf) {
243 SkDebugf("%s close 2 (%g,%g)\n", __FUNCTION__,
244 firstPt.fX, firstPt.fY);
245 }
246 break;
247 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000248 listIndex = abs(edgeIndex) - 1;
249 if (edgeIndex < 0) {
250 fTops[listIndex] = 0;
251 } else {
252 fBottoms[listIndex] = 0;
253 }
254 // if this and next edge go different directions
255 if (advance > 0 ^ edgeIndex < 0) {
256 advance = -advance;
257 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000258 } while (edgeIndex && !closed);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000259 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000260 }
261
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000262 // sort points by y, then x
263 // if x/y is identical, sort bottoms before tops
264 // if identical and both tops/bottoms, sort by angle
265 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
266 const int two) {
267 const OutEdge& oneEdge = edges[abs(one) - 1];
268 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
269 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
270 const OutEdge& twoEdge = edges[abs(two) - 1];
271 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
272 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
273 if (startPt1.fY != startPt2.fY) {
274 if (gDebugLessThan) {
275 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
276 one, two, startPt1.fY, startPt2.fY,
277 startPt1.fY < startPt2.fY ? "true" : "false");
278 }
279 return startPt1.fY < startPt2.fY;
280 }
281 if (startPt1.fX != startPt2.fX) {
282 if (gDebugLessThan) {
283 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
284 one, two, startPt1.fX, startPt2.fX,
285 startPt1.fX < startPt2.fX ? "true" : "false");
286 }
287 return startPt1.fX < startPt2.fX;
288 }
289 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
290 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
291 SkScalar dy1 = startPt1.fY - endPt1.fY;
292 SkScalar dy2 = startPt2.fY - endPt2.fY;
293 SkScalar dy1y2 = dy1 * dy2;
294 if (dy1y2 < 0) { // different signs
295 if (gDebugLessThan) {
296 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
297 dy1 > 0 ? "true" : "false");
298 }
299 return dy1 > 0; // one < two if one goes up and two goes down
300 }
301 if (dy1y2 == 0) {
302 if (gDebugLessThan) {
303 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
304 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
305 }
306 return endPt1.fX < endPt2.fX;
307 }
308 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
309 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
310 if (gDebugLessThan) {
311 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
312 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
313 }
314 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000315 }
316
caryclark@google.com6008c6562012-02-15 22:01:16 +0000317 // Sort the indices of paired points and then create more indices so
318 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000319 void bridge() {
320 size_t index;
321 size_t count = fEdges.count();
322 if (!count) {
323 return;
324 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000325 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000326 fTops.setCount(count);
327 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
328 fBottoms.setCount(count);
329 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000330 SkTDArray<int> order;
331 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000332 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000333 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000334 for (index = 1; index <= count; ++index) {
335 *order.append() = index;
336 }
337 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000338 int* lastPtr = order.end() - 1;
339 int* leftPtr = order.begin();
340 while (leftPtr < lastPtr) {
341 int leftIndex = *leftPtr;
342 int leftOutIndex = abs(leftIndex) - 1;
343 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000344 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000345 int rightIndex = *rightPtr;
346 int rightOutIndex = abs(rightIndex) - 1;
347 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000348 bool pairUp = fFill;
349 if (!pairUp) {
350 const SkPoint& leftMatch =
351 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
352 const SkPoint& rightMatch =
353 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
354 pairUp = leftMatch == rightMatch;
355 } else {
356 SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
357 == right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
358 }
359 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000360 if (leftIndex < 0) {
361 fTops[leftOutIndex] = rightIndex;
362 } else {
363 fBottoms[leftOutIndex] = rightIndex;
364 }
365 if (rightIndex < 0) {
366 fTops[rightOutIndex] = leftIndex;
367 } else {
368 fBottoms[rightOutIndex] = leftIndex;
369 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000370 ++rightPtr;
371 }
372 leftPtr = rightPtr;
373 }
374 }
375
caryclark@google.com6008c6562012-02-15 22:01:16 +0000376protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000377 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000378 SkTDArray<int> fTops;
379 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000380 bool fFill;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000381};
382
caryclark@google.comc6825902012-02-03 22:07:47 +0000383// Bounds, unlike Rect, does not consider a vertical line to be empty.
384struct Bounds : public SkRect {
385 static bool Intersects(const Bounds& a, const Bounds& b) {
386 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
387 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
388 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000389
390 bool isEmpty() {
391 return fLeft > fRight || fTop > fBottom
392 || fLeft == fRight && fTop == fBottom
393 || isnan(fLeft) || isnan(fRight)
394 || isnan(fTop) || isnan(fBottom);
395 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000396};
397
caryclark@google.comc6825902012-02-03 22:07:47 +0000398struct Intercepts {
399 SkTDArray<double> fTs;
400};
401
caryclark@google.com6680fb12012-02-07 22:10:51 +0000402struct InEdge {
403 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000404 return fBounds.fTop == rh.fBounds.fTop
405 ? fBounds.fLeft < rh.fBounds.fLeft
406 : fBounds.fTop < rh.fBounds.fTop;
407 }
408
caryclark@google.com6008c6562012-02-15 22:01:16 +0000409 bool add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000410 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000411 bool foundIntercept = false;
412 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000413 for (size_t index = 0; index < count; ++index) {
414 double t = ts[index];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000415 if (t <= 0 || t >= 1) {
416 continue;
417 }
418 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000419 size_t tCount = intercepts.fTs.count();
420 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
421 if (t <= intercepts.fTs[idx2]) {
422 if (t < intercepts.fTs[idx2]) {
423 *intercepts.fTs.insert(idx2) = t;
424 break;
425 }
426 }
427 }
428 if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
429 *intercepts.fTs.append() = t;
430 }
431 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000432 return foundIntercept;
caryclark@google.comc6825902012-02-03 22:07:47 +0000433 }
434
caryclark@google.com6680fb12012-02-07 22:10:51 +0000435 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000436 // FIXME: in the pathological case where there is a ton of edges, binary search?
437 size_t count = fCached.count();
438 for (size_t index = 0; index < count; ++index) {
439 if (edge == fCached[index]) {
440 return true;
441 }
442 if (edge < fCached[index]) {
443 *fCached.insert(index) = edge;
444 return false;
445 }
446 }
447 *fCached.append() = edge;
448 return false;
449 }
450
451 void complete(signed char winding) {
452 SkPoint* ptPtr = fPts.begin();
453 SkPoint* ptLast = fPts.end();
454 if (ptPtr == ptLast) {
455 SkDebugf("empty edge\n");
456 SkASSERT(0);
457 // FIXME: delete empty edge?
458 return;
459 }
460 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
461 ++ptPtr;
462 while (ptPtr != ptLast) {
463 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
464 ++ptPtr;
465 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000466 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000467 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
468 size_t index;
469 size_t last = fPts.count() - 1;
470 for (index = 0; index < last; ++index, --last) {
471 SkTSwap<SkPoint>(fPts[index], fPts[last]);
472 }
473 last = fVerbs.count() - 1;
474 for (index = 0; index < last; ++index, --last) {
475 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
476 }
477 }
478 fContainsIntercepts = false;
caryclark@google.comc6825902012-02-03 22:07:47 +0000479 }
480
481 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000482 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000483 SkTArray<Intercepts> fIntercepts; // one per verb
484
caryclark@google.comc6825902012-02-03 22:07:47 +0000485 // persistent data
486 SkTDArray<SkPoint> fPts;
487 SkTDArray<uint8_t> fVerbs;
488 Bounds fBounds;
489 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000490 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000491};
492
caryclark@google.com6680fb12012-02-07 22:10:51 +0000493class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000494public:
495
caryclark@google.com6680fb12012-02-07 22:10:51 +0000496InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000497 : fPath(path)
498 , fCurrentEdge(NULL)
499 , fEdges(edges)
500 , fIgnoreHorizontal(ignoreHorizontal)
501{
502 walk();
503}
504
505protected:
506
507void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000508 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000509 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
510 fPtOffset = 1;
511 *fCurrentEdge->fVerbs.append() = fVerb;
512}
513
caryclark@google.com6008c6562012-02-15 22:01:16 +0000514bool complete() {
515 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
516 fCurrentEdge->complete(fWinding);
517 fCurrentEdge = NULL;
518 return true;
519 }
520 return false;
521}
522
caryclark@google.comc6825902012-02-03 22:07:47 +0000523int direction(int count) {
524 fPtCount = count;
525 fIgnorableHorizontal = fIgnoreHorizontal && isHorizontal();
526 if (fIgnorableHorizontal) {
527 return 0;
528 }
529 int last = count - 1;
530 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000531 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
532 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000533}
534
535bool isHorizontal() {
536 SkScalar y = fPts[0].fY;
537 for (int i = 1; i < fPtCount; ++i) {
538 if (fPts[i].fY != y) {
539 return false;
540 }
541 }
542 return true;
543}
544
545void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000546 if (!fCurrentEdge) {
547 fCurrentEdge = fEdges.push_back_n(1);
548 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000549 fWinding = 0;
550 fPtOffset = 0;
551}
552
553void walk() {
554 SkPath::Iter iter(fPath, true);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000555 int winding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000556 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
557 switch (fVerb) {
558 case SkPath::kMove_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000559 if (gShowPath) {
560 SkDebugf("path.moveTo(%g, %g);\n", fPts[0].fX, fPts[0].fY);
561 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000562 startEdge();
563 continue;
564 case SkPath::kLine_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000565 if (gShowPath) {
566 SkDebugf("path.lineTo(%g, %g);\n", fPts[1].fX, fPts[1].fY);
567 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000568 winding = direction(2);
569 break;
570 case SkPath::kQuad_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000571 if (gShowPath) {
572 SkDebugf("path.quadTo(%g, %g, %g, %g);\n",
573 fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY);
574 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000575 winding = direction(3);
576 break;
577 case SkPath::kCubic_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000578 if (gShowPath) {
579 SkDebugf("path.cubicTo(%g, %g, %g, %g);\n",
580 fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY,
581 fPts[3].fX, fPts[3].fY);
582 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000583 winding = direction(4);
584 break;
585 case SkPath::kClose_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000586 if (gShowPath) {
587 SkDebugf("path.close();\n");
588 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000589 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000590 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000591 continue;
592 default:
593 SkDEBUGFAIL("bad verb");
594 return;
595 }
596 if (fIgnorableHorizontal) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000597 if (complete()) {
598 startEdge();
599 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000600 continue;
601 }
602 if (fWinding + winding == 0) {
603 // FIXME: if prior verb or this verb is a horizontal line, reverse
604 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000605 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000606 if (complete()) {
607 startEdge();
608 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000609 }
610 fWinding = winding;
611 addEdge();
612 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000613 if (!complete()) {
614 if (fCurrentEdge) {
615 fEdges.pop_back();
616 }
617 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000618 if (gShowPath) {
619 SkDebugf("\n");
620 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000621}
622
623private:
624 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000625 InEdge* fCurrentEdge;
626 SkTArray<InEdge>& fEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000627 SkPoint fPts[4];
628 SkPath::Verb fVerb;
629 int fPtCount;
630 int fPtOffset;
631 int8_t fWinding;
632 bool fIgnorableHorizontal;
633 bool fIgnoreHorizontal;
634};
635
caryclark@google.com6680fb12012-02-07 22:10:51 +0000636struct WorkEdge {
637 SkScalar bottom() const {
638 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000639 }
640
caryclark@google.com6680fb12012-02-07 22:10:51 +0000641 void init(const InEdge* edge) {
642 fEdge = edge;
643 fPts = edge->fPts.begin();
644 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000645 }
646
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000647 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000648 SkASSERT(fVerb < fEdge->fVerbs.end());
649 fPts += *fVerb++;
650 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000651 }
652
653 SkPath::Verb verb() const {
654 return (SkPath::Verb) *fVerb;
655 }
656
caryclark@google.com6008c6562012-02-15 22:01:16 +0000657 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000658 return fVerb - fEdge->fVerbs.begin();
659 }
660
661 int winding() const {
662 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000663 }
664
caryclark@google.com6680fb12012-02-07 22:10:51 +0000665 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000666 const SkPoint* fPts;
667 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000668};
669
caryclark@google.com6680fb12012-02-07 22:10:51 +0000670// always constructed with SkTDArray because new edges are inserted
671// this may be a inappropriate optimization, suggesting that a separate array of
672// ActiveEdge* may be faster to insert and search
caryclark@google.comc6825902012-02-03 22:07:47 +0000673struct ActiveEdge {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000674 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000675 return fXAbove != rh.fXAbove ? fXAbove < rh.fXAbove
676 : fXBelow < rh.fXBelow;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000677 }
678
679 void calcLeft() {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000680 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
681 if (fDone)
682 return; // nothing to do; use last
683 switch (fWorkEdge.verb()) {
684 case SkPath::kLine_Verb: {
685 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
686 // for the fTIndex, don't do it again
687 // For identical x, this lets us know which edge is first.
688 // If both edges have T values < 1, check x at next T (fXBelow).
689 int add = (fTIndex <= fTs->count()) - 1;
690 double tAbove = t(fTIndex + add);
691 fXAbove = LineXAtT(fWorkEdge.fPts, tAbove);
692 double tBelow = t(fTIndex - ~add);
693 fXBelow = LineXAtT(fWorkEdge.fPts, tBelow);
694 break;
695 }
696 default:
697 // FIXME: add support for all curve types
698 SkASSERT(0);
699 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000700 }
701
caryclark@google.com6680fb12012-02-07 22:10:51 +0000702 void init(const InEdge* edge) {
703 fWorkEdge.init(edge);
704 initT();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000705 fDone = false;
706 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000707 }
708
709 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000710 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
711 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
712 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
713 fTs = &interceptPtr->fTs;
714 // the above is conceptually the same as
715 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
716 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +0000717 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000718 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000719
720 bool isCoincidentWith(const ActiveEdge* edge) const {
721 if (fXAbove != edge->fXAbove || fXBelow != edge->fXBelow) {
722 return false;
723 }
724 switch (fWorkEdge.verb()) {
725 case SkPath::kLine_Verb: {
726 return (fWorkEdge.fPts[0].fX - fWorkEdge.fPts[1].fX) *
727 (edge->fWorkEdge.fPts[0].fY - edge->fWorkEdge.fPts[1].fY) ==
728 (fWorkEdge.fPts[0].fY - fWorkEdge.fPts[1].fY) *
729 (edge->fWorkEdge.fPts[0].fX - edge->fWorkEdge.fPts[1].fX);
730 }
731 default:
732 // FIXME: add support for all curve types
733 SkASSERT(0);
734 }
735 return false;
736 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000737
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000738 bool advanceT() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000739 SkASSERT(fTIndex <= fTs->count());
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000740 return ++fTIndex <= fTs->count();
caryclark@google.com6680fb12012-02-07 22:10:51 +0000741 }
742
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000743 bool advance() {
744 // FIXME: flip sense of next
745 bool result = fWorkEdge.advance();
746 fDone = !result;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000747 initT();
748 return result;
749 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000750
751 bool done(SkScalar y) {
752 return fDone || fYBottom > y;
753 }
754
755 double nextT() {
756 SkASSERT(fTIndex <= fTs->count());
757 return t(fTIndex + 1);
758 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000759
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000760 double t() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000761 if (fTIndex == 0) {
762 return 0;
763 }
764 if (fTIndex > fTs->count()) {
765 return 1;
766 }
767 return (*fTs)[fTIndex - 1];
768 }
769
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000770 double t(int tIndex) const {
771 if (tIndex == 0) {
772 return 0;
773 }
774 if (tIndex > fTs->count()) {
775 return 1;
776 }
777 return (*fTs)[tIndex - 1];
778 }
779
caryclark@google.com6680fb12012-02-07 22:10:51 +0000780 WorkEdge fWorkEdge;
781 const SkTDArray<double>* fTs;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000782 SkScalar fXAbove;
783 SkScalar fXBelow;
784 SkScalar fYBottom;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000785 int fTIndex;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000786 bool fSkip;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000787 bool fDone;
caryclark@google.comc6825902012-02-03 22:07:47 +0000788};
789
caryclark@google.com6680fb12012-02-07 22:10:51 +0000790static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000791 size_t count = activeEdges.count();
792 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000793 if (edge == activeEdges[index].fWorkEdge.fEdge) {
794 return;
795 }
796 }
797 ActiveEdge* active = activeEdges.append();
798 active->init(edge);
799}
800
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000801 // find any intersections in the range of active edges
802static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
803 InEdge** testPtr = currentPtr;
804 InEdge* test = *testPtr;
805 while (testPtr != lastPtr) {
806 if (test->fBounds.fBottom > bottom) {
807 WorkEdge wt;
808 wt.init(test);
809 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000810 // OPTIMIZATION: if bottom intersection does not change
811 // the winding on either side of the split, don't intersect
812 if (wt.verb() == SkPath::kLine_Verb) {
813 double wtTs[2];
814 int pts = LineIntersect(wt.fPts, bottom, wtTs);
815 if (pts) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000816 test->fContainsIntercepts |= test->add(wtTs, pts,
817 wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000818 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000819 } else {
820 // FIXME: add all curve types
821 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000822 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000823 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000824 }
825 test = *++testPtr;
826 }
827}
828
829static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000830 InEdge** testPtr = currentPtr - 1;
831 while (++testPtr != lastPtr - 1) {
832 InEdge* test = *testPtr;
833 InEdge** nextPtr = testPtr;
834 do {
835 InEdge* next = *++nextPtr;
836 if (test->cached(next)) {
837 continue;
838 }
839 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
840 continue;
841 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000842 WorkEdge wt, wn;
843 wt.init(test);
844 wn.init(next);
845 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000846 if (wt.verb() == SkPath::kLine_Verb
847 && wn.verb() == SkPath::kLine_Verb) {
848 double wtTs[2], wnTs[2];
849 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
850 if (pts) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000851 test->fContainsIntercepts |= test->add(wtTs, pts,
852 wt.verbIndex());
853 next->fContainsIntercepts |= next->add(wnTs, pts,
854 wn.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000855 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000856 } else {
857 // FIXME: add all combinations of curve types
858 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000859 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000860 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
861 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000862 }
863}
864
caryclark@google.com6008c6562012-02-15 22:01:16 +0000865static InEdge** advanceEdges(SkTDArray<ActiveEdge>& activeEdges,
866 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
867 InEdge** testPtr = currentPtr - 1;
868 while (++testPtr != lastPtr) {
869 if ((*testPtr)->fBounds.fBottom > y) {
870 continue;
871 }
872 InEdge* test = *testPtr;
873 ActiveEdge* activePtr = activeEdges.begin() - 1;
874 ActiveEdge* lastActive = activeEdges.end();
875 while (++activePtr != lastActive) {
876 if (activePtr->fWorkEdge.fEdge == test) {
877 activeEdges.remove(activePtr - activeEdges.begin());
878 break;
879 }
880 }
881 if (testPtr == currentPtr) {
882 ++currentPtr;
883 }
884 }
885 return currentPtr;
886}
887
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000888// compute bottom taking into account any intersected edges
889static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +0000890 SkScalar y, SkScalar& bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000891 ActiveEdge* activePtr = activeEdges.begin() - 1;
892 ActiveEdge* lastActive = activeEdges.end();
893 while (++activePtr != lastActive) {
894 const InEdge* test = activePtr->fWorkEdge.fEdge;
895 if (!test->fContainsIntercepts) {
896 continue;
897 }
898 WorkEdge wt;
899 wt.init(test);
900 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000901 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
902 const SkTDArray<double>& fTs = intercepts.fTs;
903 size_t count = fTs.count();
904 for (size_t index = 0; index < count; ++index) {
905 if (wt.verb() == SkPath::kLine_Verb) {
906 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000907 if (yIntercept > y && bottom > yIntercept) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000908 bottom = yIntercept;
909 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000910 } else {
911 // FIXME: add all curve types
912 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000913 }
914 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000915 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000916 }
917}
918
919static SkScalar findBottom(InEdge** currentPtr,
920 InEdge** edgeListEnd, SkTDArray<ActiveEdge>& activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +0000921 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000922 InEdge* current = *currentPtr;
923 SkScalar bottom = current->fBounds.fBottom;
924
925 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +0000926 InEdge* test = *testPtr;
927 while (testPtr != edgeListEnd) {
928 SkScalar testTop = test->fBounds.fTop;
929 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000930 break;
931 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000932 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000933 // OPTIMIZATION: Shortening the bottom is only interesting when filling
934 // and when the edge is to the left of a longer edge. If it's a framing
935 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +0000936 if (testTop > y) {
937 bottom = testTop;
938 break;
939 }
940 if (y < testBottom) {
941 if (bottom > testBottom) {
942 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000943 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000944 addToActive(activeEdges, test);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000945 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000946 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000947 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000948 return bottom;
949}
950
951static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
952 SkTDArray<InEdge*>& edgeList) {
953 size_t edgeCount = edges.count();
954 if (edgeCount == 0) {
955 return;
956 }
957 for (size_t index = 0; index < edgeCount; ++index) {
958 *edgeList.append() = &edges[index];
959 }
960 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
961 *edgeList.append() = &edgeSentinel;
962 ++edgeCount;
963 QSort<InEdge>(edgeList.begin(), edgeCount);
964}
965
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000966
967static void skipCoincidence(int lastWinding, int winding, int windingMask,
968 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
969 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
970 return;
971 }
972 if (lastWinding) {
973 activePtr->fSkip = false;
974 } else {
975 firstCoincident->fSkip = false;
976 }
977}
978
caryclark@google.com6008c6562012-02-15 22:01:16 +0000979static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000980 SkTDArray<ActiveEdge*>& edgeList, int windingMask) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000981 size_t edgeCount = activeEdges.count();
982 if (edgeCount == 0) {
983 return;
984 }
985 size_t index;
986 for (index = 0; index < edgeCount; ++index) {
987 ActiveEdge& activeEdge = activeEdges[index];
988 activeEdge.calcLeft();
989 activeEdge.fSkip = false;
990 *edgeList.append() = &activeEdge;
991 }
992 QSort<ActiveEdge>(edgeList.begin(), edgeCount);
993 // remove coincident edges
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000994 // OPTIMIZE: remove edges? This is tricky because the current logic expects
995 // the winding count to be maintained while skipping coincident edges. In
996 // addition to removing the coincident edges, the remaining edges would need
997 // to have a different winding value, possibly different per intercept span.
998 int lastWinding = 0;
999 bool lastSkipped = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001000 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001001 ActiveEdge* firstCoincident = NULL;
1002 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001003 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001004 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001005 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001006 if (activePtr->isCoincidentWith(nextPtr)) {
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001007 if (!firstCoincident) {
1008 firstCoincident = activePtr;
1009 }
1010 activePtr->fSkip = nextPtr->fSkip = lastSkipped = true;
1011 } else if (lastSkipped) {
1012 skipCoincidence(lastWinding, winding, windingMask, activePtr,
1013 firstCoincident);
1014 lastSkipped = false;
1015 firstCoincident = NULL;
1016 }
1017 if (!lastSkipped) {
1018 lastWinding = winding;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001019 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001020 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001021 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001022 if (lastSkipped) {
1023 winding += activePtr->fWorkEdge.winding();
1024 skipCoincidence(lastWinding, winding, windingMask, activePtr,
1025 firstCoincident);
1026 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001027}
1028
1029// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00001030static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001031 SkScalar bottom, int windingMask, OutEdgeBuilder& outBuilder) {
1032 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001033 ActiveEdge** activeHandle = edgeList.begin() - 1;
1034 ActiveEdge** lastActive = edgeList.end();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001035 if (gShowDebugf) {
caryclark@google.comc17972e2012-02-20 21:33:22 +00001036 SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
1037 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001038 while (++activeHandle != lastActive) {
1039 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001040 const WorkEdge& wt = activePtr->fWorkEdge;
1041 int lastWinding = winding;
1042 winding += wt.winding();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001043 if (activePtr->done(y)) {
1044 continue;
1045 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001046 int opener = (lastWinding & windingMask) == 0;
1047 bool closer = (winding & windingMask) == 0;
1048 SkASSERT(!opener | !closer);
1049 bool inWinding = opener | closer;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001050 const SkPoint* clipped;
1051 uint8_t verb = wt.verb();
1052 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001053 do {
1054 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001055 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001056 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001057 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001058 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001059 nextT = activePtr->nextT();
1060 if (verb == SkPath::kLine_Verb) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001061 SkPoint clippedPts[2];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001062 // FIXME: obtuse: want efficient way to say
1063 // !currentT && currentT != 1 || !nextT && nextT != 1
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001064 if (currentT * nextT != 0 || currentT + nextT != 1) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001065 // OPTIMIZATION: if !inWinding, we only need
1066 // clipped[1].fY
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001067 LineSubDivide(points, currentT, nextT, clippedPts);
1068 clipped = clippedPts;
1069 } else {
1070 clipped = points;
1071 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001072 if (inWinding && !activePtr->fSkip) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001073 if (gShowDebugf) {
caryclark@google.comc17972e2012-02-20 21:33:22 +00001074 SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
1075 clipped[0].fX, clipped[0].fY,
1076 clipped[1].fX, clipped[1].fY);
1077 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001078 outBuilder.addLine(clipped);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001079 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001080 } else {
1081 // FIXME: add all curve types
1082 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001083 }
1084 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001085 moreToDo = activePtr->advanceT();
1086 activePtr->fYBottom = clipped[verb].fY;
1087 aboveBottom = activePtr->fYBottom < bottom;
1088 } while (moreToDo & aboveBottom);
1089 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001090 }
1091}
1092
caryclark@google.comc6825902012-02-03 22:07:47 +00001093void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001094 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
1095 int windingMask = (path.getFillType() & 1) ? 1 : -1;
1096 simple.reset();
1097 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00001098 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001099 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
1100 // unbroken. Once we have a list, sort it, then walk the list (walk edges
1101 // twice that have y extrema's on top) and detect crossings -- look for raw
1102 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00001103 SkTArray<InEdge> edges;
1104 InEdgeBuilder builder(path, asFill, edges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001105 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001106 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001107 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001108 InEdge** currentPtr = edgeList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +00001109 // walk the sorted edges from top to bottom, computing accumulated winding
caryclark@google.com6680fb12012-02-07 22:10:51 +00001110 SkTDArray<ActiveEdge> activeEdges;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001111 OutEdgeBuilder outBuilder(asFill);
1112 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.comc6825902012-02-03 22:07:47 +00001113 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001114 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001115 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
1116 activeEdges, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001117 if (lastPtr > currentPtr) {
1118 addBottomT(currentPtr, lastPtr, bottom);
1119 addIntersectingTs(currentPtr, lastPtr);
1120 computeInterceptBottom(activeEdges, y, bottom);
1121 SkTDArray<ActiveEdge*> activeEdgeList;
1122 sortHorizontal(activeEdges, activeEdgeList, windingMask);
1123 stitchEdge(activeEdgeList, y, bottom, windingMask, outBuilder);
1124 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001125 y = bottom;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001126 currentPtr = advanceEdges(activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00001127 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00001128 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001129 outBuilder.bridge();
1130 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00001131}