blob: f3ae312a298fdaf9d45589f2ec71bfece32692b2 [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;
126 SkTDArray<uint8_t> fVerbs;
127};
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) {
168 size_t index = 0;
169 do {
170 SkTDArray<SkPoint>& downArray = fEdges[index].fPts;
171 SkPoint* pts = downArray.begin();
172 SkPoints* end = downArray.end();
173 SkPoint firstPt = pts[0];
174 simple.moveTo(pts[0].fX, pts[0].fY);
175 while (++pts < end) {
176 simple.lineTo(pts->fX, pts->fY);
177 }
178 index = fBottoms[index];
179 SkTDArray<SkPoint>& upArray = fEdges[index].fPts;
180 pts = upArray.end();
181 SkPoints* begin = upArray.begin();
182 while (--pts > begin) {
183 simple.lineTo(pts->fX, pts->fY);
184 }
185 if (pts[0] == firstPt) {
186 simple.close();
187 closed = true;
188
189 } else {
190 simple.lineTo(pts->fX, pts->fY);
191 }
192 index = advance > 0 ? fBottoms[index] : fTops[index];
193 advance = -advance;
194 } while (true);
195
196 } else {
197 if (firstAdded.fY == pts[0].fY) {
198 advance = -1;
199 pts = ptArray.end();
200 }
201 }
202 size_t count2 = ptArray.count();
203 for (size_t inner = 1; inner < count2; ++inner) {
204 pts += advance;
205 simple.lineTo(pts->fX, pts->fY);
206 }
207 if (*pts == *ptArray.begin()) {
208// lastAdded = *pts;
209 simple.close();
210 newContour = true;
211 }
212 }
213 }
214
215 static bool lessThan(const SkTArray<OutEdge>& edges, const int* onePtr,
216 const int* twoPtr) {
217 int one = *onePtr;
218 const OutEdge& oneEdge = edges[(one < 0 ? -one : one) - 1];
219 const SkPoint* onePt = one < 0 ? oneEdge.fPts.begin()
220 : oneEdge.fPts.end() - 1;
221 int two = *twoPtr;
222 const OutEdge& twoEdge = edges[(two < 0 ? -two : two) - 1];
223 const SkPoint* twoPt = two < 0 ? twoEdge.fPts.begin()
224 : twoEdge.fPts.end() - 1;
225 return onePt.fY == twoPt.fY ? onePt.fX < twoPt.fX : onePt.fY < twoPt.fY;
226 }
227
228 void bridge() {
229 size_t index;
230 size_t count = fEdges.count();
231 if (!count) {
232 return;
233 }
234 SkASSERT(!fFill || (count & 1) == 0);
235 fTops.setCount(count);
236 sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
237 fBottoms.setCount(count);
238 sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
239 for (index = 0; index < count; ++index) {
240 *fList.append() = index + 1;
241 *fList.append() = -index - 1;
242 }
243 Context context;
244 QSort<SkTArray<OutEdge>&, int>(fEdges, fList.begin(), count, lessThan);
245 connectTops();
246 // sort bottoms
247 SkTDArray<OutBottomEdge*> bottomList;
248 for (index = 0; index < count; ++index) {
249 *bottomList.append() = static_cast<OutBottomEdge*>(&fEdges[index]);
250 fBottoms[index] = -1;
251 }
252 QSort<OutBottomEdge>(bottomList.begin(), count);
253 connectBottoms(bottomList);
254 }
255
256protected:
257 void connectTops() {
258 int* lastPtr = fList.end() - 1;
259 int* leftPtr = fList.begin();
260 for (; leftPtr < lastPtr; ++leftPtr) {
261 OutEdge* left = edges[(*leftPtr < 0 ? -*leftPtr : *leftPtr) - 1];
262 int* rightPtr = leftPtr + 1;
263 OutEdge* right = edges[(*rightPtr < 0 ? -*rightPtr : *rightPtr) - 1];
264 start here;
265 // i'm a bit confused right now -- but i'm trying to sort indices
266 // of paired points and then create more indices so assemble() can
267 // look up the next edge and whether to connect the top or bottom
268 int leftIndex = leftPtr - bottomList.begin();
269 int rightIndex = rightPtr - bottomList.begin();
270 SkASSERT(!fFill || left->fPts[0].fY == right->fPts[0].fY);
271 if (fFill || left->fPts[0] == right->fPts[0]) {
272 int leftIndex = leftPtr - topList.begin();
273 int rightIndex = rightPtr - topList.begin();
274 fTops[leftIndex] = rightIndex;
275 fTops[rightIndex] = leftIndex;
276 ++rightPtr;
277 }
278 leftPtr = rightPtr;
279 }
280 }
281
282 void connectBottoms(SkTDArray<OutBottomEdge*>& bottomList) {
283 OutBottomEdge** lastPtr = bottomList.end() - 1;
284 OutBottomEdge** leftPtr = bottomList.begin();
285 size_t leftCount = (*leftPtr)->fPts.count();
286 for (; leftPtr < lastPtr; ++leftPtr) {
287 OutBottomEdge** rightPtr = leftPtr + 1;
288 size_t rightCount = (*rightPtr)->fPts.count();
289 SkASSERT(!fFill || (*leftPtr)->fPts[leftCount].fY
290 == (*rightPtr)->fPts[rightCount].fY);
291 if (fFill || (*leftPtr)->fPts[leftCount]
292 == (*rightPtr)->fPts[rightCount]) {
293 int leftIndex = leftPtr - bottomList.begin();
294 int rightIndex = rightPtr - bottomList.begin();
295 fBottoms[leftIndex] = rightIndex;
296 fBottoms[rightIndex] = leftIndex;
297 if (++rightPtr < lastPtr) {
298 rightCount = (*rightPtr)->fPts.count();
299 }
300 }
301 leftPtr = rightPtr;
302 leftCount = rightCount;
303 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000304 }
305
306 SkTArray<OutEdge> fEdges;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000307 SkTDArray<int> fList;
308 bool fFill;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000309};
310
caryclark@google.comc6825902012-02-03 22:07:47 +0000311// Bounds, unlike Rect, does not consider a vertical line to be empty.
312struct Bounds : public SkRect {
313 static bool Intersects(const Bounds& a, const Bounds& b) {
314 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
315 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
316 }
317};
318
caryclark@google.comc6825902012-02-03 22:07:47 +0000319struct Intercepts {
320 SkTDArray<double> fTs;
321};
322
caryclark@google.com6680fb12012-02-07 22:10:51 +0000323struct InEdge {
324 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000325 return fBounds.fTop == rh.fBounds.fTop
326 ? fBounds.fLeft < rh.fBounds.fLeft
327 : fBounds.fTop < rh.fBounds.fTop;
328 }
329
330 void add(double* ts, size_t count, int verbIndex) {
331 Intercepts& intercepts = fIntercepts[verbIndex];
332 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
333 for (size_t index = 0; index < count; ++index) {
334 double t = ts[index];
335 size_t tCount = intercepts.fTs.count();
336 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
337 if (t <= intercepts.fTs[idx2]) {
338 if (t < intercepts.fTs[idx2]) {
339 *intercepts.fTs.insert(idx2) = t;
340 break;
341 }
342 }
343 }
344 if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
345 *intercepts.fTs.append() = t;
346 }
347 }
348 }
349
caryclark@google.com6680fb12012-02-07 22:10:51 +0000350 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000351 // FIXME: in the pathological case where there is a ton of edges, binary search?
352 size_t count = fCached.count();
353 for (size_t index = 0; index < count; ++index) {
354 if (edge == fCached[index]) {
355 return true;
356 }
357 if (edge < fCached[index]) {
358 *fCached.insert(index) = edge;
359 return false;
360 }
361 }
362 *fCached.append() = edge;
363 return false;
364 }
365
366 void complete(signed char winding) {
367 SkPoint* ptPtr = fPts.begin();
368 SkPoint* ptLast = fPts.end();
369 if (ptPtr == ptLast) {
370 SkDebugf("empty edge\n");
371 SkASSERT(0);
372 // FIXME: delete empty edge?
373 return;
374 }
375 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
376 ++ptPtr;
377 while (ptPtr != ptLast) {
378 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
379 ++ptPtr;
380 }
381 fIntercepts.push_back_n(1);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000382 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
383 size_t index;
384 size_t last = fPts.count() - 1;
385 for (index = 0; index < last; ++index, --last) {
386 SkTSwap<SkPoint>(fPts[index], fPts[last]);
387 }
388 last = fVerbs.count() - 1;
389 for (index = 0; index < last; ++index, --last) {
390 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
391 }
392 }
393 fContainsIntercepts = false;
caryclark@google.comc6825902012-02-03 22:07:47 +0000394 }
395
396 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000397 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000398 SkTArray<Intercepts> fIntercepts; // one per verb
399
caryclark@google.comc6825902012-02-03 22:07:47 +0000400 // persistent data
401 SkTDArray<SkPoint> fPts;
402 SkTDArray<uint8_t> fVerbs;
403 Bounds fBounds;
404 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000405 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000406};
407
caryclark@google.com6680fb12012-02-07 22:10:51 +0000408class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000409public:
410
caryclark@google.com6680fb12012-02-07 22:10:51 +0000411InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000412 : fPath(path)
413 , fCurrentEdge(NULL)
414 , fEdges(edges)
415 , fIgnoreHorizontal(ignoreHorizontal)
416{
417 walk();
418}
419
420protected:
421
422void addEdge() {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000423 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000424 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
425 fPtOffset = 1;
426 *fCurrentEdge->fVerbs.append() = fVerb;
427}
428
429int direction(int count) {
430 fPtCount = count;
431 fIgnorableHorizontal = fIgnoreHorizontal && isHorizontal();
432 if (fIgnorableHorizontal) {
433 return 0;
434 }
435 int last = count - 1;
436 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000437 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
438 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000439}
440
441bool isHorizontal() {
442 SkScalar y = fPts[0].fY;
443 for (int i = 1; i < fPtCount; ++i) {
444 if (fPts[i].fY != y) {
445 return false;
446 }
447 }
448 return true;
449}
450
451void startEdge() {
452 fCurrentEdge = fEdges.push_back_n(1);
453 fWinding = 0;
454 fPtOffset = 0;
455}
456
457void walk() {
458 SkPath::Iter iter(fPath, true);
459 int winding = 0;
460 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
461 switch (fVerb) {
462 case SkPath::kMove_Verb:
463 winding = 0;
464 startEdge();
465 continue;
466 case SkPath::kLine_Verb:
467 winding = direction(2);
468 break;
469 case SkPath::kQuad_Verb:
470 winding = direction(3);
471 break;
472 case SkPath::kCubic_Verb:
473 winding = direction(4);
474 break;
475 case SkPath::kClose_Verb:
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000476 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000477 if (fCurrentEdge->fVerbs.count()) {
478 fCurrentEdge->complete(fWinding);
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000479 fCurrentEdge = NULL;
caryclark@google.comc6825902012-02-03 22:07:47 +0000480 }
481 continue;
482 default:
483 SkDEBUGFAIL("bad verb");
484 return;
485 }
486 if (fIgnorableHorizontal) {
487 continue;
488 }
489 if (fWinding + winding == 0) {
490 // FIXME: if prior verb or this verb is a horizontal line, reverse
491 // it instead of starting a new edge
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000492 SkASSERT(fCurrentEdge);
caryclark@google.comc6825902012-02-03 22:07:47 +0000493 fCurrentEdge->complete(fWinding);
494 startEdge();
495 }
496 fWinding = winding;
497 addEdge();
498 }
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000499 if (fCurrentEdge) {
500 fCurrentEdge->complete(fWinding);
501 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000502}
503
504private:
505 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000506 InEdge* fCurrentEdge;
507 SkTArray<InEdge>& fEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000508 SkPoint fPts[4];
509 SkPath::Verb fVerb;
510 int fPtCount;
511 int fPtOffset;
512 int8_t fWinding;
513 bool fIgnorableHorizontal;
514 bool fIgnoreHorizontal;
515};
516
caryclark@google.com6680fb12012-02-07 22:10:51 +0000517struct WorkEdge {
518 SkScalar bottom() const {
519 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000520 }
521
caryclark@google.com6680fb12012-02-07 22:10:51 +0000522 void init(const InEdge* edge) {
523 fEdge = edge;
524 fPts = edge->fPts.begin();
525 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000526 }
527
528 bool next() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000529 SkASSERT(fVerb < fEdge->fVerbs.end());
530 fPts += *fVerb++;
531 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000532 }
533
534 SkPath::Verb verb() const {
535 return (SkPath::Verb) *fVerb;
536 }
537
538 int verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000539 return fVerb - fEdge->fVerbs.begin();
540 }
541
542 int winding() const {
543 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000544 }
545
caryclark@google.com6680fb12012-02-07 22:10:51 +0000546 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000547 const SkPoint* fPts;
548 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000549};
550
caryclark@google.com6680fb12012-02-07 22:10:51 +0000551// always constructed with SkTDArray because new edges are inserted
552// this may be a inappropriate optimization, suggesting that a separate array of
553// ActiveEdge* may be faster to insert and search
caryclark@google.comc6825902012-02-03 22:07:47 +0000554struct ActiveEdge {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000555 void init(const InEdge* edge) {
556 fWorkEdge.init(edge);
557 initT();
558 }
559
560 void initT() {
561 fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
562 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000563 }
564
caryclark@google.com6680fb12012-02-07 22:10:51 +0000565 bool nextT() {
566 SkASSERT(fTIndex <= fTs->count());
567 return ++fTIndex == fTs->count() + 1;
568 }
569
570 bool next() {
571 bool result = fWorkEdge.next();
572 initT();
573 return result;
574 }
575
576 double t() {
577 if (fTIndex == 0) {
578 return 0;
579 }
580 if (fTIndex > fTs->count()) {
581 return 1;
582 }
583 return (*fTs)[fTIndex - 1];
584 }
585
586 WorkEdge fWorkEdge;
587 const SkTDArray<double>* fTs;
588 int fTIndex;
caryclark@google.comc6825902012-02-03 22:07:47 +0000589};
590
caryclark@google.com6680fb12012-02-07 22:10:51 +0000591static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
592 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
593 size_t count = activeEdges.count();
594 for (size_t index = 0; index < count; ++index) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000595 if (*edge < *activeEdges[index].fWorkEdge.fEdge) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000596 ActiveEdge* active = activeEdges.insert(index);
597 active->init(edge);
598 return;
599 }
600 if (edge == activeEdges[index].fWorkEdge.fEdge) {
601 return;
602 }
603 }
604 ActiveEdge* active = activeEdges.append();
605 active->init(edge);
606}
607
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000608 // find any intersections in the range of active edges
609static void addBottomT(InEdge** currentPtr, InEdge** lastPtr, SkScalar bottom) {
610 InEdge** testPtr = currentPtr;
611 InEdge* test = *testPtr;
612 while (testPtr != lastPtr) {
613 if (test->fBounds.fBottom > bottom) {
614 WorkEdge wt;
615 wt.init(test);
616 do {
617 // FIXME: add all curve types
618 // OPTIMIZATION: if bottom intersection does not change
619 // the winding on either side of the split, don't intersect
620 if (wt.verb() == SkPath::kLine_Verb) {
621 double wtTs[2];
622 int pts = LineIntersect(wt.fPts, bottom, wtTs);
623 if (pts) {
624 test->add(wtTs, pts, wt.verbIndex());
625 }
626 }
627 } while (wt.next());
628 }
629 test = *++testPtr;
630 }
631}
632
633static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
634 InEdge** testPtr = currentPtr;
635 InEdge* test = *testPtr;
636 while (testPtr != lastPtr - 1) {
637 InEdge* next = *++testPtr;
638 if (!test->cached(next)
639 && Bounds::Intersects(test->fBounds, next->fBounds)) {
640 WorkEdge wt, wn;
641 wt.init(test);
642 wn.init(next);
643 do {
644 // FIXME: add all combinations of curve types
645 if (wt.verb() == SkPath::kLine_Verb
646 && wn.verb() == SkPath::kLine_Verb) {
647 double wtTs[2], wnTs[2];
648 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
649 if (pts) {
650 test->add(wtTs, pts, wt.verbIndex());
651 test->fContainsIntercepts = true;
652 next->add(wnTs, pts, wn.verbIndex());
653 next->fContainsIntercepts = true;
654 }
655 }
656 } while (wt.bottom() <= wn.bottom() ? wt.next() : wn.next());
657 }
658 test = next;
659 }
660}
661
662// compute bottom taking into account any intersected edges
663static void computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
664 SkScalar& bottom) {
665 ActiveEdge* activePtr = activeEdges.begin() - 1;
666 ActiveEdge* lastActive = activeEdges.end();
667 while (++activePtr != lastActive) {
668 const InEdge* test = activePtr->fWorkEdge.fEdge;
669 if (!test->fContainsIntercepts) {
670 continue;
671 }
672 WorkEdge wt;
673 wt.init(test);
674 do {
675 // FIXME: add all curve types
676 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
677 const SkTDArray<double>& fTs = intercepts.fTs;
678 size_t count = fTs.count();
679 for (size_t index = 0; index < count; ++index) {
680 if (wt.verb() == SkPath::kLine_Verb) {
681 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
682 if (bottom > yIntercept) {
683 bottom = yIntercept;
684 }
685 }
686 }
687 } while (wt.next());
688 }
689}
690
691static SkScalar findBottom(InEdge** currentPtr,
692 InEdge** edgeListEnd, SkTDArray<ActiveEdge>& activeEdges, SkScalar y,
693 bool asFill, InEdge**& lastPtr) {
694 InEdge* current = *currentPtr;
695 SkScalar bottom = current->fBounds.fBottom;
696
697 // find the list of edges that cross y
698 InEdge* last = *lastPtr;
699 while (lastPtr != edgeListEnd) {
700 if (bottom <= last->fBounds.fTop) {
701 break;
702 }
703 SkScalar lastTop = last->fBounds.fTop;
704 // OPTIMIZATION: Shortening the bottom is only interesting when filling
705 // and when the edge is to the left of a longer edge. If it's a framing
706 // edge, or part of the right, it won't effect the longer edges.
707 if (lastTop > y) {
708 if (bottom > lastTop) {
709 bottom = lastTop;
710 break;
711 }
712 } else if (bottom > last->fBounds.fBottom) {
713 bottom = last->fBounds.fBottom;
714 }
715 addToActive(activeEdges, last);
716 last = *++lastPtr;
717 }
718 if (asFill && lastPtr - currentPtr <= 1) {
719 SkDebugf("expect 2 or more edges\n");
720 SkASSERT(0);
721 }
722 return bottom;
723}
724
725static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
726 SkTDArray<InEdge*>& edgeList) {
727 size_t edgeCount = edges.count();
728 if (edgeCount == 0) {
729 return;
730 }
731 for (size_t index = 0; index < edgeCount; ++index) {
732 *edgeList.append() = &edges[index];
733 }
734 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
735 *edgeList.append() = &edgeSentinel;
736 ++edgeCount;
737 QSort<InEdge>(edgeList.begin(), edgeCount);
738}
739
740static void removeEdge(SkTDArray<ActiveEdge>& activeEdges, InEdge** currentPtr) {
741 InEdge* current = *currentPtr;
742 ActiveEdge* activePtr = activeEdges.begin() - 1;
743 ActiveEdge* lastActive = activeEdges.end();
744 while (++activePtr != lastActive) {
745 if (activePtr->fWorkEdge.fEdge == current) {
746 activeEdges.remove(activePtr - activeEdges.begin());
747 return;
748 }
749 }
750}
751
752// stitch edge and t range that satisfies operation
753static void stitchEdge(SkTDArray<ActiveEdge>& activeEdges, SkScalar y,
754 SkScalar bottom, int windingMask, OutEdgeBuilder& outBuilder) {
755 int winding = 0;
756 ActiveEdge* activePtr = activeEdges.begin() - 1;
757 ActiveEdge* lastActive = activeEdges.end();
758 SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
759 while (++activePtr != lastActive) {
760 const WorkEdge& wt = activePtr->fWorkEdge;
761 int lastWinding = winding;
762 winding += wt.winding();
763 if (!(lastWinding & windingMask) && !(winding & windingMask)) {
764 continue;
765 }
766 do {
767 double currentT = activePtr->t();
768 const SkPoint* points = wt.fPts;
769 bool last;
770 do {
771 last = activePtr->nextT();
772 double nextT = activePtr->t();
773 // FIXME: add all combinations of curve types
774 if (wt.verb() == SkPath::kLine_Verb) {
775 SkPoint clippedPts[2];
776 const SkPoint* clipped;
777 if (currentT * nextT != 0 || currentT + nextT != 1) {
778 LineSubDivide(points, currentT, nextT, clippedPts);
779 clipped = clippedPts;
780 } else {
781 clipped = points;
782 }
783 SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
784 clipped[0].fX, clipped[0].fY,
785 clipped[1].fX, clipped[1].fY);
786 outBuilder.addLine(clipped);
787 if (clipped[1].fY >= bottom) {
788 goto nextEdge;
789 }
790 }
791 currentT = nextT;
792 } while (!last);
793 } while (activePtr->next());
794nextEdge:
795 ;
796 }
797}
798
caryclark@google.comc6825902012-02-03 22:07:47 +0000799void simplify(const SkPath& path, bool asFill, SkPath& simple) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000800 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
801 int windingMask = (path.getFillType() & 1) ? 1 : -1;
802 simple.reset();
803 simple.setFillType(SkPath::kEvenOdd_FillType);
caryclark@google.comc6825902012-02-03 22:07:47 +0000804 // turn path into list of edges increasing in y
805 // if an edge is a quad or a cubic with a y extrema, note it, but leave it unbroken
806 // once we have a list, sort it, then walk the list (walk edges twice that have y extrema's on top)
807 // and detect crossings -- look for raw bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +0000808 SkTArray<InEdge> edges;
809 InEdgeBuilder builder(path, asFill, edges);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000810 SkTDArray<InEdge*> edgeList;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000811 InEdge edgeSentinel;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000812 makeEdgeList(edges, edgeSentinel, edgeList);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000813 InEdge** currentPtr = edgeList.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000814 // walk the sorted edges from top to bottom, computing accumulated winding
caryclark@google.com6680fb12012-02-07 22:10:51 +0000815 SkTDArray<ActiveEdge> activeEdges;
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000816 OutEdgeBuilder outBuilder(asFill);
817 SkScalar y = (*currentPtr)->fBounds.fTop;
caryclark@google.comc6825902012-02-03 22:07:47 +0000818 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000819 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000820 SkScalar bottom = findBottom(currentPtr, edgeList.end(),
821 activeEdges, y, asFill, lastPtr);
822 addBottomT(currentPtr, lastPtr, bottom);
823 addIntersectingTs(currentPtr, lastPtr);
824 computeInterceptBottom(activeEdges, bottom);
825 stitchEdge(activeEdges, y, bottom, windingMask, outBuilder);
caryclark@google.comc6825902012-02-03 22:07:47 +0000826 y = bottom;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000827 while ((*currentPtr)->fBounds.fBottom <= y) {
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000828 removeEdge(activeEdges, currentPtr);
caryclark@google.comc6825902012-02-03 22:07:47 +0000829 ++currentPtr;
830 }
831 } while (*currentPtr != &edgeSentinel);
caryclark@google.comc6825902012-02-03 22:07:47 +0000832 // assemble output path from string of pts, verbs
caryclark@google.comf8b000d2012-02-09 22:04:27 +0000833 outBuilder.bridge();
834 outBuilder.assemble(simple);
caryclark@google.comc6825902012-02-03 22:07:47 +0000835}
836
837void testSimplify();
838
839void testSimplify() {
840 SkPath path, out;
841 path.setFillType(SkPath::kWinding_FillType);
842 path.addRect(10, 10, 30, 30);
843 path.addRect(20, 20, 40, 40);
844 simplify(path, true, out);
845 path = out;
846 path.addRect(30, 10, 40, 20);
847 path.addRect(10, 30, 20, 40);
848 simplify(path, true, out);
849 path = out;
850 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
851 simplify(path, true, out);
852 if (!out.isEmpty()) {
853 SkDebugf("expected empty\n");
854 }
855}