blob: 4f4fc9b94d1f270254c5d920f1c70b193a52fd1a [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.com6680fb12012-02-07 22:10:51 +0000107struct OutEdge {
108
109 SkTDArray<SkPoint> fPts;
110 SkTDArray<uint8_t> fVerbs;
111};
112
113class OutEdgeBuilder {
114public:
115 void addLine(SkPoint pts[2]) {
116 ;
117 OutEdge* edge;
118
119 edge = fEdges.append();
120
121 if (empty) {
122 *edge->fPts.append() = pts[0];
123 }
124 *edge->fPts.append() = pts[1];
125 }
126
127 SkTArray<OutEdge> fEdges;
128};
129
caryclark@google.comc6825902012-02-03 22:07:47 +0000130// Bounds, unlike Rect, does not consider a vertical line to be empty.
131struct Bounds : public SkRect {
132 static bool Intersects(const Bounds& a, const Bounds& b) {
133 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
134 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
135 }
136};
137
caryclark@google.comc6825902012-02-03 22:07:47 +0000138struct Intercepts {
139 SkTDArray<double> fTs;
140};
141
caryclark@google.com6680fb12012-02-07 22:10:51 +0000142struct InEdge {
143 bool operator<(const InEdge& rh) const {
caryclark@google.comc6825902012-02-03 22:07:47 +0000144 return fBounds.fTop == rh.fBounds.fTop
145 ? fBounds.fLeft < rh.fBounds.fLeft
146 : fBounds.fTop < rh.fBounds.fTop;
147 }
148
149 void add(double* ts, size_t count, int verbIndex) {
150 Intercepts& intercepts = fIntercepts[verbIndex];
151 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
152 for (size_t index = 0; index < count; ++index) {
153 double t = ts[index];
154 size_t tCount = intercepts.fTs.count();
155 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
156 if (t <= intercepts.fTs[idx2]) {
157 if (t < intercepts.fTs[idx2]) {
158 *intercepts.fTs.insert(idx2) = t;
159 break;
160 }
161 }
162 }
163 if (tCount == 0 || t > intercepts.fTs[tCount - 1]) {
164 *intercepts.fTs.append() = t;
165 }
166 }
167 }
168
caryclark@google.com6680fb12012-02-07 22:10:51 +0000169 bool cached(const InEdge* edge) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000170 // FIXME: in the pathological case where there is a ton of edges, binary search?
171 size_t count = fCached.count();
172 for (size_t index = 0; index < count; ++index) {
173 if (edge == fCached[index]) {
174 return true;
175 }
176 if (edge < fCached[index]) {
177 *fCached.insert(index) = edge;
178 return false;
179 }
180 }
181 *fCached.append() = edge;
182 return false;
183 }
184
185 void complete(signed char winding) {
186 SkPoint* ptPtr = fPts.begin();
187 SkPoint* ptLast = fPts.end();
188 if (ptPtr == ptLast) {
189 SkDebugf("empty edge\n");
190 SkASSERT(0);
191 // FIXME: delete empty edge?
192 return;
193 }
194 fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
195 ++ptPtr;
196 while (ptPtr != ptLast) {
197 fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
198 ++ptPtr;
199 }
200 fIntercepts.push_back_n(1);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000201 if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
202 size_t index;
203 size_t last = fPts.count() - 1;
204 for (index = 0; index < last; ++index, --last) {
205 SkTSwap<SkPoint>(fPts[index], fPts[last]);
206 }
207 last = fVerbs.count() - 1;
208 for (index = 0; index < last; ++index, --last) {
209 SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
210 }
211 }
212 fContainsIntercepts = false;
caryclark@google.comc6825902012-02-03 22:07:47 +0000213 }
214
215 // temporary data : move this to a separate struct?
caryclark@google.com6680fb12012-02-07 22:10:51 +0000216 SkTDArray<const InEdge*> fCached; // list of edges already intercepted
caryclark@google.comc6825902012-02-03 22:07:47 +0000217 SkTArray<Intercepts> fIntercepts; // one per verb
218
caryclark@google.comc6825902012-02-03 22:07:47 +0000219 // persistent data
220 SkTDArray<SkPoint> fPts;
221 SkTDArray<uint8_t> fVerbs;
222 Bounds fBounds;
223 signed char fWinding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000224 bool fContainsIntercepts;
caryclark@google.comc6825902012-02-03 22:07:47 +0000225};
226
caryclark@google.com6680fb12012-02-07 22:10:51 +0000227class InEdgeBuilder {
caryclark@google.comc6825902012-02-03 22:07:47 +0000228public:
229
caryclark@google.com6680fb12012-02-07 22:10:51 +0000230InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges)
caryclark@google.comc6825902012-02-03 22:07:47 +0000231 : fPath(path)
232 , fCurrentEdge(NULL)
233 , fEdges(edges)
234 , fIgnoreHorizontal(ignoreHorizontal)
235{
236 walk();
237}
238
239protected:
240
241void addEdge() {
242 fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
243 fPtOffset = 1;
244 *fCurrentEdge->fVerbs.append() = fVerb;
245}
246
247int direction(int count) {
248 fPtCount = count;
249 fIgnorableHorizontal = fIgnoreHorizontal && isHorizontal();
250 if (fIgnorableHorizontal) {
251 return 0;
252 }
253 int last = count - 1;
254 return fPts[0].fY == fPts[last].fY
caryclark@google.com6680fb12012-02-07 22:10:51 +0000255 ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX
256 ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1;
caryclark@google.comc6825902012-02-03 22:07:47 +0000257}
258
259bool isHorizontal() {
260 SkScalar y = fPts[0].fY;
261 for (int i = 1; i < fPtCount; ++i) {
262 if (fPts[i].fY != y) {
263 return false;
264 }
265 }
266 return true;
267}
268
269void startEdge() {
270 fCurrentEdge = fEdges.push_back_n(1);
271 fWinding = 0;
272 fPtOffset = 0;
273}
274
275void walk() {
276 SkPath::Iter iter(fPath, true);
277 int winding = 0;
278 while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
279 switch (fVerb) {
280 case SkPath::kMove_Verb:
281 winding = 0;
282 startEdge();
283 continue;
284 case SkPath::kLine_Verb:
285 winding = direction(2);
286 break;
287 case SkPath::kQuad_Verb:
288 winding = direction(3);
289 break;
290 case SkPath::kCubic_Verb:
291 winding = direction(4);
292 break;
293 case SkPath::kClose_Verb:
294 if (fCurrentEdge->fVerbs.count()) {
295 fCurrentEdge->complete(fWinding);
296 }
297 continue;
298 default:
299 SkDEBUGFAIL("bad verb");
300 return;
301 }
302 if (fIgnorableHorizontal) {
303 continue;
304 }
305 if (fWinding + winding == 0) {
306 // FIXME: if prior verb or this verb is a horizontal line, reverse
307 // it instead of starting a new edge
308 fCurrentEdge->complete(fWinding);
309 startEdge();
310 }
311 fWinding = winding;
312 addEdge();
313 }
314 fCurrentEdge->complete(fWinding);
315}
316
317private:
318 const SkPath& fPath;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000319 InEdge* fCurrentEdge;
320 SkTArray<InEdge>& fEdges;
caryclark@google.comc6825902012-02-03 22:07:47 +0000321 SkPoint fPts[4];
322 SkPath::Verb fVerb;
323 int fPtCount;
324 int fPtOffset;
325 int8_t fWinding;
326 bool fIgnorableHorizontal;
327 bool fIgnoreHorizontal;
328};
329
caryclark@google.com6680fb12012-02-07 22:10:51 +0000330struct WorkEdge {
331 SkScalar bottom() const {
332 return fPts[verb()].fY;
caryclark@google.comc6825902012-02-03 22:07:47 +0000333 }
334
caryclark@google.com6680fb12012-02-07 22:10:51 +0000335 void init(const InEdge* edge) {
336 fEdge = edge;
337 fPts = edge->fPts.begin();
338 fVerb = edge->fVerbs.begin();
caryclark@google.comc6825902012-02-03 22:07:47 +0000339 }
340
341 bool next() {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000342 SkASSERT(fVerb < fEdge->fVerbs.end());
343 fPts += *fVerb++;
344 return fVerb != fEdge->fVerbs.end();
caryclark@google.comc6825902012-02-03 22:07:47 +0000345 }
346
347 SkPath::Verb verb() const {
348 return (SkPath::Verb) *fVerb;
349 }
350
351 int verbIndex() const {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000352 return fVerb - fEdge->fVerbs.begin();
353 }
354
355 int winding() const {
356 return fEdge->fWinding;
caryclark@google.comc6825902012-02-03 22:07:47 +0000357 }
358
caryclark@google.com6680fb12012-02-07 22:10:51 +0000359 const InEdge* fEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000360 const SkPoint* fPts;
361 const uint8_t* fVerb;
caryclark@google.comc6825902012-02-03 22:07:47 +0000362};
363
caryclark@google.com6680fb12012-02-07 22:10:51 +0000364// always constructed with SkTDArray because new edges are inserted
365// this may be a inappropriate optimization, suggesting that a separate array of
366// ActiveEdge* may be faster to insert and search
caryclark@google.comc6825902012-02-03 22:07:47 +0000367struct ActiveEdge {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000368 void init(const InEdge* edge) {
369 fWorkEdge.init(edge);
370 initT();
371 }
372
373 void initT() {
374 fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
375 fTIndex = 0;
caryclark@google.comc6825902012-02-03 22:07:47 +0000376 }
377
caryclark@google.com6680fb12012-02-07 22:10:51 +0000378 bool nextT() {
379 SkASSERT(fTIndex <= fTs->count());
380 return ++fTIndex == fTs->count() + 1;
381 }
382
383 bool next() {
384 bool result = fWorkEdge.next();
385 initT();
386 return result;
387 }
388
389 double t() {
390 if (fTIndex == 0) {
391 return 0;
392 }
393 if (fTIndex > fTs->count()) {
394 return 1;
395 }
396 return (*fTs)[fTIndex - 1];
397 }
398
399 WorkEdge fWorkEdge;
400 const SkTDArray<double>* fTs;
401 int fTIndex;
caryclark@google.comc6825902012-02-03 22:07:47 +0000402};
403
caryclark@google.com6680fb12012-02-07 22:10:51 +0000404static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
405 // FIXME: in the pathological case where there is a ton of intercepts, binary search?
406 size_t count = activeEdges.count();
407 for (size_t index = 0; index < count; ++index) {
408 if (edge < activeEdges[index].fWorkEdge.fEdge) {
409 ActiveEdge* active = activeEdges.insert(index);
410 active->init(edge);
411 return;
412 }
413 if (edge == activeEdges[index].fWorkEdge.fEdge) {
414 return;
415 }
416 }
417 ActiveEdge* active = activeEdges.append();
418 active->init(edge);
419}
420
caryclark@google.comc6825902012-02-03 22:07:47 +0000421void simplify(const SkPath& path, bool asFill, SkPath& simple) {
422 // turn path into list of edges increasing in y
423 // if an edge is a quad or a cubic with a y extrema, note it, but leave it unbroken
424 // once we have a list, sort it, then walk the list (walk edges twice that have y extrema's on top)
425 // and detect crossings -- look for raw bounds that cross over, then tight bounds that cross
caryclark@google.com6680fb12012-02-07 22:10:51 +0000426 SkTArray<InEdge> edges;
427 InEdgeBuilder builder(path, asFill, edges);
caryclark@google.comc6825902012-02-03 22:07:47 +0000428 size_t edgeCount = edges.count();
429 simple.reset();
430 if (edgeCount == 0) {
431 return;
432 }
433 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
434 int windingMask = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000435 SkTDArray<InEdge*> edgeList;
caryclark@google.comc6825902012-02-03 22:07:47 +0000436 for (size_t index = 0; index < edgeCount; ++index) {
437 *edgeList.append() = &edges[index];
438 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000439 InEdge edgeSentinel;
caryclark@google.comc6825902012-02-03 22:07:47 +0000440 edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
441 *edgeList.append() = &edgeSentinel;
442 ++edgeCount;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000443 QSort<InEdge>(edgeList.begin(), edgeCount);
444 InEdge** currentPtr = edgeList.begin();
445 InEdge* current = *currentPtr;
caryclark@google.comc6825902012-02-03 22:07:47 +0000446 SkScalar y = current->fBounds.fTop;
447 SkScalar bottom = current->fBounds.fBottom;
448 // walk the sorted edges from top to bottom, computing accumulated winding
caryclark@google.com6680fb12012-02-07 22:10:51 +0000449 SkTDArray<ActiveEdge> activeEdges;
450 OutEdgeBuilder outBuilder;
caryclark@google.comc6825902012-02-03 22:07:47 +0000451 do {
452 // find the list of edges that cross y
caryclark@google.com6680fb12012-02-07 22:10:51 +0000453 InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
454 InEdge* last = *lastPtr;
caryclark@google.comc6825902012-02-03 22:07:47 +0000455 while (lastPtr != edgeList.end()) {
456 if (bottom <= last->fBounds.fTop) {
457 break;
458 }
459 SkScalar lastTop = last->fBounds.fTop;
460 // OPTIMIZATION: Shortening the bottom is only interesting when filling
461 // and when the edge is to the left of a longer edge. If it's a framing
462 // edge, or part of the right, it won't effect the longer edges.
463 if (lastTop > y) {
464 if (bottom > lastTop) {
465 bottom = lastTop;
466 break;
467 }
468 } else if (bottom > last->fBounds.fBottom) {
469 bottom = last->fBounds.fBottom;
470 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000471 addToActive(activeEdges, last);
caryclark@google.comc6825902012-02-03 22:07:47 +0000472 last = *++lastPtr;
473 }
474 if (asFill && lastPtr - currentPtr <= 1) {
475 SkDebugf("expect 2 or more edges\n");
476 SkASSERT(0);
477 return;
478 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000479
caryclark@google.comc6825902012-02-03 22:07:47 +0000480 // find any intersections in the range of active edges
caryclark@google.com6680fb12012-02-07 22:10:51 +0000481 InEdge** testPtr = currentPtr;
482 InEdge* test = *testPtr;
caryclark@google.comc6825902012-02-03 22:07:47 +0000483 while (testPtr != lastPtr) {
484 if (test->fBounds.fBottom > bottom) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000485 WorkEdge wt;
486 wt.init(test);
caryclark@google.comc6825902012-02-03 22:07:47 +0000487 do {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000488 // FIXME: add all curve types
489 // OPTIMIZATION: if bottom intersection does not change
490 // the winding on either side of the split, don't intersect
caryclark@google.comc6825902012-02-03 22:07:47 +0000491 if (wt.verb() == SkPath::kLine_Verb) {
492 double wtTs[2];
caryclark@google.com6680fb12012-02-07 22:10:51 +0000493 int pts = LineIntersect(wt.fPts, bottom, wtTs);
caryclark@google.comc6825902012-02-03 22:07:47 +0000494 if (pts) {
495 test->add(wtTs, pts, wt.verbIndex());
496 }
497 }
498 } while (wt.next());
499 }
500 test = *++testPtr;
501 }
502 testPtr = currentPtr;
503 test = *testPtr;
504 while (testPtr != lastPtr - 1) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000505 InEdge* next = *++testPtr;
caryclark@google.comc6825902012-02-03 22:07:47 +0000506 if (!test->cached(next)
507 && Bounds::Intersects(test->fBounds, next->fBounds)) {
caryclark@google.com6680fb12012-02-07 22:10:51 +0000508 WorkEdge wt, wn;
509 wt.init(test);
510 wn.init(next);
caryclark@google.comc6825902012-02-03 22:07:47 +0000511 do {
512 // FIXME: add all combinations of curve types
caryclark@google.com6680fb12012-02-07 22:10:51 +0000513 if (wt.verb() == SkPath::kLine_Verb
514 && wn.verb() == SkPath::kLine_Verb) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000515 double wtTs[2], wnTs[2];
caryclark@google.com6680fb12012-02-07 22:10:51 +0000516 int pts = LineIntersect(wt.fPts, wn.fPts, wtTs, wnTs);
caryclark@google.comc6825902012-02-03 22:07:47 +0000517 if (pts) {
518 test->add(wtTs, pts, wt.verbIndex());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000519 test->fContainsIntercepts = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000520 next->add(wnTs, pts, wn.verbIndex());
caryclark@google.com6680fb12012-02-07 22:10:51 +0000521 next->fContainsIntercepts = true;
caryclark@google.comc6825902012-02-03 22:07:47 +0000522 }
523 }
524 } while (wt.bottom() <= wn.bottom() ? wt.next() : wn.next());
525 }
526 test = next;
527 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000528
529 // compute bottom taking into account any intersected edges
530 ActiveEdge* activePtr = activeEdges.begin() - 1;
531 ActiveEdge* lastActive = activeEdges.end();
532 while (++activePtr != lastActive) {
533 const InEdge* test = activePtr->fWorkEdge.fEdge;
534 if (!test->fContainsIntercepts) {
535 continue;
536 }
537 WorkEdge wt;
538 wt.init(test);
539 do {
540 // FIXME: add all curve types
541 const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
542 const SkTDArray<double>& fTs = intercepts.fTs;
543 size_t count = fTs.count();
544 for (size_t index = 0; index < count; ++index) {
545 if (wt.verb() == SkPath::kLine_Verb) {
546 SkScalar yIntercept = LineYAtT(wt.fPts, fTs[index]);
547 if (bottom > yIntercept) {
548 bottom = yIntercept;
549 }
550 }
551 }
552 } while (wt.next());
553 }
554
caryclark@google.comc6825902012-02-03 22:07:47 +0000555 // stitch edge and t range that satisfies operation
556 int winding = 0;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000557 activePtr = activeEdges.begin() - 1;
558 lastActive = activeEdges.end();
559 SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
560 while (++activePtr != lastActive) {
561 const WorkEdge& wt = activePtr->fWorkEdge;
caryclark@google.comc6825902012-02-03 22:07:47 +0000562 int lastWinding = winding;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000563 winding += wt.winding();
564 if (!(lastWinding & windingMask) && !(winding & windingMask)) {
565 continue;
caryclark@google.comc6825902012-02-03 22:07:47 +0000566 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000567 do {
568 double currentT = activePtr->t();
569 const SkPoint* points = wt.fPts;
570 bool last;
571 do {
572 last = activePtr->nextT();
573 double nextT = activePtr->t();
574 // FIXME: add all combinations of curve types
575 if (wt.verb() == SkPath::kLine_Verb) {
576 SkPoint clippedPts[2];
577 const SkPoint* clipped;
578 if (currentT * nextT != 0 || currentT + nextT != 1) {
579 LineSubDivide(points, currentT, nextT, clippedPts);
580 clipped = clippedPts;
581 } else {
582 clipped = points;
583 }
584 SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
585 clipped[0].fX, clipped[0].fY,
586 clipped[1].fX, clipped[1].fY);
587 outBuilder->addLine(clipped);
588 if (clipped[1].fY >= bottom) {
589 goto nextEdge;
590 }
591 }
592 currentT = nextT;
593 } while (!last);
594 } while (activePtr->next());
595 nextEdge:
596 ;
caryclark@google.comc6825902012-02-03 22:07:47 +0000597 }
caryclark@google.com6680fb12012-02-07 22:10:51 +0000598
caryclark@google.comc6825902012-02-03 22:07:47 +0000599 y = bottom;
caryclark@google.com6680fb12012-02-07 22:10:51 +0000600 while ((*currentPtr)->fBounds.fBottom <= y) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000601 ++currentPtr;
602 }
603 } while (*currentPtr != &edgeSentinel);
caryclark@google.com6680fb12012-02-07 22:10:51 +0000604
caryclark@google.comc6825902012-02-03 22:07:47 +0000605 // assemble output path from string of pts, verbs
606 ;
607}
608
609void testSimplify();
610
611void testSimplify() {
612 SkPath path, out;
613 path.setFillType(SkPath::kWinding_FillType);
614 path.addRect(10, 10, 30, 30);
615 path.addRect(20, 20, 40, 40);
616 simplify(path, true, out);
617 path = out;
618 path.addRect(30, 10, 40, 20);
619 path.addRect(10, 30, 20, 40);
620 simplify(path, true, out);
621 path = out;
622 path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
623 simplify(path, true, out);
624 if (!out.isEmpty()) {
625 SkDebugf("expected empty\n");
626 }
627}