blob: 1ab5af48ce6885b304e244b2ed2c7a820c4a620f [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
caryclark@google.com8dcf1142012-07-02 20:27:02 +000028#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000029#define DEBUG_DUMP 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000030#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000031#define DEBUG_WINDING 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000032#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com8dcf1142012-07-02 20:27:02 +000033#define DEBUG_MARK_DONE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000034
35#else
36
37//const bool gRunTestsInOneThread = true;
38
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000039#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000040#define DEBUG_BRIDGE 1
caryclark@google.com8dcf1142012-07-02 20:27:02 +000041#define DEBUG_CROSS 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000042#define DEBUG_DUMP 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000043#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com8dcf1142012-07-02 20:27:02 +000044#define DEBUG_WINDING 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000045#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com66ca2fb2012-07-03 14:30:08 +000046#define DEBUG_MARK_DONE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000047
48#endif
49
50#if DEBUG_DUMP
51static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000052// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053static int gContourID;
54static int gSegmentID;
55#endif
56
caryclark@google.com8dcf1142012-07-02 20:27:02 +000057#ifndef DEBUG_TEST
58#define DEBUG_TEST 0
59#endif
60
caryclark@google.comfa0588f2012-04-26 21:01:06 +000061static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
62 Intersections& intersections) {
63 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
64 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
65 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
66}
67
68static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
69 Intersections& intersections) {
70 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
71 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
72 intersect(aQuad, bLine, intersections);
73 return intersections.fUsed;
74}
75
76static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
77 Intersections& intersections) {
78 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
79 {a[3].fX, a[3].fY}};
80 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
81 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
82}
83
84static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
85 Intersections& intersections) {
86 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
87 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
88 intersect(aQuad, bQuad, intersections);
89 return intersections.fUsed;
90}
91
92static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
93 Intersections& intersections) {
94 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
95 {a[3].fX, a[3].fY}};
96 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
97 {b[3].fX, b[3].fY}};
98 intersect(aCubic, bCubic, intersections);
99 return intersections.fUsed;
100}
101
102static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
103 SkScalar y, bool flipped, Intersections& intersections) {
104 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
105 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
106}
107
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
109 SkScalar y, bool flipped, Intersections& intersections) {
110 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
111 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
112}
113
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000114static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
115 SkScalar y, bool flipped, Intersections& intersections) {
116 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
117 {a[3].fX, a[3].fY}};
118 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
119}
120
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000121static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
122 SkScalar x, bool flipped, Intersections& intersections) {
123 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
124 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
125}
126
127static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
128 SkScalar x, bool flipped, Intersections& intersections) {
129 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
130 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
131}
132
133static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
134 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000135 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
136 {a[3].fX, a[3].fY}};
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000137 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138}
139
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000140static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
141 SkScalar , SkScalar , bool , Intersections& ) = {
142 NULL,
143 VLineIntersect,
144 VQuadIntersect,
145 VCubicIntersect
146};
147
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000148static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
149 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
150 double x, y;
151 xy_at_t(line, t, x, y);
152 out->fX = SkDoubleToScalar(x);
153 out->fY = SkDoubleToScalar(y);
154}
155
156static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
157 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
158 double x, y;
159 xy_at_t(quad, t, x, y);
160 out->fX = SkDoubleToScalar(x);
161 out->fY = SkDoubleToScalar(y);
162}
163
164static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
165 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
166 {a[3].fX, a[3].fY}};
167 double x, y;
168 xy_at_t(cubic, t, x, y);
169 out->fX = SkDoubleToScalar(x);
170 out->fY = SkDoubleToScalar(y);
171}
172
173static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
174 NULL,
175 LineXYAtT,
176 QuadXYAtT,
177 CubicXYAtT
178};
179
180static SkScalar LineXAtT(const SkPoint a[2], double t) {
181 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
182 double x;
183 xy_at_t(aLine, t, x, *(double*) 0);
184 return SkDoubleToScalar(x);
185}
186
187static SkScalar QuadXAtT(const SkPoint a[3], double t) {
188 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
189 double x;
190 xy_at_t(quad, t, x, *(double*) 0);
191 return SkDoubleToScalar(x);
192}
193
194static SkScalar CubicXAtT(const SkPoint a[4], double t) {
195 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
196 {a[3].fX, a[3].fY}};
197 double x;
198 xy_at_t(cubic, t, x, *(double*) 0);
199 return SkDoubleToScalar(x);
200}
201
202static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
203 NULL,
204 LineXAtT,
205 QuadXAtT,
206 CubicXAtT
207};
208
209static SkScalar LineYAtT(const SkPoint a[2], double t) {
210 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
211 double y;
212 xy_at_t(aLine, t, *(double*) 0, y);
213 return SkDoubleToScalar(y);
214}
215
216static SkScalar QuadYAtT(const SkPoint a[3], double t) {
217 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
218 double y;
219 xy_at_t(quad, t, *(double*) 0, y);
220 return SkDoubleToScalar(y);
221}
222
223static SkScalar CubicYAtT(const SkPoint a[4], double t) {
224 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
225 {a[3].fX, a[3].fY}};
226 double y;
227 xy_at_t(cubic, t, *(double*) 0, y);
228 return SkDoubleToScalar(y);
229}
230
231static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
232 NULL,
233 LineYAtT,
234 QuadYAtT,
235 CubicYAtT
236};
237
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000238static SkScalar LineDXAtT(const SkPoint a[2], double ) {
239 return a[1].fX - a[0].fX;
240}
241
242static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
243 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
244 double x;
245 dxdy_at_t(quad, t, x, *(double*) 0);
246 return SkDoubleToScalar(x);
247}
248
249static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
250 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
251 {a[3].fX, a[3].fY}};
252 double x;
253 dxdy_at_t(cubic, t, x, *(double*) 0);
254 return SkDoubleToScalar(x);
255}
256
257static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
258 NULL,
259 LineDXAtT,
260 QuadDXAtT,
261 CubicDXAtT
262};
263
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000264static void LineSubDivide(const SkPoint a[2], double startT, double endT,
265 SkPoint sub[2]) {
266 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
267 _Line dst;
268 sub_divide(aLine, startT, endT, dst);
269 sub[0].fX = SkDoubleToScalar(dst[0].x);
270 sub[0].fY = SkDoubleToScalar(dst[0].y);
271 sub[1].fX = SkDoubleToScalar(dst[1].x);
272 sub[1].fY = SkDoubleToScalar(dst[1].y);
273}
274
275static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
276 SkPoint sub[3]) {
277 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
278 {a[2].fX, a[2].fY}};
279 Quadratic dst;
280 sub_divide(aQuad, startT, endT, dst);
281 sub[0].fX = SkDoubleToScalar(dst[0].x);
282 sub[0].fY = SkDoubleToScalar(dst[0].y);
283 sub[1].fX = SkDoubleToScalar(dst[1].x);
284 sub[1].fY = SkDoubleToScalar(dst[1].y);
285 sub[2].fX = SkDoubleToScalar(dst[2].x);
286 sub[2].fY = SkDoubleToScalar(dst[2].y);
287}
288
289static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
290 SkPoint sub[4]) {
291 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
292 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
293 Cubic dst;
294 sub_divide(aCubic, startT, endT, dst);
295 sub[0].fX = SkDoubleToScalar(dst[0].x);
296 sub[0].fY = SkDoubleToScalar(dst[0].y);
297 sub[1].fX = SkDoubleToScalar(dst[1].x);
298 sub[1].fY = SkDoubleToScalar(dst[1].y);
299 sub[2].fX = SkDoubleToScalar(dst[2].x);
300 sub[2].fY = SkDoubleToScalar(dst[2].y);
301 sub[3].fX = SkDoubleToScalar(dst[3].x);
302 sub[3].fY = SkDoubleToScalar(dst[3].y);
303}
304
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000305static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
306 SkPoint []) = {
307 NULL,
308 LineSubDivide,
309 QuadSubDivide,
310 CubicSubDivide
311};
312
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000313#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000314static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
315 SkRect& bounds) {
316 SkPoint dst[3];
317 QuadSubDivide(a, startT, endT, dst);
318 bounds.fLeft = bounds.fRight = dst[0].fX;
319 bounds.fTop = bounds.fBottom = dst[0].fY;
320 for (int index = 1; index < 3; ++index) {
321 bounds.growToInclude(dst[index].fX, dst[index].fY);
322 }
323}
324
325static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
326 SkRect& bounds) {
327 SkPoint dst[4];
328 CubicSubDivide(a, startT, endT, dst);
329 bounds.fLeft = bounds.fRight = dst[0].fX;
330 bounds.fTop = bounds.fBottom = dst[0].fY;
331 for (int index = 1; index < 4; ++index) {
332 bounds.growToInclude(dst[index].fX, dst[index].fY);
333 }
334}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000335#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000336
caryclark@google.com15fa1382012-05-07 20:49:36 +0000337static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000338 SkTDArray<SkPoint>& reducePts) {
339 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
340 {a[2].fX, a[2].fY}};
341 Quadratic dst;
342 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000343 if (order == 3) {
344 return SkPath::kQuad_Verb;
345 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000346 for (int index = 0; index < order; ++index) {
347 SkPoint* pt = reducePts.append();
348 pt->fX = SkDoubleToScalar(dst[index].x);
349 pt->fY = SkDoubleToScalar(dst[index].y);
350 }
351 return (SkPath::Verb) (order - 1);
352}
353
354static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
355 SkTDArray<SkPoint>& reducePts) {
356 const Cubic aCubic = {{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 Cubic dst;
359 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000360 if (order == 4) {
361 return SkPath::kCubic_Verb;
362 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000363 for (int index = 0; index < order; ++index) {
364 SkPoint* pt = reducePts.append();
365 pt->fX = SkDoubleToScalar(dst[index].x);
366 pt->fY = SkDoubleToScalar(dst[index].y);
367 }
368 return (SkPath::Verb) (order - 1);
369}
370
caryclark@google.com15fa1382012-05-07 20:49:36 +0000371static bool QuadIsLinear(const SkPoint a[3]) {
372 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
373 {a[2].fX, a[2].fY}};
374 return isLinear(aQuad, 0, 2);
375}
376
377static bool CubicIsLinear(const SkPoint a[4]) {
378 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
379 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
380 return isLinear(aCubic, 0, 3);
381}
382
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000383static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
384 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
385 double x[2];
386 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000387 xy_at_t(aLine, endT, x[1], *(double*) 0);
388 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000389}
390
391static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
392 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
393 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000394 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395}
396
397static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
398 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
399 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000400 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000401}
402
403static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
404 NULL,
405 LineLeftMost,
406 QuadLeftMost,
407 CubicLeftMost
408};
409
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000410#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000411static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
412 const SkPoint& below) {
413 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
414 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
415 return implicit_matches_ulps(aLine, bLine, 32);
416}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000417#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000418
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000419class Segment;
420
caryclark@google.com15fa1382012-05-07 20:49:36 +0000421// sorting angles
422// given angles of {dx dy ddx ddy dddx dddy} sort them
423class Angle {
424public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000425 // FIXME: this is bogus for quads and cubics
426 // if the quads and cubics' line from end pt to ctrl pt are coincident,
427 // there's no obvious way to determine the curve ordering from the
428 // derivatives alone. In particular, if one quadratic's coincident tangent
429 // is longer than the other curve, the final control point can place the
430 // longer curve on either side of the shorter one.
431 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
432 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000433 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000434 if ((fDy < 0) ^ (rh.fDy < 0)) {
435 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000436 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000437 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
438 return fDx < rh.fDx;
439 }
440 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000441 if (cmp) {
442 return cmp < 0;
443 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
445 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000446 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000447 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
448 return fDDx < rh.fDDx;
449 }
450 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000451 if (cmp) {
452 return cmp < 0;
453 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000454 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
455 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000456 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000457 if (fDDDy == 0 && rh.fDDDy == 0) {
458 return fDDDx < rh.fDDDx;
459 }
460 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000461 }
462
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000463 bool cancels(const Angle& rh) const {
464 return fDx * rh.fDx < 0 || fDy * rh.fDy < 0;
465 }
466
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000467 int end() const {
468 return fEnd;
469 }
470
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000471 bool isHorizontal() const {
472 return fDy == 0 && fDDy == 0 && fDDDy == 0;
473 }
474
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000475 // since all angles share a point, this needs to know which point
476 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
477 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000478 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000479 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000480 SkASSERT(start != end);
481 fSegment = segment;
482 fStart = start;
483 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000484 fDx = pts[1].fX - pts[0].fX; // b - a
485 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000486 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000487 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000488 return;
489 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000490 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
491 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000492 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000493 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000494 return;
495 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
497 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
498 }
499
500 // noncoincident quads/cubics may have the same initial angle
501 // as lines, so must sort by derivatives as well
502 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000503 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000504 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000505 fSegment = segment;
506 fStart = start;
507 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000508 fDx = pts[1].fX - pts[0].fX; // b - a
509 fDy = pts[1].fY - pts[0].fY;
510 if (verb == SkPath::kLine_Verb) {
511 fDDx = fDDy = fDDDx = fDDDy = 0;
512 return;
513 }
514 if (verb == SkPath::kQuad_Verb) {
515 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
516 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
517 int larger = std::max(abs(uplsX), abs(uplsY));
518 int shift = 0;
519 double flatT;
520 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
521 LineParameters implicitLine;
522 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
523 implicitLine.lineEndPoints(tangent);
524 implicitLine.normalize();
525 while (larger > UlpsEpsilon * 1024) {
526 larger >>= 2;
527 ++shift;
528 flatT = 0.5 / (1 << shift);
529 QuadXYAtT(pts, flatT, &ddPt);
530 _Point _pt = {ddPt.fX, ddPt.fY};
531 double distance = implicitLine.pointDistance(_pt);
532 if (approximately_zero(distance)) {
533 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
534 break;
535 }
536 }
537 flatT = 0.5 / (1 << shift);
538 QuadXYAtT(pts, flatT, &ddPt);
539 fDDx = ddPt.fX - pts[0].fX;
540 fDDy = ddPt.fY - pts[0].fY;
541 SkASSERT(fDDx != 0 || fDDy != 0);
542 fDDDx = fDDDy = 0;
543 return;
544 }
545 SkASSERT(0); // FIXME: add cubic case
546 }
547
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000548 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000549 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000550 }
551
552 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000553 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000554 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000555
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000556 int start() const {
557 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000558 }
559
560private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000561 SkScalar fDx;
562 SkScalar fDy;
563 SkScalar fDDx;
564 SkScalar fDDy;
565 SkScalar fDDDx;
566 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000567 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000568 int fStart;
569 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000570};
571
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000572static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
573 int angleCount = angles.count();
574 int angleIndex;
575 angleList.setReserve(angleCount);
576 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
577 *angleList.append() = &angles[angleIndex];
578 }
579 QSort<Angle>(angleList.begin(), angleList.end() - 1);
580}
581
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000582// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000583struct Bounds : public SkRect {
584 static bool Intersects(const Bounds& a, const Bounds& b) {
585 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
586 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
587 }
588
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000589 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
590 if (left < fLeft) {
591 fLeft = left;
592 }
593 if (top < fTop) {
594 fTop = top;
595 }
596 if (right > fRight) {
597 fRight = right;
598 }
599 if (bottom > fBottom) {
600 fBottom = bottom;
601 }
602 }
603
604 void add(const Bounds& toAdd) {
605 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
606 }
607
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000608 bool isEmpty() {
609 return fLeft > fRight || fTop > fBottom
610 || fLeft == fRight && fTop == fBottom
611 || isnan(fLeft) || isnan(fRight)
612 || isnan(fTop) || isnan(fBottom);
613 }
614
615 void setCubicBounds(const SkPoint a[4]) {
616 _Rect dRect;
617 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
618 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
619 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000620 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
621 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000622 }
623
624 void setQuadBounds(const SkPoint a[3]) {
625 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
626 {a[2].fX, a[2].fY}};
627 _Rect dRect;
628 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000629 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
630 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000631 }
632};
633
caryclark@google.com15fa1382012-05-07 20:49:36 +0000634struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000635 Segment* fOther;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000636 mutable SkPoint const* fPt; // lazily computed as needed
637 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000638 double fOtherT; // value at fOther[fOtherIndex].fT
639 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000640 int fWindSum; // accumulated from contours surrounding this one
641 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000642 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000643};
644
645class Segment {
646public:
647 Segment() {
648#if DEBUG_DUMP
649 fID = ++gSegmentID;
650#endif
651 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000652
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000653 SkScalar activeTop() const {
654 SkASSERT(!done());
655 int count = fTs.count();
656 SkScalar result = SK_ScalarMax;
657 bool lastDone = true;
658 for (int index = 0; index < count; ++index) {
659 bool done = fTs[index].fDone;
660 if (!done || !lastDone) {
661 SkScalar y = yAtT(index);
662 if (result > y) {
663 result = y;
664 }
665 }
666 lastDone = done;
667 }
668 SkASSERT(result < SK_ScalarMax);
669 return result;
670 }
671
672 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000673 SkASSERT(start != end);
674 SkPoint edge[4];
675 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
676 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000677 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000678 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000679
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000680 void addCubic(const SkPoint pts[4]) {
681 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000682 fBounds.setCubicBounds(pts);
683 }
684
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000685 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000686 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000687 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000688 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000689 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000690 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000691 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000692 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
693 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
694 if (fVerb > 1) {
695 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
696 }
697 if (fVerb > 2) {
698 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
699 }
700 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000701 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000702 switch (fVerb) {
703 case SkPath::kLine_Verb:
704 path.lineTo(edge[1].fX, edge[1].fY);
705 break;
706 case SkPath::kQuad_Verb:
707 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
708 break;
709 case SkPath::kCubic_Verb:
710 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
711 edge[3].fX, edge[3].fY);
712 break;
713 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000714 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000715 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000716 }
717
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000718 void addLine(const SkPoint pts[2]) {
719 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000720 fBounds.set(pts, 2);
721 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000722
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000723 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
724 const SkPoint& pt = xyAtT(tIndex);
725 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000726 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000727 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000728 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000729 path.moveTo(pt.fX, pt.fY);
730 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000731 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000732 }
733
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000734 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000735 void addOtherT(int index, double otherT, int otherIndex) {
736 Span& span = fTs[index];
737 span.fOtherT = otherT;
738 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000739 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000740
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000741 void addQuad(const SkPoint pts[3]) {
742 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000743 fBounds.setQuadBounds(pts);
744 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000745
746 // Defer all coincident edge processing until
747 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000748
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000749// no need to be tricky; insert in normal T order
750// resolve overlapping ts when considering coincidence later
751
752 // add non-coincident intersection. Resulting edges are sorted in T.
753 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000754 // FIXME: in the pathological case where there is a ton of intercepts,
755 // binary search?
756 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000757 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000758 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000759 // OPTIMIZATION: if there are three or more identical Ts, then
760 // the fourth and following could be further insertion-sorted so
761 // that all the edges are clockwise or counterclockwise.
762 // This could later limit segment tests to the two adjacent
763 // neighbors, although it doesn't help with determining which
764 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000765 if (newT < fTs[index].fT) {
766 insertedAt = index;
767 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000768 }
769 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000770 Span* span;
771 if (insertedAt >= 0) {
772 span = fTs.insert(insertedAt);
773 } else {
774 insertedAt = tCount;
775 span = fTs.append();
776 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000777 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000778 span->fOther = other;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000779 span->fPt = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000780 span->fWindSum = SK_MinS32;
781 span->fWindValue = 1;
782 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000783 ++fDoneSpans;
784 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000785 return insertedAt;
786 }
787
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000788 // set spans from start to end to decrement by one
789 // note this walks other backwards
790 // FIMXE: there's probably an edge case that can be constructed where
791 // two span in one segment are separated by float epsilon on one span but
792 // not the other, if one segment is very small. For this
793 // case the counts asserted below may or may not be enough to separate the
794 // spans. Even if the counts work out, what if the spanw aren't correctly
795 // sorted? It feels better in such a case to match the span's other span
796 // pointer since both coincident segments must contain the same spans.
797 void addTCancel(double startT, double endT, Segment& other,
798 double oStartT, double oEndT) {
799 SkASSERT(endT - startT >= FLT_EPSILON);
800 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
801 int index = 0;
802 while (startT - fTs[index].fT >= FLT_EPSILON) {
803 ++index;
804 }
805 int oCount = other.fTs.count();
806 while (other.fTs[--oCount].fT - oEndT >= FLT_EPSILON)
807 ;
808 int oIndex = oCount;
809 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
810 ;
811 Span* test = &fTs[index];
812 Span* oTest = &other.fTs[oIndex];
813 SkDEBUGCODE(int testWindValue = test->fWindValue);
814 SkDEBUGCODE(int oTestWindValue = oTest->fWindValue);
815 SkDEBUGCODE(int startIndex = index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000816 do {
817 bool decrement = test->fWindValue && oTest->fWindValue;
818 Span* end = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000819 do {
820 SkASSERT(testWindValue == end->fWindValue);
821 if (decrement) {
822 if (--(end->fWindValue) == 0) {
823 end->fDone = true;
824 ++fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000825 }
826 }
827 end = &fTs[++index];
828 } while (end->fT - test->fT < FLT_EPSILON);
829 SkASSERT(oCount - oIndex == index - startIndex);
830 Span* oTestStart = oTest;
831 SkDEBUGCODE(oCount = oIndex);
832 do {
833 SkASSERT(oTestWindValue == oTestStart->fWindValue);
834 if (decrement) {
835 if (--(oTestStart->fWindValue) == 0) {
836 oTestStart->fDone = true;
837 ++other.fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000838 }
839 }
840 if (!oIndex) {
841 break;
842 }
843 oTestStart = &other.fTs[--oIndex];
844 } while (oTest->fT - oTestStart->fT < FLT_EPSILON);
845 test = end;
846 oTest = oTestStart;
847 } while (test->fT < endT - FLT_EPSILON);
848 SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000849 }
850
851 // set spans from start to end to increment the greater by one and decrement
852 // the lesser
853 void addTCoincident(double startT, double endT, Segment& other,
854 double oStartT, double oEndT) {
855 SkASSERT(endT - startT >= FLT_EPSILON);
856 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
857 int index = 0;
858 while (startT - fTs[index].fT >= FLT_EPSILON) {
859 ++index;
860 }
861 int oIndex = 0;
862 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
863 ++oIndex;
864 }
865 Span* test = &fTs[index];
866 Span* oTest = &other.fTs[oIndex];
867 SkDEBUGCODE(int testWindValue = test->fWindValue);
868 SkDEBUGCODE(int oTestWindValue = oTest->fWindValue);
869 SkTDArray<double> outsideTs;
870 SkTDArray<double> oOutsideTs;
871 do {
872 bool decrementOther = test->fWindValue >= oTest->fWindValue;
873 Span* end = test;
874 double startT = end->fT;
875 double oStartT = oTest->fT;
876 do {
877 SkASSERT(testWindValue == end->fWindValue);
878 if (decrementOther) {
879 ++(end->fWindValue);
880 } else {
881 if (--(end->fWindValue) == 0) {
882 end->fDone = true;
883 ++fDoneSpans;
884 *outsideTs.append() = end->fT;
885 *outsideTs.append() = oStartT;
886 }
887 }
888 end = &fTs[++index];
889 } while (end->fT - test->fT < FLT_EPSILON);
890 Span* oEnd = oTest;
891 do {
892 SkASSERT(oTestWindValue == oEnd->fWindValue);
893 if (decrementOther) {
894 if (--(oEnd->fWindValue) == 0) {
895 oEnd->fDone = true;
896 ++other.fDoneSpans;
897 *oOutsideTs.append() = oEnd->fT;
898 *oOutsideTs.append() = startT;
899 }
900 } else {
901 ++(oEnd->fWindValue);
902 }
903 oEnd = &other.fTs[++oIndex];
904 } while (oEnd->fT - oTest->fT < FLT_EPSILON);
905 test = end;
906 oTest = oEnd;
907 } while (test->fT < endT - FLT_EPSILON);
908 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
909 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
910 if (!done() && outsideTs.count()) {
911 addTOutsides(outsideTs, &other, oEndT);
912 }
913 if (!other.done() && oOutsideTs.count()) {
914 other.addTOutsides(oOutsideTs, this, endT);
915 }
916 }
917
918 void addTOutsides(const SkTDArray<double>& outsideTs, Segment* other,
919 double otherEnd) {
920 int count = outsideTs.count();
921 double endT = 0;
922 int endSpan = 0;
923 for (int index = 0; index < count; index += 2) {
924 double t = outsideTs[index];
925 double otherT = outsideTs[index + 1];
926 if (t > 1 - FLT_EPSILON) {
927 return;
928 }
929 if (t - endT > FLT_EPSILON) {
930 endSpan = addTPair(t, other, otherT);
931 }
932 do {
933 endT = fTs[++endSpan].fT;
934 } while (endT - t < FLT_EPSILON);
935 }
936 addTPair(endT, other, otherEnd);
937 }
938
939 int addTPair(double t, Segment* other, double otherT) {
940 int insertedAt = addT(t, other);
941 int otherInsertedAt = other->addT(otherT, this);
942 addOtherT(insertedAt, otherT, otherInsertedAt);
943 other->addOtherT(otherInsertedAt, t, insertedAt);
944 return insertedAt;
945 }
946
947 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000948 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000949 if (fTs[SkMin32(end, start)].fWindValue > 0) {
950 addAngle(angles, end, start);
951 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000952 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +0000953 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000954 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000955 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000956 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000957 }
958 }
959
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000960 const Bounds& bounds() const {
961 return fBounds;
962 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000963
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000964 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000965 double referenceT = fTs[index].fT;
966 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000967 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000968 buildAnglesInner(lesser, angles);
969 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000970 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000971 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000972 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000973 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000974
975 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
976 Span* span = &fTs[index];
977 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000978 // if there is only one live crossing, and no coincidence, continue
979 // in the same direction
980 // if there is coincidence, the only choice may be to reverse direction
981 // find edge on either side of intersection
982 int oIndex = span->fOtherIndex;
983 // if done == -1, prior span has already been processed
984 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000985 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000986 if (next < 0) {
987 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000988 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000989 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000990 // add candidate into and away from junction
991 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000992 }
993
994 // OPTIMIZATION: inefficient, refactor
995 bool cancels(const Segment& other) const {
996 SkTDArray<Angle> angles;
997 addAngle(angles, 0, fTs.count() - 1);
998 other.addAngle(angles, 0, other.fTs.count() - 1);
999 return angles[0].cancels(angles[1]);
1000 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001001
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001002 // figure out if the segment's ascending T goes clockwise or not
1003 // not enough context to write this as shown
1004 // instead, add all segments meeting at the top
1005 // sort them using buildAngleList
1006 // find the first in the sort
1007 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001008 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001009 SkASSERT(0); // incomplete
1010 return false;
1011 }
1012
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001013 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
1014 int start = 0;
1015 int bestT = -1;
1016 SkScalar top = bounds().fTop;
1017 SkScalar bottom = bounds().fBottom;
1018 int end;
1019 do {
1020 end = nextSpan(start, 1);
1021 SkPoint edge[4];
1022 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1023 // work with the original data directly
1024 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1025 // start here; intersect ray starting at basePt with edge
1026 Intersections intersections;
1027 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1028 false, intersections);
1029 if (pts == 0) {
1030 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001031 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001032 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1033 // if the intersection is edge on, wait for another one
1034 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001035 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001036 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1037 SkPoint pt;
1038 double foundT = intersections.fT[0][0];
1039 (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
1040 if (bestY < pt.fY) {
1041 bestY = pt.fY;
1042 bestT = foundT < 1 ? start : end;
1043 hitT = foundT;
1044 }
1045 start = end;
1046 } while (fTs[end].fT != 1);
1047 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001048 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001049
caryclark@google.com15fa1382012-05-07 20:49:36 +00001050 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001051 SkASSERT(fDoneSpans <= fTs.count());
1052 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001053 }
1054
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001055 // so the span needs to contain the pairing info found here
1056 // this should include the winding computed for the edge, and
1057 // what edge it connects to, and whether it is discarded
1058 // (maybe discarded == abs(winding) > 1) ?
1059 // only need derivatives for duration of sorting, add a new struct
1060 // for pairings, remove extra spans that have zero length and
1061 // reference an unused other
1062 // for coincident, the last span on the other may be marked done
1063 // (always?)
1064
1065 // if loop is exhausted, contour may be closed.
1066 // FIXME: pass in close point so we can check for closure
1067
1068 // given a segment, and a sense of where 'inside' is, return the next
1069 // segment. If this segment has an intersection, or ends in multiple
1070 // segments, find the mate that continues the outside.
1071 // note that if there are multiples, but no coincidence, we can limit
1072 // choices to connections in the correct direction
1073
1074 // mark found segments as done
1075
caryclark@google.com15fa1382012-05-07 20:49:36 +00001076 // start is the index of the beginning T of this edge
1077 // it is guaranteed to have an end which describes a non-zero length (?)
1078 // winding -1 means ccw, 1 means cw
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001079 // firstFind allows coincident edges to be treated differently
caryclark@google.com495f8e42012-05-31 13:13:11 +00001080 Segment* findNext(int winding, const int startIndex, const int endIndex,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 int& nextStart, int& nextEnd, bool firstFind) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001082 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001083 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001084 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1085 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001086 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001087 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001088 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001089 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001090 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001091 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001092 // mark the smaller of startIndex, endIndex done, and all adjacent
1093 // spans with the same T value (but not 'other' spans)
caryclark@google.com495f8e42012-05-31 13:13:11 +00001094 markDone(SkMin32(startIndex, endIndex), winding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001095 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001096 nextStart = endSpan->fOtherIndex;
1097 nextEnd = nextStart + step;
1098 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001099 return other;
1100 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001101 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001102 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001103 SkASSERT(startIndex - endIndex != 0);
1104 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001105 addTwoAngles(startIndex, end, angles);
1106 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001107 SkTDArray<Angle*> sorted;
1108 sortAngles(angles, sorted);
1109 // find the starting edge
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001110 int firstIndex = -1;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001111 int angleCount = angles.count();
1112 int angleIndex;
1113 const Angle* angle;
1114 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1115 angle = sorted[angleIndex];
1116 if (angle->segment() == this && angle->start() == end &&
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001117 angle->end() == startIndex) {
1118 firstIndex = angleIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001119 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001120 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001121 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001122 // back up if prior edge is coincident with firstIndex
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001123 // adjustFirst(sorted, firstIndex, winding, firstFind);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001124 SkASSERT(firstIndex >= 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001125 int startWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001126 int nextIndex = firstIndex + 1;
1127 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1128 const Angle* foundAngle = NULL;
1129 // bool alreadyMarked = angle->segment()->fTs[SkMin32(angle->start(),
1130 // angle->end())].fDone;
1131 // iterate through the angle, and compute everyone's winding
1132 bool firstEdge = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001133 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001134 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001135 nextIndex = 0;
1136 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001137 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001138 int maxWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001139 Segment* nextSegment = nextAngle->segment();
1140 int windValue = nextSegment->windValue(nextAngle);
1141 SkASSERT(windValue > 0);
1142 winding -= nextAngle->sign() * windValue;
1143 firstEdge = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001144 if (!winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001145 if (!foundAngle) {
1146 foundAngle = nextAngle;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001147 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001148 goto doNext;
1149 }
1150 if (nextSegment->done()) {
1151 goto doNext;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001152 }
1153 // if the winding is non-zero, nextAngle does not connect to
1154 // current chain. If we haven't done so already, mark the angle
1155 // as done, record the winding value, and mark connected unambiguous
1156 // segments as well.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001157 if (nextSegment->winding(nextAngle) == SK_MinS32) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001158 if (abs(maxWinding) < abs(winding)) {
1159 maxWinding = winding;
1160 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001161 if (foundAngle) {
1162 nextSegment->markAndChaseWinding(nextAngle, maxWinding);
1163 } else {
1164 nextSegment->markAndChaseDone(nextAngle, maxWinding);
1165 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001166 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001167 doNext:
1168 angle = nextAngle;
1169 } while (++nextIndex != lastIndex);
1170 // if (!alreadyMarked) {
1171 sorted[firstIndex]->segment()->
1172 markDone(SkMin32(startIndex, endIndex), startWinding);
1173 // }
1174 if (!foundAngle) {
1175 return NULL;
1176 }
1177 nextStart = foundAngle->start();
1178 nextEnd = foundAngle->end();
1179 return foundAngle->segment();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001180 }
1181
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001182 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001183 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001184 int count = fTs.count();
1185 if (count < 3) { // require t=0, x, 1 at minimum
1186 return;
1187 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001188 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001189 int moCount;
1190 Span* match;
1191 Segment* mOther;
1192 do {
1193 match = &fTs[matchIndex];
1194 mOther = match->fOther;
1195 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001196 if (moCount >= 3) {
1197 break;
1198 }
1199 if (++matchIndex >= count) {
1200 return;
1201 }
1202 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001203 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001204 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001205 // look for a pair of nearby T values that map to the same (x,y) value
1206 // if found, see if the pair of other segments share a common point. If
1207 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001208 for (int index = matchIndex + 1; index < count; ++index) {
1209 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001210 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001211 continue;
1212 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001213 Segment* tOther = test->fOther;
1214 int toCount = tOther->fTs.count();
1215 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001216 continue;
1217 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001218 const SkPoint* testPt = &xyAtT(test);
1219 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001220 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001221 moCount = toCount;
1222 match = test;
1223 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001224 matchPt = testPt;
1225 continue;
1226 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001227 int moStart = -1;
1228 int moEnd = -1;
1229 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001230 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001231 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001232 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001233 continue;
1234 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001235 if (moSpan.fOther == this) {
1236 if (moSpan.fOtherT == match->fT) {
1237 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001238 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001239 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001240 continue;
1241 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001242 if (moSpan.fOther == tOther) {
1243 SkASSERT(moEnd == -1);
1244 moEnd = moIndex;
1245 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001246 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001247 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001248 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001249 continue;
1250 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001251 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1252 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001253 continue;
1254 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001255 int toStart = -1;
1256 int toEnd = -1;
1257 double toStartT, toEndT;
1258 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1259 Span& toSpan = tOther->fTs[toIndex];
1260 if (toSpan.fOther == this) {
1261 if (toSpan.fOtherT == test->fT) {
1262 toStart = toIndex;
1263 toStartT = toSpan.fT;
1264 }
1265 continue;
1266 }
1267 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1268 SkASSERT(toEnd == -1);
1269 toEnd = toIndex;
1270 toEndT = toSpan.fT;
1271 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001272 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001273 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1274 if (toStart <= 0 || toEnd <= 0) {
1275 continue;
1276 }
1277 if (toStartT == toEndT) {
1278 continue;
1279 }
1280 // test to see if the segment between there and here is linear
1281 if (!mOther->isLinear(moStart, moEnd)
1282 || !tOther->isLinear(toStart, toEnd)) {
1283 continue;
1284 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001285 // FIXME: defer implementation until the rest works
1286 // this may share code with regular coincident detection
1287 SkASSERT(0);
1288 #if 0
1289 if (flipped) {
1290 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1291 } else {
1292 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1293 }
1294 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001295 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001296 }
1297
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001298 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1299 // and use more concise logic like the old edge walker code?
1300 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001301 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001302 // iterate through T intersections and return topmost
1303 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001304 SkASSERT(!done());
1305 int firstT;
1306 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001307 SkPoint topPt;
1308 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001309 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001310 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001311 bool lastDone = true;
1312 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001313 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001314 if (!span.fDone || !lastDone) {
1315 const SkPoint& intercept = xyAtT(&span);
1316 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1317 && topPt.fX > intercept.fX)) {
1318 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001319 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001320 } else if (topPt == intercept) {
1321 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001322 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001323 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001324 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001325 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001326 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001327 int step = 1;
1328 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001329 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001330 step = -1;
1331 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001332 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001333 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001334 // if the topmost T is not on end, or is three-way or more, find left
1335 // look for left-ness from tLeft to firstT (matching y of other)
1336 SkTDArray<Angle> angles;
1337 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001338 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001339 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001340 SkTDArray<Angle*> sorted;
1341 sortAngles(angles, sorted);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001342 // skip edges that have already been processed
1343 firstT = -1;
1344 Segment* leftSegment;
1345 do {
1346 const Angle* angle = sorted[++firstT];
1347 leftSegment = angle->segment();
1348 tIndex = angle->end();
1349 endIndex = angle->start();
1350 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001351 return leftSegment;
1352 }
1353
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001354 // FIXME: not crazy about this
1355 // when the intersections are performed, the other index is into an
1356 // incomplete array. as the array grows, the indices become incorrect
1357 // while the following fixes the indices up again, it isn't smart about
1358 // skipping segments whose indices are already correct
1359 // assuming we leave the code that wrote the index in the first place
1360 void fixOtherTIndex() {
1361 int iCount = fTs.count();
1362 for (int i = 0; i < iCount; ++i) {
1363 Span& iSpan = fTs[i];
1364 double oT = iSpan.fOtherT;
1365 Segment* other = iSpan.fOther;
1366 int oCount = other->fTs.count();
1367 for (int o = 0; o < oCount; ++o) {
1368 Span& oSpan = other->fTs[o];
1369 if (oT == oSpan.fT && this == oSpan.fOther) {
1370 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001371 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001372 }
1373 }
1374 }
1375 }
1376
caryclark@google.com495f8e42012-05-31 13:13:11 +00001377 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001378 void innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001379 int end = nextSpan(index, step);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001380 if (multipleSpans(end, step)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001381 return;
1382 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001383 const Span& endSpan = fTs[end];
1384 Segment* other = endSpan.fOther;
1385 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001386 int otherEnd = other->nextSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001387 other->innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001388 other->markDone(SkMin32(index, otherEnd), winding);
1389 }
1390
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001391 void innerChaseWinding(int index, int step, int winding) {
1392 int end = nextSpan(index, step);
1393 if (multipleSpans(end, step)) {
1394 return;
1395 }
1396 const Span& endSpan = fTs[end];
1397 Segment* other = endSpan.fOther;
1398 index = endSpan.fOtherIndex;
1399 int otherEnd = other->nextSpan(index, step);
1400 int min = SkMin32(index, otherEnd);
1401 if (other->fTs[min].fWindSum != SK_MinS32) {
1402 SkASSERT(other->fTs[index].fWindSum == winding);
1403 return;
1404 }
1405 other->innerChaseWinding(index, step, winding);
1406 other->markWinding(min, winding);
1407 }
1408
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001409 void init(const SkPoint pts[], SkPath::Verb verb) {
1410 fPts = pts;
1411 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001412 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001413 }
1414
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001415 bool intersected() const {
1416 return fTs.count() > 0;
1417 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001418
1419 bool isLinear(int start, int end) const {
1420 if (fVerb == SkPath::kLine_Verb) {
1421 return true;
1422 }
1423 if (fVerb == SkPath::kQuad_Verb) {
1424 SkPoint qPart[3];
1425 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1426 return QuadIsLinear(qPart);
1427 } else {
1428 SkASSERT(fVerb == SkPath::kCubic_Verb);
1429 SkPoint cPart[4];
1430 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1431 return CubicIsLinear(cPart);
1432 }
1433 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001434
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001435 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001436 int count = fTs.count();
1437 if (count == 2) {
1438 return true;
1439 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 double t = fTs[end].fT;
1441 if (t < FLT_EPSILON) {
1442 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001443 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001444 if (t > 1 - FLT_EPSILON) {
1445 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001446 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001447 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001448 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001449
1450 bool isHorizontal() const {
1451 return fBounds.fTop == fBounds.fBottom;
1452 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001453
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001454 bool isVertical() const {
1455 return fBounds.fLeft == fBounds.fRight;
1456 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001457
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001458 SkScalar leftMost(int start, int end) const {
1459 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1460 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001461
caryclark@google.com495f8e42012-05-31 13:13:11 +00001462 // this span is excluded by the winding rule -- chase the ends
1463 // as long as they are unambiguous to mark connections as done
1464 // and give them the same winding value
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001465 void markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001466 int index = angle->start();
1467 int endIndex = angle->end();
1468 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001469 innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001470 markDone(SkMin32(index, endIndex), winding);
1471 }
1472
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001473 void markAndChaseWinding(const Angle* angle, int winding) {
1474 int index = angle->start();
1475 int endIndex = angle->end();
1476 int min = SkMin32(index, endIndex);
1477 int step = SkSign32(endIndex - index);
1478 innerChaseWinding(index, step, winding);
1479 markWinding(min, winding);
1480 }
1481
caryclark@google.com495f8e42012-05-31 13:13:11 +00001482 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001483 // This may be called when the segment is already marked done. While this
1484 // wastes time, it shouldn't do any more than spin through the T spans.
1485 // OPTIMIZATION: abort on first done found (assuming that this code is
1486 // always called to mark segments done).
caryclark@google.com495f8e42012-05-31 13:13:11 +00001487 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001488 // SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001489 double referenceT = fTs[index].fT;
1490 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001491 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001492 Span& span = fTs[lesser];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001493 if (span.fDone) {
1494 continue;
1495 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001496 #if DEBUG_MARK_DONE
1497 const SkPoint& pt = xyAtT(&span);
1498 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1499 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1500 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001501 span.fDone = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001502 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1503 span.fWindSum = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001504 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001505 }
1506 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001507 Span& span = fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001508 // SkASSERT(!span.fDone);
1509 if (span.fDone) {
1510 continue;
1511 }
1512 #if DEBUG_MARK_DONE
1513 const SkPoint& pt = xyAtT(&span);
1514 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1515 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1516 #endif
1517 span.fDone = true;
1518 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1519 span.fWindSum = winding;
1520 fDoneSpans++;
1521 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
1522 }
1523
1524 void markWinding(int index, int winding) {
1525 SkASSERT(!done());
1526 double referenceT = fTs[index].fT;
1527 int lesser = index;
1528 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
1529 Span& span = fTs[lesser];
1530 if (span.fDone) {
1531 continue;
1532 }
1533 SkASSERT(span.fWindValue == 1 || winding == 0);
1534 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1535 #if DEBUG_MARK_DONE
1536 const SkPoint& pt = xyAtT(&span);
1537 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1538 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1539 #endif
1540 span.fWindSum = winding;
1541 }
1542 do {
1543 Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001544 // SkASSERT(!span.fDone || span.fCoincident);
1545 if (span.fDone) {
1546 continue;
1547 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001548 SkASSERT(span.fWindValue == 1 || winding == 0);
1549 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1550 #if DEBUG_MARK_DONE
1551 const SkPoint& pt = xyAtT(&span);
1552 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1553 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1554 #endif
1555 span.fWindSum = winding;
1556 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001557 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001558
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001559 bool multipleSpans(int end, int step) const {
1560 return step > 0 ? ++end < fTs.count() : end > 0;
1561 }
1562
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001563 // This has callers for two different situations: one establishes the end
1564 // of the current span, and one establishes the beginning of the next span
1565 // (thus the name). When this is looking for the end of the current span,
1566 // coincidence is found when the beginning Ts contain -step and the end
1567 // contains step. When it is looking for the beginning of the next, the
1568 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001570 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001571 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001572 int count = fTs.count();
1573 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001574 while (step > 0 ? ++to < count : --to >= 0) {
1575 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001577 continue;
1578 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001579 return to;
1580 }
1581 return -1;
1582 }
1583
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001584 const SkPoint* pts() const {
1585 return fPts;
1586 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001587
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001588 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001589 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001590 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1591 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001592 }
1593
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001594 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001595 const Span& span(int tIndex) const {
1596 return fTs[tIndex];
1597 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001598
1599 int spanSign(int startIndex, int endIndex) const {
1600 return startIndex < endIndex ? -fTs[startIndex].fWindValue :
1601 fTs[endIndex].fWindValue;
1602 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001603
1604 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001605 double t(int tIndex) const {
1606 return fTs[tIndex].fT;
1607 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001608
1609 void updatePts(const SkPoint pts[]) {
1610 fPts = pts;
1611 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001612
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001613 SkPath::Verb verb() const {
1614 return fVerb;
1615 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001616
1617 // if the only remaining spans are small, ignore them, and mark done
1618 bool virtuallyDone() {
1619 int count = fTs.count();
1620 double previous = 0;
1621 bool previousDone = fTs[0].fDone;
1622 for (int index = 1; index < count; ++index) {
1623 Span& span = fTs[index];
1624 double t = span.fT;
1625 if (t - previous < FLT_EPSILON) {
1626 if (span.fDone && !previousDone) {
1627 int prior = --index;
1628 int winding = span.fWindSum;
1629 do {
1630 Span& priorSpan = fTs[prior];
1631 priorSpan.fDone = true;
1632 priorSpan.fWindSum = winding;
1633 fDoneSpans++;
1634 } while (--prior >= 0 && t - fTs[prior].fT < FLT_EPSILON);
1635 }
1636 } else if (!previousDone) {
1637 return false;
1638 }
1639 previous = t;
1640 previousDone = span.fDone;
1641 }
1642 SkASSERT(done());
1643 return true;
1644 }
1645
1646 int winding(int tIndex) const {
1647 return fTs[tIndex].fWindSum;
1648 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001649
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001650 int winding(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001651 int start = angle->start();
1652 int end = angle->end();
1653 int index = SkMin32(start, end);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001654 return winding(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001655 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001656
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001657 int windValue(int tIndex) const {
1658 return fTs[tIndex].fWindValue;
1659 }
1660
1661 int windValue(const Angle* angle) const {
1662 int start = angle->start();
1663 int end = angle->end();
1664 int index = SkMin32(start, end);
1665 return windValue(index);
1666 }
1667
1668 SkScalar xAtT(const Span* span) const {
1669 return xyAtT(span).fX;
1670 }
1671
1672 const SkPoint& xyAtT(int index) const {
1673 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001674 }
1675
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001676 const SkPoint& xyAtT(const Span* span) const {
1677 if (!span->fPt) {
1678 if (span->fT == 0) {
1679 span->fPt = &fPts[0];
1680 } else if (span->fT == 1) {
1681 span->fPt = &fPts[fVerb];
1682 } else {
1683 SkPoint* pt = fIntersections.append();
1684 (*SegmentXYAtT[fVerb])(fPts, span->fT, pt);
1685 span->fPt = pt;
1686 }
1687 }
1688 return *span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001689 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001690
1691 SkScalar yAtT(int index) const {
1692 return yAtT(&fTs[index]);
1693 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001694
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001695 SkScalar yAtT(const Span* span) const {
1696 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001697 }
1698
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001699#if DEBUG_DUMP
1700 void dump() const {
1701 const char className[] = "Segment";
1702 const int tab = 4;
1703 for (int i = 0; i < fTs.count(); ++i) {
1704 SkPoint out;
1705 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1706 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001707 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001708 tab + sizeof(className), className, fID,
1709 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001710 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001711 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001712 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001713 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001714 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001715 }
1716#endif
1717
1718private:
1719 const SkPoint* fPts;
1720 SkPath::Verb fVerb;
1721 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001722 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001723 // OPTIMIZATION:if intersections array is a pointer, the it could only
1724 // be allocated as needed instead of always initialized -- though maybe
1725 // the initialization is lightweight enough that it hardly matters
1726 mutable SkTDArray<SkPoint> fIntersections;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001727 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001728#if DEBUG_DUMP
1729 int fID;
1730#endif
1731};
1732
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001733struct Coincidence {
1734 Segment* fSegments[2];
1735 double fTs[2][2];
1736};
1737
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001738class Contour {
1739public:
1740 Contour() {
1741 reset();
1742#if DEBUG_DUMP
1743 fID = ++gContourID;
1744#endif
1745 }
1746
1747 bool operator<(const Contour& rh) const {
1748 return fBounds.fTop == rh.fBounds.fTop
1749 ? fBounds.fLeft < rh.fBounds.fLeft
1750 : fBounds.fTop < rh.fBounds.fTop;
1751 }
1752
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001753 void addCoincident(int index, Contour* other, int otherIndex,
1754 const Intersections& ts, bool swap) {
1755 Coincidence& coincidence = *fCoincidences.append();
1756 coincidence.fSegments[0] = &fSegments[index];
1757 coincidence.fSegments[1] = &other->fSegments[otherIndex];
1758 coincidence.fTs[swap][0] = ts.fT[0][0];
1759 coincidence.fTs[swap][1] = ts.fT[0][1];
1760 coincidence.fTs[!swap][0] = ts.fT[1][0];
1761 coincidence.fTs[!swap][1] = ts.fT[1][1];
1762 }
1763
1764 void addCross(const Contour* crosser) {
1765#ifdef DEBUG_CROSS
1766 for (int index = 0; index < fCrosses.count(); ++index) {
1767 SkASSERT(fCrosses[index] != crosser);
1768 }
1769#endif
1770 *fCrosses.append() = crosser;
1771 }
1772
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001773 void addCubic(const SkPoint pts[4]) {
1774 fSegments.push_back().addCubic(pts);
1775 fContainsCurves = true;
1776 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001777
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001778 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001779 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001780 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001781 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001782
1783 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
1784 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
1785 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001786
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001787 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001788 fSegments.push_back().addQuad(pts);
1789 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001790 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001791 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001792
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001793 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
1794 containsIntercepts();
1795 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
1796 }
1797
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001798 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001799 return fBounds;
1800 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001801
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001802 void complete() {
1803 setBounds();
1804 fContainsIntercepts = false;
1805 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001806
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001807 void containsIntercepts() {
1808 fContainsIntercepts = true;
1809 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001810
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001811 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
1812 int &tIndex, double& hitT) {
1813 int segmentCount = fSegments.count();
1814 const Segment* bestSegment = NULL;
1815 for (int test = 0; test < segmentCount; ++test) {
1816 Segment* testSegment = &fSegments[test];
1817 const SkRect& bounds = testSegment->bounds();
1818 if (bounds.fTop < bestY) {
1819 continue;
1820 }
1821 if (bounds.fTop > basePt.fY) {
1822 continue;
1823 }
1824 if (bounds.fLeft > basePt.fX) {
1825 continue;
1826 }
1827 if (bounds.fRight < basePt.fX) {
1828 continue;
1829 }
1830 double testHitT;
1831 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
1832 if (testT >= 0) {
1833 bestSegment = testSegment;
1834 tIndex = testT;
1835 hitT = testHitT;
1836 }
1837 }
1838 return bestSegment;
1839 }
1840
1841 bool crosses(const Contour* crosser) const {
1842 if (this == crosser) {
1843 return true;
1844 }
1845 for (int index = 0; index < fCrosses.count(); ++index) {
1846 if (fCrosses[index] == crosser) {
1847 return true;
1848 }
1849 }
1850 return false;
1851 }
1852
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001853 void findTooCloseToCall(int winding) {
1854 int segmentCount = fSegments.count();
1855 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1856 fSegments[sIndex].findTooCloseToCall(winding);
1857 }
1858 }
1859
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001860 void fixOtherTIndex() {
1861 int segmentCount = fSegments.count();
1862 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1863 fSegments[sIndex].fixOtherTIndex();
1864 }
1865 }
1866
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001867 void reset() {
1868 fSegments.reset();
1869 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001870 fContainsCurves = fContainsIntercepts = false;
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00001871 fWindingSum = SK_MinS32;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001872 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001873
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001874 void resolveCoincidence(int winding) {
1875 int count = fCoincidences.count();
1876 for (int index = 0; index < count; ++index) {
1877 Coincidence& coincidence = fCoincidences[index];
1878 Segment* thisOne = coincidence.fSegments[0];
1879 Segment* other = coincidence.fSegments[1];
1880 double startT = coincidence.fTs[0][0];
1881 double endT = coincidence.fTs[0][1];
1882 if (startT > endT) {
1883 SkTSwap<double>(startT, endT);
1884 }
1885 SkASSERT(endT - startT >= FLT_EPSILON);
1886 double oStartT = coincidence.fTs[1][0];
1887 double oEndT = coincidence.fTs[1][1];
1888 if (oStartT > oEndT) {
1889 SkTSwap<double>(oStartT, oEndT);
1890 }
1891 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
1892 if (winding > 0 || thisOne->cancels(*other)) {
1893 thisOne->addTCancel(startT, endT, *other, oStartT, oEndT);
1894 } else {
1895 thisOne->addTCoincident(startT, endT, *other, oStartT, oEndT);
1896 }
1897 }
1898 }
1899
1900 const SkTArray<Segment>& segments() {
1901 return fSegments;
1902 }
1903
1904 void setWinding(int winding) {
1905 SkASSERT(fWindingSum < 0);
1906 fWindingSum = winding;
1907 }
1908
caryclark@google.com15fa1382012-05-07 20:49:36 +00001909 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
1910 // we need to sort and walk edges in y, but that on the surface opens the
1911 // same can of worms as before. But then, this is a rough sort based on
1912 // segments' top, and not a true sort, so it could be ameniable to regular
1913 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001914 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001915 int segmentCount = fSegments.count();
1916 SkASSERT(segmentCount > 0);
1917 int best = -1;
1918 Segment* bestSegment = NULL;
1919 while (++best < segmentCount) {
1920 Segment* testSegment = &fSegments[best];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001921 #if 0 // FIXME: remove if not needed
1922 if (testSegment->virtuallyDone()) {
1923 continue;
1924 }
1925 #else
caryclark@google.com15fa1382012-05-07 20:49:36 +00001926 if (testSegment->done()) {
1927 continue;
1928 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001929 #endif
caryclark@google.com15fa1382012-05-07 20:49:36 +00001930 bestSegment = testSegment;
1931 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001932 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001933 if (!bestSegment) {
1934 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001935 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001936 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001937 for (int test = best + 1; test < segmentCount; ++test) {
1938 Segment* testSegment = &fSegments[test];
1939 if (testSegment->done()) {
1940 continue;
1941 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001942 if (testSegment->bounds().fTop > bestTop) {
1943 continue;
1944 }
1945 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001946 if (bestTop > testTop) {
1947 bestTop = testTop;
1948 bestSegment = testSegment;
1949 }
1950 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001951 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001952 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001953 }
1954
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001955 int updateSegment(int index, const SkPoint* pts) {
1956 Segment& segment = fSegments[index];
1957 segment.updatePts(pts);
1958 return segment.verb() + 1;
1959 }
1960
1961 int winding() {
1962 if (fWindingSum >= 0) {
1963 return fWindingSum;
1964 }
1965 // check peers
1966 int count = fCrosses.count();
1967 for (int index = 0; index < count; ++index) {
1968 const Contour* crosser = fCrosses[index];
1969 if (0 <= crosser->fWindingSum) {
1970 fWindingSum = crosser->fWindingSum;
1971 break;
1972 }
1973 }
1974 return fWindingSum;
1975 }
1976
1977#if DEBUG_TEST
1978 SkTArray<Segment>& debugSegments() {
1979 return fSegments;
1980 }
1981#endif
1982
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001983#if DEBUG_DUMP
1984 void dump() {
1985 int i;
1986 const char className[] = "Contour";
1987 const int tab = 4;
1988 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
1989 for (i = 0; i < fSegments.count(); ++i) {
1990 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
1991 className, i);
1992 fSegments[i].dump();
1993 }
1994 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
1995 tab + sizeof(className), className,
1996 fBounds.fLeft, fBounds.fTop,
1997 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001998 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1999 className, fContainsIntercepts);
2000 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2001 className, fContainsCurves);
2002 }
2003#endif
2004
2005protected:
2006 void setBounds() {
2007 int count = fSegments.count();
2008 if (count == 0) {
2009 SkDebugf("%s empty contour\n", __FUNCTION__);
2010 SkASSERT(0);
2011 // FIXME: delete empty contour?
2012 return;
2013 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002014 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002015 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002016 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002017 }
2018 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002019
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002020private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002021 SkTArray<Segment> fSegments;
2022 SkTDArray<Coincidence> fCoincidences;
2023 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002024 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002025 bool fContainsIntercepts;
2026 bool fContainsCurves;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002027 int fWindingSum; // initial winding number outside
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002028#if DEBUG_DUMP
2029 int fID;
2030#endif
2031};
2032
2033class EdgeBuilder {
2034public:
2035
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002036EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002037 : fPath(path)
2038 , fCurrentContour(NULL)
2039 , fContours(contours)
2040{
2041#if DEBUG_DUMP
2042 gContourID = 0;
2043 gSegmentID = 0;
2044#endif
2045 walk();
2046}
2047
2048protected:
2049
2050void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002051 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002052 fCurrentContour->complete();
2053 fCurrentContour = NULL;
2054 }
2055}
2056
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002057void walk() {
2058 // FIXME:remove once we can access path pts directly
2059 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2060 SkPoint pts[4];
2061 SkPath::Verb verb;
2062 do {
2063 verb = iter.next(pts);
2064 *fPathVerbs.append() = verb;
2065 if (verb == SkPath::kMove_Verb) {
2066 *fPathPts.append() = pts[0];
2067 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2068 fPathPts.append(verb, &pts[1]);
2069 }
2070 } while (verb != SkPath::kDone_Verb);
2071 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002072
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002073 SkPath::Verb reducedVerb;
2074 uint8_t* verbPtr = fPathVerbs.begin();
2075 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002076 const SkPoint* finalCurveStart = NULL;
2077 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002078 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2079 switch (verb) {
2080 case SkPath::kMove_Verb:
2081 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002082 if (!fCurrentContour) {
2083 fCurrentContour = fContours.push_back_n(1);
2084 finalCurveEnd = pointsPtr++;
2085 *fExtra.append() = -1; // start new contour
2086 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002087 continue;
2088 case SkPath::kLine_Verb:
2089 // skip degenerate points
2090 if (pointsPtr[-1].fX != pointsPtr[0].fX
2091 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2092 fCurrentContour->addLine(&pointsPtr[-1]);
2093 }
2094 break;
2095 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002096
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002097 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2098 if (reducedVerb == 0) {
2099 break; // skip degenerate points
2100 }
2101 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002102 *fExtra.append() =
2103 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002104 break;
2105 }
2106 fCurrentContour->addQuad(&pointsPtr[-1]);
2107 break;
2108 case SkPath::kCubic_Verb:
2109 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2110 if (reducedVerb == 0) {
2111 break; // skip degenerate points
2112 }
2113 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002114 *fExtra.append() =
2115 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002116 break;
2117 }
2118 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002119 *fExtra.append() =
2120 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002121 break;
2122 }
2123 fCurrentContour->addCubic(&pointsPtr[-1]);
2124 break;
2125 case SkPath::kClose_Verb:
2126 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002127 if (finalCurveStart && finalCurveEnd
2128 && *finalCurveStart != *finalCurveEnd) {
2129 *fReducePts.append() = *finalCurveStart;
2130 *fReducePts.append() = *finalCurveEnd;
2131 *fExtra.append() =
2132 fCurrentContour->addLine(fReducePts.end() - 2);
2133 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002134 complete();
2135 continue;
2136 default:
2137 SkDEBUGFAIL("bad verb");
2138 return;
2139 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002140 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002141 pointsPtr += verb;
2142 SkASSERT(fCurrentContour);
2143 }
2144 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002145 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002146 fContours.pop_back();
2147 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002148 // correct pointers in contours since fReducePts may have moved as it grew
2149 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002150 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002151 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002152 int eIndex = 0;
2153 int rIndex = 0;
2154 while (++eIndex < extraCount) {
2155 int offset = fExtra[eIndex];
2156 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002157 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002158 continue;
2159 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002160 fCurrentContour = &fContours[cIndex];
2161 rIndex += fCurrentContour->updateSegment(offset - 1,
2162 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002163 }
2164 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002165}
2166
2167private:
2168 const SkPath& fPath;
2169 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002170 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002171 Contour* fCurrentContour;
2172 SkTArray<Contour>& fContours;
2173 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002174 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002175};
2176
2177class Work {
2178public:
2179 enum SegmentType {
2180 kHorizontalLine_Segment = -1,
2181 kVerticalLine_Segment = 0,
2182 kLine_Segment = SkPath::kLine_Verb,
2183 kQuad_Segment = SkPath::kQuad_Verb,
2184 kCubic_Segment = SkPath::kCubic_Verb,
2185 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002186
2187 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2188 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2189 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002190
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002191 // FIXME: does it make sense to write otherIndex now if we're going to
2192 // fix it up later?
2193 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002194 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002195 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002196
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002197 // Avoid collapsing t values that are close to the same since
2198 // we walk ts to describe consecutive intersections. Since a pair of ts can
2199 // be nearly equal, any problems caused by this should be taken care
2200 // of later.
2201 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002202 int addT(double newT, const Work& other) {
2203 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002204 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002205
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002206 bool advance() {
2207 return ++fIndex < fLast;
2208 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002209
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002210 SkScalar bottom() const {
2211 return bounds().fBottom;
2212 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002213
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002214 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002215 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002216 }
2217
2218 const SkPoint* cubic() const {
2219 return fCubic;
2220 }
2221
2222 void init(Contour* contour) {
2223 fContour = contour;
2224 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002225 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002226 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002227
2228 bool isAdjacent(const Work& next) {
2229 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2230 }
2231
2232 bool isFirstLast(const Work& next) {
2233 return fContour == next.fContour && fIndex == 0
2234 && next.fIndex == fLast - 1;
2235 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002236
2237 SkScalar left() const {
2238 return bounds().fLeft;
2239 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002240
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002241 void promoteToCubic() {
2242 fCubic[0] = pts()[0];
2243 fCubic[2] = pts()[1];
2244 fCubic[3] = pts()[2];
2245 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
2246 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
2247 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
2248 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
2249 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002250
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002251 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002252 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002253 }
2254
2255 SkScalar right() const {
2256 return bounds().fRight;
2257 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002258
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002259 ptrdiff_t segmentIndex() const {
2260 return fIndex;
2261 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002262
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002263 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002264 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002265 SegmentType type = (SegmentType) segment.verb();
2266 if (type != kLine_Segment) {
2267 return type;
2268 }
2269 if (segment.isHorizontal()) {
2270 return kHorizontalLine_Segment;
2271 }
2272 if (segment.isVertical()) {
2273 return kVerticalLine_Segment;
2274 }
2275 return kLine_Segment;
2276 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002277
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002278 bool startAfter(const Work& after) {
2279 fIndex = after.fIndex;
2280 return advance();
2281 }
2282
2283 SkScalar top() const {
2284 return bounds().fTop;
2285 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002286
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002287 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002288 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002289 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002290
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002291 SkScalar x() const {
2292 return bounds().fLeft;
2293 }
2294
2295 bool xFlipped() const {
2296 return x() != pts()[0].fX;
2297 }
2298
2299 SkScalar y() const {
2300 return bounds().fTop;
2301 }
2302
2303 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002304 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002305 }
2306
2307protected:
2308 Contour* fContour;
2309 SkPoint fCubic[4];
2310 int fIndex;
2311 int fLast;
2312};
2313
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002314#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002315static void debugShowLineIntersection(int pts, const Work& wt,
2316 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002317 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002318 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
2319 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
2320 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
2321 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002322 return;
2323 }
2324 SkPoint wtOutPt, wnOutPt;
2325 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
2326 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002327 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002328 __FUNCTION__,
2329 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
2330 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
2331 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002332 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002333 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002334 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002335 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
2336 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
2337 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002338 SkDebugf(" wnTs[1]=%g", wnTs[1]);
2339 SkDebugf("\n");
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002340 }
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002341#else
2342static void debugShowLineIntersection(int , const Work& ,
2343 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002344}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002345#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002346
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002347static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002348
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002349 if (test != next) {
2350 if (test->bounds().fBottom < next->bounds().fTop) {
2351 return false;
2352 }
2353 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
2354 return true;
2355 }
2356 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002357 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002358 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002359 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002360 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002361 Work wn;
2362 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002363 if (test == next && !wn.startAfter(wt)) {
2364 continue;
2365 }
2366 do {
2367 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
2368 continue;
2369 }
2370 int pts;
2371 Intersections ts;
2372 bool swap = false;
2373 switch (wt.segmentType()) {
2374 case Work::kHorizontalLine_Segment:
2375 swap = true;
2376 switch (wn.segmentType()) {
2377 case Work::kHorizontalLine_Segment:
2378 case Work::kVerticalLine_Segment:
2379 case Work::kLine_Segment: {
2380 pts = HLineIntersect(wn.pts(), wt.left(),
2381 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002382 debugShowLineIntersection(pts, wt, wn,
2383 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002384 break;
2385 }
2386 case Work::kQuad_Segment: {
2387 pts = HQuadIntersect(wn.pts(), wt.left(),
2388 wt.right(), wt.y(), wt.xFlipped(), ts);
2389 break;
2390 }
2391 case Work::kCubic_Segment: {
2392 pts = HCubicIntersect(wn.pts(), wt.left(),
2393 wt.right(), wt.y(), wt.xFlipped(), ts);
2394 break;
2395 }
2396 default:
2397 SkASSERT(0);
2398 }
2399 break;
2400 case Work::kVerticalLine_Segment:
2401 swap = true;
2402 switch (wn.segmentType()) {
2403 case Work::kHorizontalLine_Segment:
2404 case Work::kVerticalLine_Segment:
2405 case Work::kLine_Segment: {
2406 pts = VLineIntersect(wn.pts(), wt.top(),
2407 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002408 debugShowLineIntersection(pts, wt, wn,
2409 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002410 break;
2411 }
2412 case Work::kQuad_Segment: {
2413 pts = VQuadIntersect(wn.pts(), wt.top(),
2414 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2415 break;
2416 }
2417 case Work::kCubic_Segment: {
2418 pts = VCubicIntersect(wn.pts(), wt.top(),
2419 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2420 break;
2421 }
2422 default:
2423 SkASSERT(0);
2424 }
2425 break;
2426 case Work::kLine_Segment:
2427 switch (wn.segmentType()) {
2428 case Work::kHorizontalLine_Segment:
2429 pts = HLineIntersect(wt.pts(), wn.left(),
2430 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002431 debugShowLineIntersection(pts, wt, wn,
2432 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002433 break;
2434 case Work::kVerticalLine_Segment:
2435 pts = VLineIntersect(wt.pts(), wn.top(),
2436 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002437 debugShowLineIntersection(pts, wt, wn,
2438 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002439 break;
2440 case Work::kLine_Segment: {
2441 pts = LineIntersect(wt.pts(), wn.pts(), ts);
2442 debugShowLineIntersection(pts, wt, wn,
2443 ts.fT[1], ts.fT[0]);
2444 break;
2445 }
2446 case Work::kQuad_Segment: {
2447 swap = true;
2448 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
2449 break;
2450 }
2451 case Work::kCubic_Segment: {
2452 swap = true;
2453 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
2454 break;
2455 }
2456 default:
2457 SkASSERT(0);
2458 }
2459 break;
2460 case Work::kQuad_Segment:
2461 switch (wn.segmentType()) {
2462 case Work::kHorizontalLine_Segment:
2463 pts = HQuadIntersect(wt.pts(), wn.left(),
2464 wn.right(), wn.y(), wn.xFlipped(), ts);
2465 break;
2466 case Work::kVerticalLine_Segment:
2467 pts = VQuadIntersect(wt.pts(), wn.top(),
2468 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2469 break;
2470 case Work::kLine_Segment: {
2471 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
2472 break;
2473 }
2474 case Work::kQuad_Segment: {
2475 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
2476 break;
2477 }
2478 case Work::kCubic_Segment: {
2479 wt.promoteToCubic();
2480 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
2481 break;
2482 }
2483 default:
2484 SkASSERT(0);
2485 }
2486 break;
2487 case Work::kCubic_Segment:
2488 switch (wn.segmentType()) {
2489 case Work::kHorizontalLine_Segment:
2490 pts = HCubicIntersect(wt.pts(), wn.left(),
2491 wn.right(), wn.y(), wn.xFlipped(), ts);
2492 break;
2493 case Work::kVerticalLine_Segment:
2494 pts = VCubicIntersect(wt.pts(), wn.top(),
2495 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2496 break;
2497 case Work::kLine_Segment: {
2498 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
2499 break;
2500 }
2501 case Work::kQuad_Segment: {
2502 wn.promoteToCubic();
2503 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
2504 break;
2505 }
2506 case Work::kCubic_Segment: {
2507 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
2508 break;
2509 }
2510 default:
2511 SkASSERT(0);
2512 }
2513 break;
2514 default:
2515 SkASSERT(0);
2516 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002517 if (!foundCommonContour && pts > 0) {
2518 test->addCross(next);
2519 next->addCross(test);
2520 foundCommonContour = true;
2521 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002522 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002523 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
2524 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002525 if (wt.isAdjacent(wn)) {
2526 int testEndTAt = wt.addT(1, wn);
2527 int nextEndTAt = wn.addT(0, wt);
2528 wt.addOtherT(testEndTAt, 0, nextEndTAt);
2529 wn.addOtherT(nextEndTAt, 1, testEndTAt);
2530 }
2531 if (wt.isFirstLast(wn)) {
2532 int testStartTAt = wt.addT(0, wn);
2533 int nextStartTAt = wn.addT(1, wt);
2534 wt.addOtherT(testStartTAt, 1, nextStartTAt);
2535 wn.addOtherT(nextStartTAt, 0, testStartTAt);
2536 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002537 wt.addCoincident(wn, ts, swap);
2538 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002539 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002540 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002541 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
2542 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002543 int testTAt = wt.addT(ts.fT[swap][pt], wn);
2544 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002545 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
2546 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002547 }
2548 } while (wn.advance());
2549 } while (wt.advance());
2550 return true;
2551}
2552
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002553// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002554// see if coincidence is formed by clipping non-concident segments
2555static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
2556 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00002557 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002558 Contour* contour = contourList[cIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002559 contour->resolveCoincidence(winding);
2560 }
2561 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
2562 Contour* contour = contourList[cIndex];
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002563 contour->findTooCloseToCall(winding);
2564 }
2565}
2566
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002567// project a ray from the top of the contour up and see if it hits anything
2568// note: when we compute line intersections, we keep track of whether
2569// two contours touch, so we need only look at contours not touching this one.
2570// OPTIMIZATION: sort contourList vertically to avoid linear walk
2571static int innerContourCheck(SkTDArray<Contour*>& contourList,
2572 Contour* baseContour, const SkPoint& basePt) {
2573 int contourCount = contourList.count();
2574 int winding = 0;
2575 SkScalar bestY = SK_ScalarMin;
2576 for (int cTest = 0; cTest < contourCount; ++cTest) {
2577 Contour* contour = contourList[cTest];
2578 if (basePt.fY < contour->bounds().fTop) {
2579 continue;
2580 }
2581 if (bestY > contour->bounds().fBottom) {
2582 continue;
2583 }
2584 if (baseContour->crosses(contour)) {
2585 continue;
2586 }
2587 int tIndex;
2588 double tHit;
2589 const Segment* test = contour->crossedSegment(basePt, bestY, tIndex,
2590 tHit);
2591 if (!test) {
2592 continue;
2593 }
2594 // If the ray hit the end of a span, we need to construct the wheel of
2595 // angles to find the span closest to the ray -- even if there are just
2596 // two spokes on the wheel.
2597 if (tHit == test->t(tIndex)) {
2598 SkTDArray<Angle> angles;
2599 int end = test->nextSpan(tIndex, 1);
2600 if (end < 0) {
2601 end = test->nextSpan(tIndex, -1);
2602 }
2603 test->addTwoAngles(tIndex, end, angles);
2604 // test->buildAnglesInner(tIndex, angles);
2605 test->buildAngles(tIndex, angles);
2606 SkTDArray<Angle*> sorted;
2607 sortAngles(angles, sorted);
2608 const Angle* angle = sorted[0];
2609 test = angle->segment();
2610 SkScalar testDx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2611 if (testDx == 0) {
2612 angle = *(sorted.end() - 1);
2613 test = angle->segment();
2614 SkASSERT((*SegmentDXAtT[test->verb()])(test->pts(), tHit) != 0);
2615 }
2616 tIndex = angle->start(); // lesser Y
2617 winding = test->winding(SkMin32(tIndex, angle->end()));
2618 #if DEBUG_WINDING
2619 SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
2620 #endif
2621 } else {
2622 winding = test->winding(tIndex);
2623 #if DEBUG_WINDING
2624 SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
2625 #endif
2626 }
2627 // see if a + change in T results in a +/- change in X (compute x'(T))
2628 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2629 #if DEBUG_WINDING
2630 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
2631 #endif
2632 SkASSERT(dx != 0);
2633 if (winding * dx > 0) { // if same signs, result is negative
2634 winding += dx > 0 ? -1 : 1;
2635 #if DEBUG_WINDING
2636 SkDebugf("%s 3 winding=%d\n", __FUNCTION__, winding);
2637 #endif
2638 }
2639 }
2640 baseContour->setWinding(winding);
2641 return winding;
2642}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002643
2644// OPTIMIZATION: not crazy about linear search here to find top active y.
2645// seems like we should break down and do the sort, or maybe sort each
2646// contours' segments?
2647// Once the segment array is built, there's no reason I can think of not to
2648// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002649// FIXME: return the contour found to pass to inner contour check
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002650static Segment* findTopContour(SkTDArray<Contour*>& contourList,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002651 Contour*& topContour) {
2652 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002653 int cIndex = 0;
2654 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002655 SkScalar bestY = SK_ScalarMax;
2656 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002657 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002658 contour = contourList[cIndex];
2659 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002660 } while (!topStart && ++cIndex < contourCount);
2661 if (!topStart) {
2662 return NULL;
2663 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002664 topContour = contour;
2665 while (++cIndex < contourCount) {
2666 contour = contourList[cIndex];
2667 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002668 continue;
2669 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002670 SkScalar testY = SK_ScalarMax;
2671 Segment* test = contour->topSegment(testY);
2672 if (!test || bestY <= testY) {
2673 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002674 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002675 topContour = contour;
2676 topStart = test;
2677 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002678 }
2679 return topStart;
2680}
2681
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002682// Each segment may have an inside or an outside. Segments contained within
2683// winding may have insides on either side, and form a contour that should be
2684// ignored. Segments that are coincident with opposing direction segments may
2685// have outsides on either side, and should also disappear.
2686// 'Normal' segments will have one inside and one outside. Subsequent connections
2687// when winding should follow the intersection direction. If more than one edge
2688// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002689 // since we start with leftmost top edge, we'll traverse through a
2690 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002691static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002692 // after findTopContour has already been called once, check if
2693 // result of subsequent findTopContour has no winding set
2694 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002695 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002696 Contour* topContour;
2697 Segment* topStart = findTopContour(contourList, topContour);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002698 if (!topStart) {
2699 break;
2700 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002701 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00002702 // follow edges to intersection by changing the index by direction.
2703 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002704 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002705 int winding = 0;
2706 if (!firstContour) {
2707 int contourWinding = topContour->winding();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002708 #if DEBUG_WINDING
2709 SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
2710 #endif
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002711 if (contourWinding == SK_MinS32) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002712 const SkPoint& topPoint = current->xyAtT(endIndex);
2713 winding = innerContourCheck(contourList, topContour, topPoint);
2714 #if DEBUG_WINDING
2715 SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
2716 #endif
2717 }
2718 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002719 const SkPoint* firstPt = NULL;
2720 SkPoint lastPt;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002721 bool firstTime = true;
2722 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002723 if (firstContour) {
2724 topContour->setWinding(spanWinding);
2725 firstContour = false;
2726 }
2727 bool active = winding * spanWinding <= 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002728 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002729 SkASSERT(!current->done());
2730 int nextStart, nextEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002731 Segment* next = current->findNext(winding + spanWinding, index,
2732 endIndex, nextStart, nextEnd, firstTime);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002733 if (!next) {
2734 break;
2735 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002736 if (!firstPt) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002737 firstPt = &current->addMoveTo(index, simple, active);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002738 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002739 lastPt = current->addCurveTo(index, endIndex, simple, active);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002740 current = next;
2741 index = nextStart;
2742 endIndex = nextEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002743 spanWinding = SkSign32(spanWinding) * next->windValue(
2744 SkMin32(nextStart, nextEnd));
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002745 #if DEBUG_WINDING
2746 SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding);
2747 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002748 firstTime = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002749 } while (*firstPt != lastPt);
2750 if (firstPt) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002751 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com495f8e42012-05-31 13:13:11 +00002752 SkDebugf("%s close\n", __FUNCTION__);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002753 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00002754 simple.close();
2755 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002756 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002757}
2758
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002759static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
2760 int contourCount = contourList.count();
2761 for (int cTest = 0; cTest < contourCount; ++cTest) {
2762 Contour* contour = contourList[cTest];
2763 contour->fixOtherTIndex();
2764 }
2765}
2766
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002767static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002768 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002769 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002770 if (count == 0) {
2771 return;
2772 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002773 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002774 *list.append() = &contours[index];
2775 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002776 QSort<Contour>(list.begin(), list.end() - 1);
2777}
2778
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002779void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002780 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002781 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002782 simple.reset();
2783 simple.setFillType(SkPath::kEvenOdd_FillType);
2784
2785 // turn path into list of segments
2786 SkTArray<Contour> contours;
2787 // FIXME: add self-intersecting cubics' T values to segment
2788 EdgeBuilder builder(path, contours);
2789 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002790 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002791 Contour** currentPtr = contourList.begin();
2792 if (!currentPtr) {
2793 return;
2794 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002795 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002796 // find all intersections between segments
2797 do {
2798 Contour** nextPtr = currentPtr;
2799 Contour* current = *currentPtr++;
2800 Contour* next;
2801 do {
2802 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002803 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002804 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002805 // eat through coincident edges
2806 coincidenceCheck(contourList, winding);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002807 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002808 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002809 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002810}
2811