blob: 5d7209a09da2ef63a912f32ec9204d32190a9be6 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "CurveIntersection.h"
8#include "Intersections.h"
9#include "LineIntersection.h"
10#include "SkPath.h"
11#include "SkRect.h"
12#include "SkTArray.h"
13#include "SkTDArray.h"
14#include "ShapeOps.h"
15#include "TSearch.h"
16
17#undef SkASSERT
18#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
19
20// FIXME: remove once debugging is complete
21#if 0 // set to 1 for no debugging whatsoever
22
23//const bool gxRunTestsInOneThread = false;
24
25#define DEBUG_ADD_INTERSECTING_TS 0
26#define DEBUG_BRIDGE 0
27#define DEBUG_DUMP 0
28
29#else
30
31//const bool gRunTestsInOneThread = true;
32
33#define DEBUG_ADD_INTERSECTING_TS 1
34#define DEBUG_BRIDGE 1
35#define DEBUG_DUMP 1
36
37#endif
38
39#if DEBUG_DUMP
40static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
41static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
42static int gContourID;
43static int gSegmentID;
44#endif
45
46static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
47 Intersections& intersections) {
48 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
49 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
50 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
51}
52
53static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
54 Intersections& intersections) {
55 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
56 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
57 intersect(aQuad, bLine, intersections);
58 return intersections.fUsed;
59}
60
61static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
62 Intersections& intersections) {
63 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
64 {a[3].fX, a[3].fY}};
65 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
66 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
67}
68
69static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
70 Intersections& intersections) {
71 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
72 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
73 intersect(aQuad, bQuad, intersections);
74 return intersections.fUsed;
75}
76
77static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
78 Intersections& intersections) {
79 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
80 {a[3].fX, a[3].fY}};
81 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
82 {b[3].fX, b[3].fY}};
83 intersect(aCubic, bCubic, intersections);
84 return intersections.fUsed;
85}
86
87static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
88 SkScalar y, bool flipped, Intersections& intersections) {
89 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
90 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
91}
92
93static int VLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
94 SkScalar y, bool flipped, Intersections& intersections) {
95 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
96 return verticalIntersect(aLine, left, right, y, flipped, intersections);
97}
98
99static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
100 SkScalar y, bool flipped, Intersections& intersections) {
101 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
102 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
103}
104
105static int VQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
106 SkScalar y, bool flipped, Intersections& intersections) {
107 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
108 return verticalIntersect(aQuad, left, right, y, flipped, intersections);
109}
110
111static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
112 SkScalar y, bool flipped, Intersections& intersections) {
113 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
114 {a[3].fX, a[3].fY}};
115 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
116}
117
118static int VCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
119 SkScalar y, bool flipped, Intersections& intersections) {
120 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
121 {a[3].fX, a[3].fY}};
122 return verticalIntersect(aCubic, left, right, y, flipped, intersections);
123}
124
125static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
126 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
127 double x, y;
128 xy_at_t(line, t, x, y);
129 out->fX = SkDoubleToScalar(x);
130 out->fY = SkDoubleToScalar(y);
131}
132
133static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
134 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
135 double x, y;
136 xy_at_t(quad, t, x, y);
137 out->fX = SkDoubleToScalar(x);
138 out->fY = SkDoubleToScalar(y);
139}
140
141static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
142 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
143 {a[3].fX, a[3].fY}};
144 double x, y;
145 xy_at_t(cubic, t, x, y);
146 out->fX = SkDoubleToScalar(x);
147 out->fY = SkDoubleToScalar(y);
148}
149
150static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
151 NULL,
152 LineXYAtT,
153 QuadXYAtT,
154 CubicXYAtT
155};
156
157static SkScalar LineXAtT(const SkPoint a[2], double t) {
158 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
159 double x;
160 xy_at_t(aLine, t, x, *(double*) 0);
161 return SkDoubleToScalar(x);
162}
163
164static SkScalar QuadXAtT(const SkPoint a[3], double t) {
165 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
166 double x;
167 xy_at_t(quad, t, x, *(double*) 0);
168 return SkDoubleToScalar(x);
169}
170
171static SkScalar CubicXAtT(const SkPoint a[4], double t) {
172 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
173 {a[3].fX, a[3].fY}};
174 double x;
175 xy_at_t(cubic, t, x, *(double*) 0);
176 return SkDoubleToScalar(x);
177}
178
179static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
180 NULL,
181 LineXAtT,
182 QuadXAtT,
183 CubicXAtT
184};
185
186static SkScalar LineYAtT(const SkPoint a[2], double t) {
187 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
188 double y;
189 xy_at_t(aLine, t, *(double*) 0, y);
190 return SkDoubleToScalar(y);
191}
192
193static SkScalar QuadYAtT(const SkPoint a[3], double t) {
194 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
195 double y;
196 xy_at_t(quad, t, *(double*) 0, y);
197 return SkDoubleToScalar(y);
198}
199
200static SkScalar CubicYAtT(const SkPoint a[4], double t) {
201 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
202 {a[3].fX, a[3].fY}};
203 double y;
204 xy_at_t(cubic, t, *(double*) 0, y);
205 return SkDoubleToScalar(y);
206}
207
208static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
209 NULL,
210 LineYAtT,
211 QuadYAtT,
212 CubicYAtT
213};
214
215static void LineSubDivide(const SkPoint a[2], double startT, double endT,
216 SkPoint sub[2]) {
217 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
218 _Line dst;
219 sub_divide(aLine, startT, endT, dst);
220 sub[0].fX = SkDoubleToScalar(dst[0].x);
221 sub[0].fY = SkDoubleToScalar(dst[0].y);
222 sub[1].fX = SkDoubleToScalar(dst[1].x);
223 sub[1].fY = SkDoubleToScalar(dst[1].y);
224}
225
226static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
227 SkPoint sub[3]) {
228 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
229 {a[2].fX, a[2].fY}};
230 Quadratic dst;
231 sub_divide(aQuad, startT, endT, dst);
232 sub[0].fX = SkDoubleToScalar(dst[0].x);
233 sub[0].fY = SkDoubleToScalar(dst[0].y);
234 sub[1].fX = SkDoubleToScalar(dst[1].x);
235 sub[1].fY = SkDoubleToScalar(dst[1].y);
236 sub[2].fX = SkDoubleToScalar(dst[2].x);
237 sub[2].fY = SkDoubleToScalar(dst[2].y);
238}
239
240static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
241 SkPoint sub[4]) {
242 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
243 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
244 Cubic dst;
245 sub_divide(aCubic, startT, endT, dst);
246 sub[0].fX = SkDoubleToScalar(dst[0].x);
247 sub[0].fY = SkDoubleToScalar(dst[0].y);
248 sub[1].fX = SkDoubleToScalar(dst[1].x);
249 sub[1].fY = SkDoubleToScalar(dst[1].y);
250 sub[2].fX = SkDoubleToScalar(dst[2].x);
251 sub[2].fY = SkDoubleToScalar(dst[2].y);
252 sub[3].fX = SkDoubleToScalar(dst[3].x);
253 sub[3].fY = SkDoubleToScalar(dst[3].y);
254}
255
256static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
257 SkRect& bounds) {
258 SkPoint dst[3];
259 QuadSubDivide(a, startT, endT, dst);
260 bounds.fLeft = bounds.fRight = dst[0].fX;
261 bounds.fTop = bounds.fBottom = dst[0].fY;
262 for (int index = 1; index < 3; ++index) {
263 bounds.growToInclude(dst[index].fX, dst[index].fY);
264 }
265}
266
267static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
268 SkRect& bounds) {
269 SkPoint dst[4];
270 CubicSubDivide(a, startT, endT, dst);
271 bounds.fLeft = bounds.fRight = dst[0].fX;
272 bounds.fTop = bounds.fBottom = dst[0].fY;
273 for (int index = 1; index < 4; ++index) {
274 bounds.growToInclude(dst[index].fX, dst[index].fY);
275 }
276}
277
278static SkPath::Verb QuadReduceOrder(const SkPoint a[4],
279 SkTDArray<SkPoint>& reducePts) {
280 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
281 {a[2].fX, a[2].fY}};
282 Quadratic dst;
283 int order = reduceOrder(aQuad, dst);
284 for (int index = 0; index < order; ++index) {
285 SkPoint* pt = reducePts.append();
286 pt->fX = SkDoubleToScalar(dst[index].x);
287 pt->fY = SkDoubleToScalar(dst[index].y);
288 }
289 return (SkPath::Verb) (order - 1);
290}
291
292static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
293 SkTDArray<SkPoint>& reducePts) {
294 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
295 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
296 Cubic dst;
297 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
298 for (int index = 0; index < order; ++index) {
299 SkPoint* pt = reducePts.append();
300 pt->fX = SkDoubleToScalar(dst[index].x);
301 pt->fY = SkDoubleToScalar(dst[index].y);
302 }
303 return (SkPath::Verb) (order - 1);
304}
305
306static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
307 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
308 double x[2];
309 xy_at_t(aLine, startT, x[0], *(double*) 0);
310 xy_at_t(aLine, endT, x[0], *(double*) 0);
311 return startT < endT ? startT : endT;
312}
313
314static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
315 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
316 {a[2].fX, a[2].fY}};
317 return leftMostT(aQuad, startT, endT);
318}
319
320static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
321 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
322 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
323 return leftMostT(aCubic, startT, endT);
324}
325
326static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
327 NULL,
328 LineLeftMost,
329 QuadLeftMost,
330 CubicLeftMost
331};
332
333static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
334 const SkPoint& below) {
335 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
336 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
337 return implicit_matches_ulps(aLine, bLine, 32);
338}
339
340// Bounds, unlike Rect, does not consider a vertical line to be empty.
341struct Bounds : public SkRect {
342 static bool Intersects(const Bounds& a, const Bounds& b) {
343 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
344 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
345 }
346
347 bool isEmpty() {
348 return fLeft > fRight || fTop > fBottom
349 || fLeft == fRight && fTop == fBottom
350 || isnan(fLeft) || isnan(fRight)
351 || isnan(fTop) || isnan(fBottom);
352 }
353
354 void setCubicBounds(const SkPoint a[4]) {
355 _Rect dRect;
356 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
357 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
358 dRect.setBounds(cubic);
359 set(dRect.left, dRect.top, dRect.right, dRect.bottom);
360 }
361
362 void setQuadBounds(const SkPoint a[3]) {
363 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
364 {a[2].fX, a[2].fY}};
365 _Rect dRect;
366 dRect.setBounds(quad);
367 set(dRect.left, dRect.top, dRect.right, dRect.bottom);
368 }
369};
370
371class Segment;
372
373struct TEntry {
374 double fT;
375 const Segment* fOther;
376 double fOtherT;
377 bool fCoincident;
378};
379
380class Segment {
381public:
382 Segment() {
383#if DEBUG_DUMP
384 fID = ++gSegmentID;
385#endif
386 }
387
388 int addT(double newT, const Segment& other) {
389 // FIXME: in the pathological case where there is a ton of intercepts,
390 // binary search?
391 int insertedAt = -1;
392 TEntry* entry;
393 size_t tCount = fTs.count();
394 double delta;
395 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
396 if (newT <= fTs[idx2].fT) {
397 insertedAt = idx2;
398 entry = fTs.insert(idx2);
399 goto finish;
400 }
401 }
402 insertedAt = tCount;
403 entry = fTs.append();
404finish:
405 entry->fT = newT;
406 entry->fOther = &other;
407 return insertedAt;
408 }
409
410 bool addCubic(const SkPoint pts[4]) {
411 fPts = pts;
412 fVerb = SkPath::kCubic_Verb;
413 fBounds.setCubicBounds(pts);
414 }
415
416 bool addLine(const SkPoint pts[2]) {
417 fPts = pts;
418 fVerb = SkPath::kLine_Verb;
419 fBounds.set(pts, 2);
420 }
421
422 // add 2 to edge or out of range values to get T extremes
423 void addOtherT(int index, double other, bool coincident) {
424 fTs[index].fOtherT = other;
425 fTs[index].fCoincident = coincident;
426 }
427
428 bool addQuad(const SkPoint pts[3]) {
429 fPts = pts;
430 fVerb = SkPath::kQuad_Verb;
431 fBounds.setQuadBounds(pts);
432 }
433
434 const Bounds& bounds() const {
435 return fBounds;
436 }
437
438 int findByT(double t, const Segment* match) const {
439 // OPTIMIZATION: bsearch if count is honkin huge
440 int count = fTs.count();
441 for (int index = 0; index < count; ++index) {
442 const TEntry& entry = fTs[index];
443 if (t == entry.fT && match == entry.fOther) {
444 return index;
445 }
446 }
447 SkASSERT(0); // should never get here
448 return -1;
449 }
450
451 int findLefty(int tIndex, const SkPoint& base) const {
452 int bestTIndex;
453 SkPoint test;
454 SkScalar bestX = DBL_MAX;
455 int testTIndex = tIndex;
456 while (--testTIndex >= 0) {
457 xyAtT(testTIndex, &test);
458 if (test != base) {
459 continue;
460 }
461 bestX = test.fX;
462 bestTIndex = testTIndex;
463 break;
464 }
465 int count = fTs.count();
466 testTIndex = tIndex;
467 while (++testTIndex < count) {
468 xyAtT(testTIndex, &test);
469 if (test == base) {
470 continue;
471 }
472 return bestX > test.fX ? testTIndex : bestTIndex;
473 }
474 return -1;
475 }
476
477 const Segment* findTop(int& tIndex) const {
478 // iterate through T intersections and return topmost
479 // topmost tangent from y-min to first pt is closer to horizontal
480 int firstT = 0;
481 int lastT = 0;
482 SkScalar topY = fPts[0].fY;
483 int count = fTs.count();
484 int index;
485 for (index = 1; index < count; ++index) {
486 const TEntry& entry = fTs[index];
487 double t = entry.fT;
488 SkScalar yIntercept = (*SegmentYAtT[fVerb])(fPts, t);
489 if (topY > yIntercept) {
490 topY = yIntercept;
491 firstT = lastT = index;
492 } else if (topY == yIntercept) {
493 lastT = index;
494 }
495 }
496 // if a pair of segments go down, choose the higher endpoint
497 if (firstT == lastT && (firstT == 0 || firstT == count - 1)) {
498 tIndex = firstT;
499 return this;
500 }
501 // if the topmost T is not on end, or is three-way or more, find left
502 SkPoint leftBase;
503 xyAtT(firstT, &leftBase);
504 int tLeft = findLefty(firstT, leftBase);
505 SkASSERT(tLeft > 0);
506 const Segment* leftSegment = this;
507 SkScalar left = leftMost(firstT, tLeft);
508 for (index = firstT; index <= lastT; ++index) {
509 const Segment* other = fTs[index].fOther;
510 double otherT = fTs[index].fOtherT;
511 int otherTIndex = other->findByT(otherT, this);
512 // pick companionT closest (but not too close) on either side
513 int otherTLeft = other->findLefty(otherTIndex, leftBase);
514 if (otherTLeft < 0) {
515 continue;
516 }
517 SkScalar otherMost = other->leftMost(otherTIndex, otherTLeft);
518 if (otherMost < left) {
519 leftSegment = other;
520 }
521 }
522 return leftSegment;
523 }
524
525 bool intersected() const {
526 return fTs.count() > 0;
527 }
528
529 bool isHorizontal() const {
530 return fBounds.fTop == fBounds.fBottom;
531 }
532
533 bool isVertical() const {
534 return fBounds.fLeft == fBounds.fRight;
535 }
536
537 SkScalar leftMost(int start, int end) const {
538 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
539 }
540
541 const SkPoint* pts() const {
542 return fPts;
543 }
544
545 void reset() {
546 fPts = NULL;
547 fVerb = (SkPath::Verb) -1;
548 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
549 fTs.reset();
550 }
551
552 // OPTIMIZATION: remove this function if it's never called
553 double t(int tIndex) const {
554 return fTs[tIndex].fT;
555 }
556
557 SkPath::Verb verb() const {
558 return fVerb;
559 }
560
561 SkScalar xAtT(double t) const {
562 return (*SegmentXAtT[fVerb])(fPts, t);
563 }
564
565 void xyAtT(double t, SkPoint* pt) const {
566 (*SegmentXYAtT[fVerb])(fPts, t, pt);
567 }
568
569#if DEBUG_DUMP
570 void dump() const {
571 const char className[] = "Segment";
572 const int tab = 4;
573 for (int i = 0; i < fTs.count(); ++i) {
574 SkPoint out;
575 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
576 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
577 " otherT=%1.9g coincident=%d\n",
578 tab + sizeof(className), className, fID,
579 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
580 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fCoincident);
581 }
582 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
583 tab + sizeof(className), className, fID,
584 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
585 }
586#endif
587
588private:
589 const SkPoint* fPts;
590 SkPath::Verb fVerb;
591 Bounds fBounds;
592 SkTDArray<TEntry> fTs;
593#if DEBUG_DUMP
594 int fID;
595#endif
596};
597
598class Contour {
599public:
600 Contour() {
601 reset();
602#if DEBUG_DUMP
603 fID = ++gContourID;
604#endif
605 }
606
607 bool operator<(const Contour& rh) const {
608 return fBounds.fTop == rh.fBounds.fTop
609 ? fBounds.fLeft < rh.fBounds.fLeft
610 : fBounds.fTop < rh.fBounds.fTop;
611 }
612
613 void addCubic(const SkPoint pts[4]) {
614 fSegments.push_back().addCubic(pts);
615 fContainsCurves = true;
616 }
617 void addLine(const SkPoint pts[2]) {
618 fSegments.push_back().addLine(pts);
619 }
620
621 void addQuad(const SkPoint pts[3]) {
622 fSegments.push_back().addQuad(pts);
623 fContainsCurves = true;
624 }
625
626 const Bounds& bounds() const {
627 return fBounds;
628 }
629
630 void complete() {
631 setBounds();
632 fContainsIntercepts = false;
633 }
634
635 void containsIntercepts() {
636 fContainsIntercepts = true;
637 }
638
639 void reset() {
640 fSegments.reset();
641 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
642 fContainsCurves = fContainsIntercepts = false;
643 }
644
645 Segment& topSegment() {
646 return fSegments[fTopSegment];
647 }
648
649#if DEBUG_DUMP
650 void dump() {
651 int i;
652 const char className[] = "Contour";
653 const int tab = 4;
654 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
655 for (i = 0; i < fSegments.count(); ++i) {
656 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
657 className, i);
658 fSegments[i].dump();
659 }
660 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
661 tab + sizeof(className), className,
662 fBounds.fLeft, fBounds.fTop,
663 fBounds.fRight, fBounds.fBottom);
664 SkDebugf("%*s.fTopSegment=%d\n", tab + sizeof(className), className,
665 fTopSegment);
666 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
667 className, fContainsIntercepts);
668 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
669 className, fContainsCurves);
670 }
671#endif
672
673protected:
674 void setBounds() {
675 int count = fSegments.count();
676 if (count == 0) {
677 SkDebugf("%s empty contour\n", __FUNCTION__);
678 SkASSERT(0);
679 // FIXME: delete empty contour?
680 return;
681 }
682 fTopSegment = 0;
683 fBounds = fSegments.front().bounds();
684 SkScalar top = fBounds.fTop;
685 for (int index = 1; index < count; ++index) {
686 fBounds.growToInclude(fSegments[index].bounds());
687 if (top > fBounds.fTop) {
688 fTopSegment = index;
689 top = fBounds.fTop;
690 }
691 }
692 }
693
694public:
695 SkTArray<Segment> fSegments; // not worth accessor functions?
696
697private:
698 Bounds fBounds;
699 int fTopSegment;
700 bool fContainsIntercepts;
701 bool fContainsCurves;
702#if DEBUG_DUMP
703 int fID;
704#endif
705};
706
707class EdgeBuilder {
708public:
709
710EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
711 : fPath(path)
712 , fCurrentContour(NULL)
713 , fContours(contours)
714{
715#if DEBUG_DUMP
716 gContourID = 0;
717 gSegmentID = 0;
718#endif
719 walk();
720}
721
722protected:
723
724void complete() {
725 if (fCurrentContour && fCurrentContour->fSegments.count()) {
726 fCurrentContour->complete();
727 fCurrentContour = NULL;
728 }
729}
730
731void startContour() {
732 if (!fCurrentContour) {
733 fCurrentContour = fContours.push_back_n(1);
734 }
735}
736
737void walk() {
738 // FIXME:remove once we can access path pts directly
739 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
740 SkPoint pts[4];
741 SkPath::Verb verb;
742 do {
743 verb = iter.next(pts);
744 *fPathVerbs.append() = verb;
745 if (verb == SkPath::kMove_Verb) {
746 *fPathPts.append() = pts[0];
747 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
748 fPathPts.append(verb, &pts[1]);
749 }
750 } while (verb != SkPath::kDone_Verb);
751 // FIXME: end of section to remove once path pts are accessed directly
752
753 SkPath::Verb reducedVerb;
754 uint8_t* verbPtr = fPathVerbs.begin();
755 const SkPoint* pointsPtr = fPathPts.begin();
756 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
757 switch (verb) {
758 case SkPath::kMove_Verb:
759 complete();
760 startContour();
761 pointsPtr += 1;
762 continue;
763 case SkPath::kLine_Verb:
764 // skip degenerate points
765 if (pointsPtr[-1].fX != pointsPtr[0].fX
766 || pointsPtr[-1].fY != pointsPtr[0].fY) {
767 fCurrentContour->addLine(&pointsPtr[-1]);
768 }
769 break;
770 case SkPath::kQuad_Verb:
771 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
772 if (reducedVerb == 0) {
773 break; // skip degenerate points
774 }
775 if (reducedVerb == 1) {
776 fCurrentContour->addLine(fReducePts.end() - 2);
777 break;
778 }
779 fCurrentContour->addQuad(&pointsPtr[-1]);
780 break;
781 case SkPath::kCubic_Verb:
782 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
783 if (reducedVerb == 0) {
784 break; // skip degenerate points
785 }
786 if (reducedVerb == 1) {
787 fCurrentContour->addLine(fReducePts.end() - 2);
788 break;
789 }
790 if (reducedVerb == 2) {
791 fCurrentContour->addQuad(fReducePts.end() - 3);
792 break;
793 }
794 fCurrentContour->addCubic(&pointsPtr[-1]);
795 break;
796 case SkPath::kClose_Verb:
797 SkASSERT(fCurrentContour);
798 complete();
799 continue;
800 default:
801 SkDEBUGFAIL("bad verb");
802 return;
803 }
804 pointsPtr += verb;
805 SkASSERT(fCurrentContour);
806 }
807 complete();
808 if (fCurrentContour && !fCurrentContour->fSegments.count()) {
809 fContours.pop_back();
810 }
811}
812
813private:
814 const SkPath& fPath;
815 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
816 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
817 Contour* fCurrentContour;
818 SkTArray<Contour>& fContours;
819 SkTDArray<SkPoint> fReducePts; // segments created on the fly
820};
821
822class Work {
823public:
824 enum SegmentType {
825 kHorizontalLine_Segment = -1,
826 kVerticalLine_Segment = 0,
827 kLine_Segment = SkPath::kLine_Verb,
828 kQuad_Segment = SkPath::kQuad_Verb,
829 kCubic_Segment = SkPath::kCubic_Verb,
830 };
831
832 void addOtherT(int index, double other, bool coincident) {
833 fContour->fSegments[fIndex].addOtherT(index, other, coincident);
834 }
835
836 // Avoid collapsing t values that are close to the same since
837 // we walk ts to describe consecutive intersections. Since a pair of ts can
838 // be nearly equal, any problems caused by this should be taken care
839 // of later.
840 // On the edge or out of range values are negative; add 2 to get end
841 int addT(double newT, const Work& other) {
842 fContour->containsIntercepts();
843 return fContour->fSegments[fIndex].addT(newT,
844 other.fContour->fSegments[other.fIndex]);
845 }
846
847 bool advance() {
848 return ++fIndex < fLast;
849 }
850
851 SkScalar bottom() const {
852 return bounds().fBottom;
853 }
854
855 const Bounds& bounds() const {
856 return fContour->fSegments[fIndex].bounds();
857 }
858
859 const SkPoint* cubic() const {
860 return fCubic;
861 }
862
863 void init(Contour* contour) {
864 fContour = contour;
865 fIndex = 0;
866 fLast = contour->fSegments.count();
867 }
868
869 SkScalar left() const {
870 return bounds().fLeft;
871 }
872
873 void promoteToCubic() {
874 fCubic[0] = pts()[0];
875 fCubic[2] = pts()[1];
876 fCubic[3] = pts()[2];
877 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
878 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
879 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
880 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
881 }
882
883 const SkPoint* pts() const {
884 return fContour->fSegments[fIndex].pts();
885 }
886
887 SkScalar right() const {
888 return bounds().fRight;
889 }
890
891 ptrdiff_t segmentIndex() const {
892 return fIndex;
893 }
894
895 SegmentType segmentType() const {
896 const Segment& segment = fContour->fSegments[fIndex];
897 SegmentType type = (SegmentType) segment.verb();
898 if (type != kLine_Segment) {
899 return type;
900 }
901 if (segment.isHorizontal()) {
902 return kHorizontalLine_Segment;
903 }
904 if (segment.isVertical()) {
905 return kVerticalLine_Segment;
906 }
907 return kLine_Segment;
908 }
909
910 bool startAfter(const Work& after) {
911 fIndex = after.fIndex;
912 return advance();
913 }
914
915 SkScalar top() const {
916 return bounds().fTop;
917 }
918
919 SkPath::Verb verb() const {
920 return fContour->fSegments[fIndex].verb();
921 }
922
923 SkScalar x() const {
924 return bounds().fLeft;
925 }
926
927 bool xFlipped() const {
928 return x() != pts()[0].fX;
929 }
930
931 SkScalar y() const {
932 return bounds().fTop;
933 }
934
935 bool yFlipped() const {
936 return y() != pts()[0].fX;
937 }
938
939protected:
940 Contour* fContour;
941 SkPoint fCubic[4];
942 int fIndex;
943 int fLast;
944};
945
946static void debugShowLineIntersection(int pts, const Work& wt,
947 const Work& wn, const double wtTs[2], const double wnTs[2]) {
948#if DEBUG_ADD_INTERSECTING_TS
949 if (!pts) {
950 return;
951 }
952 SkPoint wtOutPt, wnOutPt;
953 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
954 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
955 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
956 __FUNCTION__,
957 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
958 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
959 if (pts == 2) {
960 SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
961 }
962 SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
963 __FUNCTION__,
964 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
965 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
966 if (pts == 2) {
967 SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
968 }
969#endif
970}
971
972static bool addIntersectingTs(Contour* test, Contour* next) {
973 if (test != next) {
974 if (test->bounds().fBottom < next->bounds().fTop) {
975 return false;
976 }
977 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
978 return true;
979 }
980 }
981 Work wt, wn;
982 wt.init(test);
983 wn.init(next);
984 do {
985 if (test == next && !wn.startAfter(wt)) {
986 continue;
987 }
988 do {
989 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
990 continue;
991 }
992 int pts;
993 Intersections ts;
994 bool swap = false;
995 switch (wt.segmentType()) {
996 case Work::kHorizontalLine_Segment:
997 swap = true;
998 switch (wn.segmentType()) {
999 case Work::kHorizontalLine_Segment:
1000 case Work::kVerticalLine_Segment:
1001 case Work::kLine_Segment: {
1002 pts = HLineIntersect(wn.pts(), wt.left(),
1003 wt.right(), wt.y(), wt.xFlipped(), ts);
1004 break;
1005 }
1006 case Work::kQuad_Segment: {
1007 pts = HQuadIntersect(wn.pts(), wt.left(),
1008 wt.right(), wt.y(), wt.xFlipped(), ts);
1009 break;
1010 }
1011 case Work::kCubic_Segment: {
1012 pts = HCubicIntersect(wn.pts(), wt.left(),
1013 wt.right(), wt.y(), wt.xFlipped(), ts);
1014 break;
1015 }
1016 default:
1017 SkASSERT(0);
1018 }
1019 break;
1020 case Work::kVerticalLine_Segment:
1021 swap = true;
1022 switch (wn.segmentType()) {
1023 case Work::kHorizontalLine_Segment:
1024 case Work::kVerticalLine_Segment:
1025 case Work::kLine_Segment: {
1026 pts = VLineIntersect(wn.pts(), wt.top(),
1027 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1028 break;
1029 }
1030 case Work::kQuad_Segment: {
1031 pts = VQuadIntersect(wn.pts(), wt.top(),
1032 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1033 break;
1034 }
1035 case Work::kCubic_Segment: {
1036 pts = VCubicIntersect(wn.pts(), wt.top(),
1037 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1038 break;
1039 }
1040 default:
1041 SkASSERT(0);
1042 }
1043 break;
1044 case Work::kLine_Segment:
1045 switch (wn.segmentType()) {
1046 case Work::kHorizontalLine_Segment:
1047 pts = HLineIntersect(wt.pts(), wn.left(),
1048 wn.right(), wn.y(), wn.xFlipped(), ts);
1049 break;
1050 case Work::kVerticalLine_Segment:
1051 pts = VLineIntersect(wt.pts(), wn.top(),
1052 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1053 break;
1054 case Work::kLine_Segment: {
1055 pts = LineIntersect(wt.pts(), wn.pts(), ts);
1056 debugShowLineIntersection(pts, wt, wn,
1057 ts.fT[1], ts.fT[0]);
1058 break;
1059 }
1060 case Work::kQuad_Segment: {
1061 swap = true;
1062 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
1063 break;
1064 }
1065 case Work::kCubic_Segment: {
1066 swap = true;
1067 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
1068 break;
1069 }
1070 default:
1071 SkASSERT(0);
1072 }
1073 break;
1074 case Work::kQuad_Segment:
1075 switch (wn.segmentType()) {
1076 case Work::kHorizontalLine_Segment:
1077 pts = HQuadIntersect(wt.pts(), wn.left(),
1078 wn.right(), wn.y(), wn.xFlipped(), ts);
1079 break;
1080 case Work::kVerticalLine_Segment:
1081 pts = VQuadIntersect(wt.pts(), wn.top(),
1082 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1083 break;
1084 case Work::kLine_Segment: {
1085 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
1086 break;
1087 }
1088 case Work::kQuad_Segment: {
1089 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
1090 break;
1091 }
1092 case Work::kCubic_Segment: {
1093 wt.promoteToCubic();
1094 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
1095 break;
1096 }
1097 default:
1098 SkASSERT(0);
1099 }
1100 break;
1101 case Work::kCubic_Segment:
1102 switch (wn.segmentType()) {
1103 case Work::kHorizontalLine_Segment:
1104 pts = HCubicIntersect(wt.pts(), wn.left(),
1105 wn.right(), wn.y(), wn.xFlipped(), ts);
1106 break;
1107 case Work::kVerticalLine_Segment:
1108 pts = VCubicIntersect(wt.pts(), wn.top(),
1109 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1110 break;
1111 case Work::kLine_Segment: {
1112 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
1113 break;
1114 }
1115 case Work::kQuad_Segment: {
1116 wn.promoteToCubic();
1117 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
1118 break;
1119 }
1120 case Work::kCubic_Segment: {
1121 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
1122 break;
1123 }
1124 default:
1125 SkASSERT(0);
1126 }
1127 break;
1128 default:
1129 SkASSERT(0);
1130 }
1131 // in addition to recording T values, record matching segment
1132 bool coincident = pts == 2 && wn.segmentType() <= Work::kLine_Segment
1133 && wt.segmentType() <= Work::kLine_Segment;
1134 for (int pt = 0; pt < pts; ++pt) {
1135 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
1136 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
1137 int testTAt = wt.addT(ts.fT[swap][pt], wn);
1138 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
1139 wt.addOtherT(testTAt, ts.fT[!swap][pt], coincident);
1140 wn.addOtherT(nextTAt, ts.fT[swap][pt], coincident);
1141 }
1142 } while (wn.advance());
1143 } while (wt.advance());
1144 return true;
1145}
1146
1147// Each segment may have an inside or an outside. Segments contained within
1148// winding may have insides on either side, and form a contour that should be
1149// ignored. Segments that are coincident with opposing direction segments may
1150// have outsides on either side, and should also disappear.
1151// 'Normal' segments will have one inside and one outside. Subsequent connections
1152// when winding should follow the intersection direction. If more than one edge
1153// is an option, choose first edge that continues the inside.
1154
1155static void bridge(SkTDArray<Contour*>& contourList) {
1156 // Start at the top. Above the top is outside, below is inside.
1157 Contour* topContour = contourList[0];
1158 Segment& topStart = topContour->topSegment();
1159 int index;
1160 const Segment* topSegment = topStart.findTop(index);
1161 start here ;
1162 // find span
1163 // handle coincident
1164 // mark neighbors winding coverage
1165 // output span
1166 // mark span as processed
1167
1168}
1169
1170static void makeContourList(SkTArray<Contour>& contours, Contour& sentinel,
1171 SkTDArray<Contour*>& list) {
1172 size_t count = contours.count();
1173 if (count == 0) {
1174 return;
1175 }
1176 for (size_t index = 0; index < count; ++index) {
1177 *list.append() = &contours[index];
1178 }
1179 *list.append() = &sentinel;
1180 QSort<Contour>(list.begin(), list.end() - 1);
1181}
1182
1183void simplifyx(const SkPath& path, bool asFill, SkPath& simple) {
1184 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
1185 int windingMask = (path.getFillType() & 1) ? 1 : -1;
1186 simple.reset();
1187 simple.setFillType(SkPath::kEvenOdd_FillType);
1188
1189 // turn path into list of segments
1190 SkTArray<Contour> contours;
1191 // FIXME: add self-intersecting cubics' T values to segment
1192 EdgeBuilder builder(path, contours);
1193 SkTDArray<Contour*> contourList;
1194 Contour sentinel;
1195 sentinel.reset();
1196 makeContourList(contours, sentinel, contourList);
1197 Contour** currentPtr = contourList.begin();
1198 if (!currentPtr) {
1199 return;
1200 }
1201 // find all intersections between segments
1202 do {
1203 Contour** nextPtr = currentPtr;
1204 Contour* current = *currentPtr++;
1205 Contour* next;
1206 do {
1207 next = *nextPtr++;
1208 } while (next != &sentinel && addIntersectingTs(current, next));
1209 } while (*currentPtr != &sentinel);
1210 // construct closed contours
1211 bridge(contourList);
1212}
1213