blob: f854718af58d05ef8354a35d906dfc79dc2dbd9a [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.comf839c032012-10-26 21:03:50 +000028#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000029#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000030
caryclark@google.com47580692012-07-23 12:14:49 +000031#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.come7bd5f42012-12-13 19:47:53 +000032#define FORCE_RELEASE 0 // set force release to 1 for multiple thread -- no debugging
caryclark@google.comfa0588f2012-04-26 21:01:06 +000033
caryclark@google.com31143cf2012-11-09 22:14:19 +000034#if FORCE_RELEASE || defined SK_RELEASE
caryclark@google.com47580692012-07-23 12:14:49 +000035
36const bool gRunTestsInOneThread = false;
37
38#define DEBUG_ACTIVE_SPANS 0
caryclark@google.com4eeda372012-12-06 21:47:48 +000039#define DEBUG_ACTIVE_SPANS_SHORT_FORM 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000040#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000041#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000042#define DEBUG_ANGLE 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000043#define DEBUG_ASSEMBLE 0
caryclark@google.com47580692012-07-23 12:14:49 +000044#define DEBUG_CONCIDENT 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000045#define DEBUG_CROSS 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000046#define DEBUG_FLOW 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000047#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000048#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com729e1c42012-11-21 21:36:34 +000049#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000051#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000052#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053
54#else
55
caryclark@google.com47580692012-07-23 12:14:49 +000056const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000057
caryclark@google.comc91dfe42012-10-16 12:06:27 +000058#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com4eeda372012-12-06 21:47:48 +000059#define DEBUG_ACTIVE_SPANS_SHORT_FORM 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000060#define DEBUG_ADD_INTERSECTING_TS 1
61#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000062#define DEBUG_ANGLE 1
caryclark@google.come7bd5f42012-12-13 19:47:53 +000063#define DEBUG_ASSEMBLE 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000064#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000065#define DEBUG_CROSS 0
caryclark@google.come7bd5f42012-12-13 19:47:53 +000066#define DEBUG_FLOW 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000067#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000068#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com729e1c42012-11-21 21:36:34 +000069#define DEBUG_SHOW_WINDING 0
caryclark@google.com47580692012-07-23 12:14:49 +000070#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000071#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000072#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000073
74#endif
75
caryclark@google.com6aea33f2012-10-09 14:11:58 +000076#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000077
caryclark@google.comfa0588f2012-04-26 21:01:06 +000078#if DEBUG_DUMP
79static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000080// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000081static int gContourID;
82static int gSegmentID;
83#endif
84
caryclark@google.com8dcf1142012-07-02 20:27:02 +000085#ifndef DEBUG_TEST
86#define DEBUG_TEST 0
87#endif
88
caryclark@google.com32546db2012-08-31 20:55:07 +000089#define MAKE_CONST_LINE(line, pts) \
90 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
91#define MAKE_CONST_QUAD(quad, pts) \
92 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
93 {pts[2].fX, pts[2].fY}}
94#define MAKE_CONST_CUBIC(cubic, pts) \
95 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
96 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
97
caryclark@google.comfa0588f2012-04-26 21:01:06 +000098static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
99 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000100 MAKE_CONST_LINE(aLine, a);
101 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000102 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
103}
104
105static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
106 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000107 MAKE_CONST_QUAD(aQuad, a);
108 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000109 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000110}
111
caryclark@google.com32546db2012-08-31 20:55:07 +0000112static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000113 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000114 MAKE_CONST_CUBIC(aCubic, a);
115 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000116 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
117}
118
119static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
120 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000121 MAKE_CONST_QUAD(aQuad, a);
122 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000123#define TRY_QUARTIC_SOLUTION 1
124#if TRY_QUARTIC_SOLUTION
125 intersect2(aQuad, bQuad, intersections);
126#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000127 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000128#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000129 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000130}
131
132static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
133 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000134 MAKE_CONST_CUBIC(aCubic, a);
135 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000136 intersect(aCubic, bCubic, intersections);
137 return intersections.fUsed;
138}
139
140static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
141 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000142 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000143 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
144}
145
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000146static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
147 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000148 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000149 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
150}
151
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000152static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
153 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000154 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000155 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
156}
157
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000158static int (* const HSegmentIntersect[])(const SkPoint [], SkScalar ,
159 SkScalar , SkScalar , bool , Intersections& ) = {
160 NULL,
161 HLineIntersect,
162 HQuadIntersect,
163 HCubicIntersect
164};
165
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000166static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
167 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000168 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000169 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
170}
171
172static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
173 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000174 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000175 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
176}
177
178static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
179 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000180 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000181 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000182}
183
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000184static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
185 SkScalar , SkScalar , bool , Intersections& ) = {
186 NULL,
187 VLineIntersect,
188 VQuadIntersect,
189 VCubicIntersect
190};
191
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000192static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000193 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000194 double x, y;
195 xy_at_t(line, t, x, y);
196 out->fX = SkDoubleToScalar(x);
197 out->fY = SkDoubleToScalar(y);
198}
199
200static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000201 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000202 double x, y;
203 xy_at_t(quad, t, x, y);
204 out->fX = SkDoubleToScalar(x);
205 out->fY = SkDoubleToScalar(y);
206}
207
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000208static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
209 MAKE_CONST_QUAD(quad, a);
210 xy_at_t(quad, t, out->x, out->y);
211}
212
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000213static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000214 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000215 double x, y;
216 xy_at_t(cubic, t, x, y);
217 out->fX = SkDoubleToScalar(x);
218 out->fY = SkDoubleToScalar(y);
219}
220
221static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
222 NULL,
223 LineXYAtT,
224 QuadXYAtT,
225 CubicXYAtT
226};
227
228static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000229 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000230 double x;
231 xy_at_t(aLine, t, x, *(double*) 0);
232 return SkDoubleToScalar(x);
233}
234
235static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000236 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000237 double x;
238 xy_at_t(quad, t, x, *(double*) 0);
239 return SkDoubleToScalar(x);
240}
241
242static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000243 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000244 double x;
245 xy_at_t(cubic, t, x, *(double*) 0);
246 return SkDoubleToScalar(x);
247}
248
249static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
250 NULL,
251 LineXAtT,
252 QuadXAtT,
253 CubicXAtT
254};
255
256static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000257 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000258 double y;
259 xy_at_t(aLine, t, *(double*) 0, y);
260 return SkDoubleToScalar(y);
261}
262
263static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000264 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000265 double y;
266 xy_at_t(quad, t, *(double*) 0, y);
267 return SkDoubleToScalar(y);
268}
269
270static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000271 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000272 double y;
273 xy_at_t(cubic, t, *(double*) 0, y);
274 return SkDoubleToScalar(y);
275}
276
277static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
278 NULL,
279 LineYAtT,
280 QuadYAtT,
281 CubicYAtT
282};
283
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000284static SkScalar LineDXAtT(const SkPoint a[2], double ) {
285 return a[1].fX - a[0].fX;
286}
287
288static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000289 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000290 double x;
291 dxdy_at_t(quad, t, x, *(double*) 0);
292 return SkDoubleToScalar(x);
293}
294
295static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000296 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000297 double x;
298 dxdy_at_t(cubic, t, x, *(double*) 0);
299 return SkDoubleToScalar(x);
300}
301
302static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
303 NULL,
304 LineDXAtT,
305 QuadDXAtT,
306 CubicDXAtT
307};
308
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000309static SkScalar LineDYAtT(const SkPoint a[2], double ) {
310 return a[1].fY - a[0].fY;
311}
312
313static SkScalar QuadDYAtT(const SkPoint a[3], double t) {
314 MAKE_CONST_QUAD(quad, a);
315 double y;
316 dxdy_at_t(quad, t, *(double*) 0, y);
317 return SkDoubleToScalar(y);
318}
319
320static SkScalar CubicDYAtT(const SkPoint a[4], double t) {
321 MAKE_CONST_CUBIC(cubic, a);
322 double y;
323 dxdy_at_t(cubic, t, *(double*) 0, y);
324 return SkDoubleToScalar(y);
325}
326
327static SkScalar (* const SegmentDYAtT[])(const SkPoint [], double ) = {
328 NULL,
329 LineDYAtT,
330 QuadDYAtT,
331 CubicDYAtT
332};
333
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000334static void LineSubDivide(const SkPoint a[2], double startT, double endT,
335 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000336 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000337 _Line dst;
338 sub_divide(aLine, startT, endT, dst);
339 sub[0].fX = SkDoubleToScalar(dst[0].x);
340 sub[0].fY = SkDoubleToScalar(dst[0].y);
341 sub[1].fX = SkDoubleToScalar(dst[1].x);
342 sub[1].fY = SkDoubleToScalar(dst[1].y);
343}
344
345static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
346 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000347 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000348 Quadratic dst;
349 sub_divide(aQuad, startT, endT, dst);
350 sub[0].fX = SkDoubleToScalar(dst[0].x);
351 sub[0].fY = SkDoubleToScalar(dst[0].y);
352 sub[1].fX = SkDoubleToScalar(dst[1].x);
353 sub[1].fY = SkDoubleToScalar(dst[1].y);
354 sub[2].fX = SkDoubleToScalar(dst[2].x);
355 sub[2].fY = SkDoubleToScalar(dst[2].y);
356}
357
358static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
359 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000360 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000361 Cubic dst;
362 sub_divide(aCubic, startT, endT, dst);
363 sub[0].fX = SkDoubleToScalar(dst[0].x);
364 sub[0].fY = SkDoubleToScalar(dst[0].y);
365 sub[1].fX = SkDoubleToScalar(dst[1].x);
366 sub[1].fY = SkDoubleToScalar(dst[1].y);
367 sub[2].fX = SkDoubleToScalar(dst[2].x);
368 sub[2].fY = SkDoubleToScalar(dst[2].y);
369 sub[3].fX = SkDoubleToScalar(dst[3].x);
370 sub[3].fY = SkDoubleToScalar(dst[3].y);
371}
372
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000373static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
374 SkPoint []) = {
375 NULL,
376 LineSubDivide,
377 QuadSubDivide,
378 CubicSubDivide
379};
380
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000381static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000382 _Line sub) {
383 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000384 _Line dst;
385 sub_divide(aLine, startT, endT, dst);
386 sub[0] = dst[0];
387 sub[1] = dst[1];
388}
389
390static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000391 Quadratic sub) {
392 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000393 Quadratic dst;
394 sub_divide(aQuad, startT, endT, dst);
395 sub[0] = dst[0];
396 sub[1] = dst[1];
397 sub[2] = dst[2];
398}
399
400static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000401 Cubic sub) {
402 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000403 Cubic dst;
404 sub_divide(aCubic, startT, endT, dst);
405 sub[0] = dst[0];
406 sub[1] = dst[1];
407 sub[2] = dst[2];
408 sub[3] = dst[3];
409}
410
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000411#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000412static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
413 SkRect& bounds) {
414 SkPoint dst[3];
415 QuadSubDivide(a, startT, endT, dst);
416 bounds.fLeft = bounds.fRight = dst[0].fX;
417 bounds.fTop = bounds.fBottom = dst[0].fY;
418 for (int index = 1; index < 3; ++index) {
419 bounds.growToInclude(dst[index].fX, dst[index].fY);
420 }
421}
422
423static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
424 SkRect& bounds) {
425 SkPoint dst[4];
426 CubicSubDivide(a, startT, endT, dst);
427 bounds.fLeft = bounds.fRight = dst[0].fX;
428 bounds.fTop = bounds.fBottom = dst[0].fY;
429 for (int index = 1; index < 4; ++index) {
430 bounds.growToInclude(dst[index].fX, dst[index].fY);
431 }
432}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000433#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000434
caryclark@google.com15fa1382012-05-07 20:49:36 +0000435static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000437 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438 Quadratic dst;
439 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000440 if (order == 2) { // quad became line
441 for (int index = 0; index < order; ++index) {
442 SkPoint* pt = reducePts.append();
443 pt->fX = SkDoubleToScalar(dst[index].x);
444 pt->fY = SkDoubleToScalar(dst[index].y);
445 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000446 }
447 return (SkPath::Verb) (order - 1);
448}
449
450static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
451 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000452 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000453 Cubic dst;
454 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000455 if (order == 2 || order == 3) { // cubic became line or quad
456 for (int index = 0; index < order; ++index) {
457 SkPoint* pt = reducePts.append();
458 pt->fX = SkDoubleToScalar(dst[index].x);
459 pt->fY = SkDoubleToScalar(dst[index].y);
460 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000461 }
462 return (SkPath::Verb) (order - 1);
463}
464
caryclark@google.com15fa1382012-05-07 20:49:36 +0000465static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000466 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000467 return isLinear(aQuad, 0, 2);
468}
469
470static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000471 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000472 return isLinear(aCubic, 0, 3);
473}
474
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000475static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000476 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000477 double x[2];
478 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000479 xy_at_t(aLine, endT, x[1], *(double*) 0);
480 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000481}
482
483static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000484 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000485 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000486}
487
488static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000489 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000490 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000491}
492
493static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
494 NULL,
495 LineLeftMost,
496 QuadLeftMost,
497 CubicLeftMost
498};
499
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000500#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000501static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
502 Intersections& intersections) {
503 MAKE_CONST_QUAD(aQuad, a);
504 MAKE_CONST_LINE(bLine, b);
505 return intersectRay(aQuad, bLine, intersections);
506}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000507#endif
508
509static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
510 Intersections& intersections) {
511 MAKE_CONST_QUAD(aQuad, a);
512 return intersectRay(aQuad, bLine, intersections);
513}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000514
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000515static bool LineVertical(const SkPoint a[2], double startT, double endT) {
516 MAKE_CONST_LINE(aLine, a);
517 double x[2];
518 xy_at_t(aLine, startT, x[0], *(double*) 0);
519 xy_at_t(aLine, endT, x[1], *(double*) 0);
520 return approximately_equal((float) x[0], (float) x[1]);
521}
522
523static bool QuadVertical(const SkPoint a[3], double startT, double endT) {
524 SkPoint dst[3];
525 QuadSubDivide(a, startT, endT, dst);
526 return approximately_equal(dst[0].fX, dst[1].fX) && approximately_equal(dst[1].fX, dst[2].fX);
527}
528
529static bool CubicVertical(const SkPoint a[4], double startT, double endT) {
530 SkPoint dst[4];
531 CubicSubDivide(a, startT, endT, dst);
532 return approximately_equal(dst[0].fX, dst[1].fX) && approximately_equal(dst[1].fX, dst[2].fX)
533 && approximately_equal(dst[2].fX, dst[3].fX);
534}
535
536static bool (* const SegmentVertical[])(const SkPoint [], double , double) = {
537 NULL,
538 LineVertical,
539 QuadVertical,
540 CubicVertical
541};
542
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000543class Segment;
544
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000545struct Span {
546 Segment* fOther;
547 mutable SkPoint fPt; // lazily computed as needed
548 double fT;
549 double fOtherT; // value at fOther[fOtherIndex].fT
550 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000551 int fWindSum; // accumulated from contours surrounding this one.
552 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000553 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000554 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000555 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000556 bool fUnsortableStart; // set when start is part of an unsortable pair
557 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000558 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000559};
560
caryclark@google.com15fa1382012-05-07 20:49:36 +0000561// sorting angles
562// given angles of {dx dy ddx ddy dddx dddy} sort them
563class Angle {
564public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000565 // FIXME: this is bogus for quads and cubics
566 // if the quads and cubics' line from end pt to ctrl pt are coincident,
567 // there's no obvious way to determine the curve ordering from the
568 // derivatives alone. In particular, if one quadratic's coincident tangent
569 // is longer than the other curve, the final control point can place the
570 // longer curve on either side of the shorter one.
571 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
572 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000573
caryclark@google.com32546db2012-08-31 20:55:07 +0000574 /*(
575 for quads and cubics, set up a parameterized line (e.g. LineParameters )
576 for points [0] to [1]. See if point [2] is on that line, or on one side
577 or the other. If it both quads' end points are on the same side, choose
578 the shorter tangent. If the tangents are equal, choose the better second
579 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000580
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000581 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000582 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000583 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000584 double y = dy();
585 double ry = rh.dy();
586 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
587 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000588 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000589 double x = dx();
590 double rx = rh.dx();
591 if (y == 0 && ry == 0 && x * rx < 0) {
592 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000593 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000594 double x_ry = x * ry;
595 double rx_y = rx * y;
596 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000597 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000598 return cmp < 0;
599 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000600 if (approximately_zero(x_ry) && approximately_zero(rx_y)
601 && !approximately_zero_squared(cmp)) {
602 return cmp < 0;
603 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000604 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000605 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
606 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000607 // FIXME: running demo will trigger this assertion
608 // (don't know if commenting out will trigger further assertion or not)
609 // commenting it out allows demo to run in release, though
610 // SkASSERT(fSide != rh.fSide);
611 return fSide < rh.fSide;
612 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000613 // see if either curve can be lengthened and try the tangent compare again
614 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
615 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
616 Angle longer = *this;
617 Angle rhLonger = rh;
618 if (longer.lengthen() | rhLonger.lengthen()) {
619 return longer < rhLonger;
620 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000621 // what if we extend in the other direction?
622 longer = *this;
623 rhLonger = rh;
624 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
625 return longer < rhLonger;
626 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000627 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000628 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000629 || (rh.fVerb == SkPath::kLine_Verb
630 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000631 // See general unsortable comment below. This case can happen when
632 // one line has a non-zero change in t but no change in x and y.
633 fUnsortable = true;
634 rh.fUnsortable = true;
635 return this < &rh; // even with no solution, return a stable sort
636 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000637 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
638 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
639 fUnsortable = true;
640 rh.fUnsortable = true;
641 return this < &rh; // even with no solution, return a stable sort
642 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000643 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
644 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000645 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000646 // end of the shorter tangent to midway between the end points
647 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000648 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
649 double len = fTangent1.normalSquared();
650 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000651 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000652 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000653 int roots, rroots;
654 bool flip = false;
655 do {
656 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
657 double midX = (q[0].x + q[2].x) / 2;
658 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000659 ray[0] = q[1];
660 ray[1].x = midX;
661 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000662 SkASSERT(ray[0] != ray[1]);
663 roots = QuadRayIntersect(fPts, ray, i);
664 rroots = QuadRayIntersect(rh.fPts, ray, ri);
665 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000666 if (roots == 0 || rroots == 0) {
667 // FIXME: we don't have a solution in this case. The interim solution
668 // is to mark the edges as unsortable, exclude them from this and
669 // future computations, and allow the returned path to be fragmented
670 fUnsortable = true;
671 rh.fUnsortable = true;
672 return this < &rh; // even with no solution, return a stable sort
673 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000674 _Point loc;
675 double best = SK_ScalarInfinity;
676 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000677 int index;
678 for (index = 0; index < roots; ++index) {
679 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000680 dx = loc.x - ray[0].x;
681 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000682 dist = dx * dx + dy * dy;
683 if (best > dist) {
684 best = dist;
685 }
686 }
687 for (index = 0; index < rroots; ++index) {
688 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000689 dx = loc.x - ray[0].x;
690 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000691 dist = dx * dx + dy * dy;
692 if (best > dist) {
693 return fSide < 0;
694 }
695 }
696 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000697 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000698
caryclark@google.com47580692012-07-23 12:14:49 +0000699 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000700 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000701 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000702
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000703 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000704 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000705 }
706
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000707 int end() const {
708 return fEnd;
709 }
710
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000711 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000712 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000713 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000714
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000715 bool lengthen() {
716 int newEnd = fEnd;
717 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
718 fEnd = newEnd;
719 setSpans();
720 return true;
721 }
722 return false;
723 }
724
caryclark@google.coma461ff02012-10-11 12:54:23 +0000725 bool reverseLengthen() {
726 if (fReversed) {
727 return false;
728 }
729 int newEnd = fStart;
730 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
731 fEnd = newEnd;
732 fReversed = true;
733 setSpans();
734 return true;
735 }
736 return false;
737 }
738
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000739 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000740 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000741 fSegment = segment;
742 fStart = start;
743 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000744 fPts = orig;
745 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000746 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000747 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000748 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000749 setSpans();
750 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000751
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000752 void setSpans() {
753 double startT = (*fSpans)[fStart].fT;
754 double endT = (*fSpans)[fEnd].fT;
755 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000756 case SkPath::kLine_Verb:
757 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000758 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000759 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
760 fTangent1.lineEndPoints(l);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000761 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000762 break;
763 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000764 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000765 fTangent1.quadEndPoints(fQ, 0, 1);
766 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000767 break;
768 case SkPath::kCubic_Verb:
769 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000770 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000771 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000772 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000773 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000774 default:
775 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000776 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +0000777 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.comf839c032012-10-26 21:03:50 +0000778 if (fUnsortable) {
779 return;
780 }
781 SkASSERT(fStart != fEnd);
782 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
783 for (int index = fStart; index != fEnd; index += step) {
784 if ((*fSpans)[index].fUnsortableStart) {
785 fUnsortable = true;
786 return;
787 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000788#if 0
caryclark@google.comf839c032012-10-26 21:03:50 +0000789 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000790 SkASSERT(0);
caryclark@google.comf839c032012-10-26 21:03:50 +0000791 fUnsortable = true;
792 return;
793 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000794#endif
caryclark@google.comf839c032012-10-26 21:03:50 +0000795 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000796 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000797
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000798 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000799 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000800 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000801
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000802 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000803 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000804 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000805
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000806 const SkTDArray<Span>* spans() const {
807 return fSpans;
808 }
809
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000810 int start() const {
811 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000812 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000813
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000814 bool unsortable() const {
815 return fUnsortable;
816 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000817
caryclark@google.comc899ad92012-08-23 15:24:42 +0000818#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000819 const SkPoint* pts() const {
820 return fPts;
821 }
822
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000823 SkPath::Verb verb() const {
824 return fVerb;
825 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000826
caryclark@google.comc899ad92012-08-23 15:24:42 +0000827 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000828 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000829 }
830#endif
831
caryclark@google.com15fa1382012-05-07 20:49:36 +0000832private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000833 const SkPoint* fPts;
834 Quadratic fQ;
835 SkPath::Verb fVerb;
836 double fSide;
837 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000838 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000839 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000840 int fStart;
841 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000842 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000843 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000844};
845
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000846// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000847struct Bounds : public SkRect {
848 static bool Intersects(const Bounds& a, const Bounds& b) {
849 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
850 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
851 }
852
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000853 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
854 if (left < fLeft) {
855 fLeft = left;
856 }
857 if (top < fTop) {
858 fTop = top;
859 }
860 if (right > fRight) {
861 fRight = right;
862 }
863 if (bottom > fBottom) {
864 fBottom = bottom;
865 }
866 }
867
868 void add(const Bounds& toAdd) {
869 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
870 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000871
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000872 void add(const SkPoint& pt) {
873 if (pt.fX < fLeft) fLeft = pt.fX;
874 if (pt.fY < fTop) fTop = pt.fY;
875 if (pt.fX > fRight) fRight = pt.fX;
876 if (pt.fY > fBottom) fBottom = pt.fY;
877 }
878
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000879 bool isEmpty() {
880 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000881 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000882 || isnan(fLeft) || isnan(fRight)
883 || isnan(fTop) || isnan(fBottom);
884 }
885
886 void setCubicBounds(const SkPoint a[4]) {
887 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000888 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000889 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000890 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
891 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000892 }
893
894 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000895 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000896 _Rect dRect;
897 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000898 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
899 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000900 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000901
902 void setPoint(const SkPoint& pt) {
903 fLeft = fRight = pt.fX;
904 fTop = fBottom = pt.fY;
905 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000906};
907
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000908// OPTIMIZATION: does the following also work, and is it any faster?
909// return outerWinding * innerWinding > 0
910// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000911static bool useInnerWinding(int outerWinding, int innerWinding) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000912 // SkASSERT(outerWinding != innerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +0000913 int absOut = abs(outerWinding);
914 int absIn = abs(innerWinding);
915 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
916 if (outerWinding * innerWinding < 0) {
917#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000918 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000919 outerWinding, innerWinding, result ? "true" : "false");
920#endif
921 }
922 return result;
923}
924
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000925#define F (false) // discard the edge
926#define T (true) // keep the edge
927
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000928static const bool gUnaryActiveEdge[2][2] = {
929// from=0 from=1
930// to=0,1 to=0,1
931 {F, T}, {T, F},
932};
933
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000934static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = {
935// miFrom=0 miFrom=1
936// miTo=0 miTo=1 miTo=0 miTo=1
937// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
938// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
939 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
940 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
941 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
942 {{{{F, T}, {T, F}}, {{T, F}, {F, T}}}, {{{T, F}, {F, T}}, {{F, T}, {T, F}}}}, // mi ^ su
caryclark@google.com235f56a2012-09-14 14:19:30 +0000943};
944
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000945#undef F
946#undef T
caryclark@google.com235f56a2012-09-14 14:19:30 +0000947
caryclark@google.comf839c032012-10-26 21:03:50 +0000948// wrap path to keep track of whether the contour is initialized and non-empty
949class PathWrapper {
950public:
951 PathWrapper(SkPath& path)
952 : fPathPtr(&path)
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000953 , fCloses(0)
954 , fMoves(0)
caryclark@google.comf839c032012-10-26 21:03:50 +0000955 {
956 init();
957 }
958
959 void close() {
960 if (!fHasMove) {
961 return;
962 }
963 bool callClose = isClosed();
964 lineTo();
965 if (fEmpty) {
966 return;
967 }
968 if (callClose) {
969 #if DEBUG_PATH_CONSTRUCTION
970 SkDebugf("path.close();\n");
971 #endif
972 fPathPtr->close();
caryclark@google.comd0deb4f2012-12-17 13:58:08 +0000973 fCloses++;
caryclark@google.comf839c032012-10-26 21:03:50 +0000974 }
975 init();
976 }
977
978 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
979 lineTo();
980 moveTo();
981#if DEBUG_PATH_CONSTRUCTION
982 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
983 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
984#endif
985 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
986 fDefer[0] = fDefer[1] = pt3;
987 fEmpty = false;
988 }
989
990 void deferredLine(const SkPoint& pt) {
991 if (pt == fDefer[1]) {
992 return;
993 }
994 if (changedSlopes(pt)) {
995 lineTo();
996 fDefer[0] = fDefer[1];
997 }
998 fDefer[1] = pt;
999 }
1000
1001 void deferredMove(const SkPoint& pt) {
1002 fMoved = true;
1003 fHasMove = true;
1004 fEmpty = true;
1005 fDefer[0] = fDefer[1] = pt;
1006 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001007
caryclark@google.comf839c032012-10-26 21:03:50 +00001008 void deferredMoveLine(const SkPoint& pt) {
1009 if (!fHasMove) {
1010 deferredMove(pt);
1011 }
1012 deferredLine(pt);
1013 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001014
caryclark@google.comf839c032012-10-26 21:03:50 +00001015 bool hasMove() const {
1016 return fHasMove;
1017 }
1018
1019 void init() {
1020 fEmpty = true;
1021 fHasMove = false;
1022 fMoved = false;
1023 }
1024
1025 bool isClosed() const {
1026 return !fEmpty && fFirstPt == fDefer[1];
1027 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001028
caryclark@google.comf839c032012-10-26 21:03:50 +00001029 void lineTo() {
1030 if (fDefer[0] == fDefer[1]) {
1031 return;
1032 }
1033 moveTo();
1034 fEmpty = false;
1035#if DEBUG_PATH_CONSTRUCTION
1036 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
1037#endif
1038 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
1039 fDefer[0] = fDefer[1];
1040 }
1041
1042 const SkPath* nativePath() const {
1043 return fPathPtr;
1044 }
1045
1046 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
1047 lineTo();
1048 moveTo();
1049#if DEBUG_PATH_CONSTRUCTION
1050 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
1051 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1052#endif
1053 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1054 fDefer[0] = fDefer[1] = pt2;
1055 fEmpty = false;
1056 }
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +00001057
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001058 bool someAssemblyRequired() const {
1059 return fCloses < fMoves;
1060 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001061
1062protected:
1063 bool changedSlopes(const SkPoint& pt) const {
1064 if (fDefer[0] == fDefer[1]) {
1065 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001066 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001067 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
1068 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
1069 SkScalar lineDx = pt.fX - fDefer[1].fX;
1070 SkScalar lineDy = pt.fY - fDefer[1].fY;
1071 return deferDx * lineDy != deferDy * lineDx;
1072 }
1073
1074 void moveTo() {
1075 if (!fMoved) {
1076 return;
1077 }
1078 fFirstPt = fDefer[0];
1079#if DEBUG_PATH_CONSTRUCTION
1080 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
1081#endif
1082 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
1083 fMoved = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001084 fMoves++;
caryclark@google.comf839c032012-10-26 21:03:50 +00001085 }
1086
1087private:
1088 SkPath* fPathPtr;
1089 SkPoint fDefer[2];
1090 SkPoint fFirstPt;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001091 int fCloses;
1092 int fMoves;
caryclark@google.comf839c032012-10-26 21:03:50 +00001093 bool fEmpty;
1094 bool fHasMove;
1095 bool fMoved;
1096};
1097
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001098class Segment {
1099public:
1100 Segment() {
1101#if DEBUG_DUMP
1102 fID = ++gSegmentID;
1103#endif
1104 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001105
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001106 bool operator<(const Segment& rh) const {
1107 return fBounds.fTop < rh.fBounds.fTop;
1108 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001109
caryclark@google.com4eeda372012-12-06 21:47:48 +00001110 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001111 if (activeAngleInner(index, done, angles)) {
1112 return true;
1113 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001114 int lesser = index;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001115 while (--lesser >= 0 && equalPoints(index, lesser)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001116 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001117 return true;
1118 }
1119 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001120 lesser = index;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001121 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001122 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001123 return true;
1124 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001125 } while (++index < fTs.count() && equalPoints(index, lesser));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001126 return false;
1127 }
1128
caryclark@google.com4eeda372012-12-06 21:47:48 +00001129 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001130 Span* span = &fTs[index];
1131 Segment* other = span->fOther;
1132 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001133 return other->activeAngleInner(oIndex, done, angles);
1134 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001135
caryclark@google.com4eeda372012-12-06 21:47:48 +00001136 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001137 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001138 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001139 Span& upSpan = fTs[index];
1140 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001141 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001142 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001143 done++;
1144 } else if (upSpan.fWindSum != SK_MinS32) {
1145 return true;
1146 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001147 } else if (!upSpan.fDone) {
1148 upSpan.fDone = true;
1149 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001150 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001151 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001152 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001153 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001154 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001155 Span& downSpan = fTs[prev];
1156 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001157 addAngle(angles, index, prev);
1158 if (downSpan.fDone) {
1159 done++;
1160 } else if (downSpan.fWindSum != SK_MinS32) {
1161 return true;
1162 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001163 } else if (!downSpan.fDone) {
1164 downSpan.fDone = true;
1165 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001166 }
1167 }
1168 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001169 }
1170
caryclark@google.comf839c032012-10-26 21:03:50 +00001171 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001172 SkASSERT(!done());
1173 int count = fTs.count();
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001174 result.fX = result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001175 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001176 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001177 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001178 const Span& span = fTs[index];
1179 if (span.fUnsortableStart | lastUnsortable) {
1180 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001181 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001182 if (!span.fDone | !lastDone) {
1183 const SkPoint& xy = xyAtT(index);
1184 if (result.fY < xy.fY) {
1185 goto next;
1186 }
1187 if (result.fY == xy.fY && result.fX < xy.fX) {
1188 goto next;
1189 }
1190 result = xy;
1191 }
1192 next:
1193 lastDone = span.fDone;
1194 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001195 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001196 }
1197
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001198 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1199 int sumMiWinding = updateWinding(endIndex, index);
1200 int sumSuWinding = updateOppWinding(endIndex, index);
1201 if (fOperand) {
1202 SkTSwap<int>(sumMiWinding, sumSuWinding);
1203 }
1204 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1205 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001206 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001207 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001208
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001209 bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001210 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001211 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1212 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1213 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001214 bool miFrom;
1215 bool miTo;
1216 bool suFrom;
1217 bool suTo;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001218 if (operand()) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001219 miFrom = (oppMaxWinding & xorMiMask) != 0;
1220 miTo = (oppSumWinding & xorMiMask) != 0;
1221 suFrom = (maxWinding & xorSuMask) != 0;
1222 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001223 } else {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001224 miFrom = (maxWinding & xorMiMask) != 0;
1225 miTo = (sumWinding & xorMiMask) != 0;
1226 suFrom = (oppMaxWinding & xorSuMask) != 0;
1227 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001228 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001229 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
1230 SkASSERT(result != -1);
1231 return result;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001232 }
1233
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00001234 bool activeWinding(int index, int endIndex) {
1235 int sumWinding = updateWinding(endIndex, index);
1236 int maxWinding;
1237 return activeWinding(index, endIndex, maxWinding, sumWinding);
1238 }
1239
1240 bool activeWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
1241 setUpWinding(index, endIndex, maxWinding, sumWinding);
1242 bool from = maxWinding != 0;
1243 bool to = sumWinding != 0;
1244 bool result = gUnaryActiveEdge[from][to];
1245 SkASSERT(result != -1);
1246 return result;
1247 }
1248
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001249 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001250 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001251 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001252#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001253 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001254 SkPoint angle0Pt, newPt;
1255 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1256 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1257 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1258 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1259 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1260 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001261#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001262 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001263 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001264
caryclark@google.com2ddff932012-08-07 21:25:27 +00001265 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001266 double oEnd) {
1267 int tIndex = -1;
1268 int tCount = fTs.count();
1269 int oIndex = -1;
1270 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001271 do {
1272 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001273 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001274 int tIndexStart = tIndex;
1275 do {
1276 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001277 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001278 int oIndexStart = oIndex;
1279 double nextT;
1280 do {
1281 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001282 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001283 double oNextT;
1284 do {
1285 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001286 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001287 // at this point, spans before and after are at:
1288 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1289 // if tIndexStart == 0, no prior span
1290 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001291
caryclark@google.comcc905052012-07-25 20:59:42 +00001292 // advance the span with zero winding
1293 // if the following span exists (not past the end, non-zero winding)
1294 // connect the two edges
1295 if (!fTs[tIndexStart].fWindValue) {
1296 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1297 #if DEBUG_CONCIDENT
1298 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1299 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001300 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1301 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001302 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001303 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001304 }
1305 if (nextT < 1 && fTs[tIndex].fWindValue) {
1306 #if DEBUG_CONCIDENT
1307 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1308 __FUNCTION__, fID, other.fID, tIndex,
1309 fTs[tIndex].fT, xyAtT(tIndex).fX,
1310 xyAtT(tIndex).fY);
1311 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001312 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001313 }
1314 } else {
1315 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1316 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1317 #if DEBUG_CONCIDENT
1318 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1319 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001320 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1321 other.xyAtT(oIndexStart).fY);
1322 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001323 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001324 }
1325 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1326 #if DEBUG_CONCIDENT
1327 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1328 __FUNCTION__, fID, other.fID, oIndex,
1329 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1330 other.xyAtT(oIndex).fY);
1331 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1332 #endif
1333 }
1334 }
1335 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001336
caryclark@google.comcc905052012-07-25 20:59:42 +00001337 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1338 double oEnd) {
1339 // walk this to outsideTs[0]
1340 // walk other to outsideTs[1]
1341 // if either is > 0, add a pointer to the other, copying adjacent winding
1342 int tIndex = -1;
1343 int oIndex = -1;
1344 double tStart = outsideTs[0];
1345 double oStart = outsideTs[1];
1346 do {
1347 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001348 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001349 do {
1350 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001351 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001352 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001353 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001354 }
1355 tStart = fTs[tIndex].fT;
1356 oStart = other.fTs[oIndex].fT;
1357 do {
1358 double nextT;
1359 do {
1360 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001361 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001362 tStart = nextT;
1363 do {
1364 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001365 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001366 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001367 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001368 break;
1369 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001370 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001371 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001372 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001373
caryclark@google.com4eeda372012-12-06 21:47:48 +00001374 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1375 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001376 fBounds.setCubicBounds(pts);
1377 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001378
caryclark@google.comf839c032012-10-26 21:03:50 +00001379 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001380 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001381 const SkPoint* ePtr;
1382 int lastT = fTs.count() - 1;
1383 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1384 ePtr = fPts;
1385 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001386 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001387 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1388 ePtr = edge;
1389 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001390 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001391 bool reverse = ePtr == fPts && start != 0;
1392 if (reverse) {
1393 path.deferredMoveLine(ePtr[fVerb]);
1394 switch (fVerb) {
1395 case SkPath::kLine_Verb:
1396 path.deferredLine(ePtr[0]);
1397 break;
1398 case SkPath::kQuad_Verb:
1399 path.quadTo(ePtr[1], ePtr[0]);
1400 break;
1401 case SkPath::kCubic_Verb:
1402 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1403 break;
1404 default:
1405 SkASSERT(0);
1406 }
1407 // return ePtr[0];
1408 } else {
1409 path.deferredMoveLine(ePtr[0]);
1410 switch (fVerb) {
1411 case SkPath::kLine_Verb:
1412 path.deferredLine(ePtr[1]);
1413 break;
1414 case SkPath::kQuad_Verb:
1415 path.quadTo(ePtr[1], ePtr[2]);
1416 break;
1417 case SkPath::kCubic_Verb:
1418 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1419 break;
1420 default:
1421 SkASSERT(0);
1422 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001423 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001424 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001425 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001426 }
1427
caryclark@google.com4eeda372012-12-06 21:47:48 +00001428 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1429 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001430 fBounds.set(pts, 2);
1431 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001432
caryclark@google.comf839c032012-10-26 21:03:50 +00001433#if 0
1434 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001435 const SkPoint& pt = xyAtT(tIndex);
1436 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001437 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001438 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001439 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001440 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001441#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001442
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001443 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001444 void addOtherT(int index, double otherT, int otherIndex) {
1445 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001446 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001447 if (precisely_less_than_zero(otherT)) {
1448 otherT = 0;
1449 } else if (precisely_greater_than_one(otherT)) {
1450 otherT = 1;
1451 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001452 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001453 span.fOtherT = otherT;
1454 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001455 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001456
caryclark@google.com4eeda372012-12-06 21:47:48 +00001457 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1458 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001459 fBounds.setQuadBounds(pts);
1460 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001461
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001462 // Defer all coincident edge processing until
1463 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001464
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001465// no need to be tricky; insert in normal T order
1466// resolve overlapping ts when considering coincidence later
1467
1468 // add non-coincident intersection. Resulting edges are sorted in T.
1469 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001470 // FIXME: in the pathological case where there is a ton of intercepts,
1471 // binary search?
1472 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001473 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001474 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001475 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001476 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001477 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001478 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001479 newT = 1;
1480 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001481 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001482 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001483 // OPTIMIZATION: if there are three or more identical Ts, then
1484 // the fourth and following could be further insertion-sorted so
1485 // that all the edges are clockwise or counterclockwise.
1486 // This could later limit segment tests to the two adjacent
1487 // neighbors, although it doesn't help with determining which
1488 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001489 if (newT < fTs[index].fT) {
1490 insertedAt = index;
1491 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001492 }
1493 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001494 Span* span;
1495 if (insertedAt >= 0) {
1496 span = fTs.insert(insertedAt);
1497 } else {
1498 insertedAt = tCount;
1499 span = fTs.append();
1500 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001501 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001502 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001503 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001504 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001505 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001506 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001507 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001508 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001509 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001510 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001511 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001512 span->fUnsortableStart = false;
1513 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001514 if (span - fTs.begin() > 0 && !span[-1].fDone
1515 && !precisely_negative(newT - span[-1].fT)
1516 // && approximately_negative(newT - span[-1].fT)
1517 && xyAtT(&span[-1]) == xyAtT(span)) {
1518 span[-1].fTiny = true;
1519 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001520 if (approximately_negative(newT - span[-1].fT)) {
1521 if (approximately_greater_than_one(newT)) {
1522 span[-1].fUnsortableStart = true;
1523 span[-2].fUnsortableEnd = true;
1524 }
1525 if (approximately_less_than_zero(span[-1].fT)) {
1526 span->fUnsortableStart = true;
1527 span[-1].fUnsortableEnd = true;
1528 }
1529 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001530 ++fDoneSpans;
1531 }
1532 if (fTs.end() - span > 1 && !span->fDone
1533 && !precisely_negative(span[1].fT - newT)
1534 // && approximately_negative(span[1].fT - newT)
1535 && xyAtT(&span[1]) == xyAtT(span)) {
1536 span->fTiny = true;
1537 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001538 if (approximately_negative(span[1].fT - newT)) {
1539 if (approximately_greater_than_one(span[1].fT)) {
1540 span->fUnsortableStart = true;
1541 span[-1].fUnsortableEnd = true;
1542 }
1543 if (approximately_less_than_zero(newT)) {
1544 span[1].fUnsortableStart = true;
1545 span->fUnsortableEnd = true;
1546 }
1547 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001548 ++fDoneSpans;
1549 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001550 return insertedAt;
1551 }
1552
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 // set spans from start to end to decrement by one
1554 // note this walks other backwards
1555 // FIMXE: there's probably an edge case that can be constructed where
1556 // two span in one segment are separated by float epsilon on one span but
1557 // not the other, if one segment is very small. For this
1558 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001559 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001560 // sorted? It feels better in such a case to match the span's other span
1561 // pointer since both coincident segments must contain the same spans.
1562 void addTCancel(double startT, double endT, Segment& other,
1563 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001564 SkASSERT(!approximately_negative(endT - startT));
1565 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001566 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001567 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001568 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 ++index;
1570 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001571 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001572 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001573 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001574 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575 Span* test = &fTs[index];
1576 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001577 SkTDArray<double> outsideTs;
1578 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001579 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001580 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001581 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001582 double testT = test->fT;
1583 double oTestT = oTest->fT;
1584 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001585 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001586 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001587 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001588 } else if (track && span->fT < 1 && oTestT < 1) {
1589 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001590 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001591 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001592 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001593 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001594 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1595 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1596 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001597 while (approximately_negative(otherTMatchStart - oSpan->fT)
1598 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001599 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001600 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001601 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001602 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001603 other.decrementSpan(oSpan);
1604 } else if (track && oSpan->fT < 1 && testT < 1) {
1605 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001606 }
1607 if (!oIndex) {
1608 break;
1609 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001610 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001611 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001612 test = span;
1613 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001614 } while (!approximately_negative(endT - test->fT));
1615 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001616 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001617 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001618 double tStart = outsideTs[0];
1619 double oStart = outsideTs[1];
1620 addCancelOutsides(tStart, oStart, other, oEndT);
1621 int count = outsideTs.count();
1622 if (count > 2) {
1623 double tStart = outsideTs[count - 2];
1624 double oStart = outsideTs[count - 1];
1625 addCancelOutsides(tStart, oStart, other, oEndT);
1626 }
caryclark@google.com18063442012-07-25 12:05:18 +00001627 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001628 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001629 double tStart = oOutsideTs[0];
1630 double oStart = oOutsideTs[1];
1631 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001632 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001633 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001634
caryclark@google.com4eeda372012-12-06 21:47:48 +00001635 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1636 SkTDArray<double>& outsideTs) {
1637 int oWindValue = oTest->fWindValue;
1638 int oOppValue = oTest->fOppValue;
1639 if (opp) {
1640 SkTSwap<int>(oWindValue, oOppValue);
1641 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001642 Span* const test = &fTs[index];
1643 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001644 const double oStartT = oTest->fT;
1645 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001646 if (bumpSpan(end, oWindValue, oOppValue)) {
1647 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001648 }
1649 end = &fTs[++index];
1650 } while (approximately_negative(end->fT - test->fT));
1651 return index;
1652 }
1653
1654 // because of the order in which coincidences are resolved, this and other
1655 // may not have the same intermediate points. Compute the corresponding
1656 // intermediate T values (using this as the master, other as the follower)
1657 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001658 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1659 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001660 Span* const oTest = &fTs[oIndex];
1661 Span* oEnd = oTest;
1662 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001663 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001664 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001665 && approximately_negative(oEnd->fT - oStartT)) {
1666 zeroSpan(oEnd);
1667 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001668 oEnd = &fTs[++oIndex];
1669 }
1670 return oIndex;
1671 }
1672
1673 // FIXME: need to test this case:
1674 // contourA has two segments that are coincident
1675 // contourB has two segments that are coincident in the same place
1676 // each ends up with +2/0 pairs for winding count
1677 // since logic below doesn't transfer count (only increments/decrements) can this be
1678 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001679
1680 // set spans from start to end to increment the greater by one and decrement
1681 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001682 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001683 SkASSERT(!approximately_negative(endT - startT));
1684 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001685 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001686 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001687 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001688 ++index;
1689 }
1690 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001691 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001692 ++oIndex;
1693 }
1694 Span* test = &fTs[index];
1695 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001696 SkTDArray<double> outsideTs;
1697 SkTDArray<double> oOutsideTs;
1698 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001699 // if either span has an opposite value and the operands don't match, resolve first
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001700 // SkASSERT(!test->fDone || !oTest->fDone);
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001701 if (test->fDone || oTest->fDone) {
1702 index = advanceCoincidentThis(oTest, opp, index);
1703 oIndex = other.advanceCoincidentOther(test, oEndT, oIndex);
1704 } else {
1705 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1706 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
1707 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001708 test = &fTs[index];
1709 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001710 } while (!approximately_negative(endT - test->fT));
1711 SkASSERT(approximately_negative(oTest->fT - oEndT));
1712 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001713 if (!done() && outsideTs.count()) {
1714 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001715 }
1716 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001717 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001718 }
1719 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001720
caryclark@google.comcc905052012-07-25 20:59:42 +00001721 // FIXME: this doesn't prevent the same span from being added twice
1722 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001723 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001724 int tCount = fTs.count();
1725 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1726 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001727 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001728 break;
1729 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001730 if (approximately_negative(span.fT - t) && span.fOther == &other
1731 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001732#if DEBUG_ADD_T_PAIR
1733 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1734 __FUNCTION__, fID, t, other.fID, otherT);
1735#endif
1736 return;
1737 }
1738 }
caryclark@google.com47580692012-07-23 12:14:49 +00001739#if DEBUG_ADD_T_PAIR
1740 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1741 __FUNCTION__, fID, t, other.fID, otherT);
1742#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001743 int insertedAt = addT(t, &other);
1744 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001745 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001746 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001747 matchWindingValue(insertedAt, t, borrowWind);
1748 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001749 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001750
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001751 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001752 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001753 int min = SkMin32(end, start);
1754 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001755 addAngle(angles, end, start);
1756 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001757 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001758 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001759 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001760 min = SkMin32(end, tIndex);
1761 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001762 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001763 }
1764 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001765
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001766 int advanceCoincidentThis(const Span* oTest, bool opp, int index) {
1767 Span* const test = &fTs[index];
1768 Span* end = test;
1769 do {
1770 end = &fTs[++index];
1771 } while (approximately_negative(end->fT - test->fT));
1772 return index;
1773 }
1774
1775 int advanceCoincidentOther(const Span* test, double oEndT, int& oIndex) {
1776 Span* const oTest = &fTs[oIndex];
1777 Span* oEnd = oTest;
1778 const double oStartT = oTest->fT;
1779 while (!approximately_negative(oEndT - oEnd->fT)
1780 && approximately_negative(oEnd->fT - oStartT)) {
1781 oEnd = &fTs[++oIndex];
1782 }
1783 return oIndex;
1784 }
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +00001785
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001786 bool betweenTs(int lesser, double testT, int greater) {
1787 if (lesser > greater) {
1788 SkTSwap<int>(lesser, greater);
1789 }
1790 return approximately_between(fTs[lesser].fT, testT, fTs[greater].fT);
1791 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001792
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001793 const Bounds& bounds() const {
1794 return fBounds;
1795 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001796
caryclark@google.com31143cf2012-11-09 22:14:19 +00001797 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001798 double referenceT = fTs[index].fT;
1799 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001800 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1801 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001802 buildAnglesInner(lesser, angles);
1803 }
1804 do {
1805 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001806 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1807 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001808 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001809
1810 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1811 Span* span = &fTs[index];
1812 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001813 // if there is only one live crossing, and no coincidence, continue
1814 // in the same direction
1815 // if there is coincidence, the only choice may be to reverse direction
1816 // find edge on either side of intersection
1817 int oIndex = span->fOtherIndex;
1818 // if done == -1, prior span has already been processed
1819 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001820 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001821 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001822 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001823 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001824 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001825 // add candidate into and away from junction
1826 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001827 }
1828
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001829 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001830 SkTDArray<Angle> angles;
1831 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001832 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001833 // OPTIMIZATION: check all angles to see if any have computed wind sum
1834 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001835 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001836 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001837#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001838 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001839#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001840 if (!sortable) {
1841 return SK_MinS32;
1842 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001843 int angleCount = angles.count();
1844 const Angle* angle;
1845 const Segment* base;
1846 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001847 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001848 int firstIndex = 0;
1849 do {
1850 angle = sorted[firstIndex];
1851 base = angle->segment();
1852 winding = base->windSum(angle);
1853 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001854 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001855 break;
1856 }
1857 if (++firstIndex == angleCount) {
1858 return SK_MinS32;
1859 }
1860 } while (true);
1861 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001862 int spanWinding = base->spanSign(angle);
1863 bool inner = useInnerWinding(winding + spanWinding, winding);
1864 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001865 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001866 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001867 inner ? winding + spanWinding : winding);
1868 #endif
1869 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001870 winding += spanWinding;
1871 }
1872 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001873 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001874 #endif
1875 int nextIndex = firstIndex + 1;
1876 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001877 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001878 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001879 do {
1880 if (nextIndex == angleCount) {
1881 nextIndex = 0;
1882 }
1883 angle = sorted[nextIndex];
1884 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001885 bool opp = base->fOperand ^ segment->fOperand;
1886 int maxWinding, oMaxWinding;
1887 int spanSign = segment->spanSign(angle);
1888 int oppoSign = segment->oppSign(angle);
1889 if (opp) {
1890 oMaxWinding = oWinding;
1891 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001892 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001893 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001894 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001895 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001896 } else {
1897 maxWinding = winding;
1898 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001899 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001900 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001901 oWinding -= oppoSign;
1902 }
1903 }
1904 if (segment->windSum(angle) == SK_MinS32) {
1905 if (opp) {
1906 if (useInnerWinding(oMaxWinding, oWinding)) {
1907 oMaxWinding = oWinding;
1908 }
1909 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1910 maxWinding = winding;
1911 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001912 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001913 } else {
1914 if (useInnerWinding(maxWinding, winding)) {
1915 maxWinding = winding;
1916 }
1917 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1918 oMaxWinding = oWinding;
1919 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001920 (void) segment->markAndChaseWinding(angle, maxWinding,
1921 binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001922 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001923 }
1924 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001925 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001926 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001927 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001928
caryclark@google.com3586ece2012-12-27 18:46:58 +00001929 int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool& hitSomething,
caryclark@google.com10227bf2012-12-28 22:10:41 +00001930 double mid, bool opp, bool current) const {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001931 SkScalar bottom = fBounds.fBottom;
caryclark@google.com10227bf2012-12-28 22:10:41 +00001932 int bestTIndex = -1;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001933 if (bottom <= bestY) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00001934 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001935 }
1936 SkScalar top = fBounds.fTop;
1937 if (top >= basePt.fY) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00001938 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001939 }
1940 if (fBounds.fLeft > basePt.fX) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00001941 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001942 }
1943 if (fBounds.fRight < basePt.fX) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00001944 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001945 }
1946 if (fBounds.fLeft == fBounds.fRight) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00001947 return bestTIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00001948 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00001949 // intersect ray starting at basePt with edge
1950 Intersections intersections;
1951 // OPTIMIZE: use specialty function that intersects ray with curve,
1952 // returning t values only for curve (we don't care about t on ray)
1953 int pts = (*VSegmentIntersect[fVerb])(fPts, top, bottom, basePt.fX, false, intersections);
1954 if (pts == 0 || (current && pts == 1)) {
1955 return bestTIndex;
1956 }
1957 if (current) {
1958 SkASSERT(pts > 1);
1959 int closestIdx = 0;
1960 double closest = fabs(intersections.fT[0][0] - mid);
1961 for (int idx = 1; idx < pts; ++idx) {
1962 double test = fabs(intersections.fT[0][idx] - mid);
1963 if (closest > test) {
1964 closestIdx = idx;
1965 closest = test;
1966 }
1967 }
1968 if (closestIdx < pts - 1) {
1969 intersections.fT[0][closestIdx] = intersections.fT[0][pts - 1];
1970 }
1971 --pts;
1972 }
1973 double bestT = -1;
1974 for (int index = 0; index < pts; ++index) {
1975 double foundT = intersections.fT[0][index];
1976 SkScalar testY = (*SegmentYAtT[fVerb])(fPts, foundT);
1977 if (approximately_negative(testY - bestY)
1978 || approximately_negative(basePt.fY - testY)) {
caryclark@google.com47580692012-07-23 12:14:49 +00001979 continue;
1980 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00001981 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1982 // if the intersection is edge on, wait for another one
1983 hitSomething = true;
1984 return bestTIndex;
1985 }
1986 if (fVerb > SkPath::kLine_Verb && !approximately_less_than_zero(foundT)
1987 && !approximately_greater_than_one(foundT)) {
1988 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, foundT);
1989 if (approximately_zero(dx)) {
caryclark@google.com3586ece2012-12-27 18:46:58 +00001990 continue;
1991 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001992 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00001993 bestY = testY;
1994 bestT = foundT;
caryclark@google.com3586ece2012-12-27 18:46:58 +00001995 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00001996 if (bestT < 0) {
1997 return bestTIndex;
caryclark@google.com3586ece2012-12-27 18:46:58 +00001998 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00001999 SkASSERT(bestT >= 0);
2000 SkASSERT(bestT <= 1);
2001 int start;
2002 int end = 0;
2003 do {
2004 start = end;
2005 end = nextSpan(start, 1);
2006 } while (fTs[end].fT < bestT);
2007 // FIXME: see next candidate for a better pattern to find the next start/end pair
2008 while (start + 1 < end && fTs[start].fDone) {
2009 ++start;
caryclark@google.com3586ece2012-12-27 18:46:58 +00002010 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002011 int testTIndex = bestT < 1 ? start : end;
2012 if (!isCanceled(testTIndex)) {
2013 hitT = bestT;
2014 bestTIndex = testTIndex;
2015 hitSomething = true;
caryclark@google.com3586ece2012-12-27 18:46:58 +00002016 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00002017 return bestTIndex;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002018 }
caryclark@google.com18063442012-07-25 12:05:18 +00002019
caryclark@google.com4eeda372012-12-06 21:47:48 +00002020 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00002021 SkASSERT(span->fWindValue > 0);
2022 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002023 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002024 span->fDone = true;
2025 ++fDoneSpans;
2026 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002027 }
2028 }
2029
2030 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
2031 SkASSERT(!span->fDone);
2032 span->fWindValue += windDelta;
2033 SkASSERT(span->fWindValue >= 0);
2034 span->fOppValue += oppDelta;
2035 SkASSERT(span->fOppValue >= 0);
2036 if (fXor) {
2037 span->fWindValue &= 1;
2038 }
2039 if (fOppXor) {
2040 span->fOppValue &= 1;
2041 }
2042 if (!span->fWindValue && !span->fOppValue) {
2043 span->fDone = true;
2044 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00002045 return true;
2046 }
2047 return false;
2048 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002049
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002050 // OPTIMIZE
2051 // when the edges are initially walked, they don't automatically get the prior and next
2052 // edges assigned to positions t=0 and t=1. Doing that would remove the need for this check,
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002053 // and would additionally remove the need for similar checks in condition edges. It would
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002054 // also allow intersection code to assume end of segment intersections (maybe?)
2055 bool complete() const {
2056 int count = fTs.count();
2057 return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
2058 }
caryclark@google.com18063442012-07-25 12:05:18 +00002059
caryclark@google.com15fa1382012-05-07 20:49:36 +00002060 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002061 SkASSERT(fDoneSpans <= fTs.count());
2062 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002063 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002064
caryclark@google.comf839c032012-10-26 21:03:50 +00002065 bool done(int min) const {
2066 return fTs[min].fDone;
2067 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002068
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002069 bool done(const Angle* angle) const {
2070 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00002071 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002072
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002073 bool equalPoints(int greaterTIndex, int lesserTIndex) {
2074 SkASSERT(greaterTIndex >= lesserTIndex);
2075 double greaterT = fTs[greaterTIndex].fT;
2076 double lesserT = fTs[lesserTIndex].fT;
2077 if (greaterT == lesserT) {
2078 return true;
2079 }
2080 if (!approximately_negative(greaterT - lesserT)) {
2081 return false;
2082 }
2083 return xyAtT(greaterTIndex) == xyAtT(lesserTIndex);
2084 }
2085
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002086 /*
2087 The M and S variable name parts stand for the operators.
2088 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2089 Su stands for Subtrahend
2090 The Opp variable name part designates that the value is for the Opposite operator.
2091 Opposite values result from combining coincident spans.
2092 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002093
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002094 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2095 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002096 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002097 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002098 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002099 const int count = fTs.count();
2100 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2101 const int step = SkSign32(endIndex - startIndex);
2102 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002103 SkASSERT(end >= 0);
2104 Span* endSpan = &fTs[end];
2105 Segment* other;
2106 if (isSimple(end)) {
2107 // mark the smaller of startIndex, endIndex done, and all adjacent
2108 // spans with the same T value (but not 'other' spans)
2109 #if DEBUG_WINDING
2110 SkDebugf("%s simple\n", __FUNCTION__);
2111 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002112 int min = SkMin32(startIndex, endIndex);
2113 if (fTs[min].fDone) {
2114 return NULL;
2115 }
2116 markDoneBinary(min);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002117 other = endSpan->fOther;
2118 nextStart = endSpan->fOtherIndex;
2119 double startT = other->fTs[nextStart].fT;
2120 nextEnd = nextStart;
2121 do {
2122 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002123 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002124 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002125 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2126 return other;
2127 }
2128 // more than one viable candidate -- measure angles to find best
2129 SkTDArray<Angle> angles;
2130 SkASSERT(startIndex - endIndex != 0);
2131 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2132 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002133 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002134 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002135 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002136 int angleCount = angles.count();
2137 int firstIndex = findStartingEdge(sorted, startIndex, end);
2138 SkASSERT(firstIndex >= 0);
2139 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002140 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002141 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002142 if (!sortable) {
2143 unsortable = true;
2144 return NULL;
2145 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002146 SkASSERT(sorted[firstIndex]->segment() == this);
2147 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002148 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2149 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002150 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002151 int sumMiWinding = updateWinding(endIndex, startIndex);
2152 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2153 if (operand()) {
2154 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002155 }
2156 int nextIndex = firstIndex + 1;
2157 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2158 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002159 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002160 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00002161 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002162 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002163 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002164 if (nextIndex == angleCount) {
2165 nextIndex = 0;
2166 }
2167 const Angle* nextAngle = sorted[nextIndex];
2168 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002169 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
2170 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2171 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2172 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2173 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002174 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002175 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002176 }
2177 if (nextSegment->done()) {
2178 continue;
2179 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002180 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2181 continue;
2182 }
2183 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2184 oppSumWinding, activeAngle, nextAngle);
2185 if (last) {
2186 *chase.append() = last;
2187#if DEBUG_WINDING
2188 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2189 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2190#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002191 }
2192 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002193 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002194 if (!foundAngle) {
2195 return NULL;
2196 }
2197 nextStart = foundAngle->start();
2198 nextEnd = foundAngle->end();
2199 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002200
caryclark@google.com235f56a2012-09-14 14:19:30 +00002201 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002202 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2203 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002204 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002205 return nextSegment;
2206 }
caryclark@google.com47580692012-07-23 12:14:49 +00002207
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002208 // so the span needs to contain the pairing info found here
2209 // this should include the winding computed for the edge, and
2210 // what edge it connects to, and whether it is discarded
2211 // (maybe discarded == abs(winding) > 1) ?
2212 // only need derivatives for duration of sorting, add a new struct
2213 // for pairings, remove extra spans that have zero length and
2214 // reference an unused other
2215 // for coincident, the last span on the other may be marked done
2216 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002217
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002218 // if loop is exhausted, contour may be closed.
2219 // FIXME: pass in close point so we can check for closure
2220
2221 // given a segment, and a sense of where 'inside' is, return the next
2222 // segment. If this segment has an intersection, or ends in multiple
2223 // segments, find the mate that continues the outside.
2224 // note that if there are multiples, but no coincidence, we can limit
2225 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002226
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002227 // mark found segments as done
2228
caryclark@google.com15fa1382012-05-07 20:49:36 +00002229 // start is the index of the beginning T of this edge
2230 // it is guaranteed to have an end which describes a non-zero length (?)
2231 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002232 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002233 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2234 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002235 const int startIndex = nextStart;
2236 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002237 int outerWinding = winding;
2238 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002239 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002240 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2241 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002242 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002243 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002244 outerWinding = innerWinding;
2245 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002246 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002247 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002248 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2249 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002250 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002251 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002252 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002253 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002254 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002255 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002256 // mark the smaller of startIndex, endIndex done, and all adjacent
2257 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002258 #if DEBUG_WINDING
2259 SkDebugf("%s simple\n", __FUNCTION__);
2260 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002261 int min = SkMin32(startIndex, endIndex);
2262 if (fTs[min].fDone) {
2263 return NULL;
2264 }
2265 markDone(min, outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002266 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002267 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002268 double startT = other->fTs[nextStart].fT;
2269 nextEnd = nextStart;
2270 do {
2271 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002272 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002273 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002274 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002275 return other;
2276 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002277 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002278 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002279 SkASSERT(startIndex - endIndex != 0);
2280 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002281 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002282 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002283 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002284 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002285 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002286 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002287 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002288 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002289 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002290 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002291 if (!sortable) {
2292 unsortable = true;
2293 return NULL;
2294 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002295 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002296 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002297 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002298 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002299 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002300 int nextIndex = firstIndex + 1;
2301 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2302 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002303 // FIXME: found done logic probably fails if there are more than 4
2304 // sorted angles. It should bias towards the first and last undone
2305 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002306 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002307 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002308 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002309 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002310 bool altFlipped = false;
2311 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002312 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002313 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002314 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002315 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002316 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002317 nextIndex = 0;
2318 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002319 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002320 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002321 if (sumWinding) {
2322 lastNonZeroSum = sumWinding;
2323 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002324 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002325 bool nextDone = nextSegment->done(nextAngle);
2326 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002327 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002328 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002329 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002330 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002331 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2332 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002333 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002334 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002335 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002336 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002337 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002338 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002339 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002340 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002341 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002342 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002343 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002344 return NULL;
2345 }
caryclark@google.com47580692012-07-23 12:14:49 +00002346 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002347 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002348 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002349 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002350 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002351 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002352 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002353
caryclark@google.com24bec792012-08-20 12:43:57 +00002354 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002355 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002356 if (foundAngle && foundDone2) {
2357 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002358 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002359 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002360 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002361 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002362 foundFlipped = altFlipped;
2363 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002364 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002365 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002366 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002367 }
2368 // if the winding is non-zero, nextAngle does not connect to
2369 // current chain. If we haven't done so already, mark the angle
2370 // as done, record the winding value, and mark connected unambiguous
2371 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002372 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002373 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002374 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002375 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002376 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002377 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002378 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002379 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002380 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002381 }
2382 if (last) {
2383 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002384 #if DEBUG_WINDING
2385 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2386 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2387 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002388 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002389 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002390 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002391 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002392 if (!foundAngle) {
2393 return NULL;
2394 }
2395 nextStart = foundAngle->start();
2396 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002397 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002398 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002399 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2400 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002401 if (winding) {
2402 #if DEBUG_WINDING
2403 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2404 if (foundSum == SK_MinS32) {
2405 SkDebugf("?");
2406 } else {
2407 SkDebugf("%d", foundSum);
2408 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002409 SkDebugf("\n");
2410 #endif
2411 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002412 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002413 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002414 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002415 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002416 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002417 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002418
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002419 Segment* findNextWinding(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2420 bool& unsortable) {
2421 const int startIndex = nextStart;
2422 const int endIndex = nextEnd;
2423 SkASSERT(startIndex != endIndex);
2424 const int count = fTs.count();
2425 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2426 const int step = SkSign32(endIndex - startIndex);
2427 const int end = nextExactSpan(startIndex, step);
2428 SkASSERT(end >= 0);
2429 Span* endSpan = &fTs[end];
2430 Segment* other;
2431 if (isSimple(end)) {
2432 // mark the smaller of startIndex, endIndex done, and all adjacent
2433 // spans with the same T value (but not 'other' spans)
2434 #if DEBUG_WINDING
2435 SkDebugf("%s simple\n", __FUNCTION__);
2436 #endif
2437 int min = SkMin32(startIndex, endIndex);
2438 if (fTs[min].fDone) {
2439 return NULL;
2440 }
2441 markDoneUnary(min);
2442 other = endSpan->fOther;
2443 nextStart = endSpan->fOtherIndex;
2444 double startT = other->fTs[nextStart].fT;
2445 nextEnd = nextStart;
2446 do {
2447 nextEnd += step;
2448 }
2449 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2450 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2451 return other;
2452 }
2453 // more than one viable candidate -- measure angles to find best
2454 SkTDArray<Angle> angles;
2455 SkASSERT(startIndex - endIndex != 0);
2456 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2457 addTwoAngles(startIndex, end, angles);
2458 buildAngles(end, angles, true);
2459 SkTDArray<Angle*> sorted;
2460 bool sortable = SortAngles(angles, sorted);
2461 int angleCount = angles.count();
2462 int firstIndex = findStartingEdge(sorted, startIndex, end);
2463 SkASSERT(firstIndex >= 0);
2464 #if DEBUG_SORT
2465 debugShowSort(__FUNCTION__, sorted, firstIndex);
2466 #endif
2467 if (!sortable) {
2468 unsortable = true;
2469 return NULL;
2470 }
2471 SkASSERT(sorted[firstIndex]->segment() == this);
2472 #if DEBUG_WINDING
2473 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2474 sorted[firstIndex]->sign());
2475 #endif
2476 int sumWinding = updateWinding(endIndex, startIndex);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002477 int nextIndex = firstIndex + 1;
2478 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2479 const Angle* foundAngle = NULL;
2480 bool foundDone = false;
2481 // iterate through the angle, and compute everyone's winding
2482 Segment* nextSegment;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002483 int activeCount = 0;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002484 do {
2485 SkASSERT(nextIndex != firstIndex);
2486 if (nextIndex == angleCount) {
2487 nextIndex = 0;
2488 }
2489 const Angle* nextAngle = sorted[nextIndex];
2490 nextSegment = nextAngle->segment();
2491 int maxWinding;
skia.committer@gmail.com7a03d862012-12-18 02:03:03 +00002492 bool activeAngle = nextSegment->activeWinding(nextAngle->start(), nextAngle->end(),
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002493 maxWinding, sumWinding);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002494 if (activeAngle) {
2495 ++activeCount;
2496 if (!foundAngle || (foundDone && activeCount & 1)) {
2497 if (nextSegment->tiny(nextAngle)) {
2498 unsortable = true;
2499 return NULL;
2500 }
2501 foundAngle = nextAngle;
2502 foundDone = nextSegment->done(nextAngle);
2503 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00002504 }
2505 if (nextSegment->done()) {
2506 continue;
2507 }
2508 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2509 continue;
2510 }
2511 Span* last = nextSegment->markAngle(maxWinding, sumWinding, activeAngle, nextAngle);
2512 if (last) {
2513 *chase.append() = last;
2514#if DEBUG_WINDING
2515 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2516 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2517#endif
2518 }
2519 } while (++nextIndex != lastIndex);
2520 markDoneUnary(SkMin32(startIndex, endIndex));
2521 if (!foundAngle) {
2522 return NULL;
2523 }
2524 nextStart = foundAngle->start();
2525 nextEnd = foundAngle->end();
2526 nextSegment = foundAngle->segment();
2527
2528 #if DEBUG_WINDING
2529 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2530 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
2531 #endif
2532 return nextSegment;
2533 }
2534
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002535 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002536 const int startIndex = nextStart;
2537 const int endIndex = nextEnd;
2538 SkASSERT(startIndex != endIndex);
2539 int count = fTs.count();
2540 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2541 : startIndex > 0);
2542 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002543 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002544 SkASSERT(end >= 0);
2545 Span* endSpan = &fTs[end];
2546 Segment* other;
caryclark@google.com24bec792012-08-20 12:43:57 +00002547 if (isSimple(end)) {
2548 #if DEBUG_WINDING
2549 SkDebugf("%s simple\n", __FUNCTION__);
2550 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002551 int min = SkMin32(startIndex, endIndex);
2552 if (fTs[min].fDone) {
2553 return NULL;
2554 }
2555 markDone(min, 1);
caryclark@google.com24bec792012-08-20 12:43:57 +00002556 other = endSpan->fOther;
2557 nextStart = endSpan->fOtherIndex;
2558 double startT = other->fTs[nextStart].fT;
2559 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002560 if ((approximately_less_than_zero(startT) && step < 0)
2561 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002562 step = -step;
2563 SkDEBUGCODE(firstLoop = false;)
2564 }
2565 do {
2566 nextEnd = nextStart;
2567 do {
2568 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002569 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002570 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002571 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2572 break;
2573 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002574 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002575 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002576 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002577 SkDEBUGCODE(firstLoop = false;)
2578 step = -step;
2579 } while (true);
2580 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2581 return other;
2582 }
2583 SkTDArray<Angle> angles;
2584 SkASSERT(startIndex - endIndex != 0);
2585 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2586 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002587 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002588 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002589 bool sortable = SortAngles(angles, sorted);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002590 if (!sortable) {
2591 unsortable = true;
2592 return NULL;
2593 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002594 int angleCount = angles.count();
2595 int firstIndex = findStartingEdge(sorted, startIndex, end);
2596 SkASSERT(firstIndex >= 0);
2597 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002598 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002599 #endif
2600 SkASSERT(sorted[firstIndex]->segment() == this);
2601 int nextIndex = firstIndex + 1;
2602 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2603 const Angle* nextAngle;
2604 Segment* nextSegment;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002605 bool foundAngle = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002606 do {
2607 if (nextIndex == angleCount) {
2608 nextIndex = 0;
2609 }
2610 nextAngle = sorted[nextIndex];
2611 nextSegment = nextAngle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002612 if (!nextSegment->done(nextAngle) || nextSegment->tiny(nextAngle)) {
2613 foundAngle = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00002614 break;
2615 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002616 } while (++nextIndex != lastIndex);
2617 markDone(SkMin32(startIndex, endIndex), 1);
2618 if (!foundAngle) {
2619 nextIndex = firstIndex + 1 == angleCount ? 0 : firstIndex + 1;
2620 nextAngle = sorted[nextIndex];
2621 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002622 nextStart = nextAngle->start();
2623 nextEnd = nextAngle->end();
2624 return nextSegment;
2625 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002626
2627 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2628 int angleCount = sorted.count();
2629 int firstIndex = -1;
2630 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2631 const Angle* angle = sorted[angleIndex];
2632 if (angle->segment() == this && angle->start() == end &&
2633 angle->end() == start) {
2634 firstIndex = angleIndex;
2635 break;
2636 }
2637 }
2638 return firstIndex;
2639 }
2640
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002641 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com4eeda372012-12-06 21:47:48 +00002642 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002643 int count = fTs.count();
2644 if (count < 3) { // require t=0, x, 1 at minimum
2645 return;
2646 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002647 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002648 int moCount;
2649 Span* match;
2650 Segment* mOther;
2651 do {
2652 match = &fTs[matchIndex];
2653 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002654 // FIXME: allow quads, cubics to be near coincident?
2655 if (mOther->fVerb == SkPath::kLine_Verb) {
2656 moCount = mOther->fTs.count();
2657 if (moCount >= 3) {
2658 break;
2659 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002660 }
2661 if (++matchIndex >= count) {
2662 return;
2663 }
2664 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002665 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002666 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002667 // look for a pair of nearby T values that map to the same (x,y) value
2668 // if found, see if the pair of other segments share a common point. If
2669 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002670 for (int index = matchIndex + 1; index < count; ++index) {
2671 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002672 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002673 continue;
2674 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002675 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002676 if (tOther->fVerb != SkPath::kLine_Verb) {
2677 continue; // FIXME: allow quads, cubics to be near coincident?
2678 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002679 int toCount = tOther->fTs.count();
2680 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002681 continue;
2682 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002683 const SkPoint* testPt = &xyAtT(test);
2684 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002685 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002686 moCount = toCount;
2687 match = test;
2688 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002689 matchPt = testPt;
2690 continue;
2691 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002692 int moStart = -1;
2693 int moEnd = -1;
2694 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002695 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002696 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002697 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002698 continue;
2699 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002700 if (moSpan.fOther == this) {
2701 if (moSpan.fOtherT == match->fT) {
2702 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002703 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002704 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002705 continue;
2706 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002707 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002708 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2709 moStart = -1;
2710 break;
2711 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002712 SkASSERT(moEnd == -1);
2713 moEnd = moIndex;
2714 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002715 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002716 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002717 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002718 continue;
2719 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002720 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002721 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002722 continue;
2723 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002724 int toStart = -1;
2725 int toEnd = -1;
2726 double toStartT, toEndT;
2727 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2728 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002729 if (toSpan.fDone) {
2730 continue;
2731 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002732 if (toSpan.fOther == this) {
2733 if (toSpan.fOtherT == test->fT) {
2734 toStart = toIndex;
2735 toStartT = toSpan.fT;
2736 }
2737 continue;
2738 }
2739 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002740 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2741 moStart = -1;
2742 break;
2743 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002744 SkASSERT(toEnd == -1);
2745 toEnd = toIndex;
2746 toEndT = toSpan.fT;
2747 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002748 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002749 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2750 if (toStart <= 0 || toEnd <= 0) {
2751 continue;
2752 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002753 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002754 continue;
2755 }
2756 // test to see if the segment between there and here is linear
2757 if (!mOther->isLinear(moStart, moEnd)
2758 || !tOther->isLinear(toStart, toEnd)) {
2759 continue;
2760 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002761 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002762 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002763 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002765 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002766 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002767 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002768 }
2769
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002770 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002771 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002772 // a) mark spans with either end unsortable as done, or
2773 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2774 // when encountering an unsortable span
2775
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002776 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2777 // and use more concise logic like the old edge walker code?
2778 // FIXME: this needs to deal with coincident edges
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002779 Segment* findTop(int& tIndex, int& endIndex, bool& unsortable, bool onlySortable) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002780 // iterate through T intersections and return topmost
2781 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002782 SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002783 int firstT = -1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002784 SkPoint topPt;
2785 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002786 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002787 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002788 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002789 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002790 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002791 const Span& span = fTs[index];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002792 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002793 goto next;
2794 }
2795 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002796 const SkPoint& intercept = xyAtT(&span);
2797 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2798 && topPt.fX > intercept.fX)) {
2799 topPt = intercept;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002800 firstT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002801 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002802 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002803 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002804 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002805 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002806 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002807 SkASSERT(firstT >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002808 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002809 int step = 1;
2810 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002811 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002812 step = -1;
2813 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002814 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002815 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002816 // if the topmost T is not on end, or is three-way or more, find left
2817 // look for left-ness from tLeft to firstT (matching y of other)
2818 SkTDArray<Angle> angles;
2819 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002820 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002821 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002822 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002823 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002824 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002825 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002826 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002827 if (onlySortable && !sortable) {
2828 unsortable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002829 return NULL;
2830 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002831 // skip edges that have already been processed
2832 firstT = -1;
2833 Segment* leftSegment;
2834 do {
2835 const Angle* angle = sorted[++firstT];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002836 SkASSERT(!onlySortable || !angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002837 leftSegment = angle->segment();
2838 tIndex = angle->end();
2839 endIndex = angle->start();
2840 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002841 SkASSERT(!leftSegment->fTs[SkMin32(tIndex, endIndex)].fTiny);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002842 return leftSegment;
2843 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002844
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002845 // FIXME: not crazy about this
2846 // when the intersections are performed, the other index is into an
2847 // incomplete array. as the array grows, the indices become incorrect
2848 // while the following fixes the indices up again, it isn't smart about
2849 // skipping segments whose indices are already correct
2850 // assuming we leave the code that wrote the index in the first place
2851 void fixOtherTIndex() {
2852 int iCount = fTs.count();
2853 for (int i = 0; i < iCount; ++i) {
2854 Span& iSpan = fTs[i];
2855 double oT = iSpan.fOtherT;
2856 Segment* other = iSpan.fOther;
2857 int oCount = other->fTs.count();
2858 for (int o = 0; o < oCount; ++o) {
2859 Span& oSpan = other->fTs[o];
2860 if (oT == oSpan.fT && this == oSpan.fOther) {
2861 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002862 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002863 }
2864 }
2865 }
2866 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002867
caryclark@google.com4eeda372012-12-06 21:47:48 +00002868 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002869 fDoneSpans = 0;
2870 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002871 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002872 fPts = pts;
2873 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002874 }
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00002875
caryclark@google.com3586ece2012-12-27 18:46:58 +00002876 void initWinding(int start, int end) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002877 int local = spanSign(start, end);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002878 int oppLocal = oppSign(start, end);
caryclark@google.com3586ece2012-12-27 18:46:58 +00002879 (void) markAndChaseWinding(start, end, local, oppLocal);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002880 }
2881
caryclark@google.com3586ece2012-12-27 18:46:58 +00002882 void initWinding(int start, int end, int winding, int oppWinding) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002883 int local = spanSign(start, end);
caryclark@google.com3586ece2012-12-27 18:46:58 +00002884 if (local * winding >= 0) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002885 winding += local;
2886 }
2887 int oppLocal = oppSign(start, end);
2888 if (oppLocal * oppWinding >= 0) {
2889 oppWinding += oppLocal;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002890 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002891 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002892 }
2893
caryclark@google.com3586ece2012-12-27 18:46:58 +00002894/*
2895when we start with a vertical intersect, we try to use the dx to determine if the edge is to
2896the left or the right of vertical. This determines if we need to add the span's
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00002897sign or not. However, this isn't enough.
2898If the supplied sign (winding) is zero, then we didn't hit another vertical span, so dx is needed.
caryclark@google.com3586ece2012-12-27 18:46:58 +00002899If there was a winding, then it may or may not need adjusting. If the span the winding was borrowed
2900from has the same x direction as this span, the winding should change. If the dx is opposite, then
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00002901the same winding is shared by both.
caryclark@google.com3586ece2012-12-27 18:46:58 +00002902*/
2903 void initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
2904 SkScalar hitOppDx) {
2905 SkASSERT(hitDx || !winding);
2906 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
2907 SkASSERT(dx);
2908 int windVal = windValue(SkMin32(start, end));
2909 if (!winding) {
2910 winding = dx < 0 ? windVal : -windVal;
2911 } else if (hitDx * dx >= 0) {
2912 int sideWind = winding + (dx < 0 ? windVal : -windVal);
2913 if (abs(winding) < abs(sideWind)) {
2914 winding = sideWind;
2915 }
2916 }
2917 int oppLocal = oppSign(start, end);
2918 SkASSERT(hitOppDx || !oppWind || !oppLocal);
2919 int oppWindVal = oppValue(SkMin32(start, end));
2920 if (!oppWind) {
2921 oppWind = dx < 0 ? oppWindVal : -oppWindVal;
2922 } else if (hitOppDx * dx >= 0) {
2923 int oppSideWind = oppWind + (dx < 0 ? oppWindVal : -oppWindVal);
2924 if (abs(oppWind) < abs(oppSideWind)) {
2925 oppWind = oppSideWind;
2926 }
2927 }
2928 (void) markAndChaseWinding(start, end, winding, oppWind);
2929 }
2930
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002931 bool intersected() const {
2932 return fTs.count() > 0;
2933 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002934
caryclark@google.com10227bf2012-12-28 22:10:41 +00002935 bool isCanceled(int tIndex) const {
2936 return fTs[tIndex].fWindValue == 0 && fTs[tIndex].fOppValue == 0;
2937 }
2938
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002939 bool isConnected(int startIndex, int endIndex) const {
2940 return fTs[startIndex].fWindSum != SK_MinS32
2941 || fTs[endIndex].fWindSum != SK_MinS32;
2942 }
2943
caryclark@google.com235f56a2012-09-14 14:19:30 +00002944 bool isHorizontal() const {
2945 return fBounds.fTop == fBounds.fBottom;
2946 }
2947
caryclark@google.com15fa1382012-05-07 20:49:36 +00002948 bool isLinear(int start, int end) const {
2949 if (fVerb == SkPath::kLine_Verb) {
2950 return true;
2951 }
2952 if (fVerb == SkPath::kQuad_Verb) {
2953 SkPoint qPart[3];
2954 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2955 return QuadIsLinear(qPart);
2956 } else {
2957 SkASSERT(fVerb == SkPath::kCubic_Verb);
2958 SkPoint cPart[4];
2959 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2960 return CubicIsLinear(cPart);
2961 }
2962 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002963
2964 // OPTIMIZE: successive calls could start were the last leaves off
2965 // or calls could specialize to walk forwards or backwards
2966 bool isMissing(double startT) const {
2967 size_t tCount = fTs.count();
2968 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002969 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002970 return false;
2971 }
2972 }
2973 return true;
2974 }
2975
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002976 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002977 int count = fTs.count();
2978 if (count == 2) {
2979 return true;
2980 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002981 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002982 if (approximately_less_than_zero(t)) {
2983 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002984 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002985 if (approximately_greater_than_one(t)) {
2986 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002987 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002988 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002989 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002990
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002991 bool isVertical() const {
2992 return fBounds.fLeft == fBounds.fRight;
2993 }
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +00002994
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00002995 bool isVertical(int start, int end) const {
2996 return (*SegmentVertical[fVerb])(fPts, start, end);
2997 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002998
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002999 SkScalar leftMost(int start, int end) const {
3000 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
3001 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003002
caryclark@google.com495f8e42012-05-31 13:13:11 +00003003 // this span is excluded by the winding rule -- chase the ends
3004 // as long as they are unambiguous to mark connections as done
3005 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00003006 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003007 int index = angle->start();
3008 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00003009 return markAndChaseDone(index, endIndex, winding);
3010 }
3011
caryclark@google.com31143cf2012-11-09 22:14:19 +00003012 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003013 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003014 int min = SkMin32(index, endIndex);
3015 markDone(min, winding);
3016 Span* last;
3017 Segment* other = this;
3018 while ((other = other->nextChase(index, step, min, last))) {
3019 other->markDone(min, winding);
3020 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003021 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003022 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003023
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003024 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
3025 int index = angle->start();
3026 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00003027 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003028 int min = SkMin32(index, endIndex);
3029 markDoneBinary(min, winding, oppWinding);
3030 Span* last;
3031 Segment* other = this;
3032 while ((other = other->nextChase(index, step, min, last))) {
3033 other->markDoneBinary(min, winding, oppWinding);
3034 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003035 return last;
3036 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003037
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003038 Span* markAndChaseDoneBinary(int index, int endIndex) {
3039 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003040 int min = SkMin32(index, endIndex);
3041 markDoneBinary(min);
3042 Span* last;
3043 Segment* other = this;
3044 while ((other = other->nextChase(index, step, min, last))) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003045 if (other->done()) {
3046 return NULL;
3047 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003048 other->markDoneBinary(min);
3049 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003050 return last;
3051 }
3052
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003053 Span* markAndChaseDoneUnary(int index, int endIndex) {
3054 int step = SkSign32(endIndex - index);
3055 int min = SkMin32(index, endIndex);
3056 markDoneUnary(min);
3057 Span* last;
3058 Segment* other = this;
3059 while ((other = other->nextChase(index, step, min, last))) {
3060 if (other->done()) {
3061 return NULL;
3062 }
3063 other->markDoneUnary(min);
3064 }
3065 return last;
3066 }
3067
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003068 Span* markAndChaseDoneUnary(const Angle* angle, int winding) {
3069 int index = angle->start();
3070 int endIndex = angle->end();
3071 return markAndChaseDone(index, endIndex, winding);
3072 }
3073
caryclark@google.com4eeda372012-12-06 21:47:48 +00003074 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003075 int index = angle->start();
3076 int endIndex = angle->end();
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003077 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003078 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00003079 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003080 Span* last;
3081 Segment* other = this;
3082 while ((other = other->nextChase(index, step, min, last))) {
3083 if (other->fTs[min].fWindSum != SK_MinS32) {
3084 SkASSERT(other->fTs[min].fWindSum == winding);
3085 return NULL;
3086 }
3087 other->markWinding(min, winding);
3088 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003089 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003090 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003091
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003092 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003093 int min = SkMin32(index, endIndex);
3094 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003095 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003096 Span* last;
3097 Segment* other = this;
3098 while ((other = other->nextChase(index, step, min, last))) {
3099 if (other->fTs[min].fWindSum != SK_MinS32) {
3100 SkASSERT(other->fTs[min].fWindSum == winding);
3101 return NULL;
3102 }
3103 other->markWinding(min, winding, oppWinding);
3104 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003105 return last;
3106 }
3107
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003108 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
3109 int start = angle->start();
3110 int end = angle->end();
3111 return markAndChaseWinding(start, end, winding, oppWinding);
3112 }
3113
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003114 Span* markAngle(int maxWinding, int sumWinding, bool activeAngle, const Angle* angle) {
3115 SkASSERT(angle->segment() == this);
3116 if (useInnerWinding(maxWinding, sumWinding)) {
3117 maxWinding = sumWinding;
3118 }
3119 Span* last;
3120 if (activeAngle) {
3121 last = markAndChaseWinding(angle, maxWinding);
3122 } else {
3123 last = markAndChaseDoneUnary(angle, maxWinding);
3124 }
3125 return last;
3126 }
3127
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003128 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
3129 bool activeAngle, const Angle* angle) {
3130 SkASSERT(angle->segment() == this);
3131 if (useInnerWinding(maxWinding, sumWinding)) {
3132 maxWinding = sumWinding;
3133 }
3134 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
3135 oppMaxWinding = oppSumWinding;
3136 }
3137 Span* last;
3138 if (activeAngle) {
3139 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
3140 } else {
3141 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
3142 }
3143 return last;
3144 }
3145
caryclark@google.com495f8e42012-05-31 13:13:11 +00003146 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003147 // This may be called when the segment is already marked done. While this
3148 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00003149 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003150 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00003151 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003152 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003153 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003154 double referenceT = fTs[index].fT;
3155 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003156 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3157 markOneDone(__FUNCTION__, lesser, winding);
3158 }
3159 do {
3160 markOneDone(__FUNCTION__, index, winding);
3161 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003162 }
3163
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003164 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003165 // SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003166 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003167 double referenceT = fTs[index].fT;
3168 int lesser = index;
3169 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003170 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003171 }
3172 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003173 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
3174 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3175 }
3176
3177 void markDoneBinary(int index) {
3178 double referenceT = fTs[index].fT;
3179 int lesser = index;
3180 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3181 markOneDoneBinary(__FUNCTION__, lesser);
3182 }
3183 do {
3184 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003185 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003186 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003187
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003188 void markDoneUnary(int index, int winding) {
3189 // SkASSERT(!done());
3190 SkASSERT(winding);
3191 double referenceT = fTs[index].fT;
3192 int lesser = index;
3193 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3194 markOneDoneUnary(__FUNCTION__, lesser, winding);
3195 }
3196 do {
3197 markOneDoneUnary(__FUNCTION__, index, winding);
3198 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3199 }
3200
3201 void markDoneUnary(int index) {
3202 double referenceT = fTs[index].fT;
3203 int lesser = index;
3204 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3205 markOneDoneUnary(__FUNCTION__, lesser);
3206 }
3207 do {
3208 markOneDoneUnary(__FUNCTION__, index);
3209 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
3210 }
3211
caryclark@google.com24bec792012-08-20 12:43:57 +00003212 void markOneDone(const char* funName, int tIndex, int winding) {
3213 Span* span = markOneWinding(funName, tIndex, winding);
3214 if (!span) {
3215 return;
3216 }
3217 span->fDone = true;
3218 fDoneSpans++;
3219 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003220
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003221 void markOneDoneBinary(const char* funName, int tIndex) {
3222 Span* span = verifyOneWinding(funName, tIndex);
3223 if (!span) {
3224 return;
3225 }
3226 span->fDone = true;
3227 fDoneSpans++;
3228 }
3229
3230 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003231 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
3232 if (!span) {
3233 return;
3234 }
3235 span->fDone = true;
3236 fDoneSpans++;
3237 }
3238
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003239 void markOneDoneUnary(const char* funName, int tIndex) {
3240 Span* span = verifyOneWindingU(funName, tIndex);
3241 if (!span) {
3242 return;
3243 }
3244 span->fDone = true;
3245 fDoneSpans++;
3246 }
3247
3248 void markOneDoneUnary(const char* funName, int tIndex, int winding) {
3249 Span* span = markOneWinding(funName, tIndex, winding);
3250 if (!span) {
3251 return;
3252 }
3253 span->fDone = true;
3254 fDoneSpans++;
3255 }
3256
caryclark@google.com24bec792012-08-20 12:43:57 +00003257 Span* markOneWinding(const char* funName, int tIndex, int winding) {
3258 Span& span = fTs[tIndex];
3259 if (span.fDone) {
3260 return NULL;
3261 }
3262 #if DEBUG_MARK_DONE
3263 debugShowNewWinding(funName, span, winding);
3264 #endif
3265 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00003266 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00003267 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00003268 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00003269 span.fWindSum = winding;
3270 return &span;
3271 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00003272
caryclark@google.com31143cf2012-11-09 22:14:19 +00003273 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
3274 Span& span = fTs[tIndex];
3275 if (span.fDone) {
3276 return NULL;
3277 }
3278 #if DEBUG_MARK_DONE
3279 debugShowNewWinding(funName, span, winding, oppWinding);
3280 #endif
3281 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
3282 #ifdef SK_DEBUG
3283 SkASSERT(abs(winding) <= gDebugMaxWindSum);
3284 #endif
3285 span.fWindSum = winding;
3286 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
3287 #ifdef SK_DEBUG
3288 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
3289 #endif
3290 span.fOppSum = oppWinding;
3291 return &span;
3292 }
3293
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003294 Span* verifyOneWinding(const char* funName, int tIndex) {
3295 Span& span = fTs[tIndex];
3296 if (span.fDone) {
3297 return NULL;
3298 }
3299 #if DEBUG_MARK_DONE
3300 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
3301 #endif
3302 SkASSERT(span.fWindSum != SK_MinS32);
3303 SkASSERT(span.fOppSum != SK_MinS32);
3304 return &span;
3305 }
3306
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003307 Span* verifyOneWindingU(const char* funName, int tIndex) {
3308 Span& span = fTs[tIndex];
3309 if (span.fDone) {
3310 return NULL;
3311 }
3312 #if DEBUG_MARK_DONE
3313 debugShowNewWinding(funName, span, span.fWindSum);
3314 #endif
3315 SkASSERT(span.fWindSum != SK_MinS32);
3316 return &span;
3317 }
3318
caryclark@google.comf839c032012-10-26 21:03:50 +00003319 // note that just because a span has one end that is unsortable, that's
3320 // not enough to mark it done. The other end may be sortable, allowing the
3321 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003322 void markUnsortable(int start, int end) {
3323 Span* span = &fTs[start];
3324 if (start < end) {
3325 span->fUnsortableStart = true;
3326 } else {
3327 --span;
3328 span->fUnsortableEnd = true;
3329 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003330 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003331 return;
3332 }
3333 span->fDone = true;
3334 fDoneSpans++;
3335 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003336
caryclark@google.com59823f72012-08-09 18:17:47 +00003337 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003338 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003339 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003340 double referenceT = fTs[index].fT;
3341 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003342 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3343 markOneWinding(__FUNCTION__, lesser, winding);
3344 }
3345 do {
3346 markOneWinding(__FUNCTION__, index, winding);
3347 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003348 }
3349
3350 void markWinding(int index, int winding, int oppWinding) {
3351 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00003352 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003353 double referenceT = fTs[index].fT;
3354 int lesser = index;
3355 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3356 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003357 }
3358 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003359 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3360 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003361 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003362
caryclark@google.com2ddff932012-08-07 21:25:27 +00003363 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003364 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003365 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003366 if (tIndex > 0) {
3367 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003368 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003369 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003370 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003371 }
3372 }
3373 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3374 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003375 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003376 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003377 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003378 }
3379 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003380 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3381 const Span& below = fTs[tIndex - 1];
3382 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003383 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003384 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003385 if (nextDoorWind != SK_MaxS32) {
3386 Span& newSpan = fTs[tIndex];
3387 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003388 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003389 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003390 newSpan.fDone = true;
3391 ++fDoneSpans;
3392 }
3393 }
3394 }
3395
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003396 bool moreHorizontal(int index, int endIndex, bool& unsortable) const {
3397 // find bounds
3398 Bounds bounds;
3399 bounds.setPoint(xyAtT(index));
3400 bounds.add(xyAtT(endIndex));
3401 SkScalar width = bounds.width();
3402 SkScalar height = bounds.height();
3403 if (width > height) {
3404 if (approximately_negative(width)) {
3405 unsortable = true; // edge is too small to resolve meaningfully
3406 }
3407 return false;
3408 } else {
3409 if (approximately_negative(height)) {
3410 unsortable = true; // edge is too small to resolve meaningfully
3411 }
3412 return true;
3413 }
3414 }
3415
caryclark@google.com9764cc62012-07-12 19:29:45 +00003416 // return span if when chasing, two or more radiating spans are not done
3417 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3418 // candidate and the remaining spans have windValue == 0 (canceled by
3419 // coincidence). The coincident edges could either be removed altogether,
3420 // or this code could be more complicated in detecting this case. Worth it?
3421 bool multipleSpans(int end) const {
3422 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003423 }
3424
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003425 bool nextCandidate(int& start, int& end) const {
caryclark@google.com10227bf2012-12-28 22:10:41 +00003426 while (fTs[end].fDone) {
3427 if (fTs[end].fT == 1) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003428 return false;
3429 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00003430 ++end;
3431 }
3432 start = end;
3433 end = nextExactSpan(start, 1);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003434 return true;
3435 }
3436
caryclark@google.com4eeda372012-12-06 21:47:48 +00003437 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
3438 int end = nextExactSpan(index, step);
3439 SkASSERT(end >= 0);
3440 if (multipleSpans(end)) {
3441 last = &fTs[end];
3442 return NULL;
3443 }
3444 const Span& endSpan = fTs[end];
3445 Segment* other = endSpan.fOther;
3446 index = endSpan.fOtherIndex;
3447 int otherEnd = other->nextExactSpan(index, step);
3448 min = SkMin32(index, otherEnd);
3449 return other;
3450 }
3451
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003452 // This has callers for two different situations: one establishes the end
3453 // of the current span, and one establishes the beginning of the next span
3454 // (thus the name). When this is looking for the end of the current span,
3455 // coincidence is found when the beginning Ts contain -step and the end
3456 // contains step. When it is looking for the beginning of the next, the
3457 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003458 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003459 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003460 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003461 int count = fTs.count();
3462 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003463 while (step > 0 ? ++to < count : --to >= 0) {
3464 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003465 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003466 continue;
3467 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003468 return to;
3469 }
3470 return -1;
3471 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003472
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003473 // FIXME
3474 // this returns at any difference in T, vs. a preset minimum. It may be
3475 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003476 // OPTIMIZATION splitting this into separate loops for up/down steps
3477 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003478 int nextExactSpan(int from, int step) const {
3479 const Span& fromSpan = fTs[from];
3480 int count = fTs.count();
3481 int to = from;
3482 while (step > 0 ? ++to < count : --to >= 0) {
3483 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003484 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003485 continue;
3486 }
3487 return to;
3488 }
3489 return -1;
3490 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003491
caryclark@google.com235f56a2012-09-14 14:19:30 +00003492 bool operand() const {
3493 return fOperand;
3494 }
3495
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003496 int oppSign(const Angle* angle) const {
3497 SkASSERT(angle->segment() == this);
3498 return oppSign(angle->start(), angle->end());
3499 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003500
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003501 int oppSign(int startIndex, int endIndex) const {
3502 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3503 : fTs[endIndex].fOppValue;
3504#if DEBUG_WIND_BUMP
3505 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3506#endif
3507 return result;
3508 }
3509
caryclark@google.com31143cf2012-11-09 22:14:19 +00003510 int oppSum(int tIndex) const {
3511 return fTs[tIndex].fOppSum;
3512 }
3513
3514 int oppSum(const Angle* angle) const {
3515 int lesser = SkMin32(angle->start(), angle->end());
3516 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003517 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003518
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003519 int oppValue(int tIndex) const {
3520 return fTs[tIndex].fOppValue;
3521 }
3522
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003523 int oppValue(const Angle* angle) const {
3524 int lesser = SkMin32(angle->start(), angle->end());
3525 return fTs[lesser].fOppValue;
3526 }
3527
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003528 const SkPoint* pts() const {
3529 return fPts;
3530 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003531
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003532 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003533 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003534 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3535 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003536 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003537
caryclark@google.com4eeda372012-12-06 21:47:48 +00003538 void setOppXor(bool isOppXor) {
3539 fOppXor = isOppXor;
3540 }
3541
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003542 void setUpWinding(int index, int endIndex, int& maxWinding, int& sumWinding) {
3543 int deltaSum = spanSign(index, endIndex);
3544 maxWinding = sumWinding;
3545 sumWinding = sumWinding -= deltaSum;
3546 }
3547
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003548 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3549 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3550 int deltaSum = spanSign(index, endIndex);
3551 int oppDeltaSum = oppSign(index, endIndex);
3552 if (operand()) {
3553 maxWinding = sumSuWinding;
3554 sumWinding = sumSuWinding -= deltaSum;
3555 oppMaxWinding = sumMiWinding;
3556 oppSumWinding = sumMiWinding -= oppDeltaSum;
3557 } else {
3558 maxWinding = sumMiWinding;
3559 sumWinding = sumMiWinding -= deltaSum;
3560 oppMaxWinding = sumSuWinding;
3561 oppSumWinding = sumSuWinding -= oppDeltaSum;
3562 }
3563 }
3564
caryclark@google.comf839c032012-10-26 21:03:50 +00003565 // This marks all spans unsortable so that this info is available for early
3566 // exclusion in find top and others. This could be optimized to only mark
3567 // adjacent spans that unsortable. However, this makes it difficult to later
3568 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003569 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003570 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003571 int angleCount = angles.count();
3572 int angleIndex;
3573 angleList.setReserve(angleCount);
3574 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003575 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003576 *angleList.append() = &angle;
3577 sortable &= !angle.unsortable();
3578 }
3579 if (sortable) {
3580 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3581 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3582 if (angles[angleIndex].unsortable()) {
3583 sortable = false;
3584 break;
3585 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003586 }
3587 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003588 if (!sortable) {
3589 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3590 Angle& angle = angles[angleIndex];
3591 angle.segment()->markUnsortable(angle.start(), angle.end());
3592 }
3593 }
3594 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003595 }
3596
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003597 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003598 const Span& span(int tIndex) const {
3599 return fTs[tIndex];
3600 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003601
caryclark@google.com235f56a2012-09-14 14:19:30 +00003602 int spanSign(const Angle* angle) const {
3603 SkASSERT(angle->segment() == this);
3604 return spanSign(angle->start(), angle->end());
3605 }
3606
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003607 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003608 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3609 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003610#if DEBUG_WIND_BUMP
3611 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3612#endif
3613 return result;
3614 }
3615
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003616 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003617 double t(int tIndex) const {
3618 return fTs[tIndex].fT;
3619 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003620
caryclark@google.com10227bf2012-12-28 22:10:41 +00003621 double tAtMid(int start, int end, double mid) const {
3622 return fTs[start].fT * (1 - mid) + fTs[end].fT * mid;
3623 }
3624
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003625 bool tiny(const Angle* angle) const {
3626 int start = angle->start();
3627 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003628 const Span& mSpan = fTs[SkMin32(start, end)];
3629 return mSpan.fTiny;
3630 }
3631
caryclark@google.com18063442012-07-25 12:05:18 +00003632 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3633 double start) {
3634 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003635 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003636 *outsideTs.append() = end;
3637 *outsideTs.append() = start;
3638 }
3639 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003640
caryclark@google.com24bec792012-08-20 12:43:57 +00003641 void undoneSpan(int& start, int& end) {
3642 size_t tCount = fTs.count();
3643 size_t index;
3644 for (index = 0; index < tCount; ++index) {
3645 if (!fTs[index].fDone) {
3646 break;
3647 }
3648 }
3649 SkASSERT(index < tCount - 1);
3650 start = index;
3651 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003652 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003653 SkASSERT(index < tCount);
3654 SkASSERT(index < tCount);
3655 end = index;
3656 }
caryclark@google.com18063442012-07-25 12:05:18 +00003657
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003658 bool unsortable(int index) const {
3659 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3660 }
3661
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003662 void updatePts(const SkPoint pts[]) {
3663 fPts = pts;
3664 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003665
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003666 int updateOppWinding(int index, int endIndex) const {
3667 int lesser = SkMin32(index, endIndex);
3668 int oppWinding = oppSum(lesser);
3669 int oppSpanWinding = oppSign(index, endIndex);
3670 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3671 oppWinding -= oppSpanWinding;
3672 }
3673 return oppWinding;
3674 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003675
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003676 int updateOppWinding(const Angle* angle) const {
3677 int startIndex = angle->start();
3678 int endIndex = angle->end();
3679 return updateOppWinding(endIndex, startIndex);
3680 }
3681
3682 int updateOppWindingReverse(const Angle* angle) const {
3683 int startIndex = angle->start();
3684 int endIndex = angle->end();
3685 return updateOppWinding(startIndex, endIndex);
3686 }
3687
3688 int updateWinding(int index, int endIndex) const {
3689 int lesser = SkMin32(index, endIndex);
3690 int winding = windSum(lesser);
3691 int spanWinding = spanSign(index, endIndex);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003692 if (winding && useInnerWinding(winding - spanWinding, winding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003693 winding -= spanWinding;
3694 }
3695 return winding;
3696 }
3697
3698 int updateWinding(const Angle* angle) const {
3699 int startIndex = angle->start();
3700 int endIndex = angle->end();
3701 return updateWinding(endIndex, startIndex);
3702 }
3703
3704 int updateWindingReverse(const Angle* angle) const {
3705 int startIndex = angle->start();
3706 int endIndex = angle->end();
3707 return updateWinding(startIndex, endIndex);
3708 }
3709
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003710 SkPath::Verb verb() const {
3711 return fVerb;
3712 }
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +00003713
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00003714 int windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar& dx) const {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003715 if (approximately_zero(tHit - t(tIndex))) { // if we hit the end of a span, disregard
3716 return SK_MinS32;
3717 }
3718 int winding = crossOpp ? oppSum(tIndex) : windSum(tIndex);
3719 SkASSERT(winding != SK_MinS32);
3720 int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
3721 #if DEBUG_WINDING
3722 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
3723 windVal);
3724 #endif
3725 // see if a + change in T results in a +/- change in X (compute x'(T))
caryclark@google.com3586ece2012-12-27 18:46:58 +00003726 dx = (*SegmentDXAtT[fVerb])(fPts, tHit);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003727 if (fVerb > SkPath::kLine_Verb && approximately_zero(dx)) {
3728 dx = fPts[2].fX - fPts[1].fX - dx;
3729 }
3730 #if DEBUG_WINDING
3731 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
3732 #endif
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003733 if (dx == 0) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003734 return SK_MinS32;
3735 }
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00003736 if (winding * dx > 0) { // if same signs, result is negative
3737 winding += dx > 0 ? -windVal : windVal;
3738 #if DEBUG_WINDING
3739 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
3740 #endif
3741 }
3742 return winding;
3743 }
3744
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003745 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003746 return fTs[tIndex].fWindSum;
3747 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003748
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003749 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003750 int start = angle->start();
3751 int end = angle->end();
3752 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003753 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003754 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003755
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003756 int windValue(int tIndex) const {
3757 return fTs[tIndex].fWindValue;
3758 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003759
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003760 int windValue(const Angle* angle) const {
3761 int start = angle->start();
3762 int end = angle->end();
3763 int index = SkMin32(start, end);
3764 return windValue(index);
3765 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003766
caryclark@google.com3586ece2012-12-27 18:46:58 +00003767 SkScalar xAtT(int index) const {
3768 return xAtT(&fTs[index]);
3769 }
3770
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003771 SkScalar xAtT(const Span* span) const {
3772 return xyAtT(span).fX;
3773 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003774
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003775 const SkPoint& xyAtT(int index) const {
3776 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003777 }
3778
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003779 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003780 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003781 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003782 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003783 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003784 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003785 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003786 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003787 }
3788 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003789 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003790 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003791
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003792 // used only by right angle winding finding
caryclark@google.com10227bf2012-12-28 22:10:41 +00003793 void xyAtT(double mid, SkPoint& pt) const {
3794 (*SegmentXYAtT[fVerb])(fPts, mid, &pt);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00003795 }
3796
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003797 SkScalar yAtT(int index) const {
3798 return yAtT(&fTs[index]);
3799 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003800
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003801 SkScalar yAtT(const Span* span) const {
3802 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003803 }
3804
caryclark@google.com4eeda372012-12-06 21:47:48 +00003805 void zeroCoincidentOpp(Span* oTest, int index) {
3806 Span* const test = &fTs[index];
3807 Span* end = test;
3808 do {
3809 end->fOppValue = 0;
3810 end = &fTs[++index];
3811 } while (approximately_negative(end->fT - test->fT));
3812 }
3813
3814 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3815 Span* const oTest = &fTs[oIndex];
3816 Span* oEnd = oTest;
3817 const double startT = test->fT;
3818 const double oStartT = oTest->fT;
3819 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3820 while (!approximately_negative(oEndT - oEnd->fT)
3821 && approximately_negative(oEnd->fT - otherTMatch)) {
3822 oEnd->fOppValue = 0;
3823 oEnd = &fTs[++oIndex];
3824 }
3825 }
3826
3827 void zeroSpan(Span* span) {
3828 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003829 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003830 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003831 SkASSERT(!span->fDone);
3832 span->fDone = true;
3833 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003834 }
3835
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003836#if DEBUG_DUMP
3837 void dump() const {
3838 const char className[] = "Segment";
3839 const int tab = 4;
3840 for (int i = 0; i < fTs.count(); ++i) {
3841 SkPoint out;
3842 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3843 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003844 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003845 tab + sizeof(className), className, fID,
3846 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003847 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003848 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003849 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003850 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003851 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003852 }
3853#endif
3854
caryclark@google.com47580692012-07-23 12:14:49 +00003855#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003856 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003857 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003858 for (int i = 0; i < fTs.count(); ++i) {
3859 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3860 return;
3861 }
3862 }
3863 SkASSERT(0);
3864 }
3865#endif
3866
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003867#if DEBUG_DUMP
3868 int debugID() const {
3869 return fID;
3870 }
3871#endif
3872
caryclark@google.com24bec792012-08-20 12:43:57 +00003873#if DEBUG_WINDING
3874 void debugShowSums() const {
3875 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3876 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3877 for (int i = 0; i < fTs.count(); ++i) {
3878 const Span& span = fTs[i];
3879 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3880 if (span.fWindSum == SK_MinS32) {
3881 SkDebugf("?");
3882 } else {
3883 SkDebugf("%d", span.fWindSum);
3884 }
3885 SkDebugf("]");
3886 }
3887 SkDebugf("\n");
3888 }
3889#endif
3890
caryclark@google.comcc905052012-07-25 20:59:42 +00003891#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003892 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003893 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003894 int lastWind = -1;
3895 int lastOpp = -1;
3896 double lastT = -1;
3897 int i;
3898 for (i = 0; i < fTs.count(); ++i) {
3899 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3900 || lastOpp != fTs[i].fOppValue;
3901 if (change && lastWind >= 0) {
3902 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3903 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3904 }
3905 if (change) {
3906 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3907 lastWind = fTs[i].fWindValue;
3908 lastOpp = fTs[i].fOppValue;
3909 lastT = fTs[i].fT;
3910 } else {
3911 SkDebugf(",%d", fTs[i].fOther->fID);
3912 }
3913 }
3914 if (i <= 0) {
3915 return;
3916 }
3917 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3918 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3919 if (fOperand) {
3920 SkDebugf(" operand");
3921 }
3922 if (done()) {
3923 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003924 }
3925 SkDebugf("\n");
3926 }
3927#endif
3928
caryclark@google.com027de222012-07-12 12:52:50 +00003929#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003930 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003931 if (done()) {
3932 return;
3933 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003934#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3935 int lastId = -1;
3936 double lastT = -1;
3937#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003938 for (int i = 0; i < fTs.count(); ++i) {
3939 if (fTs[i].fDone) {
3940 continue;
3941 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003942#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3943 if (lastId == fID && lastT == fTs[i].fT) {
3944 continue;
3945 }
3946 lastId = fID;
3947 lastT = fTs[i].fT;
3948#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003949 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003950 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3951 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3952 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3953 }
3954 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003955 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003956 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003957 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003958 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3959 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3960 if (fTs[i].fWindSum == SK_MinS32) {
3961 SkDebugf("?");
3962 } else {
3963 SkDebugf("%d", fTs[i].fWindSum);
3964 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003965 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003966 }
3967 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003968
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003969 // This isn't useful yet -- but leaving it in for now in case i think of something
3970 // to use it for
3971 void validateActiveSpans() const {
3972 if (done()) {
3973 return;
3974 }
3975 int tCount = fTs.count();
3976 for (int index = 0; index < tCount; ++index) {
3977 if (fTs[index].fDone) {
3978 continue;
3979 }
3980 // count number of connections which are not done
3981 int first = index;
3982 double baseT = fTs[index].fT;
3983 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3984 --first;
3985 }
3986 int last = index;
3987 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3988 ++last;
3989 }
3990 int connections = 0;
3991 connections += first > 0 && !fTs[first - 1].fDone;
3992 for (int test = first; test <= last; ++test) {
3993 connections += !fTs[test].fDone;
3994 const Segment* other = fTs[test].fOther;
3995 int oIndex = fTs[test].fOtherIndex;
3996 connections += !other->fTs[oIndex].fDone;
3997 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3998 }
3999 // SkASSERT(!(connections & 1));
4000 }
4001 }
caryclark@google.com027de222012-07-12 12:52:50 +00004002#endif
4003
caryclark@google.com0c803d02012-08-06 11:15:47 +00004004#if DEBUG_MARK_DONE
4005 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
4006 const SkPoint& pt = xyAtT(&span);
4007 SkDebugf("%s id=%d", fun, fID);
4008 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
4009 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
4010 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
4011 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004012 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
4013 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
4014 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
4015 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00004016 if (span.fWindSum == SK_MinS32) {
4017 SkDebugf("?");
4018 } else {
4019 SkDebugf("%d", span.fWindSum);
4020 }
4021 SkDebugf(" windValue=%d\n", span.fWindValue);
4022 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004023
4024 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
4025 const SkPoint& pt = xyAtT(&span);
4026 SkDebugf("%s id=%d", fun, fID);
4027 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
4028 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
4029 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
4030 }
4031 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
4032 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
4033 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
4034 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
4035 winding, oppWinding);
4036 if (span.fOppSum == SK_MinS32) {
4037 SkDebugf("?");
4038 } else {
4039 SkDebugf("%d", span.fOppSum);
4040 }
4041 SkDebugf(" windSum=");
4042 if (span.fWindSum == SK_MinS32) {
4043 SkDebugf("?");
4044 } else {
4045 SkDebugf("%d", span.fWindSum);
4046 }
4047 SkDebugf(" windValue=%d\n", span.fWindValue);
4048 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00004049#endif
4050
caryclark@google.com47580692012-07-23 12:14:49 +00004051#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00004052 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00004053 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00004054 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00004055 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004056 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004057 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004058 const Angle* firstAngle = angles[first];
4059 int windSum = lastSum - spanSign(firstAngle);
4060 int oppoSign = oppSign(firstAngle);
4061 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004062 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
4063 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00004064 int index = first;
4065 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00004066 do {
4067 const Angle& angle = *angles[index];
4068 const Segment& segment = *angle.segment();
4069 int start = angle.start();
4070 int end = angle.end();
4071 const Span& sSpan = segment.fTs[start];
4072 const Span& eSpan = segment.fTs[end];
4073 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00004074 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004075 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004076 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004077 if (opp) {
4078 oppLastSum = oppWindSum;
4079 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004080 if (oppoSign) {
4081 lastSum = windSum;
4082 windSum -= oppoSign;
4083 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004084 } else {
4085 lastSum = windSum;
4086 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004087 if (oppoSign) {
4088 oppLastSum = oppWindSum;
4089 oppWindSum -= oppoSign;
4090 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004091 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00004092 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004093 SkDebugf("%s [%d] %sid=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00004094 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004095 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004096 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004097 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
4098 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
4099 mSpan.fWindValue);
4100 if (mSpan.fWindSum == SK_MinS32) {
4101 SkDebugf("?");
4102 } else {
4103 SkDebugf("%d", mSpan.fWindSum);
4104 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004105 int last, wind;
4106 if (opp) {
4107 last = oppLastSum;
4108 wind = oppWindSum;
4109 } else {
4110 last = lastSum;
4111 wind = windSum;
4112 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004113 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00004114 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004115 useInnerWinding(last, wind) ? wind : last);
4116 } else {
4117 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
4118 opp ? windSum : oppWindSum);
4119 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00004120 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004121#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00004122 angle.debugShow(segment.xyAtT(&sSpan));
4123#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004124 ++index;
4125 if (index == angles.count()) {
4126 index = 0;
4127 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004128 if (firstTime) {
4129 firstTime = false;
4130 }
caryclark@google.com47580692012-07-23 12:14:49 +00004131 } while (index != first);
4132 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00004133
4134 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
4135 const Angle* firstAngle = angles[first];
4136 const Segment* segment = firstAngle->segment();
4137 int winding = segment->updateWinding(firstAngle);
4138 int oppWinding = segment->updateOppWinding(firstAngle);
4139 debugShowSort(fun, angles, first, winding, oppWinding);
4140 }
4141
caryclark@google.com47580692012-07-23 12:14:49 +00004142#endif
4143
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004144#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004145 static char as_digit(int value) {
4146 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
4147 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004148#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004149
caryclark@google.com729e1c42012-11-21 21:36:34 +00004150#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004151 int debugShowWindingValues(int slotCount, int ofInterest) const {
4152 if (!(1 << fID & ofInterest)) {
4153 return 0;
4154 }
4155 int sum = 0;
4156 SkTDArray<char> slots;
4157 slots.setCount(slotCount * 2);
4158 memset(slots.begin(), ' ', slotCount * 2);
4159 for (int i = 0; i < fTs.count(); ++i) {
4160 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
4161 // continue;
4162 // }
4163 sum += fTs[i].fWindValue;
4164 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
4165 sum += fTs[i].fOppValue;
4166 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
4167 }
4168 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
4169 slots.begin() + slotCount);
4170 return sum;
4171 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00004172#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004173
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004174private:
4175 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004176 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004177 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00004178 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00004179 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00004180 // OPTIMIZATION: force the following to be byte-sized
4181 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004182 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004183 bool fXor; // set if original contour had even-odd fill
4184 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004185#if DEBUG_DUMP
4186 int fID;
4187#endif
4188};
4189
caryclark@google.comb9738012012-07-03 19:53:30 +00004190class Contour;
4191
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004192struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00004193 Contour* fContours[2];
4194 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004195 double fTs[2][2];
4196};
4197
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004198class Contour {
4199public:
4200 Contour() {
4201 reset();
4202#if DEBUG_DUMP
4203 fID = ++gContourID;
4204#endif
4205 }
4206
4207 bool operator<(const Contour& rh) const {
4208 return fBounds.fTop == rh.fBounds.fTop
4209 ? fBounds.fLeft < rh.fBounds.fLeft
4210 : fBounds.fTop < rh.fBounds.fTop;
4211 }
4212
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004213 void addCoincident(int index, Contour* other, int otherIndex,
4214 const Intersections& ts, bool swap) {
4215 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004216 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00004217 coincidence.fContours[1] = other;
4218 coincidence.fSegments[0] = index;
4219 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00004220 if (fSegments[index].verb() == SkPath::kLine_Verb &&
4221 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
4222 // FIXME: coincident lines use legacy Ts instead of coincident Ts
4223 coincidence.fTs[swap][0] = ts.fT[0][0];
4224 coincidence.fTs[swap][1] = ts.fT[0][1];
4225 coincidence.fTs[!swap][0] = ts.fT[1][0];
4226 coincidence.fTs[!swap][1] = ts.fT[1][1];
4227 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
4228 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
4229 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
4230 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
4231 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
4232 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
4233 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004234 }
4235
4236 void addCross(const Contour* crosser) {
4237#ifdef DEBUG_CROSS
4238 for (int index = 0; index < fCrosses.count(); ++index) {
4239 SkASSERT(fCrosses[index] != crosser);
4240 }
4241#endif
4242 *fCrosses.append() = crosser;
4243 }
4244
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004245 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004246 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004247 fContainsCurves = true;
4248 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004249
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004250 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004251 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004252 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004253 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004254
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004255 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
4256 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
4257 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004258
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004259 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004260 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004261 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004262 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004263 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004264
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004265 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
4266 containsIntercepts();
4267 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
4268 }
4269
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004270 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004271 return fBounds;
4272 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004273
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004274 void complete() {
4275 setBounds();
4276 fContainsIntercepts = false;
4277 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004278
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004279 void containsIntercepts() {
4280 fContainsIntercepts = true;
4281 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004282
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004283 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004284 for (int index = 0; index < fCrosses.count(); ++index) {
4285 if (fCrosses[index] == crosser) {
4286 return true;
4287 }
4288 }
4289 return false;
4290 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004291
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004292 bool done() const {
4293 return fDone;
4294 }
4295
caryclark@google.comf839c032012-10-26 21:03:50 +00004296 const SkPoint& end() const {
4297 const Segment& segment = fSegments.back();
4298 return segment.pts()[segment.verb()];
4299 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004300
caryclark@google.com4eeda372012-12-06 21:47:48 +00004301 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004302 int segmentCount = fSegments.count();
4303 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004304 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004305 }
4306 }
4307
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004308 void fixOtherTIndex() {
4309 int segmentCount = fSegments.count();
4310 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
4311 fSegments[sIndex].fixOtherTIndex();
4312 }
4313 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00004314
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004315 Segment* nonVerticalSegment(int& start, int& end) {
4316 int segmentCount = fSortedSegments.count();
4317 SkASSERT(segmentCount > 0);
4318 for (int sortedIndex = fFirstSorted; sortedIndex < segmentCount; ++sortedIndex) {
4319 Segment* testSegment = fSortedSegments[sortedIndex];
4320 if (testSegment->done()) {
4321 continue;
4322 }
4323 start = end = 0;
4324 while (testSegment->nextCandidate(start, end)) {
4325 if (!testSegment->isVertical(start, end)) {
4326 return testSegment;
4327 }
4328 }
4329 }
4330 return NULL;
4331 }
4332
caryclark@google.com31143cf2012-11-09 22:14:19 +00004333 bool operand() const {
4334 return fOperand;
4335 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004336
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004337 void reset() {
4338 fSegments.reset();
4339 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004340 fContainsCurves = fContainsIntercepts = fDone = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004341 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004342
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004343 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004344 int count = fCoincidences.count();
4345 for (int index = 0; index < count; ++index) {
4346 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004347 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00004348 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004349 Segment& thisOne = fSegments[thisIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004350 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004351 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00004352 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004353 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00004354 continue;
4355 }
caryclark@google.com47580692012-07-23 12:14:49 +00004356 #if DEBUG_CONCIDENT
4357 thisOne.debugShowTs();
4358 other.debugShowTs();
4359 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004360 double startT = coincidence.fTs[0][0];
4361 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004362 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004363 if (startT > endT) {
4364 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004365 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004366 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004367 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004368 double oStartT = coincidence.fTs[1][0];
4369 double oEndT = coincidence.fTs[1][1];
4370 if (oStartT > oEndT) {
4371 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004372 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004373 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004374 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00004375 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004376 if (cancelers && !opp) {
4377 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00004378 if (startT > 0 || oEndT < 1
4379 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
4380 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004381 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004382 if (oStartT > 0 || endT < 1
4383 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
4384 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004385 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004386 if (!thisOne.done() && !other.done()) {
4387 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
4388 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004389 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004390 if (startT > 0 || oStartT > 0
4391 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004392 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004393 }
caryclark@google.com200c2112012-08-03 15:05:04 +00004394 if (endT < 1 || oEndT < 1
4395 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004396 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004397 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004398 if (!thisOne.done() && !other.done()) {
4399 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
4400 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004401 }
caryclark@google.com47580692012-07-23 12:14:49 +00004402 #if DEBUG_CONCIDENT
4403 thisOne.debugShowTs();
4404 other.debugShowTs();
4405 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00004406 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004407 debugShowWindingValues(contourList);
4408 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004409 }
4410 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004411
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004412 SkTArray<Segment>& segments() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004413 return fSegments;
4414 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004415
caryclark@google.com235f56a2012-09-14 14:19:30 +00004416 void setOperand(bool isOp) {
4417 fOperand = isOp;
4418 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00004419
caryclark@google.com4eeda372012-12-06 21:47:48 +00004420 void setOppXor(bool isOppXor) {
4421 fOppXor = isOppXor;
4422 int segmentCount = fSegments.count();
4423 for (int test = 0; test < segmentCount; ++test) {
4424 fSegments[test].setOppXor(isOppXor);
4425 }
4426 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004427
caryclark@google.com235f56a2012-09-14 14:19:30 +00004428 void setXor(bool isXor) {
4429 fXor = isXor;
4430 }
4431
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004432 void sortSegments() {
4433 int segmentCount = fSegments.count();
4434 fSortedSegments.setReserve(segmentCount);
4435 for (int test = 0; test < segmentCount; ++test) {
4436 *fSortedSegments.append() = &fSegments[test];
4437 }
4438 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
4439 fFirstSorted = 0;
4440 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004441
caryclark@google.comf839c032012-10-26 21:03:50 +00004442 const SkPoint& start() const {
4443 return fSegments.front().pts()[0];
4444 }
4445
4446 void toPath(PathWrapper& path) const {
4447 int segmentCount = fSegments.count();
4448 const SkPoint& pt = fSegments.front().pts()[0];
4449 path.deferredMove(pt);
4450 for (int test = 0; test < segmentCount; ++test) {
4451 fSegments[test].addCurveTo(0, 1, path, true);
4452 }
4453 path.close();
4454 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004455
caryclark@google.comf839c032012-10-26 21:03:50 +00004456 void toPartialBackward(PathWrapper& path) const {
4457 int segmentCount = fSegments.count();
4458 for (int test = segmentCount - 1; test >= 0; --test) {
4459 fSegments[test].addCurveTo(1, 0, path, true);
4460 }
4461 }
4462
4463 void toPartialForward(PathWrapper& path) const {
4464 int segmentCount = fSegments.count();
4465 for (int test = 0; test < segmentCount; ++test) {
4466 fSegments[test].addCurveTo(0, 1, path, true);
4467 }
4468 }
4469
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004470 void topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, Segment*& topStart) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004471 int segmentCount = fSortedSegments.count();
4472 SkASSERT(segmentCount > 0);
caryclark@google.comf839c032012-10-26 21:03:50 +00004473 int sortedIndex = fFirstSorted;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004474 fDone = true; // may be cleared below
caryclark@google.comf839c032012-10-26 21:03:50 +00004475 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4476 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004477 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004478 if (sortedIndex == fFirstSorted) {
4479 ++fFirstSorted;
4480 }
4481 continue;
4482 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004483 fDone = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004484 SkPoint testXY;
4485 testSegment->activeLeftTop(testXY);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004486 if (topStart) {
4487 if (testXY.fY < topLeft.fY) {
4488 continue;
4489 }
4490 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
4491 continue;
4492 }
4493 if (bestXY.fY < testXY.fY) {
4494 continue;
4495 }
4496 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
4497 continue;
4498 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004499 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004500 topStart = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004501 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004502 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004503 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004504
caryclark@google.com24bec792012-08-20 12:43:57 +00004505 Segment* undoneSegment(int& start, int& end) {
4506 int segmentCount = fSegments.count();
4507 for (int test = 0; test < segmentCount; ++test) {
4508 Segment* testSegment = &fSegments[test];
4509 if (testSegment->done()) {
4510 continue;
4511 }
4512 testSegment->undoneSpan(start, end);
4513 return testSegment;
4514 }
4515 return NULL;
4516 }
4517
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004518 int updateSegment(int index, const SkPoint* pts) {
4519 Segment& segment = fSegments[index];
4520 segment.updatePts(pts);
4521 return segment.verb() + 1;
4522 }
4523
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004524#if DEBUG_TEST
4525 SkTArray<Segment>& debugSegments() {
4526 return fSegments;
4527 }
4528#endif
4529
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004530#if DEBUG_DUMP
4531 void dump() {
4532 int i;
4533 const char className[] = "Contour";
4534 const int tab = 4;
4535 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4536 for (i = 0; i < fSegments.count(); ++i) {
4537 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4538 className, i);
4539 fSegments[i].dump();
4540 }
4541 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4542 tab + sizeof(className), className,
4543 fBounds.fLeft, fBounds.fTop,
4544 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004545 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4546 className, fContainsIntercepts);
4547 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4548 className, fContainsCurves);
4549 }
4550#endif
4551
caryclark@google.com027de222012-07-12 12:52:50 +00004552#if DEBUG_ACTIVE_SPANS
4553 void debugShowActiveSpans() {
4554 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004555 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004556 }
4557 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004558
4559 void validateActiveSpans() {
4560 for (int index = 0; index < fSegments.count(); ++index) {
4561 fSegments[index].validateActiveSpans();
4562 }
4563 }
caryclark@google.com027de222012-07-12 12:52:50 +00004564#endif
4565
caryclark@google.com729e1c42012-11-21 21:36:34 +00004566#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004567 int debugShowWindingValues(int totalSegments, int ofInterest) {
4568 int count = fSegments.count();
4569 int sum = 0;
4570 for (int index = 0; index < count; ++index) {
4571 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4572 }
4573 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4574 return sum;
4575 }
4576
4577 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4578 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4579 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4580 int ofInterest = 1 << 5 | 1 << 8;
4581 int total = 0;
4582 int index;
4583 for (index = 0; index < contourList.count(); ++index) {
4584 total += contourList[index]->segments().count();
4585 }
4586 int sum = 0;
4587 for (index = 0; index < contourList.count(); ++index) {
4588 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4589 }
4590 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4591 }
4592#endif
4593
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004594protected:
4595 void setBounds() {
4596 int count = fSegments.count();
4597 if (count == 0) {
4598 SkDebugf("%s empty contour\n", __FUNCTION__);
4599 SkASSERT(0);
4600 // FIXME: delete empty contour?
4601 return;
4602 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004603 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004604 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004605 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004606 }
4607 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004608
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004609private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004610 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004611 SkTDArray<Segment*> fSortedSegments;
4612 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004613 SkTDArray<Coincidence> fCoincidences;
4614 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004615 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004616 bool fContainsIntercepts;
4617 bool fContainsCurves;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00004618 bool fDone;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004619 bool fOperand; // true for the second argument to a binary operator
4620 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004621 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004622#if DEBUG_DUMP
4623 int fID;
4624#endif
4625};
4626
4627class EdgeBuilder {
4628public:
4629
caryclark@google.comf839c032012-10-26 21:03:50 +00004630EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4631 : fPath(path.nativePath())
4632 , fContours(contours)
4633{
4634 init();
4635}
4636
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004637EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004638 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004639 , fContours(contours)
4640{
caryclark@google.comf839c032012-10-26 21:03:50 +00004641 init();
4642}
4643
4644void init() {
4645 fCurrentContour = NULL;
4646 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004647 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004648#if DEBUG_DUMP
4649 gContourID = 0;
4650 gSegmentID = 0;
4651#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004652 fSecondHalf = preFetch();
4653}
4654
4655void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004656 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4657 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004658 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004659 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004660 preFetch();
4661}
4662
4663void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004664 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004665 complete();
4666 if (fCurrentContour && !fCurrentContour->segments().count()) {
4667 fContours.pop_back();
4668 }
4669 // correct pointers in contours since fReducePts may have moved as it grew
4670 int cIndex = 0;
4671 int extraCount = fExtra.count();
4672 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4673 int eIndex = 0;
4674 int rIndex = 0;
4675 while (++eIndex < extraCount) {
4676 int offset = fExtra[eIndex];
4677 if (offset < 0) {
4678 ++cIndex;
4679 continue;
4680 }
4681 fCurrentContour = &fContours[cIndex];
4682 rIndex += fCurrentContour->updateSegment(offset - 1,
4683 &fReducePts[rIndex]);
4684 }
4685 fExtra.reset(); // we're done with this
4686}
4687
4688ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004689 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004690}
4691
4692protected:
4693
4694void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004695 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004696 fCurrentContour->complete();
4697 fCurrentContour = NULL;
4698 }
4699}
4700
caryclark@google.com235f56a2012-09-14 14:19:30 +00004701// FIXME:remove once we can access path pts directly
4702int preFetch() {
4703 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004704 SkPoint pts[4];
4705 SkPath::Verb verb;
4706 do {
4707 verb = iter.next(pts);
4708 *fPathVerbs.append() = verb;
4709 if (verb == SkPath::kMove_Verb) {
4710 *fPathPts.append() = pts[0];
4711 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4712 fPathPts.append(verb, &pts[1]);
4713 }
4714 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004715 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004716}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004717
caryclark@google.com235f56a2012-09-14 14:19:30 +00004718void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004719 SkPath::Verb reducedVerb;
4720 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004721 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004722 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004723 const SkPoint* finalCurveStart = NULL;
4724 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004725 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004726 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4727 switch (verb) {
4728 case SkPath::kMove_Verb:
4729 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004730 if (!fCurrentContour) {
4731 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004732 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004733 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004734 *fExtra.append() = -1; // start new contour
4735 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004736 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004737 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004738 case SkPath::kLine_Verb:
4739 // skip degenerate points
4740 if (pointsPtr[-1].fX != pointsPtr[0].fX
4741 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4742 fCurrentContour->addLine(&pointsPtr[-1]);
4743 }
4744 break;
4745 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004746
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004747 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4748 if (reducedVerb == 0) {
4749 break; // skip degenerate points
4750 }
4751 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004752 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004753 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004754 break;
4755 }
4756 fCurrentContour->addQuad(&pointsPtr[-1]);
4757 break;
4758 case SkPath::kCubic_Verb:
4759 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4760 if (reducedVerb == 0) {
4761 break; // skip degenerate points
4762 }
4763 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004764 *fExtra.append() =
4765 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004766 break;
4767 }
4768 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004769 *fExtra.append() =
4770 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004771 break;
4772 }
4773 fCurrentContour->addCubic(&pointsPtr[-1]);
4774 break;
4775 case SkPath::kClose_Verb:
4776 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004777 if (finalCurveStart && finalCurveEnd
4778 && *finalCurveStart != *finalCurveEnd) {
4779 *fReducePts.append() = *finalCurveStart;
4780 *fReducePts.append() = *finalCurveEnd;
4781 *fExtra.append() =
4782 fCurrentContour->addLine(fReducePts.end() - 2);
4783 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004784 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004785 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004786 default:
4787 SkDEBUGFAIL("bad verb");
4788 return;
4789 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004790 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004791 pointsPtr += verb;
4792 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004793 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004794 if (verbPtr == endOfFirstHalf) {
4795 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004796 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004797 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004798}
4799
4800private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004801 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004802 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004803 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004804 Contour* fCurrentContour;
4805 SkTArray<Contour>& fContours;
4806 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004807 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004808 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004809 int fSecondHalf;
4810 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004811};
4812
4813class Work {
4814public:
4815 enum SegmentType {
4816 kHorizontalLine_Segment = -1,
4817 kVerticalLine_Segment = 0,
4818 kLine_Segment = SkPath::kLine_Verb,
4819 kQuad_Segment = SkPath::kQuad_Verb,
4820 kCubic_Segment = SkPath::kCubic_Verb,
4821 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004822
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004823 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4824 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4825 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004826
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004827 // FIXME: does it make sense to write otherIndex now if we're going to
4828 // fix it up later?
4829 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004830 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004831 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004832
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004833 // Avoid collapsing t values that are close to the same since
4834 // we walk ts to describe consecutive intersections. Since a pair of ts can
4835 // be nearly equal, any problems caused by this should be taken care
4836 // of later.
4837 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004838 int addT(double newT, const Work& other) {
4839 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004840 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004841
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004842 bool advance() {
4843 return ++fIndex < fLast;
4844 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004845
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004846 SkScalar bottom() const {
4847 return bounds().fBottom;
4848 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004849
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004850 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004851 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004852 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004853
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004854 const SkPoint* cubic() const {
4855 return fCubic;
4856 }
4857
4858 void init(Contour* contour) {
4859 fContour = contour;
4860 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004861 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004862 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004863
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004864 bool isAdjacent(const Work& next) {
4865 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4866 }
4867
4868 bool isFirstLast(const Work& next) {
4869 return fContour == next.fContour && fIndex == 0
4870 && next.fIndex == fLast - 1;
4871 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004872
4873 SkScalar left() const {
4874 return bounds().fLeft;
4875 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004876
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004877 void promoteToCubic() {
4878 fCubic[0] = pts()[0];
4879 fCubic[2] = pts()[1];
4880 fCubic[3] = pts()[2];
4881 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4882 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4883 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4884 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4885 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004886
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004887 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004888 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004889 }
4890
4891 SkScalar right() const {
4892 return bounds().fRight;
4893 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004894
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004895 ptrdiff_t segmentIndex() const {
4896 return fIndex;
4897 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004898
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004899 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004900 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004901 SegmentType type = (SegmentType) segment.verb();
4902 if (type != kLine_Segment) {
4903 return type;
4904 }
4905 if (segment.isHorizontal()) {
4906 return kHorizontalLine_Segment;
4907 }
4908 if (segment.isVertical()) {
4909 return kVerticalLine_Segment;
4910 }
4911 return kLine_Segment;
4912 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004913
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004914 bool startAfter(const Work& after) {
4915 fIndex = after.fIndex;
4916 return advance();
4917 }
4918
4919 SkScalar top() const {
4920 return bounds().fTop;
4921 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004922
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004923 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004924 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004925 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004926
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004927 SkScalar x() const {
4928 return bounds().fLeft;
4929 }
4930
4931 bool xFlipped() const {
4932 return x() != pts()[0].fX;
4933 }
4934
4935 SkScalar y() const {
4936 return bounds().fTop;
4937 }
4938
4939 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004940 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004941 }
4942
4943protected:
4944 Contour* fContour;
4945 SkPoint fCubic[4];
4946 int fIndex;
4947 int fLast;
4948};
4949
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004950#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004951static void debugShowLineIntersection(int pts, const Work& wt,
4952 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004953 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004954 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004955 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4956 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4957 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4958 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004959 return;
4960 }
4961 SkPoint wtOutPt, wnOutPt;
4962 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4963 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004964 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004965 __FUNCTION__,
4966 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4967 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4968 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004969 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004970 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004971 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004972 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4973 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4974 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004975 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4976 }
4977 SkDebugf("\n");
4978}
4979
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004980static void debugShowQuadLineIntersection(int pts, const Work& wt,
4981 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4982 if (!pts) {
4983 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004984 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004985 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4986 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004987 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004988 return;
4989 }
4990 SkPoint wtOutPt, wnOutPt;
4991 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4992 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4993 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4994 __FUNCTION__,
4995 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4996 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4997 wtOutPt.fX, wtOutPt.fY);
4998 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004999 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
5000 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005001 }
5002 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5003 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5004 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
5005 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005006 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
5007 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005008 }
5009 SkDebugf("\n");
5010}
5011
caryclark@google.coma461ff02012-10-11 12:54:23 +00005012static void debugShowQuadIntersection(int pts, const Work& wt,
5013 const Work& wn, const double wtTs[2], const double wnTs[2]) {
5014 if (!pts) {
5015 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
5016 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
5017 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00005018 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00005019 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005020 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00005021 return;
5022 }
5023 SkPoint wtOutPt, wnOutPt;
5024 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
5025 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
5026 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5027 __FUNCTION__,
5028 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
5029 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
5030 wtOutPt.fX, wtOutPt.fY);
5031 if (pts == 2) {
5032 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
5033 }
5034 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
5035 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
5036 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
5037 wnOutPt.fX, wnOutPt.fY);
5038 if (pts == 2) {
5039 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005040 }
caryclark@google.comb9738012012-07-03 19:53:30 +00005041 SkDebugf("\n");
5042}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005043#else
5044static void debugShowLineIntersection(int , const Work& ,
5045 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005046}
caryclark@google.coma461ff02012-10-11 12:54:23 +00005047
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005048static void debugShowQuadLineIntersection(int , const Work& ,
5049 const Work& , const double [2], const double [2]) {
5050}
5051
caryclark@google.coma461ff02012-10-11 12:54:23 +00005052static void debugShowQuadIntersection(int , const Work& ,
5053 const Work& , const double [2], const double [2]) {
5054}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005055#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005056
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005057static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005058
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005059 if (test != next) {
5060 if (test->bounds().fBottom < next->bounds().fTop) {
5061 return false;
5062 }
5063 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
5064 return true;
5065 }
5066 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005067 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005068 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005069 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005070 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005071 Work wn;
5072 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005073 if (test == next && !wn.startAfter(wt)) {
5074 continue;
5075 }
5076 do {
5077 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
5078 continue;
5079 }
5080 int pts;
5081 Intersections ts;
5082 bool swap = false;
5083 switch (wt.segmentType()) {
5084 case Work::kHorizontalLine_Segment:
5085 swap = true;
5086 switch (wn.segmentType()) {
5087 case Work::kHorizontalLine_Segment:
5088 case Work::kVerticalLine_Segment:
5089 case Work::kLine_Segment: {
5090 pts = HLineIntersect(wn.pts(), wt.left(),
5091 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005092 debugShowLineIntersection(pts, wt, wn,
5093 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005094 break;
5095 }
5096 case Work::kQuad_Segment: {
5097 pts = HQuadIntersect(wn.pts(), wt.left(),
5098 wt.right(), wt.y(), wt.xFlipped(), ts);
5099 break;
5100 }
5101 case Work::kCubic_Segment: {
5102 pts = HCubicIntersect(wn.pts(), wt.left(),
5103 wt.right(), wt.y(), wt.xFlipped(), ts);
5104 break;
5105 }
5106 default:
5107 SkASSERT(0);
5108 }
5109 break;
5110 case Work::kVerticalLine_Segment:
5111 swap = true;
5112 switch (wn.segmentType()) {
5113 case Work::kHorizontalLine_Segment:
5114 case Work::kVerticalLine_Segment:
5115 case Work::kLine_Segment: {
5116 pts = VLineIntersect(wn.pts(), wt.top(),
5117 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005118 debugShowLineIntersection(pts, wt, wn,
5119 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005120 break;
5121 }
5122 case Work::kQuad_Segment: {
5123 pts = VQuadIntersect(wn.pts(), wt.top(),
5124 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5125 break;
5126 }
5127 case Work::kCubic_Segment: {
5128 pts = VCubicIntersect(wn.pts(), wt.top(),
5129 wt.bottom(), wt.x(), wt.yFlipped(), ts);
5130 break;
5131 }
5132 default:
5133 SkASSERT(0);
5134 }
5135 break;
5136 case Work::kLine_Segment:
5137 switch (wn.segmentType()) {
5138 case Work::kHorizontalLine_Segment:
5139 pts = HLineIntersect(wt.pts(), wn.left(),
5140 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005141 debugShowLineIntersection(pts, wt, wn,
5142 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005143 break;
5144 case Work::kVerticalLine_Segment:
5145 pts = VLineIntersect(wt.pts(), wn.top(),
5146 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005147 debugShowLineIntersection(pts, wt, wn,
5148 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005149 break;
5150 case Work::kLine_Segment: {
5151 pts = LineIntersect(wt.pts(), wn.pts(), ts);
5152 debugShowLineIntersection(pts, wt, wn,
5153 ts.fT[1], ts.fT[0]);
5154 break;
5155 }
5156 case Work::kQuad_Segment: {
5157 swap = true;
5158 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005159 debugShowQuadLineIntersection(pts, wn, wt,
5160 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005161 break;
5162 }
5163 case Work::kCubic_Segment: {
5164 swap = true;
5165 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
5166 break;
5167 }
5168 default:
5169 SkASSERT(0);
5170 }
5171 break;
5172 case Work::kQuad_Segment:
5173 switch (wn.segmentType()) {
5174 case Work::kHorizontalLine_Segment:
5175 pts = HQuadIntersect(wt.pts(), wn.left(),
5176 wn.right(), wn.y(), wn.xFlipped(), ts);
5177 break;
5178 case Work::kVerticalLine_Segment:
5179 pts = VQuadIntersect(wt.pts(), wn.top(),
5180 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5181 break;
5182 case Work::kLine_Segment: {
5183 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005184 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005185 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005186 break;
5187 }
5188 case Work::kQuad_Segment: {
5189 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00005190 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00005191 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005192 break;
5193 }
5194 case Work::kCubic_Segment: {
5195 wt.promoteToCubic();
5196 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
5197 break;
5198 }
5199 default:
5200 SkASSERT(0);
5201 }
5202 break;
5203 case Work::kCubic_Segment:
5204 switch (wn.segmentType()) {
5205 case Work::kHorizontalLine_Segment:
5206 pts = HCubicIntersect(wt.pts(), wn.left(),
5207 wn.right(), wn.y(), wn.xFlipped(), ts);
5208 break;
5209 case Work::kVerticalLine_Segment:
5210 pts = VCubicIntersect(wt.pts(), wn.top(),
5211 wn.bottom(), wn.x(), wn.yFlipped(), ts);
5212 break;
5213 case Work::kLine_Segment: {
5214 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
5215 break;
5216 }
5217 case Work::kQuad_Segment: {
5218 wn.promoteToCubic();
5219 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
5220 break;
5221 }
5222 case Work::kCubic_Segment: {
5223 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
5224 break;
5225 }
5226 default:
5227 SkASSERT(0);
5228 }
5229 break;
5230 default:
5231 SkASSERT(0);
5232 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005233 if (!foundCommonContour && pts > 0) {
5234 test->addCross(next);
5235 next->addCross(test);
5236 foundCommonContour = true;
5237 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005238 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00005239 if (pts == 2) {
5240 if (wn.segmentType() <= Work::kLine_Segment
5241 && wt.segmentType() <= Work::kLine_Segment) {
5242 wt.addCoincident(wn, ts, swap);
5243 continue;
5244 }
5245 if (wn.segmentType() == Work::kQuad_Segment
5246 && wt.segmentType() == Work::kQuad_Segment
5247 && ts.coincidentUsed() == 2) {
5248 wt.addCoincident(wn, ts, swap);
5249 continue;
5250 }
5251
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00005252 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00005253 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005254 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
5255 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005256 int testTAt = wt.addT(ts.fT[swap][pt], wn);
5257 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005258 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
5259 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005260 }
5261 } while (wn.advance());
5262 } while (wt.advance());
5263 return true;
5264}
5265
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005266// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005267// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00005268static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005269 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00005270 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005271 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00005272 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005273 }
5274 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5275 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00005276 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005277 }
5278}
5279
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005280static int contourRangeCheckY(SkTDArray<Contour*>& contourList, Segment*& current, int& index,
caryclark@google.com3586ece2012-12-27 18:46:58 +00005281 int& endIndex, double& bestHit, SkScalar& bestDx, bool& tryAgain, double& mid, bool opp) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005282 SkPoint basePt;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005283 double tAtMid = current->tAtMid(index, endIndex, mid);
5284 current->xyAtT(tAtMid, basePt);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005285 int contourCount = contourList.count();
5286 SkScalar bestY = SK_ScalarMin;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005287 Segment* bestSeg = NULL;
5288 int bestTIndex;
5289 bool bestOpp;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005290 bool hitSomething = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005291 for (int cTest = 0; cTest < contourCount; ++cTest) {
5292 Contour* contour = contourList[cTest];
5293 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5294 if (basePt.fY < contour->bounds().fTop) {
5295 continue;
5296 }
5297 if (bestY > contour->bounds().fBottom) {
5298 continue;
5299 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005300 int segmentCount = contour->segments().count();
5301 for (int test = 0; test < segmentCount; ++test) {
5302 Segment* testSeg = &contour->segments()[test];
5303 SkScalar testY = bestY;
5304 double testHit;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005305 int testTIndex = testSeg->crossedSpanY(basePt, testY, testHit, hitSomething, tAtMid,
5306 testOpp, testSeg == current);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005307 if (testTIndex < 0) {
5308 continue;
5309 }
5310 if (testSeg == current && current->betweenTs(index, testHit, endIndex)) {
caryclark@google.com3586ece2012-12-27 18:46:58 +00005311 double baseT = current->t(index);
5312 double endT = current->t(endIndex);
5313 double newMid = (testHit - baseT) / (endT - baseT);
5314#if DEBUG_WINDING
5315 SkPoint midXY, newXY;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005316 double midT = current->tAtMid(index, endIndex, mid);
5317 current->xyAtT(midT, midXY);
5318 double newMidT = current->tAtMid(index, endIndex, newMid);
5319 current->xyAtT(newMidT, newXY);
caryclark@google.com3586ece2012-12-27 18:46:58 +00005320 SkDebugf("%s [%d] mid=%1.9g->%1.9g s=%1.9g (%1.9g,%1.9g) m=%1.9g (%1.9g,%1.9g)"
5321 " n=%1.9g (%1.9g,%1.9g) e=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__,
5322 current->debugID(), mid, newMid,
5323 baseT, current->xAtT(index), current->yAtT(index),
5324 baseT + mid * (endT - baseT), midXY.fX, midXY.fY,
5325 baseT + newMid * (endT - baseT), newXY.fX, newXY.fY,
5326 endT, current->xAtT(endIndex), current->yAtT(endIndex));
5327#endif
5328 mid = newMid * 2; // calling loop with divide by 2 before continuing
5329 return SK_MinS32;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005330 }
5331 bestSeg = testSeg;
5332 bestHit = testHit;
5333 bestOpp = testOpp;
5334 bestTIndex = testTIndex;
5335 bestY = testY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005336 }
5337 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00005338 int result;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005339 if (!bestSeg) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00005340 result = hitSomething ? SK_MinS32 : 0;
5341 } else {
5342 if (bestSeg->windSum(bestTIndex) == SK_MinS32) {
5343 current = bestSeg;
5344 index = bestTIndex;
5345 endIndex = bestSeg->nextSpan(bestTIndex, 1);
5346 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
5347 tryAgain = true;
5348 return 0;
5349 }
5350 result = bestSeg->windingAtT(bestHit, bestTIndex, bestOpp, bestDx);
5351 SkASSERT(bestDx);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005352 }
caryclark@google.com3586ece2012-12-27 18:46:58 +00005353 double baseT = current->t(index);
5354 double endT = current->t(endIndex);
5355 bestHit = baseT + mid * (endT - baseT);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005356 return result;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005357}
5358
caryclark@google.com24bec792012-08-20 12:43:57 +00005359static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
5360 int contourCount = contourList.count();
5361 Segment* result;
5362 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5363 Contour* contour = contourList[cIndex];
5364 result = contour->undoneSegment(start, end);
5365 if (result) {
5366 return result;
5367 }
5368 }
5369 return NULL;
5370}
5371
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005372#define OLD_FIND_CHASE 1
caryclark@google.com24bec792012-08-20 12:43:57 +00005373
caryclark@google.com31143cf2012-11-09 22:14:19 +00005374static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005375 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005376 Span* span;
5377 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005378 const Span& backPtr = span->fOther->span(span->fOtherIndex);
5379 Segment* segment = backPtr.fOther;
5380 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005381 SkTDArray<Angle> angles;
5382 int done = 0;
5383 if (segment->activeAngle(tIndex, done, angles)) {
5384 Angle* last = angles.end() - 1;
5385 tIndex = last->start();
5386 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005387 #if TRY_ROTATE
5388 *chase.insert(0) = span;
5389 #else
5390 *chase.append() = span;
5391 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005392 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005393 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005394 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005395 continue;
5396 }
5397 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005398 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005399 int angleCount = sorted.count();
caryclark@google.com03f97062012-08-21 13:13:52 +00005400#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005401 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005402#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005403 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005404 continue;
5405 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005406 // find first angle, initialize winding to computed fWindSum
5407 int firstIndex = -1;
5408 const Angle* angle;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005409#if OLD_FIND_CHASE
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005410 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005411 do {
5412 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005413 segment = angle->segment();
5414 winding = segment->windSum(angle);
5415 } while (winding == SK_MinS32);
5416 int spanWinding = segment->spanSign(angle->start(), angle->end());
5417 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005418 SkDebugf("%s winding=%d spanWinding=%d\n",
5419 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005420 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005421 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005422 if (spanWinding * winding < 0) {
5423 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005424 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005425 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005426 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005427 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005428 // we care about first sign and whether wind sum indicates this
5429 // edge is inside or outside. Maybe need to pass span winding
5430 // or first winding or something into this function?
5431 // advance to first undone angle, then return it and winding
5432 // (to set whether edges are active or not)
5433 int nextIndex = firstIndex + 1;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005434 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005435 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005436 winding -= angle->segment()->spanSign(angle);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005437#else
5438 do {
5439 angle = sorted[++firstIndex];
5440 segment = angle->segment();
5441 } while (segment->windSum(angle) == SK_MinS32);
5442 #if DEBUG_SORT
5443 segment->debugShowSort(__FUNCTION__, sorted, firstIndex);
5444 #endif
5445 int sumWinding = segment->updateWindingReverse(angle);
5446 int nextIndex = firstIndex + 1;
5447 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
5448 Segment* first = NULL;
5449#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005450 do {
5451 SkASSERT(nextIndex != firstIndex);
5452 if (nextIndex == angleCount) {
5453 nextIndex = 0;
5454 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005455 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005456 segment = angle->segment();
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005457#if OLD_FIND_CHASE
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005458 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005459 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005460 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005461 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5462 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005463 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005464 tIndex = angle->start();
5465 endIndex = angle->end();
5466 int lesser = SkMin32(tIndex, endIndex);
5467 const Span& nextSpan = segment->span(lesser);
5468 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005469#if 1
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005470 // FIXME: this be wrong? assign startWinding if edge is in
caryclark@google.com9764cc62012-07-12 19:29:45 +00005471 // same direction. If the direction is opposite, winding to
5472 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005473 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005474 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005475 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005476 segment->markAndChaseWinding(angle, maxWinding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005477#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005478 break;
5479 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005480#else
5481 int start = angle->start();
5482 int end = angle->end();
5483 int maxWinding;
5484 segment->setUpWinding(start, end, maxWinding, sumWinding);
5485 if (!segment->done(angle)) {
5486 if (!first) {
5487 first = segment;
5488 tIndex = start;
5489 endIndex = end;
5490 }
5491 (void) segment->markAngle(maxWinding, sumWinding, true, angle);
5492 }
5493#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005494 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005495 #if TRY_ROTATE
5496 *chase.insert(0) = span;
5497 #else
5498 *chase.append() = span;
5499 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005500 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005501 }
5502 return NULL;
5503}
5504
caryclark@google.com027de222012-07-12 12:52:50 +00005505#if DEBUG_ACTIVE_SPANS
5506static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005507 int index;
5508 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005509 contourList[index]->debugShowActiveSpans();
5510 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005511 for (index = 0; index < contourList.count(); ++ index) {
5512 contourList[index]->validateActiveSpans();
5513 }
caryclark@google.com027de222012-07-12 12:52:50 +00005514}
5515#endif
5516
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005517static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005518 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool onlySortable) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005519 Segment* result;
5520 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005521 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005522 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005523 Segment* topStart = NULL;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005524 done = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00005525 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5526 Contour* contour = contourList[cIndex];
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005527 if (contour->done()) {
5528 continue;
5529 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005530 const Bounds& bounds = contour->bounds();
5531 if (bounds.fBottom < topLeft.fY) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005532 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005533 continue;
5534 }
5535 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005536 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005537 continue;
5538 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005539 contour->topSortableSegment(topLeft, bestXY, topStart);
5540 if (!contour->done()) {
5541 done = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005542 }
5543 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005544 if (!topStart) {
5545 return NULL;
5546 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005547 topLeft = bestXY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005548 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005549 } while (!result);
5550 return result;
5551}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005552
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005553static int rightAngleWinding(SkTDArray<Contour*>& contourList,
caryclark@google.com3586ece2012-12-27 18:46:58 +00005554 Segment*& current, int& index, int& endIndex, double& tHit, SkScalar& hitDx, bool& tryAgain,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005555 bool opp) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005556 double test = 0.9;
5557 int contourWinding;
5558 do {
caryclark@google.com3586ece2012-12-27 18:46:58 +00005559 contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx,
5560 tryAgain, test, opp);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005561 if (contourWinding != SK_MinS32 || tryAgain) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005562 return contourWinding;
5563 }
5564 test /= 2;
5565 } while (!approximately_negative(test));
5566 SkASSERT(0); // should be OK to comment out, but interested when this hits
5567 return contourWinding;
5568}
5569
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005570static void skipVertical(SkTDArray<Contour*>& contourList,
5571 Segment*& current, int& index, int& endIndex) {
5572 if (!current->isVertical(index, endIndex)) {
5573 return;
5574 }
5575 int contourCount = contourList.count();
5576 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5577 Contour* contour = contourList[cIndex];
5578 if (contour->done()) {
5579 continue;
5580 }
5581 current = contour->nonVerticalSegment(index, endIndex);
5582 if (current) {
5583 return;
5584 }
5585 }
5586}
5587
caryclark@google.com3586ece2012-12-27 18:46:58 +00005588static Segment* findSortableTop(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005589 int& endIndex, SkPoint& topLeft, bool& unsortable, bool& done, bool binary) {
5590 Segment* current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, done,
5591 true);
5592 if (!current) {
5593 return NULL;
5594 }
5595 if (firstContour) {
caryclark@google.com3586ece2012-12-27 18:46:58 +00005596 current->initWinding(index, endIndex);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005597 firstContour = false;
5598 return current;
5599 }
5600 int minIndex = SkMin32(index, endIndex);
5601 int sumWinding = current->windSum(minIndex);
5602 if (sumWinding != SK_MinS32) {
5603 return current;
5604 }
5605 sumWinding = current->computeSum(index, endIndex, binary);
5606 if (sumWinding != SK_MinS32) {
5607 return current;
5608 }
5609 int contourWinding;
5610 int oppContourWinding = 0;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005611 // the simple upward projection of the unresolved points hit unsortable angles
5612 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005613 bool tryAgain;
5614 double tHit;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005615 SkScalar hitDx = 0;
5616 SkScalar hitOppDx = 0;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005617 do {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005618 // if current is vertical, find another candidate which is not
5619 // if only remaining candidates are vertical, then they can be marked done
caryclark@google.com10227bf2012-12-28 22:10:41 +00005620 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005621 skipVertical(contourList, current, index, endIndex);
caryclark@google.com10227bf2012-12-28 22:10:41 +00005622 SkASSERT(index != endIndex && index >= 0 && endIndex >= 0);
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005623 tryAgain = false;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005624 contourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitDx,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005625 tryAgain, false);
5626 if (tryAgain) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005627 continue;
5628 }
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005629 if (!binary) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005630 break;
5631 }
caryclark@google.com3586ece2012-12-27 18:46:58 +00005632 oppContourWinding = rightAngleWinding(contourList, current, index, endIndex, tHit, hitOppDx,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005633 tryAgain, true);
5634 } while (tryAgain);
skia.committer@gmail.com8d83d0d2012-12-28 02:01:18 +00005635
caryclark@google.com3586ece2012-12-27 18:46:58 +00005636 current->initWinding(index, endIndex, tHit, contourWinding, hitDx, oppContourWinding, hitOppDx);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005637 return current;
5638}
5639
5640// rewrite that abandons keeping local track of winding
caryclark@google.com3586ece2012-12-27 18:46:58 +00005641static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005642 bool firstContour = true;
5643 bool unsortable = false;
5644 bool topUnsortable = false;
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005645 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
5646 do {
5647 int index, endIndex;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005648 bool topDone;
caryclark@google.com3586ece2012-12-27 18:46:58 +00005649 Segment* current = findSortableTop(contourList, firstContour, index, endIndex, topLeft,
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005650 topUnsortable, topDone, false);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005651 if (!current) {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005652 if (topUnsortable || !topDone) {
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005653 topUnsortable = false;
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005654 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005655 topLeft.fX = topLeft.fY = SK_ScalarMin;
5656 continue;
5657 }
5658 break;
5659 }
5660 SkTDArray<Span*> chaseArray;
5661 do {
5662 if (current->activeWinding(index, endIndex)) {
5663 do {
5664 #if DEBUG_ACTIVE_SPANS
5665 if (!unsortable && current->done()) {
5666 debugShowActiveSpans(contourList);
5667 }
5668 #endif
5669 SkASSERT(unsortable || !current->done());
5670 int nextStart = index;
5671 int nextEnd = endIndex;
5672 Segment* next = current->findNextWinding(chaseArray, nextStart, nextEnd,
5673 unsortable);
5674 if (!next) {
5675 if (!unsortable && simple.hasMove()
5676 && current->verb() != SkPath::kLine_Verb
5677 && !simple.isClosed()) {
5678 current->addCurveTo(index, endIndex, simple, true);
5679 SkASSERT(simple.isClosed());
5680 }
5681 break;
5682 }
5683 current->addCurveTo(index, endIndex, simple, true);
5684 current = next;
5685 index = nextStart;
5686 endIndex = nextEnd;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005687 } while (!simple.isClosed() && ((!unsortable)
5688 || !current->done(SkMin32(index, endIndex))));
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005689 if (current->activeWinding(index, endIndex) && !simple.isClosed()) {
5690 SkASSERT(unsortable);
5691 int min = SkMin32(index, endIndex);
5692 if (!current->done(min)) {
5693 current->addCurveTo(index, endIndex, simple, true);
5694 current->markDoneUnary(min);
5695 }
5696 }
5697 simple.close();
5698 } else {
caryclark@google.comdb0b3e02012-12-21 21:34:36 +00005699 Span* last = current->markAndChaseDoneUnary(index, endIndex);
caryclark@google.comd0deb4f2012-12-17 13:58:08 +00005700 if (last) {
5701 *chaseArray.append() = last;
5702 }
5703 }
5704 current = findChase(chaseArray, index, endIndex);
5705 #if DEBUG_ACTIVE_SPANS
5706 debugShowActiveSpans(contourList);
5707 #endif
5708 if (!current) {
5709 break;
5710 }
5711 } while (true);
5712 } while (true);
5713 return simple.someAssemblyRequired();
5714}
5715
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005716// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005717static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005718 Segment* current;
5719 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005720 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005721 bool closable = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00005722 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005723 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005724 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005725 int nextStart = start;
5726 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005727 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005728 if (!next) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005729 if (!unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005730 && current->verb() != SkPath::kLine_Verb
5731 && !simple.isClosed()) {
5732 current->addCurveTo(start, end, simple, true);
5733 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005734 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005735 break;
5736 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005737 #if DEBUG_FLOW
5738 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5739 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
5740 current->xyAtT(end).fX, current->xyAtT(end).fY);
5741 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005742 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005743 current = next;
5744 start = nextStart;
5745 end = nextEnd;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005746 } while (!simple.isClosed() && (!unsortable || !current->done()));
5747 if (!simple.isClosed()) {
5748 SkASSERT(unsortable);
5749 int min = SkMin32(start, end);
5750 if (!current->done(min)) {
5751 current->addCurveTo(start, end, simple, true);
5752 current->markDone(min, 1);
5753 }
5754 closable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005755 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005756 simple.close();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005757 #if DEBUG_ACTIVE_SPANS
5758 debugShowActiveSpans(contourList);
5759 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005760 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005761 return closable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005762}
5763
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005764static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5765 int contourCount = contourList.count();
5766 for (int cTest = 0; cTest < contourCount; ++cTest) {
5767 Contour* contour = contourList[cTest];
5768 contour->fixOtherTIndex();
5769 }
5770}
5771
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005772static void sortSegments(SkTDArray<Contour*>& contourList) {
5773 int contourCount = contourList.count();
5774 for (int cTest = 0; cTest < contourCount; ++cTest) {
5775 Contour* contour = contourList[cTest];
5776 contour->sortSegments();
5777 }
5778}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005779
caryclark@google.com4eeda372012-12-06 21:47:48 +00005780static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
5781 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005782 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005783 if (count == 0) {
5784 return;
5785 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005786 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00005787 Contour& contour = contours[index];
5788 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
5789 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005790 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005791 QSort<Contour>(list.begin(), list.end() - 1);
5792}
5793
caryclark@google.comf839c032012-10-26 21:03:50 +00005794static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005795 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005796}
5797
caryclark@google.com10227bf2012-12-28 22:10:41 +00005798static bool lessThan(SkTDArray<double>& distances, const int one, const int two) {
5799 return distances[one] < distances[two];
5800}
caryclark@google.comf839c032012-10-26 21:03:50 +00005801 /*
5802 check start and end of each contour
5803 if not the same, record them
5804 match them up
5805 connect closest
5806 reassemble contour pieces into new path
5807 */
5808static void assemble(const PathWrapper& path, PathWrapper& simple) {
5809#if DEBUG_PATH_CONSTRUCTION
5810 SkDebugf("%s\n", __FUNCTION__);
5811#endif
5812 SkTArray<Contour> contours;
5813 EdgeBuilder builder(path, contours);
5814 builder.finish();
5815 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005816 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005817 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005818 for (outer = 0; outer < count; ++outer) {
5819 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005820 const SkPoint& eStart = eContour.start();
5821 const SkPoint& eEnd = eContour.end();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005822#if DEBUG_ASSEMBLE
5823 SkDebugf("%s contour", __FUNCTION__);
5824 if (!approximatelyEqual(eStart, eEnd)) {
5825 SkDebugf("[%d]", runs.count());
5826 } else {
5827 SkDebugf(" ");
5828 }
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00005829 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005830 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
5831#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005832 if (approximatelyEqual(eStart, eEnd)) {
5833 eContour.toPath(simple);
5834 continue;
5835 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005836 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005837 }
5838 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005839 if (count == 0) {
5840 return;
5841 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005842 SkTDArray<int> sLink, eLink;
5843 sLink.setCount(count);
5844 eLink.setCount(count);
caryclark@google.com10227bf2012-12-28 22:10:41 +00005845 int rIndex, iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005846 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com10227bf2012-12-28 22:10:41 +00005847 sLink[rIndex] = eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005848 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00005849 SkTDArray<double> distances;
5850 const int ends = count * 2; // all starts and ends
5851 const int entries = (ends - 1) * count; // folded triangle : n * (n - 1) / 2
5852 distances.setCount(entries);
5853 for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
5854 outer = runs[rIndex >> 1];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005855 const Contour& oContour = contours[outer];
caryclark@google.com10227bf2012-12-28 22:10:41 +00005856 const SkPoint& oPt = rIndex & 1 ? oContour.end() : oContour.start();
5857 const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
5858 * ends - rIndex - 1;
5859 for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
5860 int inner = runs[iIndex >> 1];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005861 const Contour& iContour = contours[inner];
caryclark@google.com10227bf2012-12-28 22:10:41 +00005862 const SkPoint& iPt = iIndex & 1 ? iContour.end() : iContour.start();
5863 double dx = iPt.fX - oPt.fX;
5864 double dy = iPt.fY - oPt.fY;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005865 double dist = dx * dx + dy * dy;
caryclark@google.com10227bf2012-12-28 22:10:41 +00005866 distances[row + iIndex] = dist; // oStart distance from iStart
5867 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005868 }
caryclark@google.com10227bf2012-12-28 22:10:41 +00005869 SkTDArray<int> sortedDist;
5870 sortedDist.setCount(entries);
5871 for (rIndex = 0; rIndex < entries; ++rIndex) {
5872 sortedDist[rIndex] = rIndex;
5873 }
5874 QSort<SkTDArray<double>, int>(distances, sortedDist.begin(), sortedDist.end() - 1, lessThan);
5875 int remaining = count; // number of start/end pairs
5876 for (rIndex = 0; rIndex < entries; ++rIndex) {
5877 int pair = sortedDist[rIndex];
5878 int row = pair / ends;
5879 int col = pair - row * ends;
5880 int thingOne = row < col ? row : ends - row - 2;
5881 int ndxOne = thingOne >> 1;
5882 bool endOne = thingOne & 1;
5883 int* linkOne = endOne ? eLink.begin() : sLink.begin();
5884 if (linkOne[ndxOne] != INT_MAX) {
5885 continue;
5886 }
5887 int thingTwo = row < col ? col : ends - row + col - 1;
5888 int ndxTwo = thingTwo >> 1;
5889 bool endTwo = thingTwo & 1;
5890 int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
5891 if (linkTwo[ndxTwo] != INT_MAX) {
5892 continue;
5893 }
5894 SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
5895 bool flip = endOne == endTwo;
5896 linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
5897 linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
5898 if (!--remaining) {
5899 break;
5900 }
5901 }
5902 SkASSERT(!remaining);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005903#if DEBUG_ASSEMBLE
5904 for (rIndex = 0; rIndex < count; ++rIndex) {
5905 int s = sLink[rIndex];
5906 int e = eLink[rIndex];
5907 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
5908 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
caryclark@google.comf839c032012-10-26 21:03:50 +00005909 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005910#endif
5911 rIndex = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00005912 do {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005913 bool forward = true;
5914 bool first = true;
5915 int sIndex = sLink[rIndex];
5916 SkASSERT(sIndex != INT_MAX);
5917 sLink[rIndex] = INT_MAX;
5918 int eIndex;
5919 if (sIndex < 0) {
5920 eIndex = sLink[~sIndex];
5921 sLink[~sIndex] = INT_MAX;
5922 } else {
5923 eIndex = eLink[sIndex];
5924 eLink[sIndex] = INT_MAX;
5925 }
5926 SkASSERT(eIndex != INT_MAX);
5927#if DEBUG_ASSEMBLE
5928 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00005929 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
5930 eIndex < 0 ? ~eIndex : eIndex);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005931#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005932 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005933 outer = runs[rIndex];
5934 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005935 if (first) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005936 first = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005937 const SkPoint* startPtr = &contour.start();
caryclark@google.comf839c032012-10-26 21:03:50 +00005938 simple.deferredMove(startPtr[0]);
5939 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005940 if (forward) {
5941 contour.toPartialForward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00005942 } else {
5943 contour.toPartialBackward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00005944 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005945#if DEBUG_ASSEMBLE
5946 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
skia.committer@gmail.com61b05dc2012-12-14 02:02:06 +00005947 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005948 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
5949#endif
5950 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005951 simple.close();
caryclark@google.comf839c032012-10-26 21:03:50 +00005952 break;
5953 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005954 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005955 eIndex = eLink[rIndex];
5956 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005957 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005958 if (eIndex >= 0) {
5959 SkASSERT(sLink[eIndex] == rIndex);
5960 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005961 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005962 SkASSERT(eLink[~eIndex] == ~rIndex);
5963 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005964 }
5965 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005966 eIndex = sLink[rIndex];
5967 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005968 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005969 if (eIndex >= 0) {
5970 SkASSERT(eLink[eIndex] == rIndex);
5971 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005972 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005973 SkASSERT(sLink[~eIndex] == ~rIndex);
5974 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005975 }
5976 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005977 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005978 if (rIndex < 0) {
5979 forward ^= 1;
5980 rIndex = ~rIndex;
5981 }
5982 } while (true);
5983 for (rIndex = 0; rIndex < count; ++rIndex) {
5984 if (sLink[rIndex] != INT_MAX) {
5985 break;
5986 }
5987 }
5988 } while (rIndex < count);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005989#if DEBUG_ASSEMBLE
5990 for (rIndex = 0; rIndex < count; ++rIndex) {
5991 SkASSERT(sLink[rIndex] == INT_MAX);
5992 SkASSERT(eLink[rIndex] == INT_MAX);
5993 }
5994#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005995}
5996
5997void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005998 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005999 result.reset();
6000 result.setFillType(SkPath::kEvenOdd_FillType);
6001 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006002
6003 // turn path into list of segments
6004 SkTArray<Contour> contours;
6005 // FIXME: add self-intersecting cubics' T values to segment
6006 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00006007 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006008 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00006009 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006010 Contour** currentPtr = contourList.begin();
6011 if (!currentPtr) {
6012 return;
6013 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006014 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006015 // find all intersections between segments
6016 do {
6017 Contour** nextPtr = currentPtr;
6018 Contour* current = *currentPtr++;
6019 Contour* next;
6020 do {
6021 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00006022 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00006023 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00006024 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00006025 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00006026 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00006027 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00006028#if DEBUG_ACTIVE_SPANS
6029 debugShowActiveSpans(contourList);
6030#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006031 // construct closed contours
caryclark@google.com3586ece2012-12-27 18:46:58 +00006032 if (builder.xorMask() == kWinding_Mask ? bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00006033 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00006034 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00006035 SkPath temp;
6036 temp.setFillType(SkPath::kEvenOdd_FillType);
6037 PathWrapper assembled(temp);
6038 assemble(simple, assembled);
6039 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00006040 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00006041}
6042