blob: 3c626c84b8f851c5bdb46cdbfba95e055d1cc680 [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
caryclark@google.com47580692012-07-23 12:14:49 +000020// An Edge is a Segment generated from a Span
caryclark@google.com15fa1382012-05-07 20:49:36 +000021
caryclark@google.comfa0588f2012-04-26 21:01:06 +000022// FIXME: remove once debugging is complete
caryclark@google.com47580692012-07-23 12:14:49 +000023#ifdef SK_DEBUG
24int gDebugMaxWindSum = SK_MaxS32;
25int gDebugMaxWindValue = SK_MaxS32;
26#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +000027
caryclark@google.com3350c3c2012-08-24 15:24:36 +000028#define HIGH_DEF_ANGLES 1
29
30#if HIGH_DEF_ANGLES
31typedef double AngleValue;
32#else
33typedef SkScalar AngleValue;
34#endif
35
caryclark@google.com47580692012-07-23 12:14:49 +000036#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037
caryclark@google.coma7e483d2012-08-28 20:44:43 +000038#if 0 // set to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000039
40const bool gRunTestsInOneThread = false;
41
42#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000043#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000045#define DEBUG_ANGLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000046#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000047#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000048#define DEBUG_DUMP 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000049#define DEBUG_MARK_DONE 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_PATH_CONSTRUCTION 0
51#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000052#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000053#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000054
55#else
56
caryclark@google.com47580692012-07-23 12:14:49 +000057const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000058
caryclark@google.comafe56de2012-07-24 18:11:03 +000059#define DEBUG_ACTIVE_SPANS 1
caryclark@google.comc899ad92012-08-23 15:24:42 +000060#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com24bec792012-08-20 12:43:57 +000061#define DEBUG_ADD_T_PAIR 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000062#define DEBUG_ANGLE 1
63#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000064#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000065#define DEBUG_DUMP 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000066#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000067#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000068#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000069#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000070#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000071
72#endif
73
caryclark@google.com534aa5b2012-08-02 20:08:21 +000074#if (DEBUG_ACTIVE_SPANS || DEBUG_CONCIDENT || DEBUG_SORT) && !DEBUG_DUMP
caryclark@google.com027de222012-07-12 12:52:50 +000075#undef DEBUG_DUMP
76#define DEBUG_DUMP 1
77#endif
78
caryclark@google.comfa0588f2012-04-26 21:01:06 +000079#if DEBUG_DUMP
80static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000081// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000082static int gContourID;
83static int gSegmentID;
84#endif
85
caryclark@google.com8dcf1142012-07-02 20:27:02 +000086#ifndef DEBUG_TEST
87#define DEBUG_TEST 0
88#endif
89
caryclark@google.comfa0588f2012-04-26 21:01:06 +000090static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
91 Intersections& intersections) {
92 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
93 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
94 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
95}
96
97static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
98 Intersections& intersections) {
99 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
100 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000101 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000102}
103
104static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
105 Intersections& intersections) {
106 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
107 {a[3].fX, a[3].fY}};
108 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
109 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
110}
111
112static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
113 Intersections& intersections) {
114 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
115 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
116 intersect(aQuad, bQuad, intersections);
117 return intersections.fUsed;
118}
119
120static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
121 Intersections& intersections) {
122 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
123 {a[3].fX, a[3].fY}};
124 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
125 {b[3].fX, b[3].fY}};
126 intersect(aCubic, bCubic, intersections);
127 return intersections.fUsed;
128}
129
130static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
131 SkScalar y, bool flipped, Intersections& intersections) {
132 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
133 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
134}
135
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000136static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
137 SkScalar y, bool flipped, Intersections& intersections) {
138 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
139 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
140}
141
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
143 SkScalar y, bool flipped, Intersections& intersections) {
144 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
145 {a[3].fX, a[3].fY}};
146 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
147}
148
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000149static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
150 SkScalar x, bool flipped, Intersections& intersections) {
151 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
152 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
153}
154
155static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
156 SkScalar x, bool flipped, Intersections& intersections) {
157 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
158 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
159}
160
161static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
162 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000163 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
164 {a[3].fX, a[3].fY}};
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000165 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000166}
167
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000168static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
169 SkScalar , SkScalar , bool , Intersections& ) = {
170 NULL,
171 VLineIntersect,
172 VQuadIntersect,
173 VCubicIntersect
174};
175
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000176static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
177 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
178 double x, y;
179 xy_at_t(line, t, x, y);
180 out->fX = SkDoubleToScalar(x);
181 out->fY = SkDoubleToScalar(y);
182}
183
184static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
185 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
186 double x, y;
187 xy_at_t(quad, t, x, y);
188 out->fX = SkDoubleToScalar(x);
189 out->fY = SkDoubleToScalar(y);
190}
191
192static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
193 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
194 {a[3].fX, a[3].fY}};
195 double x, y;
196 xy_at_t(cubic, t, x, y);
197 out->fX = SkDoubleToScalar(x);
198 out->fY = SkDoubleToScalar(y);
199}
200
201static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
202 NULL,
203 LineXYAtT,
204 QuadXYAtT,
205 CubicXYAtT
206};
207
208static SkScalar LineXAtT(const SkPoint a[2], double t) {
209 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
210 double x;
211 xy_at_t(aLine, t, x, *(double*) 0);
212 return SkDoubleToScalar(x);
213}
214
215static SkScalar QuadXAtT(const SkPoint a[3], double t) {
216 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
217 double x;
218 xy_at_t(quad, t, x, *(double*) 0);
219 return SkDoubleToScalar(x);
220}
221
222static SkScalar CubicXAtT(const SkPoint a[4], double t) {
223 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
224 {a[3].fX, a[3].fY}};
225 double x;
226 xy_at_t(cubic, t, x, *(double*) 0);
227 return SkDoubleToScalar(x);
228}
229
230static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
231 NULL,
232 LineXAtT,
233 QuadXAtT,
234 CubicXAtT
235};
236
237static SkScalar LineYAtT(const SkPoint a[2], double t) {
238 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
239 double y;
240 xy_at_t(aLine, t, *(double*) 0, y);
241 return SkDoubleToScalar(y);
242}
243
244static SkScalar QuadYAtT(const SkPoint a[3], double t) {
245 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
246 double y;
247 xy_at_t(quad, t, *(double*) 0, y);
248 return SkDoubleToScalar(y);
249}
250
251static SkScalar CubicYAtT(const SkPoint a[4], double t) {
252 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
253 {a[3].fX, a[3].fY}};
254 double y;
255 xy_at_t(cubic, t, *(double*) 0, y);
256 return SkDoubleToScalar(y);
257}
258
259static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
260 NULL,
261 LineYAtT,
262 QuadYAtT,
263 CubicYAtT
264};
265
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000266static SkScalar LineDXAtT(const SkPoint a[2], double ) {
267 return a[1].fX - a[0].fX;
268}
269
270static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
271 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
272 double x;
273 dxdy_at_t(quad, t, x, *(double*) 0);
274 return SkDoubleToScalar(x);
275}
276
277static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
278 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
279 {a[3].fX, a[3].fY}};
280 double x;
281 dxdy_at_t(cubic, t, x, *(double*) 0);
282 return SkDoubleToScalar(x);
283}
284
285static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
286 NULL,
287 LineDXAtT,
288 QuadDXAtT,
289 CubicDXAtT
290};
291
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000292static void LineSubDivide(const SkPoint a[2], double startT, double endT,
293 SkPoint sub[2]) {
294 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
295 _Line dst;
296 sub_divide(aLine, startT, endT, dst);
297 sub[0].fX = SkDoubleToScalar(dst[0].x);
298 sub[0].fY = SkDoubleToScalar(dst[0].y);
299 sub[1].fX = SkDoubleToScalar(dst[1].x);
300 sub[1].fY = SkDoubleToScalar(dst[1].y);
301}
302
303static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
304 SkPoint sub[3]) {
305 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
306 {a[2].fX, a[2].fY}};
307 Quadratic dst;
308 sub_divide(aQuad, startT, endT, dst);
309 sub[0].fX = SkDoubleToScalar(dst[0].x);
310 sub[0].fY = SkDoubleToScalar(dst[0].y);
311 sub[1].fX = SkDoubleToScalar(dst[1].x);
312 sub[1].fY = SkDoubleToScalar(dst[1].y);
313 sub[2].fX = SkDoubleToScalar(dst[2].x);
314 sub[2].fY = SkDoubleToScalar(dst[2].y);
315}
316
317static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
318 SkPoint sub[4]) {
319 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
320 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
321 Cubic dst;
322 sub_divide(aCubic, startT, endT, dst);
323 sub[0].fX = SkDoubleToScalar(dst[0].x);
324 sub[0].fY = SkDoubleToScalar(dst[0].y);
325 sub[1].fX = SkDoubleToScalar(dst[1].x);
326 sub[1].fY = SkDoubleToScalar(dst[1].y);
327 sub[2].fX = SkDoubleToScalar(dst[2].x);
328 sub[2].fY = SkDoubleToScalar(dst[2].y);
329 sub[3].fX = SkDoubleToScalar(dst[3].x);
330 sub[3].fY = SkDoubleToScalar(dst[3].y);
331}
332
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000333static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
334 SkPoint []) = {
335 NULL,
336 LineSubDivide,
337 QuadSubDivide,
338 CubicSubDivide
339};
340
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000341static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
342 _Point sub[]) {
343 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
344 _Line dst;
345 sub_divide(aLine, startT, endT, dst);
346 sub[0] = dst[0];
347 sub[1] = dst[1];
348}
349
350static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
351 _Point sub[]) {
352 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
353 {a[2].fX, a[2].fY}};
354 Quadratic dst;
355 sub_divide(aQuad, startT, endT, dst);
356 sub[0] = dst[0];
357 sub[1] = dst[1];
358 sub[2] = dst[2];
359}
360
361static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
362 _Point sub[]) {
363 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
364 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
365 Cubic dst;
366 sub_divide(aCubic, startT, endT, dst);
367 sub[0] = dst[0];
368 sub[1] = dst[1];
369 sub[2] = dst[2];
370 sub[3] = dst[3];
371}
372
373static void (* const SegmentSubDivideHD[])(const SkPoint [], double , double ,
374 _Point [] ) = {
375 NULL,
376 LineSubDivideHD,
377 QuadSubDivideHD,
378 CubicSubDivideHD
379};
380
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000381#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000382static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
383 SkRect& bounds) {
384 SkPoint dst[3];
385 QuadSubDivide(a, startT, endT, dst);
386 bounds.fLeft = bounds.fRight = dst[0].fX;
387 bounds.fTop = bounds.fBottom = dst[0].fY;
388 for (int index = 1; index < 3; ++index) {
389 bounds.growToInclude(dst[index].fX, dst[index].fY);
390 }
391}
392
393static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
394 SkRect& bounds) {
395 SkPoint dst[4];
396 CubicSubDivide(a, startT, endT, dst);
397 bounds.fLeft = bounds.fRight = dst[0].fX;
398 bounds.fTop = bounds.fBottom = dst[0].fY;
399 for (int index = 1; index < 4; ++index) {
400 bounds.growToInclude(dst[index].fX, dst[index].fY);
401 }
402}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000403#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000404
caryclark@google.com15fa1382012-05-07 20:49:36 +0000405static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000406 SkTDArray<SkPoint>& reducePts) {
407 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
408 {a[2].fX, a[2].fY}};
409 Quadratic dst;
410 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000411 if (order == 2) { // quad became line
412 for (int index = 0; index < order; ++index) {
413 SkPoint* pt = reducePts.append();
414 pt->fX = SkDoubleToScalar(dst[index].x);
415 pt->fY = SkDoubleToScalar(dst[index].y);
416 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000417 }
418 return (SkPath::Verb) (order - 1);
419}
420
421static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
422 SkTDArray<SkPoint>& reducePts) {
423 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
424 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
425 Cubic dst;
426 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000427 if (order == 2 || order == 3) { // cubic became line or quad
428 for (int index = 0; index < order; ++index) {
429 SkPoint* pt = reducePts.append();
430 pt->fX = SkDoubleToScalar(dst[index].x);
431 pt->fY = SkDoubleToScalar(dst[index].y);
432 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000433 }
434 return (SkPath::Verb) (order - 1);
435}
436
caryclark@google.com15fa1382012-05-07 20:49:36 +0000437static bool QuadIsLinear(const SkPoint a[3]) {
438 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
439 {a[2].fX, a[2].fY}};
440 return isLinear(aQuad, 0, 2);
441}
442
443static bool CubicIsLinear(const SkPoint a[4]) {
444 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
445 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
446 return isLinear(aCubic, 0, 3);
447}
448
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000449static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
450 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
451 double x[2];
452 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000453 xy_at_t(aLine, endT, x[1], *(double*) 0);
454 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000455}
456
457static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
458 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
459 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000460 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000461}
462
463static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
464 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
465 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000466 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000467}
468
469static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
470 NULL,
471 LineLeftMost,
472 QuadLeftMost,
473 CubicLeftMost
474};
475
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000476#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000477static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
478 const SkPoint& below) {
479 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
480 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
481 return implicit_matches_ulps(aLine, bLine, 32);
482}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000483#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000484
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000485class Segment;
486
caryclark@google.com15fa1382012-05-07 20:49:36 +0000487// sorting angles
488// given angles of {dx dy ddx ddy dddx dddy} sort them
489class Angle {
490public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000491 // FIXME: this is bogus for quads and cubics
492 // if the quads and cubics' line from end pt to ctrl pt are coincident,
493 // there's no obvious way to determine the curve ordering from the
494 // derivatives alone. In particular, if one quadratic's coincident tangent
495 // is longer than the other curve, the final control point can place the
496 // longer curve on either side of the shorter one.
497 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
498 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000499 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000500 if ((fDy < 0) ^ (rh.fDy < 0)) {
501 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000502 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000503 if (fDy == 0 && rh.fDy == 0 && fDx * rh.fDx < 0) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000504 return fDx < rh.fDx;
505 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000506 AngleValue cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000507 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000508 return cmp < 0;
509 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000510 AngleValue dy = approximately_pin(fDy + fDDy);
511 AngleValue rdy = approximately_pin(rh.fDy + rh.fDDy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000512 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000513 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000514 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000515 AngleValue dx = approximately_pin(fDx + fDDx);
516 AngleValue rdx = approximately_pin(rh.fDx + rh.fDDx);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000517 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000518 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000519 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000520 cmp = dx * rdy - rdx * dy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000521 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000522 return cmp < 0;
523 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000524 dy = approximately_pin(dy + fDDDy);
525 rdy = approximately_pin(rdy + rh.fDDDy);
526 if (dy * rdy < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000527 return dy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000528 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000529 dx = approximately_pin(dx + fDDDx);
530 rdx = approximately_pin(rdx + rh.fDDDx);
531 if (dy == 0 && rdy == 0 && dx * rdx < 0) {
caryclark@google.com03f97062012-08-21 13:13:52 +0000532 return dx < rdx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000533 }
caryclark@google.com03f97062012-08-21 13:13:52 +0000534 return dx * rdy < rdx * dy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000535 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000536
caryclark@google.com47580692012-07-23 12:14:49 +0000537 double dx() const {
538 return fDx;
539 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000540
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000541 double dy() const {
542 return fDy;
543 }
544
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000545 int end() const {
546 return fEnd;
547 }
548
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000549 bool isHorizontal() const {
550 return fDy == 0 && fDDy == 0 && fDDDy == 0;
551 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000552
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000553 // high precision version
554#if HIGH_DEF_ANGLES
555 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
556 int start, int end, double startT, double endT) {
557 Cubic pts;
558 (*SegmentSubDivideHD[verb])(orig, startT, endT, pts);
559 fSegment = segment;
560 fStart = start;
561 fEnd = end;
562 fDx = approximately_pin(pts[1].x - pts[0].x); // b - a
563 fDy = approximately_pin(pts[1].y - pts[0].y);
564 if (verb == SkPath::kLine_Verb) {
565 fDDx = fDDy = fDDDx = fDDDy = 0;
566 return;
567 }
568 fDDx = approximately_pin(pts[2].x - pts[1].x - fDx); // a - 2b + c
569 fDDy = approximately_pin(pts[2].y - pts[1].y - fDy);
570 if (verb == SkPath::kQuad_Verb) {
571 fDDDx = fDDDy = 0;
572 return;
573 }
574 fDDDx = approximately_pin(pts[3].x + 3 * (pts[1].x - pts[2].x) - pts[0].x);
575 fDDDy = approximately_pin(pts[3].y + 3 * (pts[1].y - pts[2].y) - pts[0].y);
576 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000577
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000578#else
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000579 // since all angles share a point, this needs to know which point
580 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
581 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000582 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000583 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000584 SkASSERT(start != end);
585 fSegment = segment;
586 fStart = start;
587 fEnd = end;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000588 fDx = approximately_pin(pts[1].fX - pts[0].fX); // b - a
589 fDy = approximately_pin(pts[1].fY - pts[0].fY);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000590 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000591 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000592 return;
593 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000594 fDDx = approximately_pin(pts[2].fX - pts[1].fX - fDx); // a - 2b + c
595 fDDy = approximately_pin(pts[2].fY - pts[1].fY - fDy);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000596 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000597 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000598 return;
599 }
caryclark@google.comc899ad92012-08-23 15:24:42 +0000600 fDDDx = approximately_pin(pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX);
601 fDDDy = approximately_pin(pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000602 }
603
604 // noncoincident quads/cubics may have the same initial angle
605 // as lines, so must sort by derivatives as well
606 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000607 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000608 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000609 fSegment = segment;
610 fStart = start;
611 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000612 fDx = pts[1].fX - pts[0].fX; // b - a
613 fDy = pts[1].fY - pts[0].fY;
614 if (verb == SkPath::kLine_Verb) {
615 fDDx = fDDy = fDDDx = fDDDy = 0;
616 return;
617 }
618 if (verb == SkPath::kQuad_Verb) {
619 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
620 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
621 int larger = std::max(abs(uplsX), abs(uplsY));
622 int shift = 0;
623 double flatT;
624 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
625 LineParameters implicitLine;
626 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
627 implicitLine.lineEndPoints(tangent);
628 implicitLine.normalize();
629 while (larger > UlpsEpsilon * 1024) {
630 larger >>= 2;
631 ++shift;
632 flatT = 0.5 / (1 << shift);
633 QuadXYAtT(pts, flatT, &ddPt);
634 _Point _pt = {ddPt.fX, ddPt.fY};
635 double distance = implicitLine.pointDistance(_pt);
636 if (approximately_zero(distance)) {
637 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
638 break;
639 }
640 }
641 flatT = 0.5 / (1 << shift);
642 QuadXYAtT(pts, flatT, &ddPt);
643 fDDx = ddPt.fX - pts[0].fX;
644 fDDy = ddPt.fY - pts[0].fY;
645 SkASSERT(fDDx != 0 || fDDy != 0);
646 fDDDx = fDDDy = 0;
647 return;
648 }
649 SkASSERT(0); // FIXME: add cubic case
650 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000651#endif
rmistry@google.comd6176b02012-08-23 18:14:13 +0000652
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000653 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000654 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000655 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000656
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000657 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000658 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000659 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000660
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000661 int start() const {
662 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000663 }
664
caryclark@google.comc899ad92012-08-23 15:24:42 +0000665#if DEBUG_ANGLE
666 void debugShow(const SkPoint& a) const {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000667 SkDebugf(" d=(%1.9g,%1.9g) dd=(%1.9g,%1.9g) ddd=(%1.9g,%1.9g)\n",
caryclark@google.comc899ad92012-08-23 15:24:42 +0000668 fDx, fDy, fDDx, fDDy, fDDDx, fDDDy);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000669 AngleValue ax = (AngleValue) a.fX;
670 AngleValue ay = (AngleValue) a.fY;
671 AngleValue bx, by, cx, cy, dx, dy;
672 bx = ax + fDx; // add b - a
673 by = ay + fDy;
674 cx = ax + 2 * fDx + fDDx; // add a + 2(b - a) to a - 2b + c
675 cy = ay + 2 * fDy + fDDy;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000676 if (fDDDx == 0 && fDDDy == 0) {
677 if (fDDx == 0 && fDDy == 0) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000678 SkDebugf(
679" {SkPath::kLine_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g} }},\n",
680 ax, ay, bx, by);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000681 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000682 SkDebugf(
683" {SkPath::kQuad_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
684 ax, ay, bx, by, cx, cy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000685 }
686 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000687 dx = fDDDx - ax - 3 * (cx - bx);
688 dy = fDDDy - ay - 3 * (cy - by);
689 SkDebugf(
690" {SkPath::kCubic_Verb, {{%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}, {%1.9g, %1.9g}}},\n",
691 ax, ay, bx, by, cx, cy, dx, dy);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000692 }
693 }
694#endif
695
caryclark@google.com15fa1382012-05-07 20:49:36 +0000696private:
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000697 AngleValue fDx;
698 AngleValue fDy;
699 AngleValue fDDx;
700 AngleValue fDDy;
701 AngleValue fDDDx;
702 AngleValue fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000703 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000704 int fStart;
705 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000706};
707
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000708static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
709 int angleCount = angles.count();
710 int angleIndex;
711 angleList.setReserve(angleCount);
712 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
713 *angleList.append() = &angles[angleIndex];
714 }
715 QSort<Angle>(angleList.begin(), angleList.end() - 1);
716}
717
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000718// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000719struct Bounds : public SkRect {
720 static bool Intersects(const Bounds& a, const Bounds& b) {
721 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
722 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
723 }
724
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000725 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
726 if (left < fLeft) {
727 fLeft = left;
728 }
729 if (top < fTop) {
730 fTop = top;
731 }
732 if (right > fRight) {
733 fRight = right;
734 }
735 if (bottom > fBottom) {
736 fBottom = bottom;
737 }
738 }
739
740 void add(const Bounds& toAdd) {
741 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
742 }
743
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000744 bool isEmpty() {
745 return fLeft > fRight || fTop > fBottom
746 || fLeft == fRight && fTop == fBottom
747 || isnan(fLeft) || isnan(fRight)
748 || isnan(fTop) || isnan(fBottom);
749 }
750
751 void setCubicBounds(const SkPoint a[4]) {
752 _Rect dRect;
753 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
754 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
755 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000756 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
757 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000758 }
759
760 void setQuadBounds(const SkPoint a[3]) {
761 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
762 {a[2].fX, a[2].fY}};
763 _Rect dRect;
764 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000765 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
766 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000767 }
768};
769
caryclark@google.com2ddff932012-08-07 21:25:27 +0000770static bool useInnerWinding(int outerWinding, int innerWinding) {
771 SkASSERT(outerWinding != innerWinding);
772 int absOut = abs(outerWinding);
773 int absIn = abs(innerWinding);
774 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
775 if (outerWinding * innerWinding < 0) {
776#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000777 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000778 outerWinding, innerWinding, result ? "true" : "false");
779#endif
780 }
781 return result;
782}
783
caryclark@google.com15fa1382012-05-07 20:49:36 +0000784struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000785 Segment* fOther;
caryclark@google.com27c449a2012-07-27 18:26:38 +0000786 mutable SkPoint fPt; // lazily computed as needed
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000787 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000788 double fOtherT; // value at fOther[fOtherIndex].fT
789 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000790 int fWindSum; // accumulated from contours surrounding this one
791 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000792 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000793};
794
795class Segment {
796public:
797 Segment() {
798#if DEBUG_DUMP
799 fID = ++gSegmentID;
800#endif
801 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000802
caryclark@google.com9764cc62012-07-12 19:29:45 +0000803 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
804 if (activeAngleInner(index, done, angles)) {
805 return true;
806 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000807 double referenceT = fTs[index].fT;
808 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000809 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000810 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000811 return true;
812 }
813 }
814 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000815 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000816 return true;
817 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000818 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000819 return false;
820 }
821
caryclark@google.com9764cc62012-07-12 19:29:45 +0000822 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000823 Span* span = &fTs[index];
824 Segment* other = span->fOther;
825 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +0000826 return other->activeAngleInner(oIndex, done, angles);
827 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000828
caryclark@google.com9764cc62012-07-12 19:29:45 +0000829 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
830 int next = nextSpan(index, 1);
831 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000832 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000833 if (upSpan.fWindValue) {
834 addAngle(angles, index, next);
835 if (upSpan.fDone) {
836 done++;
837 } else if (upSpan.fWindSum != SK_MinS32) {
838 return true;
839 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000840 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000841 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000842 int prev = nextSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000843 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +0000844 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +0000845 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +0000846 if (downSpan.fWindValue) {
847 addAngle(angles, index, prev);
848 if (downSpan.fDone) {
849 done++;
850 } else if (downSpan.fWindSum != SK_MinS32) {
851 return true;
852 }
caryclark@google.com9764cc62012-07-12 19:29:45 +0000853 }
854 }
855 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000856 }
857
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000858 SkScalar activeTop() const {
859 SkASSERT(!done());
860 int count = fTs.count();
861 SkScalar result = SK_ScalarMax;
862 bool lastDone = true;
863 for (int index = 0; index < count; ++index) {
864 bool done = fTs[index].fDone;
865 if (!done || !lastDone) {
866 SkScalar y = yAtT(index);
867 if (result > y) {
868 result = y;
869 }
870 }
871 lastDone = done;
872 }
873 SkASSERT(result < SK_ScalarMax);
874 return result;
875 }
876
877 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000878 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000879 Angle* angle = angles.append();
880#if HIGH_DEF_ANGLES==0 // old way
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000881 SkPoint edge[4];
882 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000883 angle->set(edge, fVerb, this, start, end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000884#else // new way : compute temp edge in higher precision
885 angle->set(fPts, fVerb, this, start, end, fTs[start].fT, fTs[end].fT);
886#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000887 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000888
caryclark@google.com2ddff932012-08-07 21:25:27 +0000889 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +0000890 double oEnd) {
891 int tIndex = -1;
892 int tCount = fTs.count();
893 int oIndex = -1;
894 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +0000895 do {
896 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000897 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000898 int tIndexStart = tIndex;
899 do {
900 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000901 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +0000902 int oIndexStart = oIndex;
903 double nextT;
904 do {
905 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000906 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000907 double oNextT;
908 do {
909 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000910 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000911 // at this point, spans before and after are at:
912 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
913 // if tIndexStart == 0, no prior span
914 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +0000915
caryclark@google.comcc905052012-07-25 20:59:42 +0000916 // advance the span with zero winding
917 // if the following span exists (not past the end, non-zero winding)
918 // connect the two edges
919 if (!fTs[tIndexStart].fWindValue) {
920 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
921 #if DEBUG_CONCIDENT
922 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
923 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000924 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
925 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +0000926 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000927 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000928 }
929 if (nextT < 1 && fTs[tIndex].fWindValue) {
930 #if DEBUG_CONCIDENT
931 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
932 __FUNCTION__, fID, other.fID, tIndex,
933 fTs[tIndex].fT, xyAtT(tIndex).fX,
934 xyAtT(tIndex).fY);
935 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +0000936 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000937 }
938 } else {
939 SkASSERT(!other.fTs[oIndexStart].fWindValue);
940 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
941 #if DEBUG_CONCIDENT
942 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
943 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +0000944 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
945 other.xyAtT(oIndexStart).fY);
946 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +0000947 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +0000948 }
949 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
950 #if DEBUG_CONCIDENT
951 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
952 __FUNCTION__, fID, other.fID, oIndex,
953 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
954 other.xyAtT(oIndex).fY);
955 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
956 #endif
957 }
958 }
959 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000960
caryclark@google.comcc905052012-07-25 20:59:42 +0000961 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
962 double oEnd) {
963 // walk this to outsideTs[0]
964 // walk other to outsideTs[1]
965 // if either is > 0, add a pointer to the other, copying adjacent winding
966 int tIndex = -1;
967 int oIndex = -1;
968 double tStart = outsideTs[0];
969 double oStart = outsideTs[1];
970 do {
971 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000972 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000973 do {
974 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000975 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +0000976 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +0000977 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +0000978 }
979 tStart = fTs[tIndex].fT;
980 oStart = other.fTs[oIndex].fT;
981 do {
982 double nextT;
983 do {
984 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000985 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000986 tStart = nextT;
987 do {
988 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000989 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000990 oStart = nextT;
991 if (tStart == 1 && oStart == 1) {
992 break;
993 }
caryclark@google.com2ddff932012-08-07 21:25:27 +0000994 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000995 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +0000996 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000997
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000998 void addCubic(const SkPoint pts[4]) {
999 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001000 fBounds.setCubicBounds(pts);
1001 }
1002
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001003 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001004 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001005 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001006 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001007 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001008 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001009 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001010 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
1011 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
1012 if (fVerb > 1) {
1013 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
1014 }
1015 if (fVerb > 2) {
1016 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
1017 }
1018 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001019 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001020 switch (fVerb) {
1021 case SkPath::kLine_Verb:
1022 path.lineTo(edge[1].fX, edge[1].fY);
1023 break;
1024 case SkPath::kQuad_Verb:
1025 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
1026 break;
1027 case SkPath::kCubic_Verb:
1028 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
1029 edge[3].fX, edge[3].fY);
1030 break;
1031 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001032 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001033 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001034 }
1035
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001036 void addLine(const SkPoint pts[2]) {
1037 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001038 fBounds.set(pts, 2);
1039 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001040
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001041 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
1042 const SkPoint& pt = xyAtT(tIndex);
1043 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001044 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001045 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001046 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001047 path.moveTo(pt.fX, pt.fY);
1048 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001049 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001050 }
1051
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001052 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001053 void addOtherT(int index, double otherT, int otherIndex) {
1054 Span& span = fTs[index];
1055 span.fOtherT = otherT;
1056 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001057 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001058
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001059 void addQuad(const SkPoint pts[3]) {
1060 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001061 fBounds.setQuadBounds(pts);
1062 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001063
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001064 // Defer all coincident edge processing until
1065 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001066
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001067// no need to be tricky; insert in normal T order
1068// resolve overlapping ts when considering coincidence later
1069
1070 // add non-coincident intersection. Resulting edges are sorted in T.
1071 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001072 // FIXME: in the pathological case where there is a ton of intercepts,
1073 // binary search?
1074 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001075 size_t tCount = fTs.count();
caryclark@google.comc899ad92012-08-23 15:24:42 +00001076 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001077 if (approximately_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001078 newT = 0;
1079 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001080 if (approximately_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001081 newT = 1;
1082 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001083 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001084 // OPTIMIZATION: if there are three or more identical Ts, then
1085 // the fourth and following could be further insertion-sorted so
1086 // that all the edges are clockwise or counterclockwise.
1087 // This could later limit segment tests to the two adjacent
1088 // neighbors, although it doesn't help with determining which
1089 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001090 if (newT < fTs[index].fT) {
1091 insertedAt = index;
1092 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001093 }
1094 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001095 Span* span;
1096 if (insertedAt >= 0) {
1097 span = fTs.insert(insertedAt);
1098 } else {
1099 insertedAt = tCount;
1100 span = fTs.append();
1101 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001102 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001103 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001104 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001105 span->fWindSum = SK_MinS32;
1106 span->fWindValue = 1;
1107 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001108 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001109 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001110 return insertedAt;
1111 }
1112
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001113 // set spans from start to end to decrement by one
1114 // note this walks other backwards
1115 // FIMXE: there's probably an edge case that can be constructed where
1116 // two span in one segment are separated by float epsilon on one span but
1117 // not the other, if one segment is very small. For this
1118 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001119 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001120 // sorted? It feels better in such a case to match the span's other span
1121 // pointer since both coincident segments must contain the same spans.
1122 void addTCancel(double startT, double endT, Segment& other,
1123 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001124 SkASSERT(!approximately_negative(endT - startT));
1125 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001126 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001127 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001128 ++index;
1129 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001130 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001131 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001132 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001133 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001134 Span* test = &fTs[index];
1135 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001136 SkTDArray<double> outsideTs;
1137 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001138 do {
1139 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001140 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001141 double testT = test->fT;
1142 double oTestT = oTest->fT;
1143 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001144 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001145 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001146 decrementSpan(span);
1147 } else if (track && span->fT < 1 && oTestT < 1) {
1148 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001149 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001150 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001151 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001152 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001153 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1154 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1155 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001156 while (approximately_negative(otherTMatchStart - oSpan->fT)
1157 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001158 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001159 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001160 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001161 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001162 other.decrementSpan(oSpan);
1163 } else if (track && oSpan->fT < 1 && testT < 1) {
1164 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001165 }
1166 if (!oIndex) {
1167 break;
1168 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001169 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001170 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001171 test = span;
1172 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001173 } while (!approximately_negative(endT - test->fT));
1174 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001175 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001176 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001177 double tStart = outsideTs[0];
1178 double oStart = outsideTs[1];
1179 addCancelOutsides(tStart, oStart, other, oEndT);
1180 int count = outsideTs.count();
1181 if (count > 2) {
1182 double tStart = outsideTs[count - 2];
1183 double oStart = outsideTs[count - 1];
1184 addCancelOutsides(tStart, oStart, other, oEndT);
1185 }
caryclark@google.com18063442012-07-25 12:05:18 +00001186 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001187 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001188 double tStart = oOutsideTs[0];
1189 double oStart = oOutsideTs[1];
1190 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001191 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001192 }
1193
1194 // set spans from start to end to increment the greater by one and decrement
1195 // the lesser
caryclark@google.com24bec792012-08-20 12:43:57 +00001196 void addTCoincident(const int xorMask, double startT, double endT, Segment& other,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001197 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001198 SkASSERT(!approximately_negative(endT - startT));
1199 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001200 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001201 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001202 ++index;
1203 }
1204 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001205 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001206 ++oIndex;
1207 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001208 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001209 Span* test = &fTs[index];
1210 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001211 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001212 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001213 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001214 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001215 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001216 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com24bec792012-08-20 12:43:57 +00001217 bool winding = xorMask < 0;
1218 bool decrementThis = (test->fWindValue < oTest->fWindValue) & winding;
1219 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001220 Span* end = test;
1221 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001222 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001223 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001224 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001225 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001226 if (transfer) {
1227 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001228 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001229 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001230 #endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001231 ++(end->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001232 } else if (decrementSpan(end)) {
1233 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001234 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001235 } else if (oTest->fWindValue) {
1236 SkASSERT(!decrementOther);
1237 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1238 TrackOutside(xOutsideTs, end->fT, oStartT);
1239 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001240 }
1241 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001242 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001243 // because of the order in which coincidences are resolved, this and other
1244 // may not have the same intermediate points. Compute the corresponding
1245 // intermediate T values (using this as the master, other as the follower)
1246 // and walk other conditionally -- hoping that it catches up in the end
1247 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001248 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001249 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001250 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001251 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001252 #ifdef SK_DEBUG
1253 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1254 #endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001255 ++(oEnd->fWindValue);
caryclark@google.com18063442012-07-25 12:05:18 +00001256 } else if (other.decrementSpan(oEnd)) {
1257 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001258 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001259 } else if (test->fWindValue) {
1260 SkASSERT(!decrementOther);
1261 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1262 SkASSERT(0); // track for later?
1263 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001264 }
1265 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001266 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001267 test = end;
1268 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001269 } while (!approximately_negative(endT - test->fT));
1270 SkASSERT(approximately_negative(oTest->fT - oEndT));
1271 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001272 if (!done()) {
1273 if (outsideTs.count()) {
1274 addCoinOutsides(outsideTs, other, oEndT);
1275 }
1276 if (xOutsideTs.count()) {
1277 addCoinOutsides(xOutsideTs, other, oEndT);
1278 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001279 }
1280 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001281 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001282 }
1283 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001284
caryclark@google.comcc905052012-07-25 20:59:42 +00001285 // FIXME: this doesn't prevent the same span from being added twice
1286 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001287 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001288 int tCount = fTs.count();
1289 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1290 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001291 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001292 break;
1293 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001294 if (approximately_negative(span.fT - t) && span.fOther == &other && span.fOtherT == otherT) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001295#if DEBUG_ADD_T_PAIR
1296 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1297 __FUNCTION__, fID, t, other.fID, otherT);
1298#endif
1299 return;
1300 }
1301 }
caryclark@google.com47580692012-07-23 12:14:49 +00001302#if DEBUG_ADD_T_PAIR
1303 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1304 __FUNCTION__, fID, t, other.fID, otherT);
1305#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001306 int insertedAt = addT(t, &other);
1307 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001308 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001309 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001310 matchWindingValue(insertedAt, t, borrowWind);
1311 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001312 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001313
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001314 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001315 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001316 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1317 addAngle(angles, end, start);
1318 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001319 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001320 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001321 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001322 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001323 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001324 }
1325 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001326
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001327 const Bounds& bounds() const {
1328 return fBounds;
1329 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001330
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001331 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001332 double referenceT = fTs[index].fT;
1333 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001334 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001335 buildAnglesInner(lesser, angles);
1336 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001337 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001338 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001339 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001340 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001341
1342 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1343 Span* span = &fTs[index];
1344 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001345 // if there is only one live crossing, and no coincidence, continue
1346 // in the same direction
1347 // if there is coincidence, the only choice may be to reverse direction
1348 // find edge on either side of intersection
1349 int oIndex = span->fOtherIndex;
1350 // if done == -1, prior span has already been processed
1351 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001352 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001353 if (next < 0) {
1354 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001355 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001356 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001357 // add candidate into and away from junction
1358 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001359 }
1360
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001361 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001362 SkASSERT(fVerb == SkPath::kLine_Verb);
1363 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1364 SkPoint dxy = fPts[0] - fPts[1];
1365 SkPoint odxy = other.fPts[0] - other.fPts[1];
1366 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001367 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001368
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001369 // figure out if the segment's ascending T goes clockwise or not
1370 // not enough context to write this as shown
1371 // instead, add all segments meeting at the top
1372 // sort them using buildAngleList
1373 // find the first in the sort
1374 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001375 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001376 SkASSERT(0); // incomplete
1377 return false;
1378 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001379
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001380 int computeSum(int startIndex, int endIndex) {
1381 SkTDArray<Angle> angles;
1382 addTwoAngles(startIndex, endIndex, angles);
1383 buildAngles(endIndex, angles);
1384 SkTDArray<Angle*> sorted;
1385 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001386#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001387 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001388#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001389 int angleCount = angles.count();
1390 const Angle* angle;
1391 const Segment* base;
1392 int winding;
1393 int firstIndex = 0;
1394 do {
1395 angle = sorted[firstIndex];
1396 base = angle->segment();
1397 winding = base->windSum(angle);
1398 if (winding != SK_MinS32) {
1399 break;
1400 }
1401 if (++firstIndex == angleCount) {
1402 return SK_MinS32;
1403 }
1404 } while (true);
1405 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001406 int spanWinding = base->spanSign(angle);
1407 bool inner = useInnerWinding(winding + spanWinding, winding);
1408 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001409 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001410 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001411 inner ? winding + spanWinding : winding);
1412 #endif
1413 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001414 winding += spanWinding;
1415 }
1416 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001417 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001418 #endif
1419 int nextIndex = firstIndex + 1;
1420 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001421 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001422 do {
1423 if (nextIndex == angleCount) {
1424 nextIndex = 0;
1425 }
1426 angle = sorted[nextIndex];
1427 Segment* segment = angle->segment();
1428 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001429 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001430 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001431 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001432 maxWinding = winding;
1433 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001434 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001435 }
1436 } while (++nextIndex != lastIndex);
1437 return windSum(SkMin32(startIndex, endIndex));
1438 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001439
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001441 int bestT = -1;
1442 SkScalar top = bounds().fTop;
1443 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001444 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001445 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001446 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001447 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001448 if (fTs[start].fWindValue == 0) {
1449 continue;
1450 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001451 SkPoint edge[4];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001452 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001453 // work with the original data directly
caryclark@google.com24bec792012-08-20 12:43:57 +00001454 double startT = fTs[start].fT;
1455 double endT = fTs[end].fT;
1456 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001457 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001458 Intersections intersections;
1459 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1460 false, intersections);
1461 if (pts == 0) {
1462 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001463 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001464 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1465 // if the intersection is edge on, wait for another one
1466 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001467 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001468 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1469 SkPoint pt;
1470 double foundT = intersections.fT[0][0];
caryclark@google.com24bec792012-08-20 12:43:57 +00001471 double testT = startT + (endT - startT) * foundT;
1472 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
caryclark@google.com59823f72012-08-09 18:17:47 +00001473 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001474 bestY = pt.fY;
1475 bestT = foundT < 1 ? start : end;
caryclark@google.com24bec792012-08-20 12:43:57 +00001476 hitT = testT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001477 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 } while (fTs[end].fT != 1);
1479 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001480 }
caryclark@google.com18063442012-07-25 12:05:18 +00001481
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001482 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1483 // if a segment is connected to this one, consider it crossing
1484 int tIndex;
1485 if (fPts[0].fX == basePt.fX) {
1486 tIndex = 0;
1487 do {
1488 const Span& sSpan = fTs[tIndex];
1489 const Segment* sOther = sSpan.fOther;
1490 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1491 continue;
1492 }
1493 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1494 : sOther->fBounds.fRight > basePt.fX) {
1495 return true;
1496 }
1497 } while (fTs[++tIndex].fT == 0);
1498 }
1499 if (fPts[fVerb].fX == basePt.fX) {
1500 tIndex = fTs.count() - 1;
1501 do {
1502 const Span& eSpan = fTs[tIndex];
1503 const Segment* eOther = eSpan.fOther;
1504 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1505 continue;
1506 }
1507 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1508 : eOther->fBounds.fRight > basePt.fX) {
1509 return true;
1510 }
1511 } while (fTs[--tIndex].fT == 1);
1512 }
1513 return false;
1514 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001515
caryclark@google.com18063442012-07-25 12:05:18 +00001516 bool decrementSpan(Span* span) {
1517 SkASSERT(span->fWindValue > 0);
1518 if (--(span->fWindValue) == 0) {
1519 span->fDone = true;
1520 ++fDoneSpans;
1521 return true;
1522 }
1523 return false;
1524 }
1525
caryclark@google.com15fa1382012-05-07 20:49:36 +00001526 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001527 SkASSERT(fDoneSpans <= fTs.count());
1528 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001529 }
1530
caryclark@google.com47580692012-07-23 12:14:49 +00001531 bool done(const Angle& angle) const {
1532 int start = angle.start();
1533 int end = angle.end();
1534 const Span& mSpan = fTs[SkMin32(start, end)];
1535 return mSpan.fDone;
1536 }
1537
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001538 // so the span needs to contain the pairing info found here
1539 // this should include the winding computed for the edge, and
1540 // what edge it connects to, and whether it is discarded
1541 // (maybe discarded == abs(winding) > 1) ?
1542 // only need derivatives for duration of sorting, add a new struct
1543 // for pairings, remove extra spans that have zero length and
1544 // reference an unused other
1545 // for coincident, the last span on the other may be marked done
1546 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001547
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001548 // if loop is exhausted, contour may be closed.
1549 // FIXME: pass in close point so we can check for closure
1550
1551 // given a segment, and a sense of where 'inside' is, return the next
1552 // segment. If this segment has an intersection, or ends in multiple
1553 // segments, find the mate that continues the outside.
1554 // note that if there are multiples, but no coincidence, we can limit
1555 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00001556
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001557 // mark found segments as done
1558
caryclark@google.com15fa1382012-05-07 20:49:36 +00001559 // start is the index of the beginning T of this edge
1560 // it is guaranteed to have an end which describes a non-zero length (?)
1561 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00001562 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
1563 int& nextStart, int& nextEnd, int& winding, int& spanWinding) {
1564 const int startIndex = nextStart;
1565 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001566 int outerWinding = winding;
1567 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00001568 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001569 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1570 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00001571 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001572 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001573 outerWinding = innerWinding;
1574 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001575 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001576 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001577 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1578 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001579 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001580 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001581 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001582 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001583 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001584 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001585 // mark the smaller of startIndex, endIndex done, and all adjacent
1586 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001587 #if DEBUG_WINDING
1588 SkDebugf("%s simple\n", __FUNCTION__);
1589 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00001590 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001591 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001592 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00001593 double startT = other->fTs[nextStart].fT;
1594 nextEnd = nextStart;
1595 do {
1596 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001597 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00001598 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001599 return other;
1600 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001601 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001602 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001603 SkASSERT(startIndex - endIndex != 0);
1604 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001605 addTwoAngles(startIndex, end, angles);
1606 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001607 SkTDArray<Angle*> sorted;
1608 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001609 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001610 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001611 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00001612 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001613 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00001614 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001615 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001616 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001617 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00001618 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001619 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001620 int nextIndex = firstIndex + 1;
1621 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1622 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00001623 // FIXME: found done logic probably fails if there are more than 4
1624 // sorted angles. It should bias towards the first and last undone
1625 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00001626 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00001627 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00001628 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001629 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00001630 bool altFlipped = false;
1631 bool foundFlipped = false;
1632 int foundMax = SK_MinS32;
1633 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001634 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00001635 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001636 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001637 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001638 nextIndex = 0;
1639 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001640 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00001641 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00001642 if (sumWinding) {
1643 lastNonZeroSum = sumWinding;
1644 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00001645 nextSegment = nextAngle->segment();
caryclark@google.com2ddff932012-08-07 21:25:27 +00001646 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001647 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001648 #if DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00001649 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00001650 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
1651 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001652 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001653 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00001654 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001655 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001656 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00001657 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00001658 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001659 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001660 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00001661 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00001662 return NULL;
1663 }
caryclark@google.com47580692012-07-23 12:14:49 +00001664 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001665 foundAngle = nextAngle;
caryclark@google.com47580692012-07-23 12:14:49 +00001666 foundDone = nextSegment->done(*nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00001667 foundFlipped = altFlipped;
1668 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001669 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001670 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001671 }
caryclark@google.com24bec792012-08-20 12:43:57 +00001672 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00001673 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001674 if (foundAngle && foundDone2) {
1675 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001676 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001677 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00001678 foundAngle = nextAngle;
caryclark@google.com24bec792012-08-20 12:43:57 +00001679 foundDone2 = nextSegment->done(*nextAngle);
1680 foundFlipped = altFlipped;
1681 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00001682 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001683 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001684 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001685 }
1686 // if the winding is non-zero, nextAngle does not connect to
1687 // current chain. If we haven't done so already, mark the angle
1688 // as done, record the winding value, and mark connected unambiguous
1689 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001690 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001691 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00001692 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001693 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001694 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001695 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001696 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001697 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00001698 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001699 }
1700 if (last) {
1701 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001702 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001703 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001704 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00001705 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001706 if (!foundAngle) {
1707 return NULL;
1708 }
1709 nextStart = foundAngle->start();
1710 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00001711 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00001712 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00001713 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
1714 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00001715 if (winding) {
1716 #if DEBUG_WINDING
1717 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
1718 if (foundSum == SK_MinS32) {
1719 SkDebugf("?");
1720 } else {
1721 SkDebugf("%d", foundSum);
1722 }
1723 SkDebugf(" foundMax=");
1724 if (foundMax == SK_MinS32) {
1725 SkDebugf("?");
1726 } else {
1727 SkDebugf("%d", foundMax);
1728 }
1729 SkDebugf("\n");
1730 #endif
1731 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001732 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00001733 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001734 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00001735 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00001736 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001737 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001738
caryclark@google.com24bec792012-08-20 12:43:57 +00001739 Segment* findNextXor(int& nextStart, int& nextEnd) {
1740 const int startIndex = nextStart;
1741 const int endIndex = nextEnd;
1742 SkASSERT(startIndex != endIndex);
1743 int count = fTs.count();
1744 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1745 : startIndex > 0);
1746 int step = SkSign32(endIndex - startIndex);
1747 int end = nextSpan(startIndex, step);
1748 SkASSERT(end >= 0);
1749 Span* endSpan = &fTs[end];
1750 Segment* other;
1751 markDone(SkMin32(startIndex, endIndex), 1);
1752 if (isSimple(end)) {
1753 #if DEBUG_WINDING
1754 SkDebugf("%s simple\n", __FUNCTION__);
1755 #endif
1756 other = endSpan->fOther;
1757 nextStart = endSpan->fOtherIndex;
1758 double startT = other->fTs[nextStart].fT;
1759 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001760 if ((approximately_less_than_zero(startT) && step < 0)
1761 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001762 step = -step;
1763 SkDEBUGCODE(firstLoop = false;)
1764 }
1765 do {
1766 nextEnd = nextStart;
1767 do {
1768 nextEnd += step;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001769 } while (approximately_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00001770 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
1771 break;
1772 }
caryclark@google.com03f97062012-08-21 13:13:52 +00001773 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00001774 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00001775 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00001776 SkDEBUGCODE(firstLoop = false;)
1777 step = -step;
1778 } while (true);
1779 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1780 return other;
1781 }
1782 SkTDArray<Angle> angles;
1783 SkASSERT(startIndex - endIndex != 0);
1784 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1785 addTwoAngles(startIndex, end, angles);
1786 buildAngles(end, angles);
1787 SkTDArray<Angle*> sorted;
1788 sortAngles(angles, sorted);
1789 int angleCount = angles.count();
1790 int firstIndex = findStartingEdge(sorted, startIndex, end);
1791 SkASSERT(firstIndex >= 0);
1792 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001793 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00001794 #endif
1795 SkASSERT(sorted[firstIndex]->segment() == this);
1796 int nextIndex = firstIndex + 1;
1797 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1798 const Angle* nextAngle;
1799 Segment* nextSegment;
1800 do {
1801 if (nextIndex == angleCount) {
1802 nextIndex = 0;
1803 }
1804 nextAngle = sorted[nextIndex];
1805 nextSegment = nextAngle->segment();
1806 if (!nextSegment->done(*nextAngle)) {
1807 break;
1808 }
1809 if (++nextIndex == lastIndex) {
1810 return NULL;
1811 }
1812 } while (true);
1813 nextStart = nextAngle->start();
1814 nextEnd = nextAngle->end();
1815 return nextSegment;
1816 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001817
1818 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1819 int angleCount = sorted.count();
1820 int firstIndex = -1;
1821 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1822 const Angle* angle = sorted[angleIndex];
1823 if (angle->segment() == this && angle->start() == end &&
1824 angle->end() == start) {
1825 firstIndex = angleIndex;
1826 break;
1827 }
1828 }
1829 return firstIndex;
1830 }
1831
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001832 // FIXME: this is tricky code; needs its own unit test
caryclark@google.comc899ad92012-08-23 15:24:42 +00001833 void findTooCloseToCall(int xorMask) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001834 int count = fTs.count();
1835 if (count < 3) { // require t=0, x, 1 at minimum
1836 return;
1837 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001838 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001839 int moCount;
1840 Span* match;
1841 Segment* mOther;
1842 do {
1843 match = &fTs[matchIndex];
1844 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00001845 // FIXME: allow quads, cubics to be near coincident?
1846 if (mOther->fVerb == SkPath::kLine_Verb) {
1847 moCount = mOther->fTs.count();
1848 if (moCount >= 3) {
1849 break;
1850 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001851 }
1852 if (++matchIndex >= count) {
1853 return;
1854 }
1855 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001856 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001857 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001858 // look for a pair of nearby T values that map to the same (x,y) value
1859 // if found, see if the pair of other segments share a common point. If
1860 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001861 for (int index = matchIndex + 1; index < count; ++index) {
1862 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001863 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001864 continue;
1865 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001866 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00001867 if (tOther->fVerb != SkPath::kLine_Verb) {
1868 continue; // FIXME: allow quads, cubics to be near coincident?
1869 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001870 int toCount = tOther->fTs.count();
1871 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001872 continue;
1873 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001874 const SkPoint* testPt = &xyAtT(test);
1875 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001876 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001877 moCount = toCount;
1878 match = test;
1879 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001880 matchPt = testPt;
1881 continue;
1882 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001883 int moStart = -1;
1884 int moEnd = -1;
1885 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001886 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001887 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001888 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001889 continue;
1890 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001891 if (moSpan.fOther == this) {
1892 if (moSpan.fOtherT == match->fT) {
1893 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001894 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001895 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001896 continue;
1897 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001898 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001899 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
1900 moStart = -1;
1901 break;
1902 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001903 SkASSERT(moEnd == -1);
1904 moEnd = moIndex;
1905 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001906 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001907 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001908 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001909 continue;
1910 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001911 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001912 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001913 continue;
1914 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001915 int toStart = -1;
1916 int toEnd = -1;
1917 double toStartT, toEndT;
1918 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1919 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00001920 if (toSpan.fDone) {
1921 continue;
1922 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001923 if (toSpan.fOther == this) {
1924 if (toSpan.fOtherT == test->fT) {
1925 toStart = toIndex;
1926 toStartT = toSpan.fT;
1927 }
1928 continue;
1929 }
1930 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001931 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
1932 moStart = -1;
1933 break;
1934 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001935 SkASSERT(toEnd == -1);
1936 toEnd = toIndex;
1937 toEndT = toSpan.fT;
1938 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001939 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001940 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1941 if (toStart <= 0 || toEnd <= 0) {
1942 continue;
1943 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001944 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001945 continue;
1946 }
1947 // test to see if the segment between there and here is linear
1948 if (!mOther->isLinear(moStart, moEnd)
1949 || !tOther->isLinear(toStart, toEnd)) {
1950 continue;
1951 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00001952 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001953 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001954 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001955 } else {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001956 mOther->addTCoincident(xorMask, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001957 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001958 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001959 }
1960
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001961 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1962 // and use more concise logic like the old edge walker code?
1963 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001964 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001965 // iterate through T intersections and return topmost
1966 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001967 SkASSERT(!done());
1968 int firstT;
1969 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001970 SkPoint topPt;
1971 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001972 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001973 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001974 bool lastDone = true;
1975 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001976 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001977 if (!span.fDone || !lastDone) {
1978 const SkPoint& intercept = xyAtT(&span);
1979 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1980 && topPt.fX > intercept.fX)) {
1981 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001982 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001983 } else if (topPt == intercept) {
1984 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001985 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001986 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001987 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001988 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001989 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001990 int step = 1;
1991 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001992 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001993 step = -1;
1994 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001995 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001996 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001997 // if the topmost T is not on end, or is three-way or more, find left
1998 // look for left-ness from tLeft to firstT (matching y of other)
1999 SkTDArray<Angle> angles;
2000 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002001 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002002 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002003 SkTDArray<Angle*> sorted;
2004 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002005 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002006 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002007 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002008 // skip edges that have already been processed
2009 firstT = -1;
2010 Segment* leftSegment;
2011 do {
2012 const Angle* angle = sorted[++firstT];
2013 leftSegment = angle->segment();
2014 tIndex = angle->end();
2015 endIndex = angle->start();
2016 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002017 return leftSegment;
2018 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002019
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002020 // FIXME: not crazy about this
2021 // when the intersections are performed, the other index is into an
2022 // incomplete array. as the array grows, the indices become incorrect
2023 // while the following fixes the indices up again, it isn't smart about
2024 // skipping segments whose indices are already correct
2025 // assuming we leave the code that wrote the index in the first place
2026 void fixOtherTIndex() {
2027 int iCount = fTs.count();
2028 for (int i = 0; i < iCount; ++i) {
2029 Span& iSpan = fTs[i];
2030 double oT = iSpan.fOtherT;
2031 Segment* other = iSpan.fOther;
2032 int oCount = other->fTs.count();
2033 for (int o = 0; o < oCount; ++o) {
2034 Span& oSpan = other->fTs[o];
2035 if (oT == oSpan.fT && this == oSpan.fOther) {
2036 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002037 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002038 }
2039 }
2040 }
2041 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002042
caryclark@google.com495f8e42012-05-31 13:13:11 +00002043 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002044 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002045 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002046 SkASSERT(end >= 0);
2047 if (multipleSpans(end)) {
2048 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002049 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002050 const Span& endSpan = fTs[end];
2051 Segment* other = endSpan.fOther;
2052 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002053 int otherEnd = other->nextSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002054 Span* last = other->innerChaseDone(index, step, winding);
2055 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002056 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002057 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002058
caryclark@google.com59823f72012-08-09 18:17:47 +00002059 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002060 int end = nextSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002061 SkASSERT(end >= 0);
2062 if (multipleSpans(end)) {
2063 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002064 }
2065 const Span& endSpan = fTs[end];
2066 Segment* other = endSpan.fOther;
2067 index = endSpan.fOtherIndex;
2068 int otherEnd = other->nextSpan(index, step);
2069 int min = SkMin32(index, otherEnd);
2070 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002071 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002072 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002073 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002074 Span* last = other->innerChaseWinding(index, step, winding);
2075 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002076 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002077 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002078
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002079 void init(const SkPoint pts[], SkPath::Verb verb) {
2080 fPts = pts;
2081 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002082 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002083 }
2084
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002085 bool intersected() const {
2086 return fTs.count() > 0;
2087 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002088
2089 bool isConnected(int startIndex, int endIndex) const {
2090 return fTs[startIndex].fWindSum != SK_MinS32
2091 || fTs[endIndex].fWindSum != SK_MinS32;
2092 }
2093
caryclark@google.com15fa1382012-05-07 20:49:36 +00002094 bool isLinear(int start, int end) const {
2095 if (fVerb == SkPath::kLine_Verb) {
2096 return true;
2097 }
2098 if (fVerb == SkPath::kQuad_Verb) {
2099 SkPoint qPart[3];
2100 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2101 return QuadIsLinear(qPart);
2102 } else {
2103 SkASSERT(fVerb == SkPath::kCubic_Verb);
2104 SkPoint cPart[4];
2105 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2106 return CubicIsLinear(cPart);
2107 }
2108 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002109
2110 // OPTIMIZE: successive calls could start were the last leaves off
2111 // or calls could specialize to walk forwards or backwards
2112 bool isMissing(double startT) const {
2113 size_t tCount = fTs.count();
2114 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002115 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002116 return false;
2117 }
2118 }
2119 return true;
2120 }
2121
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002122 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002123 int count = fTs.count();
2124 if (count == 2) {
2125 return true;
2126 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002127 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002128 if (approximately_less_than_zero(t)) {
2129 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002130 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002131 if (approximately_greater_than_one(t)) {
2132 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002133 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002134 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002135 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002136
2137 bool isHorizontal() const {
2138 return fBounds.fTop == fBounds.fBottom;
2139 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002140
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002141 bool isVertical() const {
2142 return fBounds.fLeft == fBounds.fRight;
2143 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002144
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002145 SkScalar leftMost(int start, int end) const {
2146 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2147 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002148
caryclark@google.com495f8e42012-05-31 13:13:11 +00002149 // this span is excluded by the winding rule -- chase the ends
2150 // as long as they are unambiguous to mark connections as done
2151 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002152 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002153 int index = angle->start();
2154 int endIndex = angle->end();
2155 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002156 Span* last = innerChaseDone(index, step, winding);
2157 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002158 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002159 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002160
caryclark@google.com59823f72012-08-09 18:17:47 +00002161 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002162 int index = angle->start();
2163 int endIndex = angle->end();
2164 int min = SkMin32(index, endIndex);
2165 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002166 Span* last = innerChaseWinding(index, step, winding);
2167 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002168 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002169 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002170
caryclark@google.com495f8e42012-05-31 13:13:11 +00002171 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002172 // This may be called when the segment is already marked done. While this
2173 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002174 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002175 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002176 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002177 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002178 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002179 double referenceT = fTs[index].fT;
2180 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002181 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002182 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002183 }
2184 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002185 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002186 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002187 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002188
caryclark@google.com24bec792012-08-20 12:43:57 +00002189 void markOneDone(const char* funName, int tIndex, int winding) {
2190 Span* span = markOneWinding(funName, tIndex, winding);
2191 if (!span) {
2192 return;
2193 }
2194 span->fDone = true;
2195 fDoneSpans++;
2196 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002197
caryclark@google.com24bec792012-08-20 12:43:57 +00002198 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2199 Span& span = fTs[tIndex];
2200 if (span.fDone) {
2201 return NULL;
2202 }
2203 #if DEBUG_MARK_DONE
2204 debugShowNewWinding(funName, span, winding);
2205 #endif
2206 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002207 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002208 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002209 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002210 span.fWindSum = winding;
2211 return &span;
2212 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002213
caryclark@google.com59823f72012-08-09 18:17:47 +00002214 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002215 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002216 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002217 double referenceT = fTs[index].fT;
2218 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002219 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002220 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002221 }
2222 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002223 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002224 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002225 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002226
caryclark@google.com2ddff932012-08-07 21:25:27 +00002227 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002228 int nextDoorWind = SK_MaxS32;
2229 if (tIndex > 0) {
2230 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002231 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002232 nextDoorWind = below.fWindValue;
2233 }
2234 }
2235 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2236 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002237 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002238 nextDoorWind = above.fWindValue;
2239 }
2240 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002241 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2242 const Span& below = fTs[tIndex - 1];
2243 nextDoorWind = below.fWindValue;
2244 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002245 if (nextDoorWind != SK_MaxS32) {
2246 Span& newSpan = fTs[tIndex];
2247 newSpan.fWindValue = nextDoorWind;
2248 if (!nextDoorWind) {
2249 newSpan.fDone = true;
2250 ++fDoneSpans;
2251 }
2252 }
2253 }
2254
caryclark@google.com9764cc62012-07-12 19:29:45 +00002255 // return span if when chasing, two or more radiating spans are not done
2256 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2257 // candidate and the remaining spans have windValue == 0 (canceled by
2258 // coincidence). The coincident edges could either be removed altogether,
2259 // or this code could be more complicated in detecting this case. Worth it?
2260 bool multipleSpans(int end) const {
2261 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002262 }
2263
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002264 // This has callers for two different situations: one establishes the end
2265 // of the current span, and one establishes the beginning of the next span
2266 // (thus the name). When this is looking for the end of the current span,
2267 // coincidence is found when the beginning Ts contain -step and the end
2268 // contains step. When it is looking for the beginning of the next, the
2269 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002270 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002271 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002272 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002273 int count = fTs.count();
2274 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002275 while (step > 0 ? ++to < count : --to >= 0) {
2276 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002277 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002278 continue;
2279 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002280 return to;
2281 }
2282 return -1;
2283 }
2284
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002285 const SkPoint* pts() const {
2286 return fPts;
2287 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002288
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002289 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002290 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002291 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2292 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002293 }
2294
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002295 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002296 const Span& span(int tIndex) const {
2297 return fTs[tIndex];
2298 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002299
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002300 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002301 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002302 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002303#if DEBUG_WIND_BUMP
2304 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2305#endif
2306 return result;
2307 }
2308
2309 int spanSign(const Angle* angle) const {
2310 SkASSERT(angle->segment() == this);
2311 return spanSign(angle->start(), angle->end());
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002312 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002313
2314 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002315 double t(int tIndex) const {
2316 return fTs[tIndex].fT;
2317 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002318
caryclark@google.com18063442012-07-25 12:05:18 +00002319 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
2320 double start) {
2321 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002322 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00002323 *outsideTs.append() = end;
2324 *outsideTs.append() = start;
2325 }
2326 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002327
caryclark@google.com24bec792012-08-20 12:43:57 +00002328 void undoneSpan(int& start, int& end) {
2329 size_t tCount = fTs.count();
2330 size_t index;
2331 for (index = 0; index < tCount; ++index) {
2332 if (!fTs[index].fDone) {
2333 break;
2334 }
2335 }
2336 SkASSERT(index < tCount - 1);
2337 start = index;
2338 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002339 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00002340 SkASSERT(index < tCount);
2341 SkASSERT(index < tCount);
2342 end = index;
2343 }
caryclark@google.com18063442012-07-25 12:05:18 +00002344
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002345 void updatePts(const SkPoint pts[]) {
2346 fPts = pts;
2347 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002348
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002349 SkPath::Verb verb() const {
2350 return fVerb;
2351 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002352
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002353 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002354 return fTs[tIndex].fWindSum;
2355 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002356
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002357 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002358 int start = angle->start();
2359 int end = angle->end();
2360 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002361 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002362 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002363
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002364 int windValue(int tIndex) const {
2365 return fTs[tIndex].fWindValue;
2366 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002367
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002368 int windValue(const Angle* angle) const {
2369 int start = angle->start();
2370 int end = angle->end();
2371 int index = SkMin32(start, end);
2372 return windValue(index);
2373 }
2374
2375 SkScalar xAtT(const Span* span) const {
2376 return xyAtT(span).fX;
2377 }
2378
2379 const SkPoint& xyAtT(int index) const {
2380 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002381 }
2382
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002383 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002384 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002385 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002386 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002387 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002388 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002389 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002390 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002391 }
2392 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002393 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002394 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002395
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002396 SkScalar yAtT(int index) const {
2397 return yAtT(&fTs[index]);
2398 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002399
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002400 SkScalar yAtT(const Span* span) const {
2401 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002402 }
2403
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002404#if DEBUG_DUMP
2405 void dump() const {
2406 const char className[] = "Segment";
2407 const int tab = 4;
2408 for (int i = 0; i < fTs.count(); ++i) {
2409 SkPoint out;
2410 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
2411 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002412 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002413 tab + sizeof(className), className, fID,
2414 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002415 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002416 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002417 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002418 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00002419 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002420 }
2421#endif
2422
caryclark@google.com47580692012-07-23 12:14:49 +00002423#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00002424 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002425 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00002426 for (int i = 0; i < fTs.count(); ++i) {
2427 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
2428 return;
2429 }
2430 }
2431 SkASSERT(0);
2432 }
2433#endif
2434
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002435#if DEBUG_DUMP
2436 int debugID() const {
2437 return fID;
2438 }
2439#endif
2440
caryclark@google.com24bec792012-08-20 12:43:57 +00002441#if DEBUG_WINDING
2442 void debugShowSums() const {
2443 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
2444 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
2445 for (int i = 0; i < fTs.count(); ++i) {
2446 const Span& span = fTs[i];
2447 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
2448 if (span.fWindSum == SK_MinS32) {
2449 SkDebugf("?");
2450 } else {
2451 SkDebugf("%d", span.fWindSum);
2452 }
2453 SkDebugf("]");
2454 }
2455 SkDebugf("\n");
2456 }
2457#endif
2458
caryclark@google.comcc905052012-07-25 20:59:42 +00002459#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002460 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00002461 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00002462 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00002463 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00002464 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
2465 }
2466 SkDebugf("\n");
2467 }
2468#endif
2469
caryclark@google.com027de222012-07-12 12:52:50 +00002470#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002471 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00002472 if (done()) {
2473 return;
2474 }
2475 for (int i = 0; i < fTs.count(); ++i) {
2476 if (fTs[i].fDone) {
2477 continue;
2478 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002479 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00002480 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2481 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2482 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2483 }
2484 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00002485 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00002486 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00002487 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002488 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
2489 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
2490 if (fTs[i].fWindSum == SK_MinS32) {
2491 SkDebugf("?");
2492 } else {
2493 SkDebugf("%d", fTs[i].fWindSum);
2494 }
2495 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00002496 }
2497 }
2498#endif
2499
caryclark@google.com0c803d02012-08-06 11:15:47 +00002500#if DEBUG_MARK_DONE
2501 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
2502 const SkPoint& pt = xyAtT(&span);
2503 SkDebugf("%s id=%d", fun, fID);
2504 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
2505 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
2506 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
2507 }
2508 SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
2509 span.fT, pt.fX, pt.fY, winding);
2510 if (span.fWindSum == SK_MinS32) {
2511 SkDebugf("?");
2512 } else {
2513 SkDebugf("%d", span.fWindSum);
2514 }
2515 SkDebugf(" windValue=%d\n", span.fWindValue);
2516 }
2517#endif
2518
caryclark@google.com47580692012-07-23 12:14:49 +00002519#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002520 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002521 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002522 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00002523 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002524 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002525 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00002526 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00002527 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00002528 int index = first;
2529 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00002530 do {
2531 const Angle& angle = *angles[index];
2532 const Segment& segment = *angle.segment();
2533 int start = angle.start();
2534 int end = angle.end();
2535 const Span& sSpan = segment.fTs[start];
2536 const Span& eSpan = segment.fTs[end];
2537 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002538 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002539 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00002540 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00002541 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002542 SkDebugf("%s [%d] id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
caryclark@google.comc899ad92012-08-23 15:24:42 +00002543 " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
2544 __FUNCTION__, index, segment.fID, kLVerbStr[segment.fVerb],
2545 start, segment.xAtT(&sSpan),
2546 segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
2547 segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
2548 lastSum, windSum, useInnerWinding(lastSum, windSum)
2549 ? windSum : lastSum, mSpan.fDone);
2550#if DEBUG_ANGLE
2551 angle.debugShow(segment.xyAtT(&sSpan));
2552#endif
caryclark@google.com47580692012-07-23 12:14:49 +00002553 ++index;
2554 if (index == angles.count()) {
2555 index = 0;
2556 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002557 if (firstTime) {
2558 firstTime = false;
2559 }
caryclark@google.com47580692012-07-23 12:14:49 +00002560 } while (index != first);
2561 }
2562#endif
2563
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002564#if DEBUG_WINDING
2565 bool debugVerifyWinding(int start, int end, int winding) const {
2566 const Span& span = fTs[SkMin32(start, end)];
2567 int spanWinding = span.fWindSum;
2568 if (spanWinding == SK_MinS32) {
2569 return true;
2570 }
2571 int spanSign = SkSign32(start - end);
2572 int signedVal = spanSign * span.fWindValue;
2573 if (signedVal < 0) {
2574 spanWinding -= signedVal;
2575 }
2576 return span.fWindSum == winding;
2577 }
2578#endif
2579
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002580private:
2581 const SkPoint* fPts;
2582 SkPath::Verb fVerb;
2583 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002584 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00002585 int fDoneSpans; // quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002586#if DEBUG_DUMP
2587 int fID;
2588#endif
2589};
2590
caryclark@google.comb9738012012-07-03 19:53:30 +00002591class Contour;
2592
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002593struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00002594 Contour* fContours[2];
2595 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002596 double fTs[2][2];
2597};
2598
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002599class Contour {
2600public:
2601 Contour() {
2602 reset();
2603#if DEBUG_DUMP
2604 fID = ++gContourID;
2605#endif
2606 }
2607
2608 bool operator<(const Contour& rh) const {
2609 return fBounds.fTop == rh.fBounds.fTop
2610 ? fBounds.fLeft < rh.fBounds.fLeft
2611 : fBounds.fTop < rh.fBounds.fTop;
2612 }
2613
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002614 void addCoincident(int index, Contour* other, int otherIndex,
2615 const Intersections& ts, bool swap) {
2616 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00002617 coincidence.fContours[0] = this;
2618 coincidence.fContours[1] = other;
2619 coincidence.fSegments[0] = index;
2620 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002621 coincidence.fTs[swap][0] = ts.fT[0][0];
2622 coincidence.fTs[swap][1] = ts.fT[0][1];
2623 coincidence.fTs[!swap][0] = ts.fT[1][0];
2624 coincidence.fTs[!swap][1] = ts.fT[1][1];
2625 }
2626
2627 void addCross(const Contour* crosser) {
2628#ifdef DEBUG_CROSS
2629 for (int index = 0; index < fCrosses.count(); ++index) {
2630 SkASSERT(fCrosses[index] != crosser);
2631 }
2632#endif
2633 *fCrosses.append() = crosser;
2634 }
2635
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002636 void addCubic(const SkPoint pts[4]) {
2637 fSegments.push_back().addCubic(pts);
2638 fContainsCurves = true;
2639 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002640
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002641 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002642 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002643 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002644 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002645
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002646 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
2647 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
2648 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002649
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002650 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002651 fSegments.push_back().addQuad(pts);
2652 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002653 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002654 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002655
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002656 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
2657 containsIntercepts();
2658 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
2659 }
2660
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002661 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002662 return fBounds;
2663 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002664
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002665 void complete() {
2666 setBounds();
2667 fContainsIntercepts = false;
2668 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002669
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002670 void containsIntercepts() {
2671 fContainsIntercepts = true;
2672 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002673
rmistry@google.comd6176b02012-08-23 18:14:13 +00002674 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002675 int &tIndex, double& hitT) {
2676 int segmentCount = fSegments.count();
2677 const Segment* bestSegment = NULL;
2678 for (int test = 0; test < segmentCount; ++test) {
2679 Segment* testSegment = &fSegments[test];
2680 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002681 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002682 continue;
2683 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002684 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002685 continue;
2686 }
2687 if (bounds.fLeft > basePt.fX) {
2688 continue;
2689 }
2690 if (bounds.fRight < basePt.fX) {
2691 continue;
2692 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002693 if (bounds.fLeft == bounds.fRight) {
2694 continue;
2695 }
2696 #if 0
2697 bool leftHalf = bounds.fLeft == basePt.fX;
2698 bool rightHalf = bounds.fRight == basePt.fX;
2699 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
2700 basePt, leftHalf, rightHalf)) {
2701 continue;
2702 }
2703 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002704 double testHitT;
2705 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
2706 if (testT >= 0) {
2707 bestSegment = testSegment;
2708 tIndex = testT;
2709 hitT = testHitT;
2710 }
2711 }
2712 return bestSegment;
2713 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002714
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002715 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002716 for (int index = 0; index < fCrosses.count(); ++index) {
2717 if (fCrosses[index] == crosser) {
2718 return true;
2719 }
2720 }
2721 return false;
2722 }
2723
caryclark@google.comc899ad92012-08-23 15:24:42 +00002724 void findTooCloseToCall(int xorMask) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002725 int segmentCount = fSegments.count();
2726 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002727 fSegments[sIndex].findTooCloseToCall(xorMask);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002728 }
2729 }
2730
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002731 void fixOtherTIndex() {
2732 int segmentCount = fSegments.count();
2733 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
2734 fSegments[sIndex].fixOtherTIndex();
2735 }
2736 }
2737
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002738 void reset() {
2739 fSegments.reset();
2740 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002741 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002742 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002743
caryclark@google.com24bec792012-08-20 12:43:57 +00002744 void resolveCoincidence(int xorMask) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002745 int count = fCoincidences.count();
2746 for (int index = 0; index < count; ++index) {
2747 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00002748 Contour* thisContour = coincidence.fContours[0];
2749 Contour* otherContour = coincidence.fContours[1];
2750 int thisIndex = coincidence.fSegments[0];
2751 int otherIndex = coincidence.fSegments[1];
2752 Segment& thisOne = thisContour->fSegments[thisIndex];
2753 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00002754 #if DEBUG_CONCIDENT
2755 thisOne.debugShowTs();
2756 other.debugShowTs();
2757 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002758 double startT = coincidence.fTs[0][0];
2759 double endT = coincidence.fTs[0][1];
2760 if (startT > endT) {
2761 SkTSwap<double>(startT, endT);
2762 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002763 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 double oStartT = coincidence.fTs[1][0];
2765 double oEndT = coincidence.fTs[1][1];
2766 if (oStartT > oEndT) {
2767 SkTSwap<double>(oStartT, oEndT);
2768 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002769 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002770 if (thisOne.cancels(other)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002771 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00002772 if (startT > 0 || oEndT < 1
2773 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
2774 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002775 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002776 if (oStartT > 0 || endT < 1
2777 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
2778 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002779 }
2780 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002781 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00002782 if (startT > 0 || oStartT > 0
2783 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002784 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002785 }
caryclark@google.com200c2112012-08-03 15:05:04 +00002786 if (endT < 1 || oEndT < 1
2787 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00002788 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00002789 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002790 thisOne.addTCoincident(xorMask, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002791 }
caryclark@google.com47580692012-07-23 12:14:49 +00002792 #if DEBUG_CONCIDENT
2793 thisOne.debugShowTs();
2794 other.debugShowTs();
2795 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002796 }
2797 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002798
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002799 const SkTArray<Segment>& segments() {
2800 return fSegments;
2801 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002802
caryclark@google.com15fa1382012-05-07 20:49:36 +00002803 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2804 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00002805 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00002806 // segments' top, and not a true sort, so it could be ameniable to regular
2807 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002808 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002809 int segmentCount = fSegments.count();
2810 SkASSERT(segmentCount > 0);
2811 int best = -1;
2812 Segment* bestSegment = NULL;
2813 while (++best < segmentCount) {
2814 Segment* testSegment = &fSegments[best];
2815 if (testSegment->done()) {
2816 continue;
2817 }
2818 bestSegment = testSegment;
2819 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002820 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002821 if (!bestSegment) {
2822 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002823 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002824 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002825 for (int test = best + 1; test < segmentCount; ++test) {
2826 Segment* testSegment = &fSegments[test];
2827 if (testSegment->done()) {
2828 continue;
2829 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002830 if (testSegment->bounds().fTop > bestTop) {
2831 continue;
2832 }
2833 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002834 if (bestTop > testTop) {
2835 bestTop = testTop;
2836 bestSegment = testSegment;
2837 }
2838 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002839 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002840 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002841 }
2842
caryclark@google.com24bec792012-08-20 12:43:57 +00002843 Segment* undoneSegment(int& start, int& end) {
2844 int segmentCount = fSegments.count();
2845 for (int test = 0; test < segmentCount; ++test) {
2846 Segment* testSegment = &fSegments[test];
2847 if (testSegment->done()) {
2848 continue;
2849 }
2850 testSegment->undoneSpan(start, end);
2851 return testSegment;
2852 }
2853 return NULL;
2854 }
2855
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002856 int updateSegment(int index, const SkPoint* pts) {
2857 Segment& segment = fSegments[index];
2858 segment.updatePts(pts);
2859 return segment.verb() + 1;
2860 }
2861
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002862#if DEBUG_TEST
2863 SkTArray<Segment>& debugSegments() {
2864 return fSegments;
2865 }
2866#endif
2867
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002868#if DEBUG_DUMP
2869 void dump() {
2870 int i;
2871 const char className[] = "Contour";
2872 const int tab = 4;
2873 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2874 for (i = 0; i < fSegments.count(); ++i) {
2875 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2876 className, i);
2877 fSegments[i].dump();
2878 }
2879 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2880 tab + sizeof(className), className,
2881 fBounds.fLeft, fBounds.fTop,
2882 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002883 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2884 className, fContainsIntercepts);
2885 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2886 className, fContainsCurves);
2887 }
2888#endif
2889
caryclark@google.com027de222012-07-12 12:52:50 +00002890#if DEBUG_ACTIVE_SPANS
2891 void debugShowActiveSpans() {
2892 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002893 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00002894 }
2895 }
2896#endif
2897
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002898protected:
2899 void setBounds() {
2900 int count = fSegments.count();
2901 if (count == 0) {
2902 SkDebugf("%s empty contour\n", __FUNCTION__);
2903 SkASSERT(0);
2904 // FIXME: delete empty contour?
2905 return;
2906 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002907 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002908 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002909 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002910 }
2911 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002912
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002913private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002914 SkTArray<Segment> fSegments;
2915 SkTDArray<Coincidence> fCoincidences;
2916 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002917 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002918 bool fContainsIntercepts;
2919 bool fContainsCurves;
2920#if DEBUG_DUMP
2921 int fID;
2922#endif
2923};
2924
2925class EdgeBuilder {
2926public:
2927
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002928EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002929 : fPath(path)
2930 , fCurrentContour(NULL)
2931 , fContours(contours)
2932{
2933#if DEBUG_DUMP
2934 gContourID = 0;
2935 gSegmentID = 0;
2936#endif
2937 walk();
2938}
2939
2940protected:
2941
2942void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002943 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002944 fCurrentContour->complete();
2945 fCurrentContour = NULL;
2946 }
2947}
2948
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002949void walk() {
2950 // FIXME:remove once we can access path pts directly
2951 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2952 SkPoint pts[4];
2953 SkPath::Verb verb;
2954 do {
2955 verb = iter.next(pts);
2956 *fPathVerbs.append() = verb;
2957 if (verb == SkPath::kMove_Verb) {
2958 *fPathPts.append() = pts[0];
2959 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2960 fPathPts.append(verb, &pts[1]);
2961 }
2962 } while (verb != SkPath::kDone_Verb);
2963 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002964
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002965 SkPath::Verb reducedVerb;
2966 uint8_t* verbPtr = fPathVerbs.begin();
2967 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002968 const SkPoint* finalCurveStart = NULL;
2969 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002970 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2971 switch (verb) {
2972 case SkPath::kMove_Verb:
2973 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002974 if (!fCurrentContour) {
2975 fCurrentContour = fContours.push_back_n(1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002976 *fExtra.append() = -1; // start new contour
2977 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002978 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002979 continue;
2980 case SkPath::kLine_Verb:
2981 // skip degenerate points
2982 if (pointsPtr[-1].fX != pointsPtr[0].fX
2983 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2984 fCurrentContour->addLine(&pointsPtr[-1]);
2985 }
2986 break;
2987 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00002988
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002989 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2990 if (reducedVerb == 0) {
2991 break; // skip degenerate points
2992 }
2993 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00002994 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002995 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002996 break;
2997 }
2998 fCurrentContour->addQuad(&pointsPtr[-1]);
2999 break;
3000 case SkPath::kCubic_Verb:
3001 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3002 if (reducedVerb == 0) {
3003 break; // skip degenerate points
3004 }
3005 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003006 *fExtra.append() =
3007 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003008 break;
3009 }
3010 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003011 *fExtra.append() =
3012 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003013 break;
3014 }
3015 fCurrentContour->addCubic(&pointsPtr[-1]);
3016 break;
3017 case SkPath::kClose_Verb:
3018 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003019 if (finalCurveStart && finalCurveEnd
3020 && *finalCurveStart != *finalCurveEnd) {
3021 *fReducePts.append() = *finalCurveStart;
3022 *fReducePts.append() = *finalCurveEnd;
3023 *fExtra.append() =
3024 fCurrentContour->addLine(fReducePts.end() - 2);
3025 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003026 complete();
3027 continue;
3028 default:
3029 SkDEBUGFAIL("bad verb");
3030 return;
3031 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003032 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003033 pointsPtr += verb;
3034 SkASSERT(fCurrentContour);
3035 }
3036 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003037 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003038 fContours.pop_back();
3039 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003040 // correct pointers in contours since fReducePts may have moved as it grew
3041 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003042 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003043 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003044 int eIndex = 0;
3045 int rIndex = 0;
3046 while (++eIndex < extraCount) {
3047 int offset = fExtra[eIndex];
3048 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003049 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003050 continue;
3051 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003052 fCurrentContour = &fContours[cIndex];
3053 rIndex += fCurrentContour->updateSegment(offset - 1,
3054 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003055 }
3056 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003057}
3058
3059private:
3060 const SkPath& fPath;
3061 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003062 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003063 Contour* fCurrentContour;
3064 SkTArray<Contour>& fContours;
3065 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003066 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003067};
3068
3069class Work {
3070public:
3071 enum SegmentType {
3072 kHorizontalLine_Segment = -1,
3073 kVerticalLine_Segment = 0,
3074 kLine_Segment = SkPath::kLine_Verb,
3075 kQuad_Segment = SkPath::kQuad_Verb,
3076 kCubic_Segment = SkPath::kCubic_Verb,
3077 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00003078
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003079 void addCoincident(Work& other, const Intersections& ts, bool swap) {
3080 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
3081 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003082
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003083 // FIXME: does it make sense to write otherIndex now if we're going to
3084 // fix it up later?
3085 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003086 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003087 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003088
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003089 // Avoid collapsing t values that are close to the same since
3090 // we walk ts to describe consecutive intersections. Since a pair of ts can
3091 // be nearly equal, any problems caused by this should be taken care
3092 // of later.
3093 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003094 int addT(double newT, const Work& other) {
3095 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003096 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003097
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003098 bool advance() {
3099 return ++fIndex < fLast;
3100 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003101
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003102 SkScalar bottom() const {
3103 return bounds().fBottom;
3104 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003105
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003106 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003107 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003108 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003109
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003110 const SkPoint* cubic() const {
3111 return fCubic;
3112 }
3113
3114 void init(Contour* contour) {
3115 fContour = contour;
3116 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003117 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003118 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003119
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003120 bool isAdjacent(const Work& next) {
3121 return fContour == next.fContour && fIndex + 1 == next.fIndex;
3122 }
3123
3124 bool isFirstLast(const Work& next) {
3125 return fContour == next.fContour && fIndex == 0
3126 && next.fIndex == fLast - 1;
3127 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003128
3129 SkScalar left() const {
3130 return bounds().fLeft;
3131 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003132
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003133 void promoteToCubic() {
3134 fCubic[0] = pts()[0];
3135 fCubic[2] = pts()[1];
3136 fCubic[3] = pts()[2];
3137 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
3138 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
3139 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
3140 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
3141 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003142
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003143 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003144 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003145 }
3146
3147 SkScalar right() const {
3148 return bounds().fRight;
3149 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003150
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003151 ptrdiff_t segmentIndex() const {
3152 return fIndex;
3153 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003154
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003155 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003156 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003157 SegmentType type = (SegmentType) segment.verb();
3158 if (type != kLine_Segment) {
3159 return type;
3160 }
3161 if (segment.isHorizontal()) {
3162 return kHorizontalLine_Segment;
3163 }
3164 if (segment.isVertical()) {
3165 return kVerticalLine_Segment;
3166 }
3167 return kLine_Segment;
3168 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003169
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003170 bool startAfter(const Work& after) {
3171 fIndex = after.fIndex;
3172 return advance();
3173 }
3174
3175 SkScalar top() const {
3176 return bounds().fTop;
3177 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003178
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003179 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003180 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003181 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003182
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003183 SkScalar x() const {
3184 return bounds().fLeft;
3185 }
3186
3187 bool xFlipped() const {
3188 return x() != pts()[0].fX;
3189 }
3190
3191 SkScalar y() const {
3192 return bounds().fTop;
3193 }
3194
3195 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003196 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003197 }
3198
3199protected:
3200 Contour* fContour;
3201 SkPoint fCubic[4];
3202 int fIndex;
3203 int fLast;
3204};
3205
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003206#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003207static void debugShowLineIntersection(int pts, const Work& wt,
3208 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003209 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003210 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
3211 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
3212 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
3213 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003214 return;
3215 }
3216 SkPoint wtOutPt, wnOutPt;
3217 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
3218 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003219 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003220 __FUNCTION__,
3221 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
3222 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
3223 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003224 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003225 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003226 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003227 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
3228 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
3229 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003230 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003231 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003232 SkDebugf("\n");
3233}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003234#else
3235static void debugShowLineIntersection(int , const Work& ,
3236 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003237}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003238#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003239
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003240static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003241
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003242 if (test != next) {
3243 if (test->bounds().fBottom < next->bounds().fTop) {
3244 return false;
3245 }
3246 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
3247 return true;
3248 }
3249 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003250 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003251 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003252 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003253 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003254 Work wn;
3255 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003256 if (test == next && !wn.startAfter(wt)) {
3257 continue;
3258 }
3259 do {
3260 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
3261 continue;
3262 }
3263 int pts;
3264 Intersections ts;
3265 bool swap = false;
3266 switch (wt.segmentType()) {
3267 case Work::kHorizontalLine_Segment:
3268 swap = true;
3269 switch (wn.segmentType()) {
3270 case Work::kHorizontalLine_Segment:
3271 case Work::kVerticalLine_Segment:
3272 case Work::kLine_Segment: {
3273 pts = HLineIntersect(wn.pts(), wt.left(),
3274 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003275 debugShowLineIntersection(pts, wt, wn,
3276 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003277 break;
3278 }
3279 case Work::kQuad_Segment: {
3280 pts = HQuadIntersect(wn.pts(), wt.left(),
3281 wt.right(), wt.y(), wt.xFlipped(), ts);
3282 break;
3283 }
3284 case Work::kCubic_Segment: {
3285 pts = HCubicIntersect(wn.pts(), wt.left(),
3286 wt.right(), wt.y(), wt.xFlipped(), ts);
3287 break;
3288 }
3289 default:
3290 SkASSERT(0);
3291 }
3292 break;
3293 case Work::kVerticalLine_Segment:
3294 swap = true;
3295 switch (wn.segmentType()) {
3296 case Work::kHorizontalLine_Segment:
3297 case Work::kVerticalLine_Segment:
3298 case Work::kLine_Segment: {
3299 pts = VLineIntersect(wn.pts(), wt.top(),
3300 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003301 debugShowLineIntersection(pts, wt, wn,
3302 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003303 break;
3304 }
3305 case Work::kQuad_Segment: {
3306 pts = VQuadIntersect(wn.pts(), wt.top(),
3307 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3308 break;
3309 }
3310 case Work::kCubic_Segment: {
3311 pts = VCubicIntersect(wn.pts(), wt.top(),
3312 wt.bottom(), wt.x(), wt.yFlipped(), ts);
3313 break;
3314 }
3315 default:
3316 SkASSERT(0);
3317 }
3318 break;
3319 case Work::kLine_Segment:
3320 switch (wn.segmentType()) {
3321 case Work::kHorizontalLine_Segment:
3322 pts = HLineIntersect(wt.pts(), wn.left(),
3323 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003324 debugShowLineIntersection(pts, wt, wn,
3325 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003326 break;
3327 case Work::kVerticalLine_Segment:
3328 pts = VLineIntersect(wt.pts(), wn.top(),
3329 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003330 debugShowLineIntersection(pts, wt, wn,
3331 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003332 break;
3333 case Work::kLine_Segment: {
3334 pts = LineIntersect(wt.pts(), wn.pts(), ts);
3335 debugShowLineIntersection(pts, wt, wn,
3336 ts.fT[1], ts.fT[0]);
3337 break;
3338 }
3339 case Work::kQuad_Segment: {
3340 swap = true;
3341 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
3342 break;
3343 }
3344 case Work::kCubic_Segment: {
3345 swap = true;
3346 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
3347 break;
3348 }
3349 default:
3350 SkASSERT(0);
3351 }
3352 break;
3353 case Work::kQuad_Segment:
3354 switch (wn.segmentType()) {
3355 case Work::kHorizontalLine_Segment:
3356 pts = HQuadIntersect(wt.pts(), wn.left(),
3357 wn.right(), wn.y(), wn.xFlipped(), ts);
3358 break;
3359 case Work::kVerticalLine_Segment:
3360 pts = VQuadIntersect(wt.pts(), wn.top(),
3361 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3362 break;
3363 case Work::kLine_Segment: {
3364 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
3365 break;
3366 }
3367 case Work::kQuad_Segment: {
3368 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
3369 break;
3370 }
3371 case Work::kCubic_Segment: {
3372 wt.promoteToCubic();
3373 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
3374 break;
3375 }
3376 default:
3377 SkASSERT(0);
3378 }
3379 break;
3380 case Work::kCubic_Segment:
3381 switch (wn.segmentType()) {
3382 case Work::kHorizontalLine_Segment:
3383 pts = HCubicIntersect(wt.pts(), wn.left(),
3384 wn.right(), wn.y(), wn.xFlipped(), ts);
3385 break;
3386 case Work::kVerticalLine_Segment:
3387 pts = VCubicIntersect(wt.pts(), wn.top(),
3388 wn.bottom(), wn.x(), wn.yFlipped(), ts);
3389 break;
3390 case Work::kLine_Segment: {
3391 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
3392 break;
3393 }
3394 case Work::kQuad_Segment: {
3395 wn.promoteToCubic();
3396 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
3397 break;
3398 }
3399 case Work::kCubic_Segment: {
3400 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
3401 break;
3402 }
3403 default:
3404 SkASSERT(0);
3405 }
3406 break;
3407 default:
3408 SkASSERT(0);
3409 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003410 if (!foundCommonContour && pts > 0) {
3411 test->addCross(next);
3412 next->addCross(test);
3413 foundCommonContour = true;
3414 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003415 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003416 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
3417 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003418 wt.addCoincident(wn, ts, swap);
3419 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003420 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003421 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003422 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
3423 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003424 int testTAt = wt.addT(ts.fT[swap][pt], wn);
3425 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003426 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
3427 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003428 }
3429 } while (wn.advance());
3430 } while (wt.advance());
3431 return true;
3432}
3433
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003434// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003435// see if coincidence is formed by clipping non-concident segments
caryclark@google.com24bec792012-08-20 12:43:57 +00003436static void coincidenceCheck(SkTDArray<Contour*>& contourList, int xorMask) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003437 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00003438 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003439 Contour* contour = contourList[cIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00003440 contour->resolveCoincidence(xorMask);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003441 }
3442 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3443 Contour* contour = contourList[cIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00003444 contour->findTooCloseToCall(xorMask);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003445 }
3446}
3447
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003448// project a ray from the top of the contour up and see if it hits anything
3449// note: when we compute line intersections, we keep track of whether
3450// two contours touch, so we need only look at contours not touching this one.
3451// OPTIMIZATION: sort contourList vertically to avoid linear walk
3452static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003453 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003454 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003455 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003456 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00003457 const Segment* test = NULL;
3458 int tIndex;
3459 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003460 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003461 for (int cTest = 0; cTest < contourCount; ++cTest) {
3462 Contour* contour = contourList[cTest];
3463 if (basePt.fY < contour->bounds().fTop) {
3464 continue;
3465 }
3466 if (bestY > contour->bounds().fBottom) {
3467 continue;
3468 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003469#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003470 // even though the contours crossed, if spans cancel through concidence,
3471 // the contours may be not have any span links to chase, and the current
3472 // segment may be isolated. Detect this by seeing if current has
3473 // uninitialized wind sums. If so, project a ray instead of relying on
3474 // previously found intersections.
3475 if (baseContour == contour) {
3476 continue;
3477 }
3478 if (checkCrosses && baseContour->crosses(contour)) {
3479 if (current->isConnected(index, endIndex)) {
3480 continue;
3481 }
3482 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003483 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003484#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003485 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
3486 if (next) {
3487 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003488 }
caryclark@google.com47580692012-07-23 12:14:49 +00003489 }
3490 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00003491 return 0;
3492 }
3493 int winding, windValue;
3494 // If the ray hit the end of a span, we need to construct the wheel of
3495 // angles to find the span closest to the ray -- even if there are just
3496 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003497 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003498 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00003499 SkTDArray<Angle> angles;
3500 int end = test->nextSpan(tIndex, 1);
3501 if (end < 0) {
3502 end = test->nextSpan(tIndex, -1);
3503 }
3504 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00003505 SkASSERT(angles.count() > 0);
3506 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
3507#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00003508 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00003509#endif
3510 return 0;
3511 }
caryclark@google.com47580692012-07-23 12:14:49 +00003512 test->buildAngles(tIndex, angles);
3513 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00003514 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00003515 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003516 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00003517 // hour after 6 o'clock
3518 sortAngles(angles, sorted);
3519#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003520 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00003521#endif
3522 // walk the sorted angle fan to find the lowest angle
3523 // above the base point. Currently, the first angle in the sorted array
3524 // is 12 noon or an earlier hour (the next counterclockwise)
3525 int count = sorted.count();
3526 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003527 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00003528 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003529 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00003530 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00003531 angle = sorted[index];
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003532 if (baseMatches && angle->isHorizontal()) {
3533 continue;
3534 }
3535 double indexDx = angle->dx();
caryclark@google.com47580692012-07-23 12:14:49 +00003536 if (indexDx < 0) {
3537 left = index;
3538 } else if (indexDx > 0) {
3539 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00003540 int previous = index - 1;
3541 if (previous < 0) {
3542 previous = count - 1;
3543 }
3544 const Angle* prev = sorted[previous];
3545 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
3546#if DEBUG_SORT
3547 SkDebugf("%s use prev\n", __FUNCTION__);
3548#endif
3549 right = previous;
3550 }
caryclark@google.com47580692012-07-23 12:14:49 +00003551 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003552 } else {
3553 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003554 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003555 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003556 if (left < 0 && right < 0) {
3557 left = mid;
3558 }
caryclark@google.com47580692012-07-23 12:14:49 +00003559 SkASSERT(left >= 0 || right >= 0);
3560 if (left < 0) {
3561 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003562 } else if (left >= 0 && mid >= 0 && right >= 0
3563 && sorted[mid]->sign() == sorted[right]->sign()) {
3564 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003565 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003566 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00003567 test = angle->segment();
3568 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00003569 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003570 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00003571#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003572 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
3573 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00003574#endif
3575 } else {
3576 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00003577 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00003578 windValue = test->windValue(tIndex);
3579#if DEBUG_WINDING
3580 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3581 windValue);
3582#endif
3583 }
3584 // see if a + change in T results in a +/- change in X (compute x'(T))
3585 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
3586#if DEBUG_WINDING
3587 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3588#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00003589 SkASSERT(dx != 0);
3590 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00003591 winding += dx > 0 ? -windValue : windValue;
3592#if DEBUG_WINDING
3593 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3594#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003595 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003596 // start here;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00003597 // we're broken because we find a vertical span
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003598 return winding;
3599}
rmistry@google.comd6176b02012-08-23 18:14:13 +00003600
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003601// OPTIMIZATION: not crazy about linear search here to find top active y.
3602// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00003603// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003604// Once the segment array is built, there's no reason I can think of not to
3605// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003606// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003607static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003608 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003609 int cIndex = 0;
3610 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003611 SkScalar bestY = SK_ScalarMax;
3612 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003613 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003614 contour = contourList[cIndex];
3615 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003616 } while (!topStart && ++cIndex < contourCount);
3617 if (!topStart) {
3618 return NULL;
3619 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003620 while (++cIndex < contourCount) {
3621 contour = contourList[cIndex];
3622 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003623 continue;
3624 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003625 SkScalar testY = SK_ScalarMax;
3626 Segment* test = contour->topSegment(testY);
3627 if (!test || bestY <= testY) {
3628 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003629 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003630 topStart = test;
3631 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003632 }
3633 return topStart;
3634}
3635
caryclark@google.com24bec792012-08-20 12:43:57 +00003636static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
3637 int contourCount = contourList.count();
3638 Segment* result;
3639 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
3640 Contour* contour = contourList[cIndex];
3641 result = contour->undoneSegment(start, end);
3642 if (result) {
3643 return result;
3644 }
3645 }
3646 return NULL;
3647}
3648
3649
3650
caryclark@google.come21cb182012-07-23 21:26:31 +00003651static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
3652 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003653 while (chase.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00003654 Span* span = chase[chase.count() - 1];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003655 const Span& backPtr = span->fOther->span(span->fOtherIndex);
3656 Segment* segment = backPtr.fOther;
3657 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003658 SkTDArray<Angle> angles;
3659 int done = 0;
3660 if (segment->activeAngle(tIndex, done, angles)) {
3661 Angle* last = angles.end() - 1;
3662 tIndex = last->start();
3663 endIndex = last->end();
3664 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003665 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00003666 if (done == angles.count()) {
3667 chase.pop(&span);
3668 continue;
3669 }
3670 SkTDArray<Angle*> sorted;
3671 sortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00003672#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003673 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00003674#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003675 // find first angle, initialize winding to computed fWindSum
3676 int firstIndex = -1;
3677 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003678 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003679 do {
3680 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003681 segment = angle->segment();
3682 winding = segment->windSum(angle);
3683 } while (winding == SK_MinS32);
3684 int spanWinding = segment->spanSign(angle->start(), angle->end());
3685 #if DEBUG_WINDING
3686 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
3687 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00003688 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003689 // turn swinding into contourWinding
3690 if (spanWinding * winding < 0) {
3691 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003692 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003693 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00003694 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003695 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003696 // we care about first sign and whether wind sum indicates this
3697 // edge is inside or outside. Maybe need to pass span winding
3698 // or first winding or something into this function?
3699 // advance to first undone angle, then return it and winding
3700 // (to set whether edges are active or not)
3701 int nextIndex = firstIndex + 1;
3702 int angleCount = sorted.count();
3703 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003704 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00003705 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00003706 do {
3707 SkASSERT(nextIndex != firstIndex);
3708 if (nextIndex == angleCount) {
3709 nextIndex = 0;
3710 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003711 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00003712 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003713 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003714 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003715 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00003716 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
3717 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003718 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003719 tIndex = angle->start();
3720 endIndex = angle->end();
3721 int lesser = SkMin32(tIndex, endIndex);
3722 const Span& nextSpan = segment->span(lesser);
3723 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003724#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00003725 // FIXME: this be wrong. assign startWinding if edge is in
3726 // same direction. If the direction is opposite, winding to
3727 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00003728 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003729 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00003730 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003731 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003732#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00003733 break;
3734 }
3735 } while (++nextIndex != lastIndex);
3736 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003737 }
3738 return NULL;
3739}
3740
caryclark@google.com027de222012-07-12 12:52:50 +00003741#if DEBUG_ACTIVE_SPANS
3742static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
3743 for (int index = 0; index < contourList.count(); ++ index) {
3744 contourList[index]->debugShowActiveSpans();
3745 }
3746}
3747#endif
3748
caryclark@google.com27c449a2012-07-27 18:26:38 +00003749static bool windingIsActive(int winding, int spanWinding) {
3750 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
3751 && (!winding || !spanWinding || winding == -spanWinding);
3752}
3753
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003754// Each segment may have an inside or an outside. Segments contained within
3755// winding may have insides on either side, and form a contour that should be
3756// ignored. Segments that are coincident with opposing direction segments may
3757// have outsides on either side, and should also disappear.
3758// 'Normal' segments will have one inside and one outside. Subsequent connections
3759// when winding should follow the intersection direction. If more than one edge
3760// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003761 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00003762 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com24bec792012-08-20 12:43:57 +00003763static void bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003764 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003765 do {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003766 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003767 if (!topStart) {
3768 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00003769 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003770 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00003771 // follow edges to intersection by changing the index by direction.
3772 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003773 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003774 int contourWinding;
3775 if (firstContour) {
3776 contourWinding = 0;
3777 firstContour = false;
3778 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003779 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003780 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00003781 if (sumWinding == SK_MinS32) {
3782 sumWinding = current->computeSum(index, endIndex);
3783 }
3784 if (sumWinding == SK_MinS32) {
3785 contourWinding = innerContourCheck(contourList, current,
3786 index, endIndex);
3787 } else {
3788 contourWinding = sumWinding;
3789 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003790 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
3791 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003792 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003793 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003794#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00003795 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00003796 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00003797 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003798#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003799 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003800#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003801 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003802 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
3803#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003804 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003805 SkPoint lastPt;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003806 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003807 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003808 // FIXME: needs work. While it works in limited situations, it does
3809 // not always compute winding correctly. Active should be removed and instead
3810 // the initial winding should be correctly passed in so that if the
3811 // inner contour is wound the same way, it never finds an accumulated
3812 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00003813 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00003814 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003815 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003816 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00003817 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00003818 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
3819 __FUNCTION__, active ? "true" : "false",
3820 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003821 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003822 const SkPoint* firstPt = NULL;
3823 do {
3824 SkASSERT(!current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003825 int nextStart = index;
3826 int nextEnd = endIndex;
3827 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.com27c449a2012-07-27 18:26:38 +00003828 nextStart, nextEnd, winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003829 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00003830 if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
3831 lastPt = current->addCurveTo(index, endIndex, simple, true);
3832 SkASSERT(*firstPt == lastPt);
3833 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003834 break;
3835 }
3836 if (!firstPt) {
3837 firstPt = &current->addMoveTo(index, simple, active);
3838 }
3839 lastPt = current->addCurveTo(index, endIndex, simple, active);
3840 current = next;
3841 index = nextStart;
3842 endIndex = nextEnd;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003843 } while (*firstPt != lastPt && (active || !current->done()));
3844 if (firstPt && active) {
3845 #if DEBUG_PATH_CONSTRUCTION
3846 SkDebugf("%s close\n", __FUNCTION__);
3847 #endif
3848 simple.close();
3849 }
caryclark@google.come21cb182012-07-23 21:26:31 +00003850 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003851 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00003852 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00003853 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003854 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003855 break;
3856 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003857 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003858 spanWinding = current->spanSign(index, endIndex);
3859 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003860 bool inner = useInnerWinding(winding - spanWinding, winding);
3861 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00003862 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00003863 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00003864 __FUNCTION__, current->debugID(), current->t(lesser),
3865 spanWinding, winding, SkSign32(index - endIndex),
3866 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00003867 inner ? winding - spanWinding : winding);
3868 #endif
3869 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003870 winding -= spanWinding;
3871 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003872 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003873 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003874 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003875}
3876
caryclark@google.com24bec792012-08-20 12:43:57 +00003877static void bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
3878 Segment* current;
3879 int start, end;
3880 while ((current = findUndone(contourList, start, end))) {
3881 const SkPoint* firstPt = NULL;
3882 SkPoint lastPt;
3883 do {
3884 SkASSERT(!current->done());
3885 int nextStart = start;
3886 int nextEnd = end;
3887 Segment* next = current->findNextXor(nextStart, nextEnd);
3888 if (!next) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00003889 if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
3890 lastPt = current->addCurveTo(start, end, simple, true);
3891 SkASSERT(*firstPt == lastPt);
3892 }
caryclark@google.com24bec792012-08-20 12:43:57 +00003893 break;
3894 }
3895 if (!firstPt) {
3896 firstPt = &current->addMoveTo(start, simple, true);
3897 }
3898 lastPt = current->addCurveTo(start, end, simple, true);
3899 current = next;
3900 start = nextStart;
3901 end = nextEnd;
3902 } while (*firstPt != lastPt);
3903 if (firstPt) {
3904 #if DEBUG_PATH_CONSTRUCTION
3905 SkDebugf("%s close\n", __FUNCTION__);
3906 #endif
3907 simple.close();
3908 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003909 }
caryclark@google.com24bec792012-08-20 12:43:57 +00003910}
3911
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003912static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
3913 int contourCount = contourList.count();
3914 for (int cTest = 0; cTest < contourCount; ++cTest) {
3915 Contour* contour = contourList[cTest];
3916 contour->fixOtherTIndex();
3917 }
3918}
3919
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003920static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003921 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003922 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003923 if (count == 0) {
3924 return;
3925 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003926 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003927 *list.append() = &contours[index];
3928 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003929 QSort<Contour>(list.begin(), list.end() - 1);
3930}
3931
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003932void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003933 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.com24bec792012-08-20 12:43:57 +00003934 int xorMask = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003935 simple.reset();
3936 simple.setFillType(SkPath::kEvenOdd_FillType);
3937
3938 // turn path into list of segments
3939 SkTArray<Contour> contours;
3940 // FIXME: add self-intersecting cubics' T values to segment
3941 EdgeBuilder builder(path, contours);
3942 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003943 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003944 Contour** currentPtr = contourList.begin();
3945 if (!currentPtr) {
3946 return;
3947 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003948 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003949 // find all intersections between segments
3950 do {
3951 Contour** nextPtr = currentPtr;
3952 Contour* current = *currentPtr++;
3953 Contour* next;
3954 do {
3955 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003956 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003957 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003958 // eat through coincident edges
caryclark@google.com24bec792012-08-20 12:43:57 +00003959 coincidenceCheck(contourList, xorMask);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003960 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003961 // construct closed contours
caryclark@google.com24bec792012-08-20 12:43:57 +00003962 if (xorMask < 0) {
3963 bridgeWinding(contourList, simple);
3964 } else {
3965 bridgeXor(contourList, simple);
3966 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003967}
3968