blob: 771e63960f7dff7cad561ae43d39db6928405497 [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.com4917f172012-03-05 22:01:21 +000017static bool gShowDebugf = false; // FIXME: remove once debugging is complete
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000018static bool gShowPath = false;
caryclark@google.com4917f172012-03-05 22:01:21 +000019static 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.comcd4421d2012-03-01 19:16:31 +000033static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000034 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
caryclark@google.comcd4421d2012-03-01 19:16:31 +000035 double x, y;
36 xy_at_t(aLine, t, x, y);
37 out->fX = SkDoubleToScalar(x);
38 out->fY = SkDoubleToScalar(y);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +000039}
40
caryclark@google.com6680fb12012-02-07 22:10:51 +000041static SkScalar LineYAtT(const SkPoint a[2], double t) {
42 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
43 double y;
44 xy_at_t(aLine, t, *(double*) 0, y);
45 return SkDoubleToScalar(y);
46}
47
48static void LineSubDivide(const SkPoint a[2], double startT, double endT,
49 SkPoint sub[2]) {
50 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
51 _Line dst;
52 sub_divide(aLine, startT, endT, dst);
53 sub[0].fX = SkDoubleToScalar(dst[0].x);
54 sub[0].fY = SkDoubleToScalar(dst[0].y);
55 sub[1].fX = SkDoubleToScalar(dst[1].x);
56 sub[1].fY = SkDoubleToScalar(dst[1].y);
57}
58
59
caryclark@google.comc6825902012-02-03 22:07:47 +000060// functions
61void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
62void simplify(const SkPath& path, bool asFill, SkPath& simple);
63/*
64list of edges
65bounds for edge
66sort
67active T
68
69if a contour's bounds is outside of the active area, no need to create edges
70*/
71
72/* given one or more paths,
73 find the bounds of each contour, select the active contours
74 for each active contour, compute a set of edges
75 each edge corresponds to one or more lines and curves
76 leave edges unbroken as long as possible
77 when breaking edges, compute the t at the break but leave the control points alone
78
79 */
80
81void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
82 SkPath::Iter iter(path, false);
83 SkPoint pts[4];
84 SkPath::Verb verb;
85 SkRect bounds;
86 bounds.setEmpty();
87 int count = 0;
88 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
89 switch (verb) {
90 case SkPath::kMove_Verb:
91 if (!bounds.isEmpty()) {
92 *boundsArray.append() = bounds;
93 }
94 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
95 count = 0;
96 break;
97 case SkPath::kLine_Verb:
98 count = 1;
99 break;
100 case SkPath::kQuad_Verb:
101 count = 2;
102 break;
103 case SkPath::kCubic_Verb:
104 count = 3;
105 break;
106 case SkPath::kClose_Verb:
107 count = 0;
108 break;
109 default:
110 SkDEBUGFAIL("bad verb");
111 return;
112 }
113 for (int i = 1; i <= count; ++i) {
114 bounds.growToInclude(pts[i].fX, pts[i].fY);
115 }
116 }
117}
118
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000119static bool extendLine(const SkPoint line[2], const SkPoint& add) {
120 // FIXME: allow this to extend lines that have slopes that are nearly equal
121 SkScalar dx1 = line[1].fX - line[0].fX;
122 SkScalar dy1 = line[1].fY - line[0].fY;
123 SkScalar dx2 = add.fX - line[0].fX;
124 SkScalar dy2 = add.fY - line[0].fY;
125 return dx1 * dy2 == dx2 * dy1;
126}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000127
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000128struct OutEdge {
129 bool operator<(const OutEdge& rh) const {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000130 const SkPoint& first = fPts[0];
131 const SkPoint& rhFirst = rh.fPts[0];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000132 return first.fY == rhFirst.fY
133 ? first.fX < rhFirst.fX
134 : first.fY < rhFirst.fY;
135 }
136
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000137 SkPoint fPts[4];
138 uint8_t fVerb; // FIXME: not read from everywhere
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000139};
140
caryclark@google.com6680fb12012-02-07 22:10:51 +0000141class OutEdgeBuilder {
142public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000143 OutEdgeBuilder(bool fill)
144 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000145 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000146
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000147 void addLine(const SkPoint line[2]) {
caryclark@google.comc17972e2012-02-20 21:33:22 +0000148 OutEdge& newEdge = fEdges.push_back();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000149 newEdge.fPts[0] = line[0];
150 newEdge.fPts[1] = line[1];
151 newEdge.fVerb = SkPath::kLine_Verb;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000152 }
153
154 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000155 size_t listCount = fEdges.count();
156 if (listCount == 0) {
157 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000158 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000159 do {
160 size_t listIndex = 0;
161 int advance = 1;
162 while (listIndex < listCount && fTops[listIndex] == 0) {
163 ++listIndex;
164 }
165 if (listIndex >= listCount) {
166 break;
167 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000168 int closeEdgeIndex = -listIndex - 1;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000169 SkPoint firstPt, lastLine[2];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000170 bool doMove = true;
171 int edgeIndex;
172 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000173 SkPoint* ptArray = fEdges[listIndex].fPts;
174 uint8_t verb = fEdges[listIndex].fVerb;
175 SkPoint* start, * end;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000176 if (advance < 0) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000177 start = &ptArray[verb];
178 end = &ptArray[0];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000179 } else {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000180 start = &ptArray[0];
181 end = &ptArray[verb];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000182 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000183 switch (verb) {
184 case SkPath::kLine_Verb:
185 bool gap;
186 if (doMove) {
187 firstPt = *start;
188 simple.moveTo(start->fX, start->fY);
189 if (gShowDebugf) {
190 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
191 start->fX, start->fY);
192 }
193 lastLine[0] = *start;
194 lastLine[1] = *end;
195 doMove = false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000196 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;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000218 break;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000219 default:
220 // FIXME: add other curve types
221 ;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000222 }
223 if (advance < 0) {
224 edgeIndex = fTops[listIndex];
225 fTops[listIndex] = 0;
226 } else {
227 edgeIndex = fBottoms[listIndex];
228 fBottoms[listIndex] = 0;
229 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000230 if (edgeIndex) {
231 listIndex = abs(edgeIndex) - 1;
232 if (edgeIndex < 0) {
233 fTops[listIndex] = 0;
234 } else {
235 fBottoms[listIndex] = 0;
236 }
237 }
238 if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
239 if (lastLine[1] != firstPt) {
240 simple.lineTo(lastLine[1].fX, lastLine[1].fY);
241 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000242 simple.lineTo(firstPt.fX, firstPt.fY);
243 simple.close();
244 if (gShowDebugf) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000245 SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
246 firstPt.fX, firstPt.fY);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000247 }
248 break;
249 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000250 // if this and next edge go different directions
251 if (advance > 0 ^ edgeIndex < 0) {
252 advance = -advance;
253 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000254 } while (edgeIndex);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000255 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000256 }
257
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000258 // sort points by y, then x
259 // if x/y is identical, sort bottoms before tops
260 // if identical and both tops/bottoms, sort by angle
261 static bool lessThan(SkTArray<OutEdge>& edges, const int one,
262 const int two) {
263 const OutEdge& oneEdge = edges[abs(one) - 1];
264 int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
265 const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
266 const OutEdge& twoEdge = edges[abs(two) - 1];
267 int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
268 const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
269 if (startPt1.fY != startPt2.fY) {
270 if (gDebugLessThan) {
271 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
272 one, two, startPt1.fY, startPt2.fY,
273 startPt1.fY < startPt2.fY ? "true" : "false");
274 }
275 return startPt1.fY < startPt2.fY;
276 }
277 if (startPt1.fX != startPt2.fX) {
278 if (gDebugLessThan) {
279 SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
280 one, two, startPt1.fX, startPt2.fX,
281 startPt1.fX < startPt2.fX ? "true" : "false");
282 }
283 return startPt1.fX < startPt2.fX;
284 }
285 const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
286 const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
287 SkScalar dy1 = startPt1.fY - endPt1.fY;
288 SkScalar dy2 = startPt2.fY - endPt2.fY;
289 SkScalar dy1y2 = dy1 * dy2;
290 if (dy1y2 < 0) { // different signs
291 if (gDebugLessThan) {
292 SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
293 dy1 > 0 ? "true" : "false");
294 }
295 return dy1 > 0; // one < two if one goes up and two goes down
296 }
297 if (dy1y2 == 0) {
298 if (gDebugLessThan) {
299 SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
300 one, two, endPt1.fX < endPt2.fX ? "true" : "false");
301 }
302 return endPt1.fX < endPt2.fX;
303 }
304 SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
305 SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
306 if (gDebugLessThan) {
307 SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
308 one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
309 }
310 return dy2 > 0 ^ dx1y2 < dx2y1;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000311 }
312
caryclark@google.com6008c6562012-02-15 22:01:16 +0000313 // Sort the indices of paired points and then create more indices so
314 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000315 void bridge() {
316 size_t index;
317 size_t count = fEdges.count();
318 if (!count) {
319 return;
320 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000321 SkASSERT(!fFill || count > 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000322 fTops.setCount(count);
323 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
324 fBottoms.setCount(count);
325 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000326 SkTDArray<int> order;
327 for (index = 1; index <= count; ++index) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000328 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000329 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000330 for (index = 1; index <= count; ++index) {
331 *order.append() = index;
332 }
333 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000334 int* lastPtr = order.end() - 1;
335 int* leftPtr = order.begin();
336 while (leftPtr < lastPtr) {
337 int leftIndex = *leftPtr;
338 int leftOutIndex = abs(leftIndex) - 1;
339 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000340 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000341 int rightIndex = *rightPtr;
342 int rightOutIndex = abs(rightIndex) - 1;
343 const OutEdge& right = fEdges[rightOutIndex];
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000344 bool pairUp = fFill;
345 if (!pairUp) {
346 const SkPoint& leftMatch =
347 left.fPts[leftIndex < 0 ? 0 : left.fVerb];
348 const SkPoint& rightMatch =
349 right.fPts[rightIndex < 0 ? 0 : right.fVerb];
350 pairUp = leftMatch == rightMatch;
351 } else {
352 SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
353 == right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
354 }
355 if (pairUp) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000356 if (leftIndex < 0) {
357 fTops[leftOutIndex] = rightIndex;
358 } else {
359 fBottoms[leftOutIndex] = rightIndex;
360 }
361 if (rightIndex < 0) {
362 fTops[rightOutIndex] = leftIndex;
363 } else {
364 fBottoms[rightOutIndex] = leftIndex;
365 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000366 ++rightPtr;
367 }
368 leftPtr = rightPtr;
369 }
370 }
371
caryclark@google.com6008c6562012-02-15 22:01:16 +0000372protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000373 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000374 SkTDArray<int> fTops;
375 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000376 bool fFill;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000377};
378
caryclark@google.comc6825902012-02-03 22:07:47 +0000379// Bounds, unlike Rect, does not consider a vertical line to be empty.
380struct Bounds : public SkRect {
381 static bool Intersects(const Bounds& a, const Bounds& b) {
382 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
383 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
384 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000385
386 bool isEmpty() {
387 return fLeft > fRight || fTop > fBottom
388 || fLeft == fRight && fTop == fBottom
389 || isnan(fLeft) || isnan(fRight)
390 || isnan(fTop) || isnan(fBottom);
391 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000392};
393
caryclark@google.com4917f172012-03-05 22:01:21 +0000394class Intercepts {
395public:
396 Intercepts()
397 : fTopIntercepts(0)
398 , fBottomIntercepts(0) {
399 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000400 SkTDArray<double> fTs;
caryclark@google.com4917f172012-03-05 22:01:21 +0000401 int fTopIntercepts;
402 int fBottomIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000403};
404
caryclark@google.com6680fb12012-02-07 22:10:51 +0000405struct InEdge {
406 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000407 return fBounds.fTop == rh.fBounds.fTop
408 ? fBounds.fLeft < rh.fBounds.fLeft
409 : fBounds.fTop < rh.fBounds.fTop;
410 }
411
caryclark@google.com4917f172012-03-05 22:01:21 +0000412 void add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000413 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000414 bool foundIntercept = false;
415 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000416 for (size_t index = 0; index < count; ++index) {
417 double t = ts[index];
caryclark@google.com4917f172012-03-05 22:01:21 +0000418 if (t <= 0) {
419 fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
420 continue;
421 }
422 if (t >= 1) {
423 fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000424 continue;
425 }
426 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000427 size_t tCount = intercepts.fTs.count();
428 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
429 if (t <= intercepts.fTs[idx2]) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000430 double delta = intercepts.fTs[idx2] - t;
431 if (delta > 0) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000432 *intercepts.fTs.insert(idx2) = t;
caryclark@google.comc6825902012-02-03 22:07:47 +0000433 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000434 fContainsIntercepts = true;
435 return;
caryclark@google.comc6825902012-02-03 22:07:47 +0000436 }
437 }
438 if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
439 *intercepts.fTs.append() = t;
440 }
441 }
caryclark@google.com4917f172012-03-05 22:01:21 +0000442 fContainsIntercepts |= foundIntercept;
caryclark@google.comc6825902012-02-03 22:07:47 +0000443 }
444
caryclark@google.com6680fb12012-02-07 22:10:51 +0000445 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000446 // FIXME: in the pathological case where there is a ton of edges, binary search?
447 size_t count = fCached.count();
448 for (size_t index = 0; index < count; ++index) {
449 if (edge == fCached[index]) {
450 return true;
451 }
452 if (edge < fCached[index]) {
453 *fCached.insert(index) = edge;
454 return false;
455 }
456 }
457 *fCached.append() = edge;
458 return false;
459 }
460
461 void complete(signed char winding) {
462 SkPoint* ptPtr = fPts.begin();
463 SkPoint* ptLast = fPts.end();
464 if (ptPtr == ptLast) {
465 SkDebugf("empty edge\n");
466 SkASSERT(0);
467 // FIXME: delete empty edge?
468 return;
469 }
470 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
471 ++ptPtr;
472 while (ptPtr != ptLast) {
473 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
474 ++ptPtr;
475 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000476 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000477 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
478 size_t index;
479 size_t last = fPts.count() - 1;
480 for (index = 0; index < last; ++index, --last) {
481 SkTSwap<SkPoint>(fPts[index], fPts[last]);
482 }
483 last = fVerbs.count() - 1;
484 for (index = 0; index < last; ++index, --last) {
485 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
486 }
487 }
488 fContainsIntercepts = false;
caryclark@google.comc6825902012-02-03 22:07:47 +0000489 }
490
491 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000492 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000493 SkTArray<Intercepts> fIntercepts; // one per verb
caryclark@google.com4917f172012-03-05 22:01:21 +0000494
caryclark@google.comc6825902012-02-03 22:07:47 +0000495 // persistent data
496 SkTDArray<SkPoint> fPts;
497 SkTDArray<uint8_t> fVerbs;
498 Bounds fBounds;
499 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000500 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000501};
502
caryclark@google.com6680fb12012-02-07 22:10:51 +0000503class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000504public:
505
caryclark@google.com6680fb12012-02-07 22:10:51 +0000506InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000507 : fPath(path)
508 , fCurrentEdge(NULL)
509 , fEdges(edges)
510 , fIgnoreHorizontal(ignoreHorizontal)
511{
512 walk();
513}
514
515protected:
516
517void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000518 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000519 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
520 fPtOffset = 1;
521 *fCurrentEdge->fVerbs.append() = fVerb;
522}
523
caryclark@google.com6008c6562012-02-15 22:01:16 +0000524bool complete() {
525 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
526 fCurrentEdge->complete(fWinding);
527 fCurrentEdge = NULL;
528 return true;
529 }
530 return false;
531}
532
caryclark@google.comc6825902012-02-03 22:07:47 +0000533int direction(int count) {
534 fPtCount = count;
535 fIgnorableHorizontal = fIgnoreHorizontal && isHorizontal();
536 if (fIgnorableHorizontal) {
537 return 0;
538 }
539 int last = count - 1;
540 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000541 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
542 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000543}
544
545bool isHorizontal() {
546 SkScalar y = fPts[0].fY;
547 for (int i = 1; i < fPtCount; ++i) {
548 if (fPts[i].fY != y) {
549 return false;
550 }
551 }
552 return true;
553}
554
555void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000556 if (!fCurrentEdge) {
557 fCurrentEdge = fEdges.push_back_n(1);
558 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000559 fWinding = 0;
560 fPtOffset = 0;
561}
562
563void walk() {
564 SkPath::Iter iter(fPath, true);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000565 int winding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000566 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
567 switch (fVerb) {
568 case SkPath::kMove_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000569 if (gShowPath) {
570 SkDebugf("path.moveTo(%g, %g);\n", fPts[0].fX, fPts[0].fY);
571 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000572 startEdge();
573 continue;
574 case SkPath::kLine_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000575 if (gShowPath) {
576 SkDebugf("path.lineTo(%g, %g);\n", fPts[1].fX, fPts[1].fY);
577 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000578 winding = direction(2);
579 break;
580 case SkPath::kQuad_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000581 if (gShowPath) {
582 SkDebugf("path.quadTo(%g, %g, %g, %g);\n",
583 fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY);
584 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000585 winding = direction(3);
586 break;
587 case SkPath::kCubic_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000588 if (gShowPath) {
589 SkDebugf("path.cubicTo(%g, %g, %g, %g);\n",
590 fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY,
591 fPts[3].fX, fPts[3].fY);
592 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000593 winding = direction(4);
594 break;
595 case SkPath::kClose_Verb:
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000596 if (gShowPath) {
597 SkDebugf("path.close();\n");
598 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000599 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000600 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000601 continue;
602 default:
603 SkDEBUGFAIL("bad verb");
604 return;
605 }
606 if (fIgnorableHorizontal) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000607 if (complete()) {
608 startEdge();
609 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000610 continue;
611 }
612 if (fWinding + winding == 0) {
613 // FIXME: if prior verb or this verb is a horizontal line, reverse
614 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000615 SkASSERT(fCurrentEdge);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000616 if (complete()) {
617 startEdge();
618 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000619 }
620 fWinding = winding;
621 addEdge();
622 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +0000623 if (!complete()) {
624 if (fCurrentEdge) {
625 fEdges.pop_back();
626 }
627 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000628 if (gShowPath) {
629 SkDebugf("\n");
630 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000631}
632
633private:
634 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000635 InEdge* fCurrentEdge;
636 SkTArray<InEdge>& fEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000637 SkPoint fPts[4];
638 SkPath::Verb fVerb;
639 int fPtCount;
640 int fPtOffset;
641 int8_t fWinding;
642 bool fIgnorableHorizontal;
643 bool fIgnoreHorizontal;
644};
645
caryclark@google.com6680fb12012-02-07 22:10:51 +0000646struct WorkEdge {
647 SkScalar bottom() const {
648 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000649 }
650
caryclark@google.com6680fb12012-02-07 22:10:51 +0000651 void init(const InEdge* edge) {
652 fEdge = edge;
653 fPts = edge->fPts.begin();
654 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000655 }
656
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000657 bool advance() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000658 SkASSERT(fVerb < fEdge->fVerbs.end());
659 fPts += *fVerb++;
660 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000661 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000662
663 SkPath::Verb lastVerb() const {
664 SkASSERT(fVerb > fEdge->fVerbs.begin());
665 return (SkPath::Verb) fVerb[-1];
666 }
667
caryclark@google.comc6825902012-02-03 22:07:47 +0000668
669 SkPath::Verb verb() const {
670 return (SkPath::Verb) *fVerb;
671 }
672
caryclark@google.com6008c6562012-02-15 22:01:16 +0000673 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000674 return fVerb - fEdge->fVerbs.begin();
675 }
676
677 int winding() const {
678 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000679 }
680
caryclark@google.com6680fb12012-02-07 22:10:51 +0000681 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000682 const SkPoint* fPts;
683 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000684};
685
caryclark@google.com6680fb12012-02-07 22:10:51 +0000686// always constructed with SkTDArray because new edges are inserted
687// this may be a inappropriate optimization, suggesting that a separate array of
688// ActiveEdge* may be faster to insert and search
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000689
690// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
691// as active edges are introduced, only local sorting should be required
caryclark@google.comc6825902012-02-03 22:07:47 +0000692struct ActiveEdge {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000693 // OPTIMIZATION: fold return statements into one
caryclark@google.com6008c6562012-02-15 22:01:16 +0000694 bool operator<(const ActiveEdge& rh) const {
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000695 if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY
696 && fBelow.fY < rh.fBelow.fY
697 || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY
698 && rh.fBelow.fY < fBelow.fY) {
699 // FIXME: need to compute distance, not check for points equal
700 const SkPoint& check = rh.fBelow.fY <= fBelow.fY
701 && fBelow != rh.fBelow ? rh.fBelow :
702 rh.fAbove;
703 if (gDebugLessThan) {
704 SkDebugf("%s < %c %cthis (%d){%1.2g,%1.2g %1.2g,%1.2g}"
705 " < rh (%d){%1.2g,%1.2g %1.2g,%1.2g}\n", __FUNCTION__,
706 rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A',
707 (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
708 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)
709 ? ' ' : '!',
710 fIndex, fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
711 rh.fIndex, rh.fAbove.fX, rh.fAbove.fY,
712 rh.fBelow.fX, rh.fBelow.fY);
713 }
714 return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX)
715 < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX);
716 }
717 // FIXME: need to compute distance, not check for points equal
718 const SkPoint& check = fBelow.fY <= rh.fBelow.fY
719 && fBelow != rh.fBelow ? fBelow : fAbove;
720 if (gDebugLessThan) {
721 SkDebugf("%s > %c %cthis (%d){%1.2g,%1.2g %1.2g,%1.2g}"
722 " < rh (%d){%1.2g,%1.2g %1.2g,%1.2g}\n", __FUNCTION__,
723 fBelow.fY <= rh.fBelow.fY & fBelow != rh.fBelow ? 'B' : 'A',
724 (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
725 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)
726 ? ' ' : '!',
727 fIndex, fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY,
728 rh.fIndex, rh.fAbove.fX, rh.fAbove.fY,
729 rh.fBelow.fX, rh.fBelow.fY);
730 }
731 return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
732 < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000733 }
734
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000735 bool advanceT() {
736 SkASSERT(fTIndex <= fTs->count());
737 return ++fTIndex <= fTs->count();
738 }
739
740 bool advance() {
741 // FIXME: flip sense of next
742 bool result = fWorkEdge.advance();
743 fDone = !result;
744 initT();
745 return result;
746 }
747
748 void calcLeft(SkScalar y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000749 // OPTIMIZE: put a kDone_Verb at the end of the verb list?
caryclark@google.com4917f172012-03-05 22:01:21 +0000750 if (fDone || fBelow.fY > y) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000751 return; // nothing to do; use last
caryclark@google.com4917f172012-03-05 22:01:21 +0000752 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000753 calcLeft();
754 }
755
756 void calcLeft() {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000757 switch (fWorkEdge.verb()) {
758 case SkPath::kLine_Verb: {
759 // OPTIMIZATION: if fXAbove, fXBelow have already been computed
760 // for the fTIndex, don't do it again
761 // For identical x, this lets us know which edge is first.
762 // If both edges have T values < 1, check x at next T (fXBelow).
763 int add = (fTIndex <= fTs->count()) - 1;
764 double tAbove = t(fTIndex + add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000765 // OPTIMIZATION: may not need Y
766 LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000767 double tBelow = t(fTIndex - ~add);
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000768 LineXYAtT(fWorkEdge.fPts, tBelow, &fBelow);
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000769 break;
770 }
771 default:
772 // FIXME: add support for all curve types
773 SkASSERT(0);
774 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000775 }
776
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000777 bool done(SkScalar y) {
778 return fDone || fYBottom > y;
779 }
780
caryclark@google.com6680fb12012-02-07 22:10:51 +0000781 void init(const InEdge* edge) {
782 fWorkEdge.init(edge);
783 initT();
caryclark@google.com4917f172012-03-05 22:01:21 +0000784 fBelow.fY = SK_ScalarMin;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000785 fDone = false;
786 fYBottom = SK_ScalarMin;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000787 }
788
789 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000790 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
791 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
792 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
793 fTs = &interceptPtr->fTs;
794 // the above is conceptually the same as
795 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
796 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +0000797 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000798 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000799
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000800 // OPTIMIZATION: record if two edges are coincident when the are intersected
801 // It's unclear how to do this -- seems more complicated than recording the
802 // t values, since the same t values could exist intersecting non-coincident
803 // edges.
804 bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const {
caryclark@google.com4917f172012-03-05 22:01:21 +0000805 if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000806 return false;
807 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000808 uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
809 uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb()
810 : edge->fWorkEdge.verb();
811 if (verb != edgeVerb) {
812 return false;
813 }
814 switch (verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000815 case SkPath::kLine_Verb: {
caryclark@google.com4917f172012-03-05 22:01:21 +0000816 return true;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000817 }
818 default:
819 // FIXME: add support for all curve types
820 SkASSERT(0);
821 }
822 return false;
823 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000824
825 bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
826 if (fBelow.fY >= bottom || fDone || edge->fDone) {
827 return false;
828 }
829 ActiveEdge thisWork = *this;
830 ActiveEdge edgeWork = *edge;
831 while ((thisWork.advanceT() || thisWork.advance())
832 && (edgeWork.advanceT() || edgeWork.advance())) {
833 thisWork.calcLeft();
834 edgeWork.calcLeft();
835 if (thisWork < edgeWork) {
836 return false;
837 }
838 if (edgeWork < thisWork) {
839 return true;
840 }
841 }
842 return false;
843 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000844
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000845 double nextT() {
846 SkASSERT(fTIndex <= fTs->count());
847 return t(fTIndex + 1);
848 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000849
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000850 double t() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000851 if (fTIndex == 0) {
852 return 0;
853 }
854 if (fTIndex > fTs->count()) {
855 return 1;
856 }
857 return (*fTs)[fTIndex - 1];
858 }
859
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000860 double t(int tIndex) const {
861 if (tIndex == 0) {
862 return 0;
863 }
864 if (tIndex > fTs->count()) {
865 return 1;
866 }
867 return (*fTs)[tIndex - 1];
868 }
869
caryclark@google.com6680fb12012-02-07 22:10:51 +0000870 WorkEdge fWorkEdge;
871 const SkTDArray<double>* fTs;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000872 SkPoint fAbove;
873 SkPoint fBelow;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000874 SkScalar fYBottom;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000875 int fTIndex;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000876 bool fSkip;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000877 bool fDone;
caryclark@google.comcd4421d2012-03-01 19:16:31 +0000878 int fIndex; // REMOVE: debugging only
caryclark@google.comc6825902012-02-03 22:07:47 +0000879};
880
caryclark@google.com6680fb12012-02-07 22:10:51 +0000881static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000882 size_t count = activeEdges.count();
883 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000884 if (edge == activeEdges[index].fWorkEdge.fEdge) {
885 return;
886 }
887 }
888 ActiveEdge* active = activeEdges.append();
889 active->init(edge);
890}
891
caryclark@google.com4917f172012-03-05 22:01:21 +0000892// Find any intersections in the range of active edges. A pair of edges, on
893// either side of another edge, may change the winding contribution for part of
894// the edge.
895// OPTIMIZATION: Another approach would be to keep horizontal edges just for
896// the purpose of computing when edges change their winding contribution, since
897// this is essentially computing the horizontal intersection.
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000898static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
899 InEdge** testPtr = currentPtr;
900 InEdge* test = *testPtr;
901 while (testPtr != lastPtr) {
902 if (test->fBounds.fBottom > bottom) {
903 WorkEdge wt;
904 wt.init(test);
905 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000906 // OPTIMIZATION: if bottom intersection does not change
907 // the winding on either side of the split, don't intersect
908 if (wt.verb() == SkPath::kLine_Verb) {
909 double wtTs[2];
910 int pts = LineIntersect(wt.fPts, bottom, wtTs);
911 if (pts) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000912 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000913 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000914 } else {
915 // FIXME: add all curve types
916 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000917 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000918 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000919 }
920 test = *++testPtr;
921 }
922}
923
924static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000925 InEdge** testPtr = currentPtr - 1;
926 while (++testPtr != lastPtr - 1) {
927 InEdge* test = *testPtr;
928 InEdge** nextPtr = testPtr;
929 do {
930 InEdge* next = *++nextPtr;
931 if (test->cached(next)) {
932 continue;
933 }
934 if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
935 continue;
936 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000937 WorkEdge wt, wn;
938 wt.init(test);
939 wn.init(next);
940 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000941 if (wt.verb() == SkPath::kLine_Verb
942 && wn.verb() == SkPath::kLine_Verb) {
943 double wtTs[2], wnTs[2];
944 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
945 if (pts) {
caryclark@google.com4917f172012-03-05 22:01:21 +0000946 test->add(wtTs, pts, wt.verbIndex());
947 next->add(wnTs, pts, wn.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000948 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000949 } else {
950 // FIXME: add all combinations of curve types
951 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000952 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +0000953 } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
954 } while (nextPtr != lastPtr);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000955 }
956}
957
caryclark@google.com6008c6562012-02-15 22:01:16 +0000958static InEdge** advanceEdges(SkTDArray<ActiveEdge>& activeEdges,
959 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
960 InEdge** testPtr = currentPtr - 1;
961 while (++testPtr != lastPtr) {
962 if ((*testPtr)->fBounds.fBottom > y) {
963 continue;
964 }
965 InEdge* test = *testPtr;
966 ActiveEdge* activePtr = activeEdges.begin() - 1;
967 ActiveEdge* lastActive = activeEdges.end();
968 while (++activePtr != lastActive) {
969 if (activePtr->fWorkEdge.fEdge == test) {
970 activeEdges.remove(activePtr - activeEdges.begin());
971 break;
972 }
973 }
974 if (testPtr == currentPtr) {
975 ++currentPtr;
976 }
977 }
978 return currentPtr;
979}
980
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000981// compute bottom taking into account any intersected edges
982static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +0000983 SkScalar y, SkScalar& bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000984 ActiveEdge* activePtr = activeEdges.begin() - 1;
985 ActiveEdge* lastActive = activeEdges.end();
986 while (++activePtr != lastActive) {
987 const InEdge* test = activePtr->fWorkEdge.fEdge;
988 if (!test->fContainsIntercepts) {
989 continue;
990 }
991 WorkEdge wt;
992 wt.init(test);
993 do {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000994 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
caryclark@google.com4917f172012-03-05 22:01:21 +0000995 if (intercepts.fTopIntercepts > 1) {
996 SkScalar yTop = wt.fPts[0].fY;
997 if (yTop > y && bottom > yTop) {
998 bottom = yTop;
999 }
1000 }
1001 if (intercepts.fBottomIntercepts > 1) {
1002 SkScalar yBottom = wt.fPts[wt.verb()].fY;
1003 if (yBottom > y && bottom > yBottom) {
1004 bottom = yBottom;
1005 }
1006 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001007 const SkTDArray<double>& fTs = intercepts.fTs;
1008 size_t count = fTs.count();
1009 for (size_t index = 0; index < count; ++index) {
1010 if (wt.verb() == SkPath::kLine_Verb) {
1011 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001012 if (yIntercept > y && bottom > yIntercept) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001013 bottom = yIntercept;
1014 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001015 } else {
1016 // FIXME: add all curve types
1017 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001018 }
1019 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001020 } while (wt.advance());
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001021 }
1022}
1023
1024static SkScalar findBottom(InEdge** currentPtr,
1025 InEdge** edgeListEnd, SkTDArray<ActiveEdge>& activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +00001026 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001027 InEdge* current = *currentPtr;
1028 SkScalar bottom = current->fBounds.fBottom;
1029
1030 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +00001031 InEdge* test = *testPtr;
1032 while (testPtr != edgeListEnd) {
1033 SkScalar testTop = test->fBounds.fTop;
1034 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001035 break;
1036 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001037 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001038 // OPTIMIZATION: Shortening the bottom is only interesting when filling
1039 // and when the edge is to the left of a longer edge. If it's a framing
1040 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +00001041 if (testTop > y) {
1042 bottom = testTop;
1043 break;
1044 }
1045 if (y < testBottom) {
1046 if (bottom > testBottom) {
1047 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001048 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001049 addToActive(activeEdges, test);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001050 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001051 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001052 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001053 return bottom;
1054}
1055
1056static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
1057 SkTDArray<InEdge*>& edgeList) {
1058 size_t edgeCount = edges.count();
1059 if (edgeCount == 0) {
1060 return;
1061 }
1062 for (size_t index = 0; index < edgeCount; ++index) {
1063 *edgeList.append() = &edges[index];
1064 }
1065 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1066 *edgeList.append() = &edgeSentinel;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001067 QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001068}
1069
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001070
1071static void skipCoincidence(int lastWinding, int winding, int windingMask,
1072 ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
1073 if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
1074 return;
1075 }
1076 if (lastWinding) {
1077 activePtr->fSkip = false;
1078 } else {
1079 firstCoincident->fSkip = false;
1080 }
1081}
1082
caryclark@google.com6008c6562012-02-15 22:01:16 +00001083static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001084 SkTDArray<ActiveEdge*>& edgeList, int windingMask, SkScalar y,
1085 SkScalar bottom) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001086 size_t edgeCount = activeEdges.count();
1087 if (edgeCount == 0) {
1088 return;
1089 }
1090 size_t index;
1091 for (index = 0; index < edgeCount; ++index) {
1092 ActiveEdge& activeEdge = activeEdges[index];
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001093 activeEdge.calcLeft(y);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001094 activeEdge.fSkip = false;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001095 activeEdge.fIndex = index; // REMOVE: debugging only
caryclark@google.com6008c6562012-02-15 22:01:16 +00001096 *edgeList.append() = &activeEdge;
1097 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001098 QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001099 // remove coincident edges
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001100 // OPTIMIZE: remove edges? This is tricky because the current logic expects
1101 // the winding count to be maintained while skipping coincident edges. In
1102 // addition to removing the coincident edges, the remaining edges would need
1103 // to have a different winding value, possibly different per intercept span.
1104 int lastWinding = 0;
1105 bool lastSkipped = false;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001106 ActiveEdge* activePtr = edgeList[0];
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001107 ActiveEdge* firstCoincident = NULL;
1108 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001109 for (index = 1; index < edgeCount; ++index) {
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001110 winding += activePtr->fWorkEdge.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001111 ActiveEdge* nextPtr = edgeList[index];
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001112 if (activePtr->isCoincidentWith(nextPtr, y)) {
1113 // the coincident edges may not have been sorted above -- advance
1114 // the edges and resort if needed
1115 // OPTIMIZE: if sorting is done incrementally as new edges are added
1116 // and not all at once as is done here, fold this test into the
1117 // current less than test.
1118 if (activePtr->swapCoincident(nextPtr, bottom)) {
caryclark@google.com4917f172012-03-05 22:01:21 +00001119 winding -= activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001120 SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
1121 SkTSwap<ActiveEdge*>(activePtr, nextPtr);
caryclark@google.com4917f172012-03-05 22:01:21 +00001122 winding += activePtr->fWorkEdge.winding();
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001123 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001124 if (!firstCoincident) {
1125 firstCoincident = activePtr;
1126 }
1127 activePtr->fSkip = nextPtr->fSkip = lastSkipped = true;
1128 } else if (lastSkipped) {
1129 skipCoincidence(lastWinding, winding, windingMask, activePtr,
1130 firstCoincident);
1131 lastSkipped = false;
1132 firstCoincident = NULL;
1133 }
1134 if (!lastSkipped) {
1135 lastWinding = winding;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001136 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001137 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001138 }
caryclark@google.com6b9cfb32012-02-16 21:24:41 +00001139 if (lastSkipped) {
1140 winding += activePtr->fWorkEdge.winding();
1141 skipCoincidence(lastWinding, winding, windingMask, activePtr,
1142 firstCoincident);
1143 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001144}
1145
1146// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +00001147static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001148 SkScalar bottom, int windingMask, OutEdgeBuilder& outBuilder) {
1149 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001150 ActiveEdge** activeHandle = edgeList.begin() - 1;
1151 ActiveEdge** lastActive = edgeList.end();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001152 if (gShowDebugf) {
caryclark@google.comc17972e2012-02-20 21:33:22 +00001153 SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
1154 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001155 while (++activeHandle != lastActive) {
1156 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001157 const WorkEdge& wt = activePtr->fWorkEdge;
1158 int lastWinding = winding;
1159 winding += wt.winding();
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001160 if (activePtr->done(y)) {
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001161 // FIXME: if this is successful, rewrite done to take bottom as well
1162 if (activePtr->fDone) {
1163 continue;
1164 }
1165 if (activePtr->fYBottom >= bottom) {
1166 continue;
1167 }
1168 if (0) {
1169 SkDebugf("%s bot %g,%g\n", __FUNCTION__, activePtr->fYBottom, bottom);
1170 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001171 }
caryclark@google.comc17972e2012-02-20 21:33:22 +00001172 int opener = (lastWinding & windingMask) == 0;
1173 bool closer = (winding & windingMask) == 0;
1174 SkASSERT(!opener | !closer);
1175 bool inWinding = opener | closer;
caryclark@google.com4917f172012-03-05 22:01:21 +00001176 SkPoint clippedPts[2];
1177 const SkPoint* clipped = NULL;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001178 uint8_t verb = wt.verb();
1179 bool moreToDo, aboveBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001180 do {
1181 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +00001182 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001183 const SkPoint* points = wt.fPts;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001184 double nextT;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001185 do {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001186 nextT = activePtr->nextT();
1187 if (verb == SkPath::kLine_Verb) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001188 // FIXME: obtuse: want efficient way to say
1189 // !currentT && currentT != 1 || !nextT && nextT != 1
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001190 if (currentT * nextT != 0 || currentT + nextT != 1) {
caryclark@google.com6008c6562012-02-15 22:01:16 +00001191 // OPTIMIZATION: if !inWinding, we only need
1192 // clipped[1].fY
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001193 LineSubDivide(points, currentT, nextT, clippedPts);
1194 clipped = clippedPts;
1195 } else {
1196 clipped = points;
1197 }
caryclark@google.com6008c6562012-02-15 22:01:16 +00001198 if (inWinding && !activePtr->fSkip) {
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001199 if (gShowDebugf) {
caryclark@google.comc17972e2012-02-20 21:33:22 +00001200 SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
1201 clipped[0].fX, clipped[0].fY,
1202 clipped[1].fX, clipped[1].fY);
1203 }
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001204 outBuilder.addLine(clipped);
caryclark@google.com6008c6562012-02-15 22:01:16 +00001205 }
caryclark@google.com4917f172012-03-05 22:01:21 +00001206 if (clipped[1].fY > activePtr->fBelow.fY
1207 && bottom >= activePtr->fBelow.fY ) {
1208 activePtr->fAbove = activePtr->fBelow;
1209 activePtr->fBelow = clipped[1];
1210 }
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001211 activePtr->fSkip = false;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001212 } else {
1213 // FIXME: add all curve types
1214 SkASSERT(0);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001215 }
1216 currentT = nextT;
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001217 moreToDo = activePtr->advanceT();
1218 activePtr->fYBottom = clipped[verb].fY;
1219 aboveBottom = activePtr->fYBottom < bottom;
1220 } while (moreToDo & aboveBottom);
1221 } while ((moreToDo || activePtr->advance()) & aboveBottom);
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001222 }
1223}
1224
caryclark@google.comc6825902012-02-03 22:07:47 +00001225void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001226 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
1227 int windingMask = (path.getFillType() & 1) ? 1 : -1;
1228 simple.reset();
1229 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +00001230 // turn path into list of edges increasing in y
caryclark@google.comcef7e3f2012-02-28 16:57:05 +00001231 // if an edge is a quad or a cubic with a y extrema, note it, but leave it
1232 // unbroken. Once we have a list, sort it, then walk the list (walk edges
1233 // twice that have y extrema's on top) and detect crossings -- look for raw
1234 // bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +00001235 SkTArray<InEdge> edges;
1236 InEdgeBuilder builder(path, asFill, edges);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001237 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +00001238 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001239 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com6680fb12012-02-07 22:10:51 +00001240 InEdge** currentPtr = edgeList.begin();
caryclark@google.com4917f172012-03-05 22:01:21 +00001241 if (!currentPtr) {
1242 return;
1243 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001244 // walk the sorted edges from top to bottom, computing accumulated winding
caryclark@google.com6680fb12012-02-07 22:10:51 +00001245 SkTDArray<ActiveEdge> activeEdges;
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001246 OutEdgeBuilder outBuilder(asFill);
1247 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.comc6825902012-02-03 22:07:47 +00001248 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +00001249 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001250 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
1251 activeEdges, y, asFill, lastPtr);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001252 if (lastPtr > currentPtr) {
1253 addBottomT(currentPtr, lastPtr, bottom);
1254 addIntersectingTs(currentPtr, lastPtr);
1255 computeInterceptBottom(activeEdges, y, bottom);
1256 SkTDArray<ActiveEdge*> activeEdgeList;
caryclark@google.comcd4421d2012-03-01 19:16:31 +00001257 sortHorizontal(activeEdges, activeEdgeList, windingMask, y, bottom);
caryclark@google.comc17972e2012-02-20 21:33:22 +00001258 stitchEdge(activeEdgeList, y, bottom, windingMask, outBuilder);
1259 }
caryclark@google.comc6825902012-02-03 22:07:47 +00001260 y = bottom;
caryclark@google.com6008c6562012-02-15 22:01:16 +00001261 currentPtr = advanceEdges(activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +00001262 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +00001263 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +00001264 outBuilder.bridge();
1265 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +00001266}