blob: 22db31269927ebcc6f3528c9ba61bce6e3ee0ab9 [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.com6680fb12012-02-07 22:10:51 +000017static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
caryclark@google.comc6825902012-02-03 22:07:47 +000018 double aRange[2], double bRange[2]) {
19 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
20 _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
21 return intersect(aLine, bLine, aRange, bRange);
22}
23
caryclark@google.com6680fb12012-02-07 22:10:51 +000024static int LineIntersect(const SkPoint a[2], SkScalar y, double aRange[2]) {
caryclark@google.comc6825902012-02-03 22:07:47 +000025 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
26 return horizontalIntersect(aLine, y, aRange);
27}
28
caryclark@google.com6680fb12012-02-07 22:10:51 +000029static SkScalar LineYAtT(const SkPoint a[2], double t) {
30 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
31 double y;
32 xy_at_t(aLine, t, *(double*) 0, y);
33 return SkDoubleToScalar(y);
34}
35
36static void LineSubDivide(const SkPoint a[2], double startT, double endT,
37 SkPoint sub[2]) {
38 _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
39 _Line dst;
40 sub_divide(aLine, startT, endT, dst);
41 sub[0].fX = SkDoubleToScalar(dst[0].x);
42 sub[0].fY = SkDoubleToScalar(dst[0].y);
43 sub[1].fX = SkDoubleToScalar(dst[1].x);
44 sub[1].fY = SkDoubleToScalar(dst[1].y);
45}
46
47
caryclark@google.comc6825902012-02-03 22:07:47 +000048// functions
49void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
50void simplify(const SkPath& path, bool asFill, SkPath& simple);
51/*
52list of edges
53bounds for edge
54sort
55active T
56
57if a contour's bounds is outside of the active area, no need to create edges
58*/
59
60/* given one or more paths,
61 find the bounds of each contour, select the active contours
62 for each active contour, compute a set of edges
63 each edge corresponds to one or more lines and curves
64 leave edges unbroken as long as possible
65 when breaking edges, compute the t at the break but leave the control points alone
66
67 */
68
69void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
70 SkPath::Iter iter(path, false);
71 SkPoint pts[4];
72 SkPath::Verb verb;
73 SkRect bounds;
74 bounds.setEmpty();
75 int count = 0;
76 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
77 switch (verb) {
78 case SkPath::kMove_Verb:
79 if (!bounds.isEmpty()) {
80 *boundsArray.append() = bounds;
81 }
82 bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
83 count = 0;
84 break;
85 case SkPath::kLine_Verb:
86 count = 1;
87 break;
88 case SkPath::kQuad_Verb:
89 count = 2;
90 break;
91 case SkPath::kCubic_Verb:
92 count = 3;
93 break;
94 case SkPath::kClose_Verb:
95 count = 0;
96 break;
97 default:
98 SkDEBUGFAIL("bad verb");
99 return;
100 }
101 for (int i = 1; i <= count; ++i) {
102 bounds.growToInclude(pts[i].fX, pts[i].fY);
103 }
104 }
105}
106
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000107static bool extendLine(const SkPoint line[2], const SkPoint& add) {
108 // FIXME: allow this to extend lines that have slopes that are nearly equal
109 SkScalar dx1 = line[1].fX - line[0].fX;
110 SkScalar dy1 = line[1].fY - line[0].fY;
111 SkScalar dx2 = add.fX - line[0].fX;
112 SkScalar dy2 = add.fY - line[0].fY;
113 return dx1 * dy2 == dx2 * dy1;
114}
caryclark@google.com6680fb12012-02-07 22:10:51 +0000115
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000116struct OutEdge {
117 bool operator<(const OutEdge& rh) const {
118 const SkPoint& first = fPts.begin()[0];
119 const SkPoint& rhFirst = rh.fPts.begin()[0];
120 return first.fY == rhFirst.fY
121 ? first.fX < rhFirst.fX
122 : first.fY < rhFirst.fY;
123 }
124
caryclark@google.com6680fb12012-02-07 22:10:51 +0000125 SkTDArray<SkPoint> fPts;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000126 SkTDArray<uint8_t> fVerbs; // FIXME: unused for now
caryclark@google.com6680fb12012-02-07 22:10:51 +0000127};
128
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000129// for sorting only
130class OutBottomEdge : public OutEdge {
131public:
132 bool operator<(const OutBottomEdge& rh) const {
133 const SkPoint& last = fPts.end()[-1];
134 const SkPoint& rhLast = rh.fPts.end()[-1];
135 return last.fY == rhLast.fY
136 ? last.fX < rhLast.fX
137 : last.fY < rhLast.fY;
138 }
139
140};
141
caryclark@google.com6680fb12012-02-07 22:10:51 +0000142class OutEdgeBuilder {
143public:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000144 OutEdgeBuilder(bool fill)
145 : fFill(fill) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000146 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000147
148 void addLine(const SkPoint line[2]) {
149 size_t count = fEdges.count();
150 for (size_t index = 0; index < count; ++index) {
151 SkTDArray<SkPoint>& pts = fEdges[index].fPts;
152 SkPoint* last = pts.end() - 1;
153 if (last[0] == line[0]) {
154 if (extendLine(&last[-1], line[1])) {
155 last[0] = line[1];
156 } else {
157 *pts.append() = line[1];
158 }
159 return;
160 }
161 }
162 OutEdge& edge = fEdges.push_back();
163 *edge.fPts.append() = line[0];
164 *edge.fPts.append() = line[1];
165 }
166
167 void assemble(SkPath& simple) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000168 size_t listCount = fEdges.count();
169 if (listCount == 0) {
170 return;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000171 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000172 do {
173 size_t listIndex = 0;
174 int advance = 1;
175 while (listIndex < listCount && fTops[listIndex] == 0) {
176 ++listIndex;
177 }
178 if (listIndex >= listCount) {
179 break;
180 }
181 SkPoint firstPt;
182 bool doMove = true;
183 int edgeIndex;
184 do {
185 SkTDArray<SkPoint>& ptArray = fEdges[listIndex].fPts;
186 SkASSERT(ptArray.count() > 0);
187 SkPoint* pts, * end;
188 if (advance < 0) {
189 pts = ptArray.end() - 1;
190 end = ptArray.begin();
191 } else {
192 pts = ptArray.begin();
193 end = ptArray.end() - 1;
194 }
195 if (doMove) {
196 firstPt = pts[0];
197 simple.moveTo(pts[0].fX, pts[0].fY);
198 SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
199 doMove = false;
200 } else {
201 simple.lineTo(pts[0].fX, pts[0].fY);
202 SkDebugf("%s 1 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
203 if (firstPt == pts[0]) {
204 simple.close();
205 SkDebugf("%s close\n", __FUNCTION__);
206 break;
207 }
208 }
209 while (pts != end) {
210 pts += advance;
211 simple.lineTo(pts->fX, pts->fY);
212 SkDebugf("%s 2 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
213 }
214 if (advance < 0) {
215 edgeIndex = fTops[listIndex];
216 fTops[listIndex] = 0;
217 } else {
218 edgeIndex = fBottoms[listIndex];
219 fBottoms[listIndex] = 0;
220 }
221 listIndex = abs(edgeIndex) - 1;
222 if (edgeIndex < 0) {
223 fTops[listIndex] = 0;
224 } else {
225 fBottoms[listIndex] = 0;
226 }
227 // if this and next edge go different directions
228 if (advance > 0 ^ edgeIndex < 0) {
229 advance = -advance;
230 }
231 } while (edgeIndex);
232 } while (true);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000233 }
234
caryclark@google.com6008c6562012-02-15 22:01:16 +0000235 static bool lessThan(SkTArray<OutEdge>& edges, const int* onePtr,
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000236 const int* twoPtr) {
237 int one = *onePtr;
238 const OutEdge& oneEdge = edges[(one < 0 ? -one : one) - 1];
239 const SkPoint* onePt = one < 0 ? oneEdge.fPts.begin()
240 : oneEdge.fPts.end() - 1;
241 int two = *twoPtr;
242 const OutEdge& twoEdge = edges[(two < 0 ? -two : two) - 1];
243 const SkPoint* twoPt = two < 0 ? twoEdge.fPts.begin()
244 : twoEdge.fPts.end() - 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000245 return onePt->fY == twoPt->fY ? onePt->fX < twoPt->fX : onePt->fY < twoPt->fY;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000246 }
247
caryclark@google.com6008c6562012-02-15 22:01:16 +0000248 // Sort the indices of paired points and then create more indices so
249 // assemble() can find the next edge and connect the top or bottom
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000250 void bridge() {
251 size_t index;
252 size_t count = fEdges.count();
253 if (!count) {
254 return;
255 }
256 SkASSERT(!fFill || (count & 1) == 0);
257 fTops.setCount(count);
258 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
259 fBottoms.setCount(count);
260 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000261 SkTDArray<int> order;
262 for (index = 1; index <= count; ++index) {
263 *order.append() = index;
264 *order.append() = -index;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000265 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000266 QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), count * 2, lessThan);
267 int* lastPtr = order.end() - 1;
268 int* leftPtr = order.begin();
269 while (leftPtr < lastPtr) {
270 int leftIndex = *leftPtr;
271 int leftOutIndex = abs(leftIndex) - 1;
272 const OutEdge& left = fEdges[leftOutIndex];
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000273 int* rightPtr = leftPtr + 1;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000274 int rightIndex = *rightPtr;
275 int rightOutIndex = abs(rightIndex) - 1;
276 const OutEdge& right = fEdges[rightOutIndex];
277 // OPTIMIZATION: if fFill is true, we don't need leftMatch, rightMatch
278 SkPoint& leftMatch = left.fPts[leftIndex < 0 ? 0
279 : left.fPts.count() - 1];
280 SkPoint& rightMatch = right.fPts[rightIndex < 0 ? 0
281 : right.fPts.count() - 1];
282 SkASSERT(!fFill || leftMatch.fY == rightMatch.fY);
283 if (fFill || leftMatch == rightMatch) {
284 if (leftIndex < 0) {
285 fTops[leftOutIndex] = rightIndex;
286 } else {
287 fBottoms[leftOutIndex] = rightIndex;
288 }
289 if (rightIndex < 0) {
290 fTops[rightOutIndex] = leftIndex;
291 } else {
292 fBottoms[rightOutIndex] = leftIndex;
293 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000294 ++rightPtr;
295 }
296 leftPtr = rightPtr;
297 }
298 }
299
caryclark@google.com6008c6562012-02-15 22:01:16 +0000300protected:
caryclark@google.com6680fb12012-02-07 22:10:51 +0000301 SkTArray<OutEdge> fEdges;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000302 SkTDArray<int> fTops;
303 SkTDArray<int> fBottoms;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000304 bool fFill;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000305};
306
caryclark@google.comc6825902012-02-03 22:07:47 +0000307// Bounds, unlike Rect, does not consider a vertical line to be empty.
308struct Bounds : public SkRect {
309 static bool Intersects(const Bounds& a, const Bounds& b) {
310 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
311 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
312 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000313
314 bool isEmpty() {
315 return fLeft > fRight || fTop > fBottom
316 || fLeft == fRight && fTop == fBottom
317 || isnan(fLeft) || isnan(fRight)
318 || isnan(fTop) || isnan(fBottom);
319 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000320};
321
caryclark@google.comc6825902012-02-03 22:07:47 +0000322struct Intercepts {
323 SkTDArray<double> fTs;
324};
325
caryclark@google.com6680fb12012-02-07 22:10:51 +0000326struct InEdge {
327 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000328 return fBounds.fTop == rh.fBounds.fTop
329 ? fBounds.fLeft < rh.fBounds.fLeft
330 : fBounds.fTop < rh.fBounds.fTop;
331 }
332
caryclark@google.com6008c6562012-02-15 22:01:16 +0000333 bool add(double* ts, size_t count, ptrdiff_t verbIndex) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000334 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
caryclark@google.com6008c6562012-02-15 22:01:16 +0000335 bool foundIntercept = false;
336 Intercepts& intercepts = fIntercepts[verbIndex];
caryclark@google.comc6825902012-02-03 22:07:47 +0000337 for (size_t index = 0; index < count; ++index) {
338 double t = ts[index];
caryclark@google.com6008c6562012-02-15 22:01:16 +0000339 if (t <= 0 || t >= 1) {
340 continue;
341 }
342 foundIntercept = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000343 size_t tCount = intercepts.fTs.count();
344 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
345 if (t <= intercepts.fTs[idx2]) {
346 if (t < intercepts.fTs[idx2]) {
347 *intercepts.fTs.insert(idx2) = t;
348 break;
349 }
350 }
351 }
352 if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
353 *intercepts.fTs.append() = t;
354 }
355 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000356 return foundIntercept;
caryclark@google.comc6825902012-02-03 22:07:47 +0000357 }
358
caryclark@google.com6680fb12012-02-07 22:10:51 +0000359 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000360 // FIXME: in the pathological case where there is a ton of edges, binary search?
361 size_t count = fCached.count();
362 for (size_t index = 0; index < count; ++index) {
363 if (edge == fCached[index]) {
364 return true;
365 }
366 if (edge < fCached[index]) {
367 *fCached.insert(index) = edge;
368 return false;
369 }
370 }
371 *fCached.append() = edge;
372 return false;
373 }
374
375 void complete(signed char winding) {
376 SkPoint* ptPtr = fPts.begin();
377 SkPoint* ptLast = fPts.end();
378 if (ptPtr == ptLast) {
379 SkDebugf("empty edge\n");
380 SkASSERT(0);
381 // FIXME: delete empty edge?
382 return;
383 }
384 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
385 ++ptPtr;
386 while (ptPtr != ptLast) {
387 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
388 ++ptPtr;
389 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000390 fIntercepts.push_back_n(fVerbs.count());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000391 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
392 size_t index;
393 size_t last = fPts.count() - 1;
394 for (index = 0; index < last; ++index, --last) {
395 SkTSwap<SkPoint>(fPts[index], fPts[last]);
396 }
397 last = fVerbs.count() - 1;
398 for (index = 0; index < last; ++index, --last) {
399 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
400 }
401 }
402 fContainsIntercepts = false;
caryclark@google.comc6825902012-02-03 22:07:47 +0000403 }
404
405 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000406 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000407 SkTArray<Intercepts> fIntercepts; // one per verb
408
caryclark@google.comc6825902012-02-03 22:07:47 +0000409 // persistent data
410 SkTDArray<SkPoint> fPts;
411 SkTDArray<uint8_t> fVerbs;
412 Bounds fBounds;
413 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000414 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000415};
416
caryclark@google.com6680fb12012-02-07 22:10:51 +0000417class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000418public:
419
caryclark@google.com6680fb12012-02-07 22:10:51 +0000420InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000421 : fPath(path)
422 , fCurrentEdge(NULL)
423 , fEdges(edges)
424 , fIgnoreHorizontal(ignoreHorizontal)
425{
426 walk();
427}
428
429protected:
430
431void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000432 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000433 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
434 fPtOffset = 1;
435 *fCurrentEdge->fVerbs.append() = fVerb;
436}
437
caryclark@google.com6008c6562012-02-15 22:01:16 +0000438bool complete() {
439 if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
440 fCurrentEdge->complete(fWinding);
441 fCurrentEdge = NULL;
442 return true;
443 }
444 return false;
445}
446
caryclark@google.comc6825902012-02-03 22:07:47 +0000447int direction(int count) {
448 fPtCount = count;
449 fIgnorableHorizontal = fIgnoreHorizontal && isHorizontal();
450 if (fIgnorableHorizontal) {
451 return 0;
452 }
453 int last = count - 1;
454 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000455 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
456 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000457}
458
459bool isHorizontal() {
460 SkScalar y = fPts[0].fY;
461 for (int i = 1; i < fPtCount; ++i) {
462 if (fPts[i].fY != y) {
463 return false;
464 }
465 }
466 return true;
467}
468
469void startEdge() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000470 if (!fCurrentEdge) {
471 fCurrentEdge = fEdges.push_back_n(1);
472 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000473 fWinding = 0;
474 fPtOffset = 0;
475}
476
477void walk() {
478 SkPath::Iter iter(fPath, true);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000479 int winding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000480 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
481 switch (fVerb) {
482 case SkPath::kMove_Verb:
caryclark@google.comc6825902012-02-03 22:07:47 +0000483 startEdge();
484 continue;
485 case SkPath::kLine_Verb:
486 winding = direction(2);
487 break;
488 case SkPath::kQuad_Verb:
489 winding = direction(3);
490 break;
491 case SkPath::kCubic_Verb:
492 winding = direction(4);
493 break;
494 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000495 SkASSERT(fCurrentEdge);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000496 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000497 continue;
498 default:
499 SkDEBUGFAIL("bad verb");
500 return;
501 }
502 if (fIgnorableHorizontal) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000503 if (complete()) {
504 startEdge();
505 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000506 continue;
507 }
508 if (fWinding + winding == 0) {
509 // FIXME: if prior verb or this verb is a horizontal line, reverse
510 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000511 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000512 fCurrentEdge->complete(fWinding);
513 startEdge();
514 }
515 fWinding = winding;
516 addEdge();
517 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000518 complete();
caryclark@google.comc6825902012-02-03 22:07:47 +0000519}
520
521private:
522 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000523 InEdge* fCurrentEdge;
524 SkTArray<InEdge>& fEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000525 SkPoint fPts[4];
526 SkPath::Verb fVerb;
527 int fPtCount;
528 int fPtOffset;
529 int8_t fWinding;
530 bool fIgnorableHorizontal;
531 bool fIgnoreHorizontal;
532};
533
caryclark@google.com6680fb12012-02-07 22:10:51 +0000534struct WorkEdge {
535 SkScalar bottom() const {
536 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000537 }
538
caryclark@google.com6680fb12012-02-07 22:10:51 +0000539 void init(const InEdge* edge) {
540 fEdge = edge;
541 fPts = edge->fPts.begin();
542 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000543 }
544
545 bool next() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000546 SkASSERT(fVerb < fEdge->fVerbs.end());
547 fPts += *fVerb++;
548 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000549 }
550
551 SkPath::Verb verb() const {
552 return (SkPath::Verb) *fVerb;
553 }
554
caryclark@google.com6008c6562012-02-15 22:01:16 +0000555 ptrdiff_t verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000556 return fVerb - fEdge->fVerbs.begin();
557 }
558
559 int winding() const {
560 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000561 }
562
caryclark@google.com6680fb12012-02-07 22:10:51 +0000563 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000564 const SkPoint* fPts;
565 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000566};
567
caryclark@google.com6680fb12012-02-07 22:10:51 +0000568// always constructed with SkTDArray because new edges are inserted
569// this may be a inappropriate optimization, suggesting that a separate array of
570// ActiveEdge* may be faster to insert and search
caryclark@google.comc6825902012-02-03 22:07:47 +0000571struct ActiveEdge {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000572 bool operator<(const ActiveEdge& rh) const {
573 return fX < rh.fX;
574 }
575
576 void calcLeft() {
577 fX = fWorkEdge.fPts[fWorkEdge.verb()].fX;
578 }
579
caryclark@google.com6680fb12012-02-07 22:10:51 +0000580 void init(const InEdge* edge) {
581 fWorkEdge.init(edge);
582 initT();
583 }
584
585 void initT() {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000586 const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
587 SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
588 const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
589 fTs = &interceptPtr->fTs;
590 // the above is conceptually the same as
591 // fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
592 // but templated arrays don't allow returning a pointer to the end() element
caryclark@google.com6680fb12012-02-07 22:10:51 +0000593 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000594 }
595
caryclark@google.com6680fb12012-02-07 22:10:51 +0000596 bool nextT() {
597 SkASSERT(fTIndex <= fTs->count());
598 return ++fTIndex == fTs->count() + 1;
599 }
600
601 bool next() {
602 bool result = fWorkEdge.next();
603 initT();
604 return result;
605 }
606
607 double t() {
608 if (fTIndex == 0) {
609 return 0;
610 }
611 if (fTIndex > fTs->count()) {
612 return 1;
613 }
614 return (*fTs)[fTIndex - 1];
615 }
616
617 WorkEdge fWorkEdge;
618 const SkTDArray<double>* fTs;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000619 SkScalar fX;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000620 int fTIndex;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000621 bool fSkip;
caryclark@google.comc6825902012-02-03 22:07:47 +0000622};
623
caryclark@google.com6680fb12012-02-07 22:10:51 +0000624static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000625 size_t count = activeEdges.count();
626 for (size_t index = 0; index < count; ++index) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000627 if (edge == activeEdges[index].fWorkEdge.fEdge) {
628 return;
629 }
630 }
631 ActiveEdge* active = activeEdges.append();
632 active->init(edge);
633}
634
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000635 // find any intersections in the range of active edges
636static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
637 InEdge** testPtr = currentPtr;
638 InEdge* test = *testPtr;
639 while (testPtr != lastPtr) {
640 if (test->fBounds.fBottom > bottom) {
641 WorkEdge wt;
642 wt.init(test);
643 do {
644 // FIXME: add all curve types
645 // OPTIMIZATION: if bottom intersection does not change
646 // the winding on either side of the split, don't intersect
647 if (wt.verb() == SkPath::kLine_Verb) {
648 double wtTs[2];
649 int pts = LineIntersect(wt.fPts, bottom, wtTs);
650 if (pts) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000651 test->fContainsIntercepts |= test->add(wtTs, pts,
652 wt.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000653 }
654 }
655 } while (wt.next());
656 }
657 test = *++testPtr;
658 }
659}
660
661static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
662 InEdge** testPtr = currentPtr;
663 InEdge* test = *testPtr;
664 while (testPtr != lastPtr - 1) {
665 InEdge* next = *++testPtr;
666 if (!test->cached(next)
667 && Bounds::Intersects(test->fBounds, next->fBounds)) {
668 WorkEdge wt, wn;
669 wt.init(test);
670 wn.init(next);
671 do {
672 // FIXME: add all combinations of curve types
673 if (wt.verb() == SkPath::kLine_Verb
674 && wn.verb() == SkPath::kLine_Verb) {
675 double wtTs[2], wnTs[2];
676 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
677 if (pts) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000678 test->fContainsIntercepts |= test->add(wtTs, pts,
679 wt.verbIndex());
680 next->fContainsIntercepts |= next->add(wnTs, pts,
681 wn.verbIndex());
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000682 }
683 }
684 } while (wt.bottom() <= wn.bottom() ? wt.next() : wn.next());
685 }
686 test = next;
687 }
688}
689
caryclark@google.com6008c6562012-02-15 22:01:16 +0000690static InEdge** advanceEdges(SkTDArray<ActiveEdge>& activeEdges,
691 InEdge** currentPtr, InEdge** lastPtr, SkScalar y) {
692 InEdge** testPtr = currentPtr - 1;
693 while (++testPtr != lastPtr) {
694 if ((*testPtr)->fBounds.fBottom > y) {
695 continue;
696 }
697 InEdge* test = *testPtr;
698 ActiveEdge* activePtr = activeEdges.begin() - 1;
699 ActiveEdge* lastActive = activeEdges.end();
700 while (++activePtr != lastActive) {
701 if (activePtr->fWorkEdge.fEdge == test) {
702 activeEdges.remove(activePtr - activeEdges.begin());
703 break;
704 }
705 }
706 if (testPtr == currentPtr) {
707 ++currentPtr;
708 }
709 }
710 return currentPtr;
711}
712
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000713// compute bottom taking into account any intersected edges
714static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
caryclark@google.com6008c6562012-02-15 22:01:16 +0000715 SkScalar y, SkScalar& bottom) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000716 ActiveEdge* activePtr = activeEdges.begin() - 1;
717 ActiveEdge* lastActive = activeEdges.end();
718 while (++activePtr != lastActive) {
719 const InEdge* test = activePtr->fWorkEdge.fEdge;
720 if (!test->fContainsIntercepts) {
721 continue;
722 }
723 WorkEdge wt;
724 wt.init(test);
725 do {
726 // FIXME: add all curve types
727 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
728 const SkTDArray<double>& fTs = intercepts.fTs;
729 size_t count = fTs.count();
730 for (size_t index = 0; index < count; ++index) {
731 if (wt.verb() == SkPath::kLine_Verb) {
732 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000733 if (yIntercept > y && bottom > yIntercept) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000734 bottom = yIntercept;
735 }
736 }
737 }
738 } while (wt.next());
739 }
740}
741
742static SkScalar findBottom(InEdge** currentPtr,
743 InEdge** edgeListEnd, SkTDArray<ActiveEdge>& activeEdges, SkScalar y,
caryclark@google.com6008c6562012-02-15 22:01:16 +0000744 bool asFill, InEdge**& testPtr) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000745 InEdge* current = *currentPtr;
746 SkScalar bottom = current->fBounds.fBottom;
747
748 // find the list of edges that cross y
caryclark@google.com6008c6562012-02-15 22:01:16 +0000749 InEdge* test = *testPtr;
750 while (testPtr != edgeListEnd) {
751 SkScalar testTop = test->fBounds.fTop;
752 if (bottom <= testTop) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000753 break;
754 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000755 SkScalar testBottom = test->fBounds.fBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000756 // OPTIMIZATION: Shortening the bottom is only interesting when filling
757 // and when the edge is to the left of a longer edge. If it's a framing
758 // edge, or part of the right, it won't effect the longer edges.
caryclark@google.com6008c6562012-02-15 22:01:16 +0000759 if (testTop > y) {
760 bottom = testTop;
761 break;
762 }
763 if (y < testBottom) {
764 if (bottom > testBottom) {
765 bottom = testBottom;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000766 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000767 addToActive(activeEdges, test);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000768 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000769 test = *++testPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000770 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000771 if (asFill && testPtr - currentPtr <= 1) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000772 SkDebugf("expect 2 or more edges\n");
773 SkASSERT(0);
774 }
775 return bottom;
776}
777
778static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
779 SkTDArray<InEdge*>& edgeList) {
780 size_t edgeCount = edges.count();
781 if (edgeCount == 0) {
782 return;
783 }
784 for (size_t index = 0; index < edgeCount; ++index) {
785 *edgeList.append() = &edges[index];
786 }
787 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
788 *edgeList.append() = &edgeSentinel;
789 ++edgeCount;
790 QSort<InEdge>(edgeList.begin(), edgeCount);
791}
792
caryclark@google.com6008c6562012-02-15 22:01:16 +0000793static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
794 SkTDArray<ActiveEdge*>& edgeList) {
795 size_t edgeCount = activeEdges.count();
796 if (edgeCount == 0) {
797 return;
798 }
799 size_t index;
800 for (index = 0; index < edgeCount; ++index) {
801 ActiveEdge& activeEdge = activeEdges[index];
802 activeEdge.calcLeft();
803 activeEdge.fSkip = false;
804 *edgeList.append() = &activeEdge;
805 }
806 QSort<ActiveEdge>(edgeList.begin(), edgeCount);
807 // remove coincident edges
808 ActiveEdge* activePtr = edgeList[0];
809 for (index = 1; index < edgeCount; ++index) {
810 ActiveEdge* nextPtr = edgeList[index];
811 if (activePtr->fX == nextPtr->fX && activePtr->fWorkEdge.winding()
812 + nextPtr->fWorkEdge.winding() == 0) {
813 SkDebugf("%s coincident\n", __FUNCTION__);
814 // OPTIMIZE: remove edges?
815 activePtr->fSkip = true;
816 nextPtr->fSkip = true;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000817 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000818 activePtr = nextPtr;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000819 }
820}
821
822// stitch edge and t range that satisfies operation
caryclark@google.com6008c6562012-02-15 22:01:16 +0000823static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000824 SkScalar bottom, int windingMask, OutEdgeBuilder& outBuilder) {
825 int winding = 0;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000826 ActiveEdge** activeHandle = edgeList.begin() - 1;
827 ActiveEdge** lastActive = edgeList.end();
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000828 SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000829 while (++activeHandle != lastActive) {
830 ActiveEdge* activePtr = *activeHandle;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000831 const WorkEdge& wt = activePtr->fWorkEdge;
832 int lastWinding = winding;
833 winding += wt.winding();
caryclark@google.com6008c6562012-02-15 22:01:16 +0000834 bool inWinding = (lastWinding & windingMask) == 0
835 || (winding & windingMask) == 0;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000836 do {
837 double currentT = activePtr->t();
caryclark@google.com6008c6562012-02-15 22:01:16 +0000838 SkASSERT(currentT < 1);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000839 const SkPoint* points = wt.fPts;
840 bool last;
841 do {
842 last = activePtr->nextT();
843 double nextT = activePtr->t();
844 // FIXME: add all combinations of curve types
845 if (wt.verb() == SkPath::kLine_Verb) {
846 SkPoint clippedPts[2];
847 const SkPoint* clipped;
848 if (currentT * nextT != 0 || currentT + nextT != 1) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000849 // OPTIMIZATION: if !inWinding, we only need
850 // clipped[1].fY
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000851 LineSubDivide(points, currentT, nextT, clippedPts);
852 clipped = clippedPts;
853 } else {
854 clipped = points;
855 }
caryclark@google.com6008c6562012-02-15 22:01:16 +0000856 if (inWinding && !activePtr->fSkip) {
857 SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
858 clipped[0].fX, clipped[0].fY,
859 clipped[1].fX, clipped[1].fY);
860 outBuilder.addLine(clipped);
861 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000862 if (clipped[1].fY >= bottom) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000863 if (last) {
864 activePtr->next();
865 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000866 goto nextEdge;
867 }
868 }
869 currentT = nextT;
870 } while (!last);
871 } while (activePtr->next());
872nextEdge:
873 ;
874 }
875}
876
caryclark@google.comc6825902012-02-03 22:07:47 +0000877void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000878 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
879 int windingMask = (path.getFillType() & 1) ? 1 : -1;
880 simple.reset();
881 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +0000882 // turn path into list of edges increasing in y
883 // if an edge is a quad or a cubic with a y extrema, note it, but leave it unbroken
884 // once we have a list, sort it, then walk the list (walk edges twice that have y extrema's on top)
885 // and detect crossings -- look for raw bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +0000886 SkTArray<InEdge> edges;
887 InEdgeBuilder builder(path, asFill, edges);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000888 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000889 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000890 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000891 InEdge** currentPtr = edgeList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000892 // walk the sorted edges from top to bottom, computing accumulated winding
caryclark@google.com6680fb12012-02-07 22:10:51 +0000893 SkTDArray<ActiveEdge> activeEdges;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000894 OutEdgeBuilder outBuilder(asFill);
895 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.comc6825902012-02-03 22:07:47 +0000896 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000897 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000898 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
899 activeEdges, y, asFill, lastPtr);
900 addBottomT(currentPtr, lastPtr, bottom);
901 addIntersectingTs(currentPtr, lastPtr);
caryclark@google.com6008c6562012-02-15 22:01:16 +0000902 computeInterceptBottom(activeEdges, y, bottom);
903 SkTDArray<ActiveEdge*> activeEdgeList;
904 sortHorizontal(activeEdges, activeEdgeList);
905 stitchEdge(activeEdgeList, y, bottom, windingMask, outBuilder);
caryclark@google.comc6825902012-02-03 22:07:47 +0000906 y = bottom;
caryclark@google.com6008c6562012-02-15 22:01:16 +0000907 currentPtr = advanceEdges(activeEdges, currentPtr, lastPtr, y);
caryclark@google.comc6825902012-02-03 22:07:47 +0000908 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +0000909 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000910 outBuilder.bridge();
911 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +0000912}
913
caryclark@google.com6008c6562012-02-15 22:01:16 +0000914static void testSimplifyCoincidentVertical() {
915 SkPath path, out;
916 path.setFillType(SkPath::kWinding_FillType);
917 path.addRect(10, 10, 30, 30);
918 path.addRect(10, 30, 30, 40);
919 simplify(path, true, out);
920 SkRect rect;
921 if (!out.isRect(&rect)) {
922 SkDebugf("%s expected rect\n", __FUNCTION__);
923 }
924 if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
925 SkDebugf("%s expected union\n", __FUNCTION__);
926 }
927}
caryclark@google.comc6825902012-02-03 22:07:47 +0000928
caryclark@google.com6008c6562012-02-15 22:01:16 +0000929static void testSimplifyCoincidentHorizontal() {
930 SkPath path, out;
931 path.setFillType(SkPath::kWinding_FillType);
932 path.addRect(10, 10, 30, 30);
933 path.addRect(30, 10, 40, 30);
934 simplify(path, true, out);
935 SkRect rect;
936 if (!out.isRect(&rect)) {
937 SkDebugf("%s expected rect\n", __FUNCTION__);
938 }
939 if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
940 SkDebugf("%s expected union\n", __FUNCTION__);
941 }
942}
943
944static void testSimplifyMulti() {
caryclark@google.comc6825902012-02-03 22:07:47 +0000945 SkPath path, out;
946 path.setFillType(SkPath::kWinding_FillType);
947 path.addRect(10, 10, 30, 30);
948 path.addRect(20, 20, 40, 40);
949 simplify(path, true, out);
950 path = out;
951 path.addRect(30, 10, 40, 20);
952 path.addRect(10, 30, 20, 40);
953 simplify(path, true, out);
954 path = out;
955 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
956 simplify(path, true, out);
957 if (!out.isEmpty()) {
caryclark@google.com6008c6562012-02-15 22:01:16 +0000958 SkDebugf("%s expected empty\n", __FUNCTION__);
caryclark@google.comc6825902012-02-03 22:07:47 +0000959 }
960}
caryclark@google.com6008c6562012-02-15 22:01:16 +0000961
962static void testSimplifyAddL() {
963 SkPath path, out;
964 path.moveTo(10,10); // 'L' shape
965 path.lineTo(10,40);
966 path.lineTo(40,40);
967 path.lineTo(40,20);
968 path.lineTo(30,20);
969 path.lineTo(30,10);
970 path.lineTo(10,10);
971 path.close();
972 path.addRect(30, 10, 40, 20); // missing notch of 'L'
973 simplify(path, true, out);
974 SkRect rect;
975 if (!out.isRect(&rect)) {
976 SkDebugf("%s expected rect\n", __FUNCTION__);
977 }
978 if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
979 SkDebugf("%s expected union\n", __FUNCTION__);
980 }
981}
982
983void testSimplify();
984
985void (*simplifyTests[])() = {
986 testSimplifyCoincidentVertical, // 0
987 testSimplifyCoincidentHorizontal, // 1
988 testSimplifyMulti, // 2
989 testSimplifyAddL // 3
990};
991
992size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
993
994static int firstTest = 3;
995
996void testSimplify() {
997 for (size_t index = firstTest; index < simplifyTestsCount; ++index) {
998 (*simplifyTests[index])();
999 }
1000}
1001
1002