blob: 4ab12cb4e77688cb38b40d7e8e4c4a24238a8890 [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 */
caryclark@google.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
20
caryclark@google.comfa0588f2012-04-26 21:01:06 +000021// FIXME: remove once debugging is complete
22#if 0 // set to 1 for no debugging whatsoever
23
24//const bool gxRunTestsInOneThread = false;
25
26#define DEBUG_ADD_INTERSECTING_TS 0
27#define DEBUG_BRIDGE 0
28#define DEBUG_DUMP 0
29
30#else
31
32//const bool gRunTestsInOneThread = true;
33
34#define DEBUG_ADD_INTERSECTING_TS 1
35#define DEBUG_BRIDGE 1
36#define DEBUG_DUMP 1
37
38#endif
39
40#if DEBUG_DUMP
41static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
42static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
43static int gContourID;
44static int gSegmentID;
45#endif
46
47static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
48 Intersections& intersections) {
49 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
50 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
51 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
52}
53
54static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
55 Intersections& intersections) {
56 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
57 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
58 intersect(aQuad, bLine, intersections);
59 return intersections.fUsed;
60}
61
62static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
63 Intersections& intersections) {
64 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
65 {a[3].fX, a[3].fY}};
66 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
67 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
68}
69
70static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
71 Intersections& intersections) {
72 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
73 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
74 intersect(aQuad, bQuad, intersections);
75 return intersections.fUsed;
76}
77
78static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
79 Intersections& intersections) {
80 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
81 {a[3].fX, a[3].fY}};
82 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
83 {b[3].fX, b[3].fY}};
84 intersect(aCubic, bCubic, intersections);
85 return intersections.fUsed;
86}
87
88static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
89 SkScalar y, bool flipped, Intersections& intersections) {
90 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
91 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
92}
93
94static int VLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
95 SkScalar y, bool flipped, Intersections& intersections) {
96 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
97 return verticalIntersect(aLine, left, right, y, flipped, intersections);
98}
99
100static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
101 SkScalar y, bool flipped, Intersections& intersections) {
102 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
103 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
104}
105
106static int VQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
107 SkScalar y, bool flipped, Intersections& intersections) {
108 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
109 return verticalIntersect(aQuad, left, right, y, flipped, intersections);
110}
111
112static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
113 SkScalar y, bool flipped, Intersections& intersections) {
114 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
115 {a[3].fX, a[3].fY}};
116 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
117}
118
119static int VCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
120 SkScalar y, bool flipped, Intersections& intersections) {
121 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
122 {a[3].fX, a[3].fY}};
123 return verticalIntersect(aCubic, left, right, y, flipped, intersections);
124}
125
126static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
127 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
128 double x, y;
129 xy_at_t(line, t, x, y);
130 out->fX = SkDoubleToScalar(x);
131 out->fY = SkDoubleToScalar(y);
132}
133
134static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
135 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
136 double x, y;
137 xy_at_t(quad, t, x, y);
138 out->fX = SkDoubleToScalar(x);
139 out->fY = SkDoubleToScalar(y);
140}
141
142static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
143 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
144 {a[3].fX, a[3].fY}};
145 double x, y;
146 xy_at_t(cubic, t, x, y);
147 out->fX = SkDoubleToScalar(x);
148 out->fY = SkDoubleToScalar(y);
149}
150
151static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
152 NULL,
153 LineXYAtT,
154 QuadXYAtT,
155 CubicXYAtT
156};
157
158static SkScalar LineXAtT(const SkPoint a[2], double t) {
159 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
160 double x;
161 xy_at_t(aLine, t, x, *(double*) 0);
162 return SkDoubleToScalar(x);
163}
164
165static SkScalar QuadXAtT(const SkPoint a[3], double t) {
166 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
167 double x;
168 xy_at_t(quad, t, x, *(double*) 0);
169 return SkDoubleToScalar(x);
170}
171
172static SkScalar CubicXAtT(const SkPoint a[4], double t) {
173 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
174 {a[3].fX, a[3].fY}};
175 double x;
176 xy_at_t(cubic, t, x, *(double*) 0);
177 return SkDoubleToScalar(x);
178}
179
180static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
181 NULL,
182 LineXAtT,
183 QuadXAtT,
184 CubicXAtT
185};
186
187static SkScalar LineYAtT(const SkPoint a[2], double t) {
188 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
189 double y;
190 xy_at_t(aLine, t, *(double*) 0, y);
191 return SkDoubleToScalar(y);
192}
193
194static SkScalar QuadYAtT(const SkPoint a[3], double t) {
195 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
196 double y;
197 xy_at_t(quad, t, *(double*) 0, y);
198 return SkDoubleToScalar(y);
199}
200
201static SkScalar CubicYAtT(const SkPoint a[4], double t) {
202 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
203 {a[3].fX, a[3].fY}};
204 double y;
205 xy_at_t(cubic, t, *(double*) 0, y);
206 return SkDoubleToScalar(y);
207}
208
209static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
210 NULL,
211 LineYAtT,
212 QuadYAtT,
213 CubicYAtT
214};
215
216static void LineSubDivide(const SkPoint a[2], double startT, double endT,
217 SkPoint sub[2]) {
218 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
219 _Line dst;
220 sub_divide(aLine, startT, endT, dst);
221 sub[0].fX = SkDoubleToScalar(dst[0].x);
222 sub[0].fY = SkDoubleToScalar(dst[0].y);
223 sub[1].fX = SkDoubleToScalar(dst[1].x);
224 sub[1].fY = SkDoubleToScalar(dst[1].y);
225}
226
227static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
228 SkPoint sub[3]) {
229 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
230 {a[2].fX, a[2].fY}};
231 Quadratic dst;
232 sub_divide(aQuad, startT, endT, dst);
233 sub[0].fX = SkDoubleToScalar(dst[0].x);
234 sub[0].fY = SkDoubleToScalar(dst[0].y);
235 sub[1].fX = SkDoubleToScalar(dst[1].x);
236 sub[1].fY = SkDoubleToScalar(dst[1].y);
237 sub[2].fX = SkDoubleToScalar(dst[2].x);
238 sub[2].fY = SkDoubleToScalar(dst[2].y);
239}
240
241static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
242 SkPoint sub[4]) {
243 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
244 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
245 Cubic dst;
246 sub_divide(aCubic, startT, endT, dst);
247 sub[0].fX = SkDoubleToScalar(dst[0].x);
248 sub[0].fY = SkDoubleToScalar(dst[0].y);
249 sub[1].fX = SkDoubleToScalar(dst[1].x);
250 sub[1].fY = SkDoubleToScalar(dst[1].y);
251 sub[2].fX = SkDoubleToScalar(dst[2].x);
252 sub[2].fY = SkDoubleToScalar(dst[2].y);
253 sub[3].fX = SkDoubleToScalar(dst[3].x);
254 sub[3].fY = SkDoubleToScalar(dst[3].y);
255}
256
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000257static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
258 SkPoint []) = {
259 NULL,
260 LineSubDivide,
261 QuadSubDivide,
262 CubicSubDivide
263};
264
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000265static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
266 SkRect& bounds) {
267 SkPoint dst[3];
268 QuadSubDivide(a, startT, endT, dst);
269 bounds.fLeft = bounds.fRight = dst[0].fX;
270 bounds.fTop = bounds.fBottom = dst[0].fY;
271 for (int index = 1; index < 3; ++index) {
272 bounds.growToInclude(dst[index].fX, dst[index].fY);
273 }
274}
275
276static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
277 SkRect& bounds) {
278 SkPoint dst[4];
279 CubicSubDivide(a, startT, endT, dst);
280 bounds.fLeft = bounds.fRight = dst[0].fX;
281 bounds.fTop = bounds.fBottom = dst[0].fY;
282 for (int index = 1; index < 4; ++index) {
283 bounds.growToInclude(dst[index].fX, dst[index].fY);
284 }
285}
286
caryclark@google.com15fa1382012-05-07 20:49:36 +0000287static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000288 SkTDArray<SkPoint>& reducePts) {
289 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
290 {a[2].fX, a[2].fY}};
291 Quadratic dst;
292 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000293 if (order == 3) {
294 return SkPath::kQuad_Verb;
295 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000296 for (int index = 0; index < order; ++index) {
297 SkPoint* pt = reducePts.append();
298 pt->fX = SkDoubleToScalar(dst[index].x);
299 pt->fY = SkDoubleToScalar(dst[index].y);
300 }
301 return (SkPath::Verb) (order - 1);
302}
303
304static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
305 SkTDArray<SkPoint>& reducePts) {
306 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
307 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
308 Cubic dst;
309 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000310 if (order == 4) {
311 return SkPath::kCubic_Verb;
312 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000313 for (int index = 0; index < order; ++index) {
314 SkPoint* pt = reducePts.append();
315 pt->fX = SkDoubleToScalar(dst[index].x);
316 pt->fY = SkDoubleToScalar(dst[index].y);
317 }
318 return (SkPath::Verb) (order - 1);
319}
320
caryclark@google.com15fa1382012-05-07 20:49:36 +0000321static bool QuadIsLinear(const SkPoint a[3]) {
322 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
323 {a[2].fX, a[2].fY}};
324 return isLinear(aQuad, 0, 2);
325}
326
327static bool CubicIsLinear(const SkPoint a[4]) {
328 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
329 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
330 return isLinear(aCubic, 0, 3);
331}
332
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000333static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
334 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
335 double x[2];
336 xy_at_t(aLine, startT, x[0], *(double*) 0);
337 xy_at_t(aLine, endT, x[0], *(double*) 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000338 return startT < endT ? (float) startT : (float) endT;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000339}
340
341static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
342 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
343 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000344 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000345}
346
347static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
348 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
349 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000350 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000351}
352
353static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
354 NULL,
355 LineLeftMost,
356 QuadLeftMost,
357 CubicLeftMost
358};
359
360static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
361 const SkPoint& below) {
362 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
363 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
364 return implicit_matches_ulps(aLine, bLine, 32);
365}
366
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000367class Segment;
368
caryclark@google.com15fa1382012-05-07 20:49:36 +0000369// sorting angles
370// given angles of {dx dy ddx ddy dddx dddy} sort them
371class Angle {
372public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000373 // FIXME: this is bogus for quads and cubics
374 // if the quads and cubics' line from end pt to ctrl pt are coincident,
375 // there's no obvious way to determine the curve ordering from the
376 // derivatives alone. In particular, if one quadratic's coincident tangent
377 // is longer than the other curve, the final control point can place the
378 // longer curve on either side of the shorter one.
379 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
380 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000381 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000382 if ((fDy < 0) ^ (rh.fDy < 0)) {
383 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000384 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000385 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
386 return fDx < rh.fDx;
387 }
388 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000389 if (cmp) {
390 return cmp < 0;
391 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000392 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
393 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000394 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000395 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
396 return fDDx < rh.fDDx;
397 }
398 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000399 if (cmp) {
400 return cmp < 0;
401 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000402 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
403 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000404 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000405 if (fDDDy == 0 && rh.fDDDy == 0) {
406 return fDDDx < rh.fDDDx;
407 }
408 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000409 }
410
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000411 int end() const {
412 return fEnd;
413 }
414
415 // since all angles share a point, this needs to know which point
416 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
417 // practically, this should only be called by addAngle
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000418 void set(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000419 int start, int end, bool coincident) {
420 SkASSERT(start != end);
421 fSegment = segment;
422 fStart = start;
423 fEnd = end;
424 fCoincident = coincident;
425 fDx = pts[1].fX - pts[0].fX; // b - a
426 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000427 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000428 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000429 return;
430 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000431 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
432 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000433 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000434 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000435 return;
436 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000437 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
438 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
439 }
440
441 // noncoincident quads/cubics may have the same initial angle
442 // as lines, so must sort by derivatives as well
443 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000444 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000445 int start, int end, bool coincident) {
446 fSegment = segment;
447 fStart = start;
448 fEnd = end;
449 fCoincident = coincident;
450 fDx = pts[1].fX - pts[0].fX; // b - a
451 fDy = pts[1].fY - pts[0].fY;
452 if (verb == SkPath::kLine_Verb) {
453 fDDx = fDDy = fDDDx = fDDDy = 0;
454 return;
455 }
456 if (verb == SkPath::kQuad_Verb) {
457 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
458 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
459 int larger = std::max(abs(uplsX), abs(uplsY));
460 int shift = 0;
461 double flatT;
462 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
463 LineParameters implicitLine;
464 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
465 implicitLine.lineEndPoints(tangent);
466 implicitLine.normalize();
467 while (larger > UlpsEpsilon * 1024) {
468 larger >>= 2;
469 ++shift;
470 flatT = 0.5 / (1 << shift);
471 QuadXYAtT(pts, flatT, &ddPt);
472 _Point _pt = {ddPt.fX, ddPt.fY};
473 double distance = implicitLine.pointDistance(_pt);
474 if (approximately_zero(distance)) {
475 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
476 break;
477 }
478 }
479 flatT = 0.5 / (1 << shift);
480 QuadXYAtT(pts, flatT, &ddPt);
481 fDDx = ddPt.fX - pts[0].fX;
482 fDDy = ddPt.fY - pts[0].fY;
483 SkASSERT(fDDx != 0 || fDDy != 0);
484 fDDDx = fDDDy = 0;
485 return;
486 }
487 SkASSERT(0); // FIXME: add cubic case
488 }
489
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000490 Segment* segment() const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000491 return fSegment;
492 }
493
494 int sign() const {
495 int result = fStart - fEnd >> 31 | 1;
496 SkASSERT(result == fStart < fEnd ? -1 : 1);
497 return result;
498 }
499
500 int start() const {
501 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000502 }
503
504private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000505 SkScalar fDx;
506 SkScalar fDy;
507 SkScalar fDDx;
508 SkScalar fDDy;
509 SkScalar fDDDx;
510 SkScalar fDDDy;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000511 Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000512 int fStart;
513 int fEnd;
514 bool fCoincident;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000515};
516
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000517static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
518 int angleCount = angles.count();
519 int angleIndex;
520 angleList.setReserve(angleCount);
521 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
522 *angleList.append() = &angles[angleIndex];
523 }
524 QSort<Angle>(angleList.begin(), angleList.end() - 1);
525}
526
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000527// Bounds, unlike Rect, does not consider a vertical line to be empty.
528struct Bounds : public SkRect {
529 static bool Intersects(const Bounds& a, const Bounds& b) {
530 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
531 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
532 }
533
534 bool isEmpty() {
535 return fLeft > fRight || fTop > fBottom
536 || fLeft == fRight && fTop == fBottom
537 || isnan(fLeft) || isnan(fRight)
538 || isnan(fTop) || isnan(fBottom);
539 }
540
541 void setCubicBounds(const SkPoint a[4]) {
542 _Rect dRect;
543 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
544 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
545 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000546 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
547 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000548 }
549
550 void setQuadBounds(const SkPoint a[3]) {
551 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
552 {a[2].fX, a[2].fY}};
553 _Rect dRect;
554 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000555 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
556 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000557 }
558};
559
caryclark@google.com15fa1382012-05-07 20:49:36 +0000560struct Span {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000561 double fT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000562 Segment* fOther;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000563 double fOtherT; // value at fOther[fOtherIndex].fT
564 int fOtherIndex; // can't be used during intersection
caryclark@google.com15fa1382012-05-07 20:49:36 +0000565 int fWinding; // accumulated from contours surrounding this one
566 // OPTIMIZATION: done needs only 2 bits (values are -1, 0, 1)
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000567 int fDone; // set when this pointer to the other span is processed
caryclark@google.com15fa1382012-05-07 20:49:36 +0000568 // OPTIMIZATION: done needs only 2 bits (values are -1, 0, 1)
569 int fCoincident; // -1 start of coincidence, 0 no coincidence, 1 end
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000570};
571
572class Segment {
573public:
574 Segment() {
575#if DEBUG_DUMP
576 fID = ++gSegmentID;
577#endif
578 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000579
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000580 void addAngle(SkTDArray<Angle>& angles, int start, int end,
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000581 bool coincident) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000582 SkASSERT(start != end);
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000583 int smaller = start < end ? start : end;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000584 if (fTs[start].fDone) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000585 return;
586 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000587 SkPoint edge[4];
588 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
589 Angle* angle = angles.append();
590 angle->set(edge, fVerb, this, start, end, coincident);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000591 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000592
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000593 void addCubic(const SkPoint pts[4]) {
594 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000595 fBounds.setCubicBounds(pts);
596 }
597
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000598 void addCurveTo(int start, int end, SkPath& path) {
599 SkPoint edge[4];
600 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
601 switch (fVerb) {
602 case SkPath::kLine_Verb:
603 path.lineTo(edge[1].fX, edge[1].fY);
604 break;
605 case SkPath::kQuad_Verb:
606 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
607 break;
608 case SkPath::kCubic_Verb:
609 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
610 edge[3].fX, edge[3].fY);
611 break;
612 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000613 int smaller = start < end ? start : end;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000614 }
615
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000616 void addLine(const SkPoint pts[2]) {
617 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000618 fBounds.set(pts, 2);
619 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000620
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000621 void addMoveTo(int tIndex, SkPath& path) {
622 SkPoint pt;
623 double firstT = t(tIndex);
624 xyAtT(firstT, &pt);
625 path.moveTo(pt.fX, pt.fY);
626 }
627
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000628 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000629 void addOtherT(int index, double otherT, int otherIndex) {
630 Span& span = fTs[index];
631 span.fOtherT = otherT;
632 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000633 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000634
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000635 void addQuad(const SkPoint pts[3]) {
636 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000637 fBounds.setQuadBounds(pts);
638 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000639
caryclark@google.com15fa1382012-05-07 20:49:36 +0000640 int addT(double newT, Segment& other, int coincident) {
641 // FIXME: in the pathological case where there is a ton of intercepts,
642 // binary search?
643 int insertedAt = -1;
644 Span* span;
645 size_t tCount = fTs.count();
646 double delta;
647 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
648 // OPTIMIZATION: if there are three or more identical Ts, then
649 // the fourth and following could be further insertion-sorted so
650 // that all the edges are clockwise or counterclockwise.
651 // This could later limit segment tests to the two adjacent
652 // neighbors, although it doesn't help with determining which
653 // circular direction to go in.
654 if (newT <= fTs[idx2].fT) {
655 insertedAt = idx2;
656 span = fTs.insert(idx2);
657 goto finish;
658 }
659 }
660 insertedAt = tCount;
661 span = fTs.append();
662finish:
663 span->fT = newT;
664 span->fOther = &other;
665 span->fWinding = 1;
666 span->fDone = 0;
667 span->fCoincident = coincident;
668 fCoincident |= coincident;
669 return insertedAt;
670 }
671
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000672 void addTwoAngles(int start, int end, const SkPoint& endLoc,
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000673 const Span* endSpan, bool startCo, SkTDArray<Angle>& angles) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000674 // add edge leading into junction
675 addAngle(angles, end, start, startCo);
676 // add edge leading away from junction
677 bool coincident;
678 int step = start < end ? 1 : -1;
679 int tIndex = nextSpan(end, step, endLoc, endSpan, NULL, coincident);
680 if (tIndex >= 0) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000681 lastSpan(tIndex, step, endLoc, endSpan->fT, coincident);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000682 addAngle(angles, end, tIndex, coincident);
683 }
684 }
685
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000686 const Bounds& bounds() const {
687 return fBounds;
688 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000689
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000690 void buildAngles(int index, int last, int step, const SkPoint& loc,
691 SkTDArray<Angle>& angles) const {
692 SkASSERT(index - last != 0);
693 SkASSERT((index - last < 0) ^ (step < 0));
694 int end = last + step;
695 do {
696 Span* span = &fTs[index];
697 Segment* other = span->fOther;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000698 if (other->done()) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000699 continue;
700 }
701 // if there is only one live crossing, and no coincidence, continue
702 // in the same direction
703 // if there is coincidence, the only choice may be to reverse direction
704 // find edge on either side of intersection
705 int oIndex = span->fOtherIndex;
706 Span* otherSpan = &other->fTs[oIndex];
707 SkASSERT(otherSpan->fOther == this);
708 // if done == -1, prior span has already been processed
709 bool otherCo;
710 int localStep = step;
711 int next = other->nextSpan(oIndex, localStep, loc, otherSpan,
712 NULL, otherCo);
713 if (next < 0) {
714 localStep = -step;
715 next = other->nextSpan(oIndex, localStep, loc, otherSpan,
716 NULL, otherCo);
717 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000718 other->lastSpan(next, localStep, loc, otherSpan->fT, otherCo);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000719 // add candidate into and away from junction
720 other->addTwoAngles(next, oIndex, loc, span, otherCo, angles);
721
722 } while ((index += step) != end);
723 }
724
725 // figure out if the segment's ascending T goes clockwise or not
726 // not enough context to write this as shown
727 // instead, add all segments meeting at the top
728 // sort them using buildAngleList
729 // find the first in the sort
730 // see if ascendingT goes to top
731 bool clockwise(int tIndex) const {
732 SkASSERT(0); // incomplete
733 return false;
734 }
735
caryclark@google.com15fa1382012-05-07 20:49:36 +0000736 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000737 SkASSERT(fDoneSpans <= fTs.count());
738 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000739 }
740
caryclark@google.com15fa1382012-05-07 20:49:36 +0000741 int findCoincidentEnd(int start) const {
742 int tCount = fTs.count();
743 SkASSERT(start < tCount);
744 const Span& span = fTs[start];
745 SkASSERT(span.fCoincident);
746 for (int index = start + 1; index < tCount; ++index) {
747 const Span& match = fTs[index];
748 if (match.fOther == span.fOther) {
749 SkASSERT(match.fCoincident);
750 return index;
751 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000752 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000753 SkASSERT(0); // should never get here
754 return -1;
755 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000756
caryclark@google.com15fa1382012-05-07 20:49:36 +0000757 // start is the index of the beginning T of this edge
758 // it is guaranteed to have an end which describes a non-zero length (?)
759 // winding -1 means ccw, 1 means cw
760 // step is in/out -1 or 1
761 // spanIndex is returned
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000762 Segment* findNext(int winding, int& startIndex, int& endIndex) {
763 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000764 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000765 SkASSERT(startIndex < endIndex ? startIndex < count - 1
766 : startIndex > 0);
767 Span* startSpan = &fTs[startIndex];
caryclark@google.com15fa1382012-05-07 20:49:36 +0000768 // FIXME:
769 // since Ts can be stepped either way, done markers must be careful
770 // not to assume that segment was only ascending in T. This shouldn't
771 // be a problem unless pathologically a segment can be partially
772 // ascending and partially descending -- maybe quads/cubic can do this?
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000773
774
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000775 int step = startIndex < endIndex ? 1 : -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000776 SkPoint startLoc; // OPTIMIZATION: store this in the t span?
777 xyAtT(startSpan->fT, &startLoc);
778 SkPoint endLoc;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000779 bool startCo;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000780 int end = nextSpan(startIndex, step, startLoc, startSpan, &endLoc,
781 startCo);
782 SkASSERT(end >= 0);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000783
784 // preflight for coincidence -- if present, it may change winding
785 // considerations and whether reversed edges can be followed
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000786 bool many;
787 int last = lastSpan(end, step, endLoc, fTs[end].fT, startCo, &many);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000788
789 // Discard opposing direction candidates if no coincidence was found.
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000790 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000791 Segment* other;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000792 if (!many) {
793 // mark the smaller of startIndex, endIndex done, and all adjacent
794 // spans with the same T value (but not 'other' spans)
795 markDone(startIndex < endIndex ? startIndex : endIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000796 SkASSERT(!startCo);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000797 // move in winding direction until edge in correct direction
798 // balance wrong direction edges before finding correct one
799 // this requres that the intersection is angularly sorted
800 // for a single intersection, special case -- choose the opposite
801 // edge that steps the same
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000802 other = endSpan->fOther;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000803 startIndex = endSpan->fOtherIndex;
804 endIndex = startIndex + step;
805 SkASSERT(step < 0 ? endIndex >= 0 : endIndex < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +0000806 return other;
807 }
808
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000809 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +0000810 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000811 SkASSERT(startIndex - endIndex != 0);
812 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000813 addTwoAngles(startIndex, end, endLoc, endSpan, startCo, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000814 buildAngles(end, last, step, endLoc, angles);
815 SkTDArray<Angle*> sorted;
816 sortAngles(angles, sorted);
817 // find the starting edge
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000818 int firstIndex = -1;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000819 int angleCount = angles.count();
820 int angleIndex;
821 const Angle* angle;
822 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
823 angle = sorted[angleIndex];
824 if (angle->segment() == this && angle->start() == end &&
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000825 angle->end() == startIndex) {
826 firstIndex = angleIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000827 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000828 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000829 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000830 SkASSERT(firstIndex >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000831 winding += angle->sign();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000832 int nextIndex = firstIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000833 const Angle* nextAngle;
834 do {
835 if (++nextIndex == angleCount) {
836 nextIndex = 0;
837 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000838 SkASSERT(nextIndex != firstIndex); // should never wrap around
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000839 nextAngle = sorted[nextIndex];
840 // OPTIMIZATION: Figure out all connections, given the initial
841 // winding info (e.g., accumulate winding in span for reuse)
842 winding -= nextAngle->sign();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000843
844 // start here;
845 // if the winding is non-zero, nextAngle does not connect to
846 // current chain. We may be able to deduce whether it will be
847 // in some future chain or ignored altogether based on winding,
848 // but for the first cut, just detach it from this chain.
849 if (!winding) {
850 break;
851 }
852 // but how to detach? Maybe it is correct to mark both ends
853 // for all of the sorted angles as done, regardless of whether we
854 // also compute the connectedness and/or winding for the inner ones.
855
856 } while (true);
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000857 markDone(startIndex < endIndex ? startIndex : endIndex);
858 other = nextAngle->segment();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000859 startIndex = nextAngle->end();
860 endIndex = nextAngle->start();
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000861 return other;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000862 }
863
864
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000865 // so the span needs to contain the pairing info found here
866 // this should include the winding computed for the edge, and
867 // what edge it connects to, and whether it is discarded
868 // (maybe discarded == abs(winding) > 1) ?
869 // only need derivatives for duration of sorting, add a new struct
870 // for pairings, remove extra spans that have zero length and
871 // reference an unused other
872 // for coincident, the last span on the other may be marked done
873 // (always?)
874
caryclark@google.com15fa1382012-05-07 20:49:36 +0000875 // if loop is exhausted, contour may be closed.
876 // FIXME: pass in close point so we can check for closure
877
878 // given a segment, and a sense of where 'inside' is, return the next
879 // segment. If this segment has an intersection, or ends in multiple
880 // segments, find the mate that continues the outside.
881 // note that if there are multiples, but no coincidence, we can limit
882 // choices to connections in the correct direction
883
884 // mark found segments as done
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000885
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000886 // FIXME: this is tricky code; needs its own unit test
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000887 void findTooCloseToCall(int winding) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000888 int count = fTs.count();
889 if (count < 3) { // require t=0, x, 1 at minimum
890 return;
891 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000892 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000893 int moCount;
894 Span* match;
895 Segment* mOther;
896 do {
897 match = &fTs[matchIndex];
898 mOther = match->fOther;
899 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000900 if (moCount >= 3) {
901 break;
902 }
903 if (++matchIndex >= count) {
904 return;
905 }
906 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +0000907 SkPoint matchPt;
908 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
909 xyAtT(match->fT, &matchPt);
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000910 // look for a pair of nearby T values that map to the same (x,y) value
911 // if found, see if the pair of other segments share a common point. If
912 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000913 for (int index = matchIndex + 1; index < count; ++index) {
914 Span* test = &fTs[index];
915 Segment* tOther = test->fOther;
916 int toCount = tOther->fTs.count();
917 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000918 continue;
919 }
920 SkPoint testPt;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000921 xyAtT(test->fT, &testPt);
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000922 if (matchPt != testPt) {
923 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000924 moCount = toCount;
925 match = test;
926 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000927 matchPt = testPt;
928 continue;
929 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000930 int moStart = -1;
931 int moEnd = -1;
932 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000933 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000934 Span& moSpan = mOther->fTs[moIndex];
935 if (moSpan.fOther == this) {
936 if (moSpan.fOtherT == match->fT) {
937 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000938 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000939 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000940 continue;
941 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000942 if (moSpan.fOther == tOther) {
943 SkASSERT(moEnd == -1);
944 moEnd = moIndex;
945 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000946 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000947 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000948 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000949 continue;
950 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000951 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
952 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000953 continue;
954 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000955 int toStart = -1;
956 int toEnd = -1;
957 double toStartT, toEndT;
958 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
959 Span& toSpan = tOther->fTs[toIndex];
960 if (toSpan.fOther == this) {
961 if (toSpan.fOtherT == test->fT) {
962 toStart = toIndex;
963 toStartT = toSpan.fT;
964 }
965 continue;
966 }
967 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
968 SkASSERT(toEnd == -1);
969 toEnd = toIndex;
970 toEndT = toSpan.fT;
971 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000972 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000973 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
974 if (toStart <= 0 || toEnd <= 0) {
975 continue;
976 }
977 if (toStartT == toEndT) {
978 continue;
979 }
980 // test to see if the segment between there and here is linear
981 if (!mOther->isLinear(moStart, moEnd)
982 || !tOther->isLinear(toStart, toEnd)) {
983 continue;
984 }
985 mOther->fTs[moStart].fCoincident = -1;
986 tOther->fTs[toStart].fCoincident = -1;
987 mOther->fTs[moEnd].fCoincident = 1;
988 tOther->fTs[toEnd].fCoincident = 1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000989 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000990 }
991
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000992 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
993 // and use more concise logic like the old edge walker code?
994 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000995 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000996 // iterate through T intersections and return topmost
997 // topmost tangent from y-min to first pt is closer to horizontal
998 int firstT = 0;
999 int lastT = 0;
1000 SkScalar topY = fPts[0].fY;
1001 int count = fTs.count();
1002 int index;
1003 for (index = 1; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001004 const Span& span = fTs[index];
1005 double t = span.fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001006 SkScalar yIntercept = t == 1 ? fPts[fVerb].fY : yAtT(t);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001007 if (topY > yIntercept) {
1008 topY = yIntercept;
1009 firstT = lastT = index;
1010 } else if (topY == yIntercept) {
1011 lastT = index;
1012 }
1013 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001014 // if there's only a pair of segments, go with the endpoint chosen above
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001015 if (firstT == lastT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001016 tIndex = firstT;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001017 endIndex = firstT > 0 ? tIndex - 1 : tIndex + 1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001018 return this;
1019 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001020 // sort the edges to find the leftmost
1021 SkPoint startLoc; // OPTIMIZATION: store this in the t span?
1022 const Span* startSpan = &fTs[firstT];
1023 xyAtT(startSpan->fT, &startLoc);
1024 SkPoint endLoc;
1025 bool nextCo;
1026 int end = nextSpan(firstT, 1, startLoc, startSpan, &endLoc, nextCo);
1027 if (end == -1) {
1028 end = nextSpan(firstT, -1, startLoc, startSpan, &endLoc, nextCo);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001029 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001030 // if the topmost T is not on end, or is three-way or more, find left
1031 // look for left-ness from tLeft to firstT (matching y of other)
1032 SkTDArray<Angle> angles;
1033 SkASSERT(firstT - end != 0);
1034 addTwoAngles(end, firstT, endLoc, &fTs[firstT], nextCo, angles);
1035 buildAngles(firstT, lastT, 1, startLoc, angles);
1036 SkTDArray<Angle*> sorted;
1037 sortAngles(angles, sorted);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001038 Segment* leftSegment = sorted[0]->segment();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001039 tIndex = sorted[0]->end();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001040 endIndex = sorted[0]->start();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001041 return leftSegment;
1042 }
1043
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001044 // FIXME: not crazy about this
1045 // when the intersections are performed, the other index is into an
1046 // incomplete array. as the array grows, the indices become incorrect
1047 // while the following fixes the indices up again, it isn't smart about
1048 // skipping segments whose indices are already correct
1049 // assuming we leave the code that wrote the index in the first place
1050 void fixOtherTIndex() {
1051 int iCount = fTs.count();
1052 for (int i = 0; i < iCount; ++i) {
1053 Span& iSpan = fTs[i];
1054 double oT = iSpan.fOtherT;
1055 Segment* other = iSpan.fOther;
1056 int oCount = other->fTs.count();
1057 for (int o = 0; o < oCount; ++o) {
1058 Span& oSpan = other->fTs[o];
1059 if (oT == oSpan.fT && this == oSpan.fOther) {
1060 iSpan.fOtherIndex = o;
1061 }
1062 }
1063 }
1064 }
1065
1066 void init(const SkPoint pts[], SkPath::Verb verb) {
1067 fPts = pts;
1068 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001069 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001070 fCoincident = 0;
1071 }
1072
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001073 bool intersected() const {
1074 return fTs.count() > 0;
1075 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001076
1077 bool isLinear(int start, int end) const {
1078 if (fVerb == SkPath::kLine_Verb) {
1079 return true;
1080 }
1081 if (fVerb == SkPath::kQuad_Verb) {
1082 SkPoint qPart[3];
1083 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1084 return QuadIsLinear(qPart);
1085 } else {
1086 SkASSERT(fVerb == SkPath::kCubic_Verb);
1087 SkPoint cPart[4];
1088 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1089 return CubicIsLinear(cPart);
1090 }
1091 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001092
1093 bool isHorizontal() const {
1094 return fBounds.fTop == fBounds.fBottom;
1095 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001096
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001097 bool isVertical() const {
1098 return fBounds.fLeft == fBounds.fRight;
1099 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001100
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001101 int lastSpan(int end, int step, const SkPoint& startLoc,
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001102 double startT, bool& coincident, bool* manyPtr = NULL) const {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001103 int last = end;
1104 int count = fTs.count();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001105 SkPoint lastLoc;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001106 int found = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001107 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001108 end = last;
1109 if (fTs[end].fCoincident == -step) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001110 coincident = true;
1111 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001112 if (step > 0 ? ++last >= count : --last < 0) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001113 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001114 }
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001115 const Span& lastSpan = fTs[last];
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001116 if (lastSpan.fDone) {
1117 continue;
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001118 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001119 if (lastSpan.fT == startT) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001120 ++found;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001121 continue;
1122 }
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001123 xyAtT(lastSpan.fT, &lastLoc);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001124 if (startLoc != lastLoc) {
1125 break;
1126 }
1127 ++found;
1128 } while (true);
1129 if (manyPtr) {
1130 *manyPtr = found > 0;
1131 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001132 return end;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001133 }
1134
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001135 SkScalar leftMost(int start, int end) const {
1136 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1137 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001138
1139 void markDone(int index) {
1140 SkASSERT(!fTs[index].fDone);
1141 double referenceT = fTs[index].fT;
1142 int lesser = index;
1143 while (--lesser >= 0 && referenceT == fTs[lesser].fT) {
1144 SkASSERT(!fTs[lesser].fDone);
1145 fTs[lesser].fDone = true;
1146 }
1147 do {
1148 SkASSERT(!fTs[index].fDone);
1149 fTs[index].fDone = true;
1150 } while (++index < fTs.count() && referenceT == fTs[index].fT);
1151 SkASSERT(!done());
1152 fDoneSpans++;
1153 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001154
caryclark@google.com15fa1382012-05-07 20:49:36 +00001155 int nextSpan(int from, int step, const SkPoint& fromLoc,
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001156 const Span* fromSpan, SkPoint* toLoc, bool& coincident) const {
1157 coincident = false;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001158 if (done()) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001159 return -1;
1160 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001161 int count = fTs.count();
1162 int to = from;
1163 while (step > 0 ? ++to < count : --to >= 0) {
1164 Span* span = &fTs[to];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001165 if (span->fCoincident == step) {
1166 coincident = true;
1167 }
1168 if (fromSpan->fT == span->fT) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001169 continue;
1170 }
1171 SkPoint loc;
1172 xyAtT(span->fT, &loc);
1173 if (fromLoc == loc) {
1174 continue;
1175 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001176 if (span->fDone) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001177 return -1;
1178 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001179 if (toLoc) {
1180 *toLoc = loc;
1181 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001182 return to;
1183 }
1184 return -1;
1185 }
1186
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001187 const SkPoint* pts() const {
1188 return fPts;
1189 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001190
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001191 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001192 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001193 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1194 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001195 }
1196
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001197 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001198 double t(int tIndex) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001199 SkASSERT(tIndex >= 0);
1200 SkASSERT(tIndex < fTs.count());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001201 return fTs[tIndex].fT;
1202 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001203
1204 void updatePts(const SkPoint pts[]) {
1205 fPts = pts;
1206 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001207
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001208 SkPath::Verb verb() const {
1209 return fVerb;
1210 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001211
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001212 SkScalar xAtT(double t) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001213 SkASSERT(t >= 0 && t <= 1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001214 return (*SegmentXAtT[fVerb])(fPts, t);
1215 }
1216
1217 void xyAtT(double t, SkPoint* pt) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001218 SkASSERT(t >= 0 && t <= 1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001219 (*SegmentXYAtT[fVerb])(fPts, t, pt);
1220 }
1221
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001222 SkScalar yAtT(double t) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001223 SkASSERT(t >= 0 && t <= 1);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001224 return (*SegmentYAtT[fVerb])(fPts, t);
1225 }
1226
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001227#if DEBUG_DUMP
1228 void dump() const {
1229 const char className[] = "Segment";
1230 const int tab = 4;
1231 for (int i = 0; i < fTs.count(); ++i) {
1232 SkPoint out;
1233 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1234 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001235 " otherT=%1.9g winding=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001236 tab + sizeof(className), className, fID,
1237 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001238 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWinding);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001239 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001240 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001241 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001242 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001243 }
1244#endif
1245
1246private:
1247 const SkPoint* fPts;
1248 SkPath::Verb fVerb;
1249 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001250 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
1251 // FIXME: coincident only needs two bits (-1, 0, 1)
1252 int fCoincident; // non-zero if some coincident span inside
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001253 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001254#if DEBUG_DUMP
1255 int fID;
1256#endif
1257};
1258
1259class Contour {
1260public:
1261 Contour() {
1262 reset();
1263#if DEBUG_DUMP
1264 fID = ++gContourID;
1265#endif
1266 }
1267
1268 bool operator<(const Contour& rh) const {
1269 return fBounds.fTop == rh.fBounds.fTop
1270 ? fBounds.fLeft < rh.fBounds.fLeft
1271 : fBounds.fTop < rh.fBounds.fTop;
1272 }
1273
1274 void addCubic(const SkPoint pts[4]) {
1275 fSegments.push_back().addCubic(pts);
1276 fContainsCurves = true;
1277 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001278
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001279 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001280 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001281 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001282 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001283
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001284 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001285 fSegments.push_back().addQuad(pts);
1286 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001287 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001288 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001289
1290 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001291 return fBounds;
1292 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001293
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001294 void complete() {
1295 setBounds();
1296 fContainsIntercepts = false;
1297 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001298
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001299 void containsIntercepts() {
1300 fContainsIntercepts = true;
1301 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001302
1303 void findTooCloseToCall(int winding) {
1304 int segmentCount = fSegments.count();
1305 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1306 fSegments[sIndex].findTooCloseToCall(winding);
1307 }
1308 }
1309
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001310 void fixOtherTIndex() {
1311 int segmentCount = fSegments.count();
1312 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1313 fSegments[sIndex].fixOtherTIndex();
1314 }
1315 }
1316
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001317 void reset() {
1318 fSegments.reset();
1319 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001320 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001321 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001322
1323 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
1324 // we need to sort and walk edges in y, but that on the surface opens the
1325 // same can of worms as before. But then, this is a rough sort based on
1326 // segments' top, and not a true sort, so it could be ameniable to regular
1327 // sorting instead of linear searching. Still feel like I'm missing something
1328 Segment* topSegment() {
1329 int segmentCount = fSegments.count();
1330 SkASSERT(segmentCount > 0);
1331 int best = -1;
1332 Segment* bestSegment = NULL;
1333 while (++best < segmentCount) {
1334 Segment* testSegment = &fSegments[best];
1335 if (testSegment->done()) {
1336 continue;
1337 }
1338 bestSegment = testSegment;
1339 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001340 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001341 if (!bestSegment) {
1342 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001343 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001344 SkScalar bestTop = bestSegment->bounds().fTop;
1345 for (int test = best + 1; test < segmentCount; ++test) {
1346 Segment* testSegment = &fSegments[test];
1347 if (testSegment->done()) {
1348 continue;
1349 }
1350 SkScalar testTop = testSegment->bounds().fTop;
1351 if (bestTop > testTop) {
1352 bestTop = testTop;
1353 bestSegment = testSegment;
1354 }
1355 }
1356 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001357 }
1358
1359#if DEBUG_DUMP
1360 void dump() {
1361 int i;
1362 const char className[] = "Contour";
1363 const int tab = 4;
1364 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
1365 for (i = 0; i < fSegments.count(); ++i) {
1366 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
1367 className, i);
1368 fSegments[i].dump();
1369 }
1370 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
1371 tab + sizeof(className), className,
1372 fBounds.fLeft, fBounds.fTop,
1373 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001374 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1375 className, fContainsIntercepts);
1376 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
1377 className, fContainsCurves);
1378 }
1379#endif
1380
1381protected:
1382 void setBounds() {
1383 int count = fSegments.count();
1384 if (count == 0) {
1385 SkDebugf("%s empty contour\n", __FUNCTION__);
1386 SkASSERT(0);
1387 // FIXME: delete empty contour?
1388 return;
1389 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001390 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001391 for (int index = 1; index < count; ++index) {
1392 fBounds.growToInclude(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001393 }
1394 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001395
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001396public:
1397 SkTArray<Segment> fSegments; // not worth accessor functions?
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001398
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001399private:
1400 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001401 bool fContainsIntercepts;
1402 bool fContainsCurves;
1403#if DEBUG_DUMP
1404 int fID;
1405#endif
1406};
1407
1408class EdgeBuilder {
1409public:
1410
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001411EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001412 : fPath(path)
1413 , fCurrentContour(NULL)
1414 , fContours(contours)
1415{
1416#if DEBUG_DUMP
1417 gContourID = 0;
1418 gSegmentID = 0;
1419#endif
1420 walk();
1421}
1422
1423protected:
1424
1425void complete() {
1426 if (fCurrentContour && fCurrentContour->fSegments.count()) {
1427 fCurrentContour->complete();
1428 fCurrentContour = NULL;
1429 }
1430}
1431
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001432void walk() {
1433 // FIXME:remove once we can access path pts directly
1434 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
1435 SkPoint pts[4];
1436 SkPath::Verb verb;
1437 do {
1438 verb = iter.next(pts);
1439 *fPathVerbs.append() = verb;
1440 if (verb == SkPath::kMove_Verb) {
1441 *fPathPts.append() = pts[0];
1442 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
1443 fPathPts.append(verb, &pts[1]);
1444 }
1445 } while (verb != SkPath::kDone_Verb);
1446 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001447
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001448 SkPath::Verb reducedVerb;
1449 uint8_t* verbPtr = fPathVerbs.begin();
1450 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001451 const SkPoint* finalCurveStart = NULL;
1452 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001453 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
1454 switch (verb) {
1455 case SkPath::kMove_Verb:
1456 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001457 if (!fCurrentContour) {
1458 fCurrentContour = fContours.push_back_n(1);
1459 finalCurveEnd = pointsPtr++;
1460 *fExtra.append() = -1; // start new contour
1461 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001462 continue;
1463 case SkPath::kLine_Verb:
1464 // skip degenerate points
1465 if (pointsPtr[-1].fX != pointsPtr[0].fX
1466 || pointsPtr[-1].fY != pointsPtr[0].fY) {
1467 fCurrentContour->addLine(&pointsPtr[-1]);
1468 }
1469 break;
1470 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001471
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001472 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
1473 if (reducedVerb == 0) {
1474 break; // skip degenerate points
1475 }
1476 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001477 *fExtra.append() =
1478 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001479 break;
1480 }
1481 fCurrentContour->addQuad(&pointsPtr[-1]);
1482 break;
1483 case SkPath::kCubic_Verb:
1484 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
1485 if (reducedVerb == 0) {
1486 break; // skip degenerate points
1487 }
1488 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001489 *fExtra.append() =
1490 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001491 break;
1492 }
1493 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001494 *fExtra.append() =
1495 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001496 break;
1497 }
1498 fCurrentContour->addCubic(&pointsPtr[-1]);
1499 break;
1500 case SkPath::kClose_Verb:
1501 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001502 if (finalCurveStart && finalCurveEnd
1503 && *finalCurveStart != *finalCurveEnd) {
1504 *fReducePts.append() = *finalCurveStart;
1505 *fReducePts.append() = *finalCurveEnd;
1506 *fExtra.append() =
1507 fCurrentContour->addLine(fReducePts.end() - 2);
1508 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001509 complete();
1510 continue;
1511 default:
1512 SkDEBUGFAIL("bad verb");
1513 return;
1514 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001515 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001516 pointsPtr += verb;
1517 SkASSERT(fCurrentContour);
1518 }
1519 complete();
1520 if (fCurrentContour && !fCurrentContour->fSegments.count()) {
1521 fContours.pop_back();
1522 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001523 // correct pointers in contours since fReducePts may have moved as it grew
1524 int cIndex = 0;
1525 fCurrentContour = &fContours[0];
1526 int extraCount = fExtra.count();
1527 SkASSERT(fExtra[0] == -1);
1528 int eIndex = 0;
1529 int rIndex = 0;
1530 while (++eIndex < extraCount) {
1531 int offset = fExtra[eIndex];
1532 if (offset < 0) {
1533 fCurrentContour = &fContours[++cIndex];
1534 continue;
1535 }
1536 Segment& segment = fCurrentContour->fSegments[offset - 1];
1537 segment.updatePts(&fReducePts[rIndex]);
1538 rIndex += segment.verb() + 1;
1539 }
1540 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001541}
1542
1543private:
1544 const SkPath& fPath;
1545 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001546 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001547 Contour* fCurrentContour;
1548 SkTArray<Contour>& fContours;
1549 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001550 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001551};
1552
1553class Work {
1554public:
1555 enum SegmentType {
1556 kHorizontalLine_Segment = -1,
1557 kVerticalLine_Segment = 0,
1558 kLine_Segment = SkPath::kLine_Verb,
1559 kQuad_Segment = SkPath::kQuad_Verb,
1560 kCubic_Segment = SkPath::kCubic_Verb,
1561 };
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001562
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001563 // FIXME: does it make sense to write otherIndex now if we're going to
1564 // fix it up later?
1565 void addOtherT(int index, double otherT, int otherIndex) {
1566 fContour->fSegments[fIndex].addOtherT(index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001567 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001568
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001569 // Avoid collapsing t values that are close to the same since
1570 // we walk ts to describe consecutive intersections. Since a pair of ts can
1571 // be nearly equal, any problems caused by this should be taken care
1572 // of later.
1573 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com15fa1382012-05-07 20:49:36 +00001574 int addT(double newT, const Work& other, int coincident) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001575 fContour->containsIntercepts();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001576 return fContour->fSegments[fIndex].addT(newT,
1577 other.fContour->fSegments[other.fIndex], coincident);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001578 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001579
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001580 bool advance() {
1581 return ++fIndex < fLast;
1582 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001583
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001584 SkScalar bottom() const {
1585 return bounds().fBottom;
1586 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001587
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001588 const Bounds& bounds() const {
1589 return fContour->fSegments[fIndex].bounds();
1590 }
1591
1592 const SkPoint* cubic() const {
1593 return fCubic;
1594 }
1595
1596 void init(Contour* contour) {
1597 fContour = contour;
1598 fIndex = 0;
1599 fLast = contour->fSegments.count();
1600 }
1601
1602 SkScalar left() const {
1603 return bounds().fLeft;
1604 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001605
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001606 void promoteToCubic() {
1607 fCubic[0] = pts()[0];
1608 fCubic[2] = pts()[1];
1609 fCubic[3] = pts()[2];
1610 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
1611 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
1612 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
1613 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
1614 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001615
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001616 const SkPoint* pts() const {
1617 return fContour->fSegments[fIndex].pts();
1618 }
1619
1620 SkScalar right() const {
1621 return bounds().fRight;
1622 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001623
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001624 ptrdiff_t segmentIndex() const {
1625 return fIndex;
1626 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001627
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001628 SegmentType segmentType() const {
1629 const Segment& segment = fContour->fSegments[fIndex];
1630 SegmentType type = (SegmentType) segment.verb();
1631 if (type != kLine_Segment) {
1632 return type;
1633 }
1634 if (segment.isHorizontal()) {
1635 return kHorizontalLine_Segment;
1636 }
1637 if (segment.isVertical()) {
1638 return kVerticalLine_Segment;
1639 }
1640 return kLine_Segment;
1641 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001642
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001643 bool startAfter(const Work& after) {
1644 fIndex = after.fIndex;
1645 return advance();
1646 }
1647
1648 SkScalar top() const {
1649 return bounds().fTop;
1650 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001651
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001652 SkPath::Verb verb() const {
1653 return fContour->fSegments[fIndex].verb();
1654 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001655
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001656 SkScalar x() const {
1657 return bounds().fLeft;
1658 }
1659
1660 bool xFlipped() const {
1661 return x() != pts()[0].fX;
1662 }
1663
1664 SkScalar y() const {
1665 return bounds().fTop;
1666 }
1667
1668 bool yFlipped() const {
1669 return y() != pts()[0].fX;
1670 }
1671
1672protected:
1673 Contour* fContour;
1674 SkPoint fCubic[4];
1675 int fIndex;
1676 int fLast;
1677};
1678
1679static void debugShowLineIntersection(int pts, const Work& wt,
1680 const Work& wn, const double wtTs[2], const double wnTs[2]) {
1681#if DEBUG_ADD_INTERSECTING_TS
1682 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001683 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
1684 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
1685 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
1686 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001687 return;
1688 }
1689 SkPoint wtOutPt, wnOutPt;
1690 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
1691 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001692 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001693 __FUNCTION__,
1694 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
1695 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
1696 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001697 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001698 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001699 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001700 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
1701 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
1702 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001703 SkDebugf(" wnTs[1]=%g", wnTs[1]);
1704 SkDebugf("\n");
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001705 }
1706#endif
1707}
1708
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001709static bool addIntersectTs(Contour* test, Contour* next, int winding) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001710
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001711 if (test != next) {
1712 if (test->bounds().fBottom < next->bounds().fTop) {
1713 return false;
1714 }
1715 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
1716 return true;
1717 }
1718 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001719 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001720 wt.init(test);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001721 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001722 Work wn;
1723 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001724 if (test == next && !wn.startAfter(wt)) {
1725 continue;
1726 }
1727 do {
1728 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
1729 continue;
1730 }
1731 int pts;
1732 Intersections ts;
1733 bool swap = false;
1734 switch (wt.segmentType()) {
1735 case Work::kHorizontalLine_Segment:
1736 swap = true;
1737 switch (wn.segmentType()) {
1738 case Work::kHorizontalLine_Segment:
1739 case Work::kVerticalLine_Segment:
1740 case Work::kLine_Segment: {
1741 pts = HLineIntersect(wn.pts(), wt.left(),
1742 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001743 debugShowLineIntersection(pts, wt, wn,
1744 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001745 break;
1746 }
1747 case Work::kQuad_Segment: {
1748 pts = HQuadIntersect(wn.pts(), wt.left(),
1749 wt.right(), wt.y(), wt.xFlipped(), ts);
1750 break;
1751 }
1752 case Work::kCubic_Segment: {
1753 pts = HCubicIntersect(wn.pts(), wt.left(),
1754 wt.right(), wt.y(), wt.xFlipped(), ts);
1755 break;
1756 }
1757 default:
1758 SkASSERT(0);
1759 }
1760 break;
1761 case Work::kVerticalLine_Segment:
1762 swap = true;
1763 switch (wn.segmentType()) {
1764 case Work::kHorizontalLine_Segment:
1765 case Work::kVerticalLine_Segment:
1766 case Work::kLine_Segment: {
1767 pts = VLineIntersect(wn.pts(), wt.top(),
1768 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001769 debugShowLineIntersection(pts, wt, wn,
1770 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001771 break;
1772 }
1773 case Work::kQuad_Segment: {
1774 pts = VQuadIntersect(wn.pts(), wt.top(),
1775 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1776 break;
1777 }
1778 case Work::kCubic_Segment: {
1779 pts = VCubicIntersect(wn.pts(), wt.top(),
1780 wt.bottom(), wt.x(), wt.yFlipped(), ts);
1781 break;
1782 }
1783 default:
1784 SkASSERT(0);
1785 }
1786 break;
1787 case Work::kLine_Segment:
1788 switch (wn.segmentType()) {
1789 case Work::kHorizontalLine_Segment:
1790 pts = HLineIntersect(wt.pts(), wn.left(),
1791 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001792 debugShowLineIntersection(pts, wt, wn,
1793 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001794 break;
1795 case Work::kVerticalLine_Segment:
1796 pts = VLineIntersect(wt.pts(), wn.top(),
1797 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001798 debugShowLineIntersection(pts, wt, wn,
1799 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001800 break;
1801 case Work::kLine_Segment: {
1802 pts = LineIntersect(wt.pts(), wn.pts(), ts);
1803 debugShowLineIntersection(pts, wt, wn,
1804 ts.fT[1], ts.fT[0]);
1805 break;
1806 }
1807 case Work::kQuad_Segment: {
1808 swap = true;
1809 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
1810 break;
1811 }
1812 case Work::kCubic_Segment: {
1813 swap = true;
1814 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
1815 break;
1816 }
1817 default:
1818 SkASSERT(0);
1819 }
1820 break;
1821 case Work::kQuad_Segment:
1822 switch (wn.segmentType()) {
1823 case Work::kHorizontalLine_Segment:
1824 pts = HQuadIntersect(wt.pts(), wn.left(),
1825 wn.right(), wn.y(), wn.xFlipped(), ts);
1826 break;
1827 case Work::kVerticalLine_Segment:
1828 pts = VQuadIntersect(wt.pts(), wn.top(),
1829 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1830 break;
1831 case Work::kLine_Segment: {
1832 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
1833 break;
1834 }
1835 case Work::kQuad_Segment: {
1836 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
1837 break;
1838 }
1839 case Work::kCubic_Segment: {
1840 wt.promoteToCubic();
1841 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
1842 break;
1843 }
1844 default:
1845 SkASSERT(0);
1846 }
1847 break;
1848 case Work::kCubic_Segment:
1849 switch (wn.segmentType()) {
1850 case Work::kHorizontalLine_Segment:
1851 pts = HCubicIntersect(wt.pts(), wn.left(),
1852 wn.right(), wn.y(), wn.xFlipped(), ts);
1853 break;
1854 case Work::kVerticalLine_Segment:
1855 pts = VCubicIntersect(wt.pts(), wn.top(),
1856 wn.bottom(), wn.x(), wn.yFlipped(), ts);
1857 break;
1858 case Work::kLine_Segment: {
1859 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
1860 break;
1861 }
1862 case Work::kQuad_Segment: {
1863 wn.promoteToCubic();
1864 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
1865 break;
1866 }
1867 case Work::kCubic_Segment: {
1868 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
1869 break;
1870 }
1871 default:
1872 SkASSERT(0);
1873 }
1874 break;
1875 default:
1876 SkASSERT(0);
1877 }
1878 // in addition to recording T values, record matching segment
caryclark@google.com15fa1382012-05-07 20:49:36 +00001879 int coincident = pts == 2 && wn.segmentType() <= Work::kLine_Segment
1880 && wt.segmentType() <= Work::kLine_Segment ? -1 :0;
1881 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001882 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
1883 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001884 int testTAt = wt.addT(ts.fT[swap][pt], wn, coincident);
1885 int nextTAt = wn.addT(ts.fT[!swap][pt], wt, coincident);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001886 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
1887 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001888 coincident = -coincident;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001889 }
1890 } while (wn.advance());
1891 } while (wt.advance());
1892 return true;
1893}
1894
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001895// see if coincidence is formed by clipping non-concident segments
1896static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
1897 int contourCount = contourList.count();
1898 for (size_t cIndex = 0; cIndex < contourCount; ++cIndex) {
1899 Contour* contour = contourList[cIndex];
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001900 contour->findTooCloseToCall(winding);
1901 }
1902}
1903
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001904
1905// OPTIMIZATION: not crazy about linear search here to find top active y.
1906// seems like we should break down and do the sort, or maybe sort each
1907// contours' segments?
1908// Once the segment array is built, there's no reason I can think of not to
1909// sort it in Y. hmmm
1910static Segment* findTopContour(SkTDArray<Contour*>& contourList,
1911 int contourCount) {
1912 int cIndex = 0;
1913 Segment* topStart;
1914 do {
1915 Contour* topContour = contourList[cIndex];
1916 topStart = topContour->topSegment();
1917 } while (!topStart && ++cIndex < contourCount);
1918 if (!topStart) {
1919 return NULL;
1920 }
1921 SkScalar top = topStart->bounds().fTop;
1922 for (int cTest = cIndex + 1; cTest < contourCount; ++cTest) {
1923 Contour* contour = contourList[cTest];
1924 if (top < contour->bounds().fTop) {
1925 continue;
1926 }
1927 Segment* test = contour->topSegment();
1928 if (top > test->bounds().fTop) {
1929 cIndex = cTest;
1930 topStart = test;
1931 top = test->bounds().fTop;
1932 }
1933 }
1934 return topStart;
1935}
1936
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001937// Each segment may have an inside or an outside. Segments contained within
1938// winding may have insides on either side, and form a contour that should be
1939// ignored. Segments that are coincident with opposing direction segments may
1940// have outsides on either side, and should also disappear.
1941// 'Normal' segments will have one inside and one outside. Subsequent connections
1942// when winding should follow the intersection direction. If more than one edge
1943// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001944 // since we start with leftmost top edge, we'll traverse through a
1945 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001946static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001947 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001948 int winding = 0; // there are no contours outside this one
caryclark@google.com15fa1382012-05-07 20:49:36 +00001949 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001950 Segment* topStart = findTopContour(contourList, contourCount);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001951 if (!topStart) {
1952 break;
1953 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001954 // Start at the top. Above the top is outside, below is inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001955 // follow edges to intersection by changing the tIndex by direction.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001956 int tIndex, endIndex;
1957 Segment* topSegment = topStart->findTop(tIndex, endIndex);
1958 Segment* next = topSegment;
1959 next->addMoveTo(tIndex, simple);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001960 do {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001961 SkASSERT(!next->done());
1962 next->addCurveTo(tIndex, endIndex, simple);
1963 next = next->findNext(winding, tIndex, endIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001964 } while (next != topSegment);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001965 simple.close();
1966 } while (true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001967
caryclark@google.com15fa1382012-05-07 20:49:36 +00001968 // at intersection, stay on outside, but mark remaining edges as inside
1969 // or, only mark first pair as inside?
1970 // how is this going to work for contained (but not intersecting)
1971 // segments?
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001972 // start here ;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001973 // find span
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001974 // mark neighbors winding coverage
1975 // output span
1976 // mark span as processed
caryclark@google.com15fa1382012-05-07 20:49:36 +00001977
caryclark@google.com15fa1382012-05-07 20:49:36 +00001978
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001979
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001980}
1981
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001982static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
1983 int contourCount = contourList.count();
1984 for (int cTest = 0; cTest < contourCount; ++cTest) {
1985 Contour* contour = contourList[cTest];
1986 contour->fixOtherTIndex();
1987 }
1988}
1989
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001990static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001991 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001992 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001993 if (count == 0) {
1994 return;
1995 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001996 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001997 *list.append() = &contours[index];
1998 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001999 QSort<Contour>(list.begin(), list.end() - 1);
2000}
2001
2002void simplifyx(const SkPath& path, bool asFill, SkPath& simple) {
2003 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002004 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002005 simple.reset();
2006 simple.setFillType(SkPath::kEvenOdd_FillType);
2007
2008 // turn path into list of segments
2009 SkTArray<Contour> contours;
2010 // FIXME: add self-intersecting cubics' T values to segment
2011 EdgeBuilder builder(path, contours);
2012 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002013 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002014 Contour** currentPtr = contourList.begin();
2015 if (!currentPtr) {
2016 return;
2017 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002018 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002019 // find all intersections between segments
2020 do {
2021 Contour** nextPtr = currentPtr;
2022 Contour* current = *currentPtr++;
2023 Contour* next;
2024 do {
2025 next = *nextPtr++;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002026 } while (addIntersectTs(current, next, winding) && nextPtr != listEnd);
2027 } while (currentPtr != listEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002028 fixOtherTIndex(contourList);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002029 // eat through coincident edges
2030 coincidenceCheck(contourList, winding);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002031 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002032 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002033}
2034