blob: d0b22ce24326f62fb8fe8ee2501fa950a5fc65ff [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.comb45a1b42012-05-18 20:50:33 +0000515class Segment;
516
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000517struct Span {
518 Segment* fOther;
519 mutable SkPoint fPt; // lazily computed as needed
520 double fT;
521 double fOtherT; // value at fOther[fOtherIndex].fT
522 int fOtherIndex; // can't be used during intersection
caryclark@google.com31143cf2012-11-09 22:14:19 +0000523 int fWindSum; // accumulated from contours surrounding this one.
524 int fOppSum; // for binary operators: the opposite winding sum
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000525 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com57cff8d2012-11-14 21:14:56 +0000526 int fOppValue; // normally 0 -- when binary coincident edges combine, opp value goes here
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000527 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000528 bool fUnsortableStart; // set when start is part of an unsortable pair
529 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000530 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000531};
532
caryclark@google.com15fa1382012-05-07 20:49:36 +0000533// sorting angles
534// given angles of {dx dy ddx ddy dddx dddy} sort them
535class Angle {
536public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000537 // FIXME: this is bogus for quads and cubics
538 // if the quads and cubics' line from end pt to ctrl pt are coincident,
539 // there's no obvious way to determine the curve ordering from the
540 // derivatives alone. In particular, if one quadratic's coincident tangent
541 // is longer than the other curve, the final control point can place the
542 // longer curve on either side of the shorter one.
543 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
544 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000545
caryclark@google.com32546db2012-08-31 20:55:07 +0000546 /*(
547 for quads and cubics, set up a parameterized line (e.g. LineParameters )
548 for points [0] to [1]. See if point [2] is on that line, or on one side
549 or the other. If it both quads' end points are on the same side, choose
550 the shorter tangent. If the tangents are equal, choose the better second
551 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000552
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000553 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000554 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000555 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000556 double y = dy();
557 double ry = rh.dy();
558 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
559 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000560 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000561 double x = dx();
562 double rx = rh.dx();
563 if (y == 0 && ry == 0 && x * rx < 0) {
564 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000565 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000566 double x_ry = x * ry;
567 double rx_y = rx * y;
568 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000569 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000570 return cmp < 0;
571 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000572 if (approximately_zero(x_ry) && approximately_zero(rx_y)
573 && !approximately_zero_squared(cmp)) {
574 return cmp < 0;
575 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000576 // at this point, the initial tangent line is coincident
caryclark@google.com31143cf2012-11-09 22:14:19 +0000577 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide)
578 || !approximately_zero(rh.fSide))) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000579 // FIXME: running demo will trigger this assertion
580 // (don't know if commenting out will trigger further assertion or not)
581 // commenting it out allows demo to run in release, though
582 // SkASSERT(fSide != rh.fSide);
583 return fSide < rh.fSide;
584 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000585 // see if either curve can be lengthened and try the tangent compare again
586 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
587 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
588 Angle longer = *this;
589 Angle rhLonger = rh;
590 if (longer.lengthen() | rhLonger.lengthen()) {
591 return longer < rhLonger;
592 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000593 // what if we extend in the other direction?
594 longer = *this;
595 rhLonger = rh;
596 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
597 return longer < rhLonger;
598 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000599 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000600 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
caryclark@google.com31143cf2012-11-09 22:14:19 +0000601 || (rh.fVerb == SkPath::kLine_Verb
602 && approximately_zero(rx) && approximately_zero(ry))) {
caryclark@google.com185c7c42012-10-19 18:26:24 +0000603 // See general unsortable comment below. This case can happen when
604 // one line has a non-zero change in t but no change in x and y.
605 fUnsortable = true;
606 rh.fUnsortable = true;
607 return this < &rh; // even with no solution, return a stable sort
608 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000609 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny
610 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) {
611 fUnsortable = true;
612 rh.fUnsortable = true;
613 return this < &rh; // even with no solution, return a stable sort
614 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000615 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
616 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000617 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000618 // end of the shorter tangent to midway between the end points
619 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000620 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
621 double len = fTangent1.normalSquared();
622 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000623 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000624 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000625 int roots, rroots;
626 bool flip = false;
627 do {
628 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
629 double midX = (q[0].x + q[2].x) / 2;
630 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000631 ray[0] = q[1];
632 ray[1].x = midX;
633 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000634 SkASSERT(ray[0] != ray[1]);
635 roots = QuadRayIntersect(fPts, ray, i);
636 rroots = QuadRayIntersect(rh.fPts, ray, ri);
637 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000638 if (roots == 0 || rroots == 0) {
639 // FIXME: we don't have a solution in this case. The interim solution
640 // is to mark the edges as unsortable, exclude them from this and
641 // future computations, and allow the returned path to be fragmented
642 fUnsortable = true;
643 rh.fUnsortable = true;
644 return this < &rh; // even with no solution, return a stable sort
645 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000646 _Point loc;
647 double best = SK_ScalarInfinity;
648 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000649 int index;
650 for (index = 0; index < roots; ++index) {
651 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000652 dx = loc.x - ray[0].x;
653 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000654 dist = dx * dx + dy * dy;
655 if (best > dist) {
656 best = dist;
657 }
658 }
659 for (index = 0; index < rroots; ++index) {
660 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000661 dx = loc.x - ray[0].x;
662 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000663 dist = dx * dx + dy * dy;
664 if (best > dist) {
665 return fSide < 0;
666 }
667 }
668 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000669 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000670
caryclark@google.com47580692012-07-23 12:14:49 +0000671 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000672 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000673 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000674
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000675 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000676 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000677 }
678
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000679 int end() const {
680 return fEnd;
681 }
682
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000683 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000684 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000685 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000686
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000687 bool lengthen() {
688 int newEnd = fEnd;
689 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
690 fEnd = newEnd;
691 setSpans();
692 return true;
693 }
694 return false;
695 }
696
caryclark@google.coma461ff02012-10-11 12:54:23 +0000697 bool reverseLengthen() {
698 if (fReversed) {
699 return false;
700 }
701 int newEnd = fStart;
702 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
703 fEnd = newEnd;
704 fReversed = true;
705 setSpans();
706 return true;
707 }
708 return false;
709 }
710
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000711 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000712 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000713 fSegment = segment;
714 fStart = start;
715 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000716 fPts = orig;
717 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000718 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000719 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000720 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000721 setSpans();
722 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000723
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000724 void setSpans() {
725 double startT = (*fSpans)[fStart].fT;
726 double endT = (*fSpans)[fEnd].fT;
727 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000728 case SkPath::kLine_Verb:
729 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000730 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000731 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
732 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000733 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000734 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000735 break;
736 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000737 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000738 fTangent1.quadEndPoints(fQ, 0, 1);
739 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000740 break;
741 case SkPath::kCubic_Verb:
742 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000743 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000744 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000745 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000746 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000747 default:
748 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000749 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000750 if (fUnsortable) {
751 return;
752 }
753 SkASSERT(fStart != fEnd);
754 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
755 for (int index = fStart; index != fEnd; index += step) {
756 if ((*fSpans)[index].fUnsortableStart) {
757 fUnsortable = true;
758 return;
759 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000760#if 0
caryclark@google.comf839c032012-10-26 21:03:50 +0000761 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000762 SkASSERT(0);
caryclark@google.comf839c032012-10-26 21:03:50 +0000763 fUnsortable = true;
764 return;
765 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000766#endif
caryclark@google.comf839c032012-10-26 21:03:50 +0000767 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000768 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000769
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000770 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000771 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000772 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000773
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000774 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000775 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000776 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000777
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000778 const SkTDArray<Span>* spans() const {
779 return fSpans;
780 }
781
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000782 int start() const {
783 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000784 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000785
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000786 bool unsortable() const {
787 return fUnsortable;
788 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000789
caryclark@google.comc899ad92012-08-23 15:24:42 +0000790#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000791 const SkPoint* pts() const {
792 return fPts;
793 }
794
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000795 SkPath::Verb verb() const {
796 return fVerb;
797 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000798
caryclark@google.comc899ad92012-08-23 15:24:42 +0000799 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000800 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000801 }
802#endif
803
caryclark@google.com15fa1382012-05-07 20:49:36 +0000804private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000805 const SkPoint* fPts;
806 Quadratic fQ;
807 SkPath::Verb fVerb;
808 double fSide;
809 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000810 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000811 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000812 int fStart;
813 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000814 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000815 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000816};
817
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000818// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000819struct Bounds : public SkRect {
820 static bool Intersects(const Bounds& a, const Bounds& b) {
821 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
822 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
823 }
824
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000825 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
826 if (left < fLeft) {
827 fLeft = left;
828 }
829 if (top < fTop) {
830 fTop = top;
831 }
832 if (right > fRight) {
833 fRight = right;
834 }
835 if (bottom > fBottom) {
836 fBottom = bottom;
837 }
838 }
839
840 void add(const Bounds& toAdd) {
841 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
842 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +0000843
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000844 void add(const SkPoint& pt) {
845 if (pt.fX < fLeft) fLeft = pt.fX;
846 if (pt.fY < fTop) fTop = pt.fY;
847 if (pt.fX > fRight) fRight = pt.fX;
848 if (pt.fY > fBottom) fBottom = pt.fY;
849 }
850
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000851 bool isEmpty() {
852 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000853 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000854 || isnan(fLeft) || isnan(fRight)
855 || isnan(fTop) || isnan(fBottom);
856 }
857
858 void setCubicBounds(const SkPoint a[4]) {
859 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000860 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000861 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000862 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
863 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000864 }
865
866 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000867 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000868 _Rect dRect;
869 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000870 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
871 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000872 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +0000873
874 void setPoint(const SkPoint& pt) {
875 fLeft = fRight = pt.fX;
876 fTop = fBottom = pt.fY;
877 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000878};
879
caryclark@google.com7ba591e2012-11-20 14:21:54 +0000880// OPTIMIZATION: does the following also work, and is it any faster?
881// return outerWinding * innerWinding > 0
882// || ((outerWinding + innerWinding < 0) ^ ((outerWinding - innerWinding) < 0)))
caryclark@google.com2ddff932012-08-07 21:25:27 +0000883static bool useInnerWinding(int outerWinding, int innerWinding) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000884 // SkASSERT(outerWinding != innerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +0000885 int absOut = abs(outerWinding);
886 int absIn = abs(innerWinding);
887 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
888 if (outerWinding * innerWinding < 0) {
889#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000890 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000891 outerWinding, innerWinding, result ? "true" : "false");
892#endif
893 }
894 return result;
895}
896
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000897#define F (false) // discard the edge
898#define T (true) // keep the edge
899
900static const bool gActiveEdge[kShapeOp_Count][2][2][2][2] = {
901// miFrom=0 miFrom=1
902// miTo=0 miTo=1 miTo=0 miTo=1
903// suFrom=0 1 suFrom=0 1 suFrom=0 1 suFrom=0 1
904// suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1 suTo=0,1
905 {{{{F, F}, {F, F}}, {{T, F}, {T, F}}}, {{{T, T}, {F, F}}, {{F, T}, {T, F}}}}, // mi - su
906 {{{{F, F}, {F, F}}, {{F, T}, {F, T}}}, {{{F, F}, {T, T}}, {{F, T}, {T, F}}}}, // mi & su
907 {{{{F, T}, {T, F}}, {{T, T}, {F, F}}}, {{{T, F}, {T, F}}, {{F, F}, {F, F}}}}, // mi | su
908 {{{{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 +0000909};
910
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000911#undef F
912#undef T
caryclark@google.com235f56a2012-09-14 14:19:30 +0000913
caryclark@google.comf839c032012-10-26 21:03:50 +0000914// wrap path to keep track of whether the contour is initialized and non-empty
915class PathWrapper {
916public:
917 PathWrapper(SkPath& path)
918 : fPathPtr(&path)
919 {
920 init();
921 }
922
923 void close() {
924 if (!fHasMove) {
925 return;
926 }
927 bool callClose = isClosed();
928 lineTo();
929 if (fEmpty) {
930 return;
931 }
932 if (callClose) {
933 #if DEBUG_PATH_CONSTRUCTION
934 SkDebugf("path.close();\n");
935 #endif
936 fPathPtr->close();
937 }
938 init();
939 }
940
941 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
942 lineTo();
943 moveTo();
944#if DEBUG_PATH_CONSTRUCTION
945 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
946 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
947#endif
948 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
949 fDefer[0] = fDefer[1] = pt3;
950 fEmpty = false;
951 }
952
953 void deferredLine(const SkPoint& pt) {
954 if (pt == fDefer[1]) {
955 return;
956 }
957 if (changedSlopes(pt)) {
958 lineTo();
959 fDefer[0] = fDefer[1];
960 }
961 fDefer[1] = pt;
962 }
963
964 void deferredMove(const SkPoint& pt) {
965 fMoved = true;
966 fHasMove = true;
967 fEmpty = true;
968 fDefer[0] = fDefer[1] = pt;
969 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000970
caryclark@google.comf839c032012-10-26 21:03:50 +0000971 void deferredMoveLine(const SkPoint& pt) {
972 if (!fHasMove) {
973 deferredMove(pt);
974 }
975 deferredLine(pt);
976 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000977
caryclark@google.comf839c032012-10-26 21:03:50 +0000978 bool hasMove() const {
979 return fHasMove;
980 }
981
982 void init() {
983 fEmpty = true;
984 fHasMove = false;
985 fMoved = false;
986 }
987
988 bool isClosed() const {
989 return !fEmpty && fFirstPt == fDefer[1];
990 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000991
caryclark@google.comf839c032012-10-26 21:03:50 +0000992 void lineTo() {
993 if (fDefer[0] == fDefer[1]) {
994 return;
995 }
996 moveTo();
997 fEmpty = false;
998#if DEBUG_PATH_CONSTRUCTION
999 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
1000#endif
1001 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
1002 fDefer[0] = fDefer[1];
1003 }
1004
1005 const SkPath* nativePath() const {
1006 return fPathPtr;
1007 }
1008
1009 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
1010 lineTo();
1011 moveTo();
1012#if DEBUG_PATH_CONSTRUCTION
1013 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
1014 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1015#endif
1016 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
1017 fDefer[0] = fDefer[1] = pt2;
1018 fEmpty = false;
1019 }
1020
1021protected:
1022 bool changedSlopes(const SkPoint& pt) const {
1023 if (fDefer[0] == fDefer[1]) {
1024 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001025 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001026 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
1027 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
1028 SkScalar lineDx = pt.fX - fDefer[1].fX;
1029 SkScalar lineDy = pt.fY - fDefer[1].fY;
1030 return deferDx * lineDy != deferDy * lineDx;
1031 }
1032
1033 void moveTo() {
1034 if (!fMoved) {
1035 return;
1036 }
1037 fFirstPt = fDefer[0];
1038#if DEBUG_PATH_CONSTRUCTION
1039 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
1040#endif
1041 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
1042 fMoved = false;
1043 }
1044
1045private:
1046 SkPath* fPathPtr;
1047 SkPoint fDefer[2];
1048 SkPoint fFirstPt;
1049 bool fEmpty;
1050 bool fHasMove;
1051 bool fMoved;
1052};
1053
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001054class Segment {
1055public:
1056 Segment() {
1057#if DEBUG_DUMP
1058 fID = ++gSegmentID;
1059#endif
1060 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001061
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001062 bool operator<(const Segment& rh) const {
1063 return fBounds.fTop < rh.fBounds.fTop;
1064 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001065
caryclark@google.com4eeda372012-12-06 21:47:48 +00001066 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001067 if (activeAngleInner(index, done, angles)) {
1068 return true;
1069 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001070 double referenceT = fTs[index].fT;
1071 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001072 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001073 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001074 return true;
1075 }
1076 }
1077 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001078 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001079 return true;
1080 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001081 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001082 return false;
1083 }
1084
caryclark@google.com4eeda372012-12-06 21:47:48 +00001085 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001086 Span* span = &fTs[index];
1087 Segment* other = span->fOther;
1088 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001089 return other->activeAngleInner(oIndex, done, angles);
1090 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001091
caryclark@google.com4eeda372012-12-06 21:47:48 +00001092 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001093 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001094 if (next > 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001095 Span& upSpan = fTs[index];
1096 if (upSpan.fWindValue || upSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001097 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001098 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001099 done++;
1100 } else if (upSpan.fWindSum != SK_MinS32) {
1101 return true;
1102 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001103 } else if (!upSpan.fDone) {
1104 upSpan.fDone = true;
1105 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001106 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001107 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001108 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001109 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001110 if (prev >= 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001111 Span& downSpan = fTs[prev];
1112 if (downSpan.fWindValue || downSpan.fOppValue) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001113 addAngle(angles, index, prev);
1114 if (downSpan.fDone) {
1115 done++;
1116 } else if (downSpan.fWindSum != SK_MinS32) {
1117 return true;
1118 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001119 } else if (!downSpan.fDone) {
1120 downSpan.fDone = true;
1121 fDoneSpans++;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001122 }
1123 }
1124 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001125 }
1126
caryclark@google.comf839c032012-10-26 21:03:50 +00001127 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001128 SkASSERT(!done());
1129 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001130 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001131 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001132 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001133 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001134 const Span& span = fTs[index];
1135 if (span.fUnsortableStart | lastUnsortable) {
1136 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001137 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001138 if (!span.fDone | !lastDone) {
1139 const SkPoint& xy = xyAtT(index);
1140 if (result.fY < xy.fY) {
1141 goto next;
1142 }
1143 if (result.fY == xy.fY && result.fX < xy.fX) {
1144 goto next;
1145 }
1146 result = xy;
1147 }
1148 next:
1149 lastDone = span.fDone;
1150 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001151 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001152 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001153 }
1154
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001155 bool activeOp(int index, int endIndex, int xorMiMask, int xorSuMask, ShapeOp op) {
1156 int sumMiWinding = updateWinding(endIndex, index);
1157 int sumSuWinding = updateOppWinding(endIndex, index);
1158 if (fOperand) {
1159 SkTSwap<int>(sumMiWinding, sumSuWinding);
1160 }
1161 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
1162 return activeOp(xorMiMask, xorSuMask, index, endIndex, op, sumMiWinding, sumSuWinding,
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001163 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001164 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001165
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001166 bool activeOp(int xorMiMask, int xorSuMask, int index, int endIndex, ShapeOp op,
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00001167 int& sumMiWinding, int& sumSuWinding,
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001168 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
1169 setUpWindings(index, endIndex, sumMiWinding, sumSuWinding,
1170 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001171 bool miFrom;
1172 bool miTo;
1173 bool suFrom;
1174 bool suTo;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001175 if (operand()) {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001176 miFrom = (oppMaxWinding & xorMiMask) != 0;
1177 miTo = (oppSumWinding & xorMiMask) != 0;
1178 suFrom = (maxWinding & xorSuMask) != 0;
1179 suTo = (sumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001180 } else {
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001181 miFrom = (maxWinding & xorMiMask) != 0;
1182 miTo = (sumWinding & xorMiMask) != 0;
1183 suFrom = (oppMaxWinding & xorSuMask) != 0;
1184 suTo = (oppSumWinding & xorSuMask) != 0;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001185 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00001186 bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
1187 SkASSERT(result != -1);
1188 return result;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001189 }
1190
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001191 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001192 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001193 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001194#if DEBUG_ANGLE
caryclark@google.com31143cf2012-11-09 22:14:19 +00001195 if (angles.count() > 1 && !fTs[start].fTiny) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001196 SkPoint angle0Pt, newPt;
1197 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1198 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1199 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1200 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1201 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1202 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001203#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001204 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001205 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001206
caryclark@google.com2ddff932012-08-07 21:25:27 +00001207 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001208 double oEnd) {
1209 int tIndex = -1;
1210 int tCount = fTs.count();
1211 int oIndex = -1;
1212 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001213 do {
1214 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001215 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001216 int tIndexStart = tIndex;
1217 do {
1218 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001219 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001220 int oIndexStart = oIndex;
1221 double nextT;
1222 do {
1223 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001224 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001225 double oNextT;
1226 do {
1227 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001228 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001229 // at this point, spans before and after are at:
1230 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1231 // if tIndexStart == 0, no prior span
1232 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001233
caryclark@google.comcc905052012-07-25 20:59:42 +00001234 // advance the span with zero winding
1235 // if the following span exists (not past the end, non-zero winding)
1236 // connect the two edges
1237 if (!fTs[tIndexStart].fWindValue) {
1238 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1239 #if DEBUG_CONCIDENT
1240 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1241 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001242 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1243 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001244 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001245 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001246 }
1247 if (nextT < 1 && fTs[tIndex].fWindValue) {
1248 #if DEBUG_CONCIDENT
1249 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1250 __FUNCTION__, fID, other.fID, tIndex,
1251 fTs[tIndex].fT, xyAtT(tIndex).fX,
1252 xyAtT(tIndex).fY);
1253 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001254 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001255 }
1256 } else {
1257 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1258 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1259 #if DEBUG_CONCIDENT
1260 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1261 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001262 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1263 other.xyAtT(oIndexStart).fY);
1264 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001265 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001266 }
1267 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1268 #if DEBUG_CONCIDENT
1269 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1270 __FUNCTION__, fID, other.fID, oIndex,
1271 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1272 other.xyAtT(oIndex).fY);
1273 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1274 #endif
1275 }
1276 }
1277 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001278
caryclark@google.comcc905052012-07-25 20:59:42 +00001279 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1280 double oEnd) {
1281 // walk this to outsideTs[0]
1282 // walk other to outsideTs[1]
1283 // if either is > 0, add a pointer to the other, copying adjacent winding
1284 int tIndex = -1;
1285 int oIndex = -1;
1286 double tStart = outsideTs[0];
1287 double oStart = outsideTs[1];
1288 do {
1289 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001290 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001291 do {
1292 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001293 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001294 if (tIndex > 0 || oIndex > 0 || fOperand != other.fOperand) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001295 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001296 }
1297 tStart = fTs[tIndex].fT;
1298 oStart = other.fTs[oIndex].fT;
1299 do {
1300 double nextT;
1301 do {
1302 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001303 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001304 tStart = nextT;
1305 do {
1306 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001307 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001308 oStart = nextT;
caryclark@google.com4eeda372012-12-06 21:47:48 +00001309 if (tStart == 1 && oStart == 1 && fOperand == other.fOperand) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001310 break;
1311 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001312 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001313 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001314 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001315
caryclark@google.com4eeda372012-12-06 21:47:48 +00001316 void addCubic(const SkPoint pts[4], bool operand, bool evenOdd) {
1317 init(pts, SkPath::kCubic_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001318 fBounds.setCubicBounds(pts);
1319 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001320
caryclark@google.comf839c032012-10-26 21:03:50 +00001321 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001322 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001323 const SkPoint* ePtr;
1324 int lastT = fTs.count() - 1;
1325 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1326 ePtr = fPts;
1327 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001328 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001329 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1330 ePtr = edge;
1331 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001332 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001333 bool reverse = ePtr == fPts && start != 0;
1334 if (reverse) {
1335 path.deferredMoveLine(ePtr[fVerb]);
1336 switch (fVerb) {
1337 case SkPath::kLine_Verb:
1338 path.deferredLine(ePtr[0]);
1339 break;
1340 case SkPath::kQuad_Verb:
1341 path.quadTo(ePtr[1], ePtr[0]);
1342 break;
1343 case SkPath::kCubic_Verb:
1344 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1345 break;
1346 default:
1347 SkASSERT(0);
1348 }
1349 // return ePtr[0];
1350 } else {
1351 path.deferredMoveLine(ePtr[0]);
1352 switch (fVerb) {
1353 case SkPath::kLine_Verb:
1354 path.deferredLine(ePtr[1]);
1355 break;
1356 case SkPath::kQuad_Verb:
1357 path.quadTo(ePtr[1], ePtr[2]);
1358 break;
1359 case SkPath::kCubic_Verb:
1360 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1361 break;
1362 default:
1363 SkASSERT(0);
1364 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001365 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001366 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001367 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001368 }
1369
caryclark@google.com4eeda372012-12-06 21:47:48 +00001370 void addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
1371 init(pts, SkPath::kLine_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001372 fBounds.set(pts, 2);
1373 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001374
caryclark@google.comf839c032012-10-26 21:03:50 +00001375#if 0
1376 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001377 const SkPoint& pt = xyAtT(tIndex);
1378 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001379 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001380 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001381 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001382 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001383#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001384
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001385 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001386 void addOtherT(int index, double otherT, int otherIndex) {
1387 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001388 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001389 if (precisely_less_than_zero(otherT)) {
1390 otherT = 0;
1391 } else if (precisely_greater_than_one(otherT)) {
1392 otherT = 1;
1393 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001394 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001395 span.fOtherT = otherT;
1396 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001397 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001398
caryclark@google.com4eeda372012-12-06 21:47:48 +00001399 void addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
1400 init(pts, SkPath::kQuad_Verb, operand, evenOdd);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001401 fBounds.setQuadBounds(pts);
1402 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001403
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001404 // Defer all coincident edge processing until
1405 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001406
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001407// no need to be tricky; insert in normal T order
1408// resolve overlapping ts when considering coincidence later
1409
1410 // add non-coincident intersection. Resulting edges are sorted in T.
1411 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001412 // FIXME: in the pathological case where there is a ton of intercepts,
1413 // binary search?
1414 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001415 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001416 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001417 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001418 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001419 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001420 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001421 newT = 1;
1422 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001423 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001424 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001425 // OPTIMIZATION: if there are three or more identical Ts, then
1426 // the fourth and following could be further insertion-sorted so
1427 // that all the edges are clockwise or counterclockwise.
1428 // This could later limit segment tests to the two adjacent
1429 // neighbors, although it doesn't help with determining which
1430 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001431 if (newT < fTs[index].fT) {
1432 insertedAt = index;
1433 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001434 }
1435 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001436 Span* span;
1437 if (insertedAt >= 0) {
1438 span = fTs.insert(insertedAt);
1439 } else {
1440 insertedAt = tCount;
1441 span = fTs.append();
1442 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001443 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001444 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001445 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001446 span->fWindSum = SK_MinS32;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001447 span->fOppSum = SK_MinS32;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001448 span->fWindValue = 1;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001449 span->fOppValue = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001450 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001451 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001452 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001453 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001454 span->fUnsortableStart = false;
1455 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001456 if (span - fTs.begin() > 0 && !span[-1].fDone
1457 && !precisely_negative(newT - span[-1].fT)
1458 // && approximately_negative(newT - span[-1].fT)
1459 && xyAtT(&span[-1]) == xyAtT(span)) {
1460 span[-1].fTiny = true;
1461 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001462 if (approximately_negative(newT - span[-1].fT)) {
1463 if (approximately_greater_than_one(newT)) {
1464 span[-1].fUnsortableStart = true;
1465 span[-2].fUnsortableEnd = true;
1466 }
1467 if (approximately_less_than_zero(span[-1].fT)) {
1468 span->fUnsortableStart = true;
1469 span[-1].fUnsortableEnd = true;
1470 }
1471 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001472 ++fDoneSpans;
1473 }
1474 if (fTs.end() - span > 1 && !span->fDone
1475 && !precisely_negative(span[1].fT - newT)
1476 // && approximately_negative(span[1].fT - newT)
1477 && xyAtT(&span[1]) == xyAtT(span)) {
1478 span->fTiny = true;
1479 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001480 if (approximately_negative(span[1].fT - newT)) {
1481 if (approximately_greater_than_one(span[1].fT)) {
1482 span->fUnsortableStart = true;
1483 span[-1].fUnsortableEnd = true;
1484 }
1485 if (approximately_less_than_zero(newT)) {
1486 span[1].fUnsortableStart = true;
1487 span->fUnsortableEnd = true;
1488 }
1489 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001490 ++fDoneSpans;
1491 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001492 return insertedAt;
1493 }
1494
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001495 // set spans from start to end to decrement by one
1496 // note this walks other backwards
1497 // FIMXE: there's probably an edge case that can be constructed where
1498 // two span in one segment are separated by float epsilon on one span but
1499 // not the other, if one segment is very small. For this
1500 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001501 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001502 // sorted? It feels better in such a case to match the span's other span
1503 // pointer since both coincident segments must contain the same spans.
1504 void addTCancel(double startT, double endT, Segment& other,
1505 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001506 SkASSERT(!approximately_negative(endT - startT));
1507 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001508 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001509 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001510 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001511 ++index;
1512 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001513 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001514 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001515 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001516 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001517 Span* test = &fTs[index];
1518 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001519 SkTDArray<double> outsideTs;
1520 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001521 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001522 bool decrement = test->fWindValue && oTest->fWindValue && !binary;
caryclark@google.comcc905052012-07-25 20:59:42 +00001523 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001524 double testT = test->fT;
1525 double oTestT = oTest->fT;
1526 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001527 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001528 if (decrement) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00001529 decrementSpan(span);
caryclark@google.com200c2112012-08-03 15:05:04 +00001530 } else if (track && span->fT < 1 && oTestT < 1) {
1531 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001532 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001533 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001534 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001535 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001536 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1537 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1538 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001539 while (approximately_negative(otherTMatchStart - oSpan->fT)
1540 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001541 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001542 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001543 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001544 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001545 other.decrementSpan(oSpan);
1546 } else if (track && oSpan->fT < 1 && testT < 1) {
1547 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001548 }
1549 if (!oIndex) {
1550 break;
1551 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001552 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001553 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001554 test = span;
1555 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001556 } while (!approximately_negative(endT - test->fT));
1557 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001558 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001559 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001560 double tStart = outsideTs[0];
1561 double oStart = outsideTs[1];
1562 addCancelOutsides(tStart, oStart, other, oEndT);
1563 int count = outsideTs.count();
1564 if (count > 2) {
1565 double tStart = outsideTs[count - 2];
1566 double oStart = outsideTs[count - 1];
1567 addCancelOutsides(tStart, oStart, other, oEndT);
1568 }
caryclark@google.com18063442012-07-25 12:05:18 +00001569 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001570 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001571 double tStart = oOutsideTs[0];
1572 double oStart = oOutsideTs[1];
1573 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001574 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00001576
caryclark@google.com4eeda372012-12-06 21:47:48 +00001577 int bumpCoincidentThis(const Span* oTest, bool opp, int index,
1578 SkTDArray<double>& outsideTs) {
1579 int oWindValue = oTest->fWindValue;
1580 int oOppValue = oTest->fOppValue;
1581 if (opp) {
1582 SkTSwap<int>(oWindValue, oOppValue);
1583 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001584 Span* const test = &fTs[index];
1585 Span* end = test;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001586 const double oStartT = oTest->fT;
1587 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001588 if (bumpSpan(end, oWindValue, oOppValue)) {
1589 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001590 }
1591 end = &fTs[++index];
1592 } while (approximately_negative(end->fT - test->fT));
1593 return index;
1594 }
1595
1596 // because of the order in which coincidences are resolved, this and other
1597 // may not have the same intermediate points. Compute the corresponding
1598 // intermediate T values (using this as the master, other as the follower)
1599 // and walk other conditionally -- hoping that it catches up in the end
caryclark@google.com4eeda372012-12-06 21:47:48 +00001600 int bumpCoincidentOther(const Span* test, double oEndT, int& oIndex,
1601 SkTDArray<double>& oOutsideTs) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001602 Span* const oTest = &fTs[oIndex];
1603 Span* oEnd = oTest;
1604 const double startT = test->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001605 const double oStartT = oTest->fT;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001606 while (!approximately_negative(oEndT - oEnd->fT)
caryclark@google.com4eeda372012-12-06 21:47:48 +00001607 && approximately_negative(oEnd->fT - oStartT)) {
1608 zeroSpan(oEnd);
1609 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001610 oEnd = &fTs[++oIndex];
1611 }
1612 return oIndex;
1613 }
1614
1615 // FIXME: need to test this case:
1616 // contourA has two segments that are coincident
1617 // contourB has two segments that are coincident in the same place
1618 // each ends up with +2/0 pairs for winding count
1619 // since logic below doesn't transfer count (only increments/decrements) can this be
1620 // resolved to +4/0 ?
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001621
1622 // set spans from start to end to increment the greater by one and decrement
1623 // the lesser
caryclark@google.com4eeda372012-12-06 21:47:48 +00001624 void addTCoincident(double startT, double endT, Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001625 SkASSERT(!approximately_negative(endT - startT));
1626 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001627 bool opp = fOperand ^ other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001628 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001629 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001630 ++index;
1631 }
1632 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001633 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001634 ++oIndex;
1635 }
1636 Span* test = &fTs[index];
1637 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001638 SkTDArray<double> outsideTs;
1639 SkTDArray<double> oOutsideTs;
1640 do {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001641 // if either span has an opposite value and the operands don't match, resolve first
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001642 // SkASSERT(!test->fDone || !oTest->fDone);
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001643 if (test->fDone || oTest->fDone) {
1644 index = advanceCoincidentThis(oTest, opp, index);
1645 oIndex = other.advanceCoincidentOther(test, oEndT, oIndex);
1646 } else {
1647 index = bumpCoincidentThis(oTest, opp, index, outsideTs);
1648 oIndex = other.bumpCoincidentOther(test, oEndT, oIndex, oOutsideTs);
1649 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00001650 test = &fTs[index];
1651 oTest = &other.fTs[oIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001652 } while (!approximately_negative(endT - test->fT));
1653 SkASSERT(approximately_negative(oTest->fT - oEndT));
1654 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00001655 if (!done() && outsideTs.count()) {
1656 addCoinOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001657 }
1658 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001659 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001660 }
1661 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001662
caryclark@google.comcc905052012-07-25 20:59:42 +00001663 // FIXME: this doesn't prevent the same span from being added twice
1664 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001665 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001666 int tCount = fTs.count();
1667 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1668 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001669 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001670 break;
1671 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001672 if (approximately_negative(span.fT - t) && span.fOther == &other
1673 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001674#if DEBUG_ADD_T_PAIR
1675 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1676 __FUNCTION__, fID, t, other.fID, otherT);
1677#endif
1678 return;
1679 }
1680 }
caryclark@google.com47580692012-07-23 12:14:49 +00001681#if DEBUG_ADD_T_PAIR
1682 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1683 __FUNCTION__, fID, t, other.fID, otherT);
1684#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001685 int insertedAt = addT(t, &other);
1686 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001687 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001688 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001689 matchWindingValue(insertedAt, t, borrowWind);
1690 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001691 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001692
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001693 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001694 // add edge leading into junction
caryclark@google.com4eeda372012-12-06 21:47:48 +00001695 int min = SkMin32(end, start);
1696 if (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001697 addAngle(angles, end, start);
1698 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001699 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001700 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001701 int tIndex = nextExactSpan(end, step);
caryclark@google.com4eeda372012-12-06 21:47:48 +00001702 min = SkMin32(end, tIndex);
1703 if (tIndex >= 0 && (fTs[min].fWindValue > 0 || fTs[min].fOppValue > 0)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001704 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001705 }
1706 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001707
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001708 int advanceCoincidentThis(const Span* oTest, bool opp, int index) {
1709 Span* const test = &fTs[index];
1710 Span* end = test;
1711 do {
1712 end = &fTs[++index];
1713 } while (approximately_negative(end->fT - test->fT));
1714 return index;
1715 }
1716
1717 int advanceCoincidentOther(const Span* test, double oEndT, int& oIndex) {
1718 Span* const oTest = &fTs[oIndex];
1719 Span* oEnd = oTest;
1720 const double oStartT = oTest->fT;
1721 while (!approximately_negative(oEndT - oEnd->fT)
1722 && approximately_negative(oEnd->fT - oStartT)) {
1723 oEnd = &fTs[++oIndex];
1724 }
1725 return oIndex;
1726 }
1727
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001728 const Bounds& bounds() const {
1729 return fBounds;
1730 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001731
caryclark@google.com31143cf2012-11-09 22:14:19 +00001732 void buildAngles(int index, SkTDArray<Angle>& angles, bool includeOpp) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001733 double referenceT = fTs[index].fT;
1734 int lesser = index;
caryclark@google.com31143cf2012-11-09 22:14:19 +00001735 while (--lesser >= 0 && (includeOpp || fTs[lesser].fOther->fOperand == fOperand)
1736 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00001737 buildAnglesInner(lesser, angles);
1738 }
1739 do {
1740 buildAnglesInner(index, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001741 } while (++index < fTs.count() && (includeOpp || fTs[index].fOther->fOperand == fOperand)
1742 && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001743 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001744
1745 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1746 Span* span = &fTs[index];
1747 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001748 // if there is only one live crossing, and no coincidence, continue
1749 // in the same direction
1750 // if there is coincidence, the only choice may be to reverse direction
1751 // find edge on either side of intersection
1752 int oIndex = span->fOtherIndex;
1753 // if done == -1, prior span has already been processed
1754 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001755 int next = other->nextExactSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001756 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001757 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001758 next = other->nextExactSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001759 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001760 // add candidate into and away from junction
1761 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001762 }
1763
caryclark@google.com7fce0de2012-11-29 14:31:50 +00001764 int computeSum(int startIndex, int endIndex, bool binary) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001765 SkTDArray<Angle> angles;
1766 addTwoAngles(startIndex, endIndex, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00001767 buildAngles(endIndex, angles, false);
caryclark@google.comd1688742012-09-18 20:08:37 +00001768 // OPTIMIZATION: check all angles to see if any have computed wind sum
1769 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001770 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001771 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001772#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00001773 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001774#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001775 if (!sortable) {
1776 return SK_MinS32;
1777 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001778 int angleCount = angles.count();
1779 const Angle* angle;
1780 const Segment* base;
1781 int winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001782 int oWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001783 int firstIndex = 0;
1784 do {
1785 angle = sorted[firstIndex];
1786 base = angle->segment();
1787 winding = base->windSum(angle);
1788 if (winding != SK_MinS32) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001789 oWinding = base->oppSum(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001790 break;
1791 }
1792 if (++firstIndex == angleCount) {
1793 return SK_MinS32;
1794 }
1795 } while (true);
1796 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001797 int spanWinding = base->spanSign(angle);
1798 bool inner = useInnerWinding(winding + spanWinding, winding);
1799 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001800 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001801 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001802 inner ? winding + spanWinding : winding);
1803 #endif
1804 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001805 winding += spanWinding;
1806 }
1807 #if DEBUG_SORT
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001808 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, oWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001809 #endif
1810 int nextIndex = firstIndex + 1;
1811 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001812 winding -= base->spanSign(angle);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001813 oWinding -= base->oppSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001814 do {
1815 if (nextIndex == angleCount) {
1816 nextIndex = 0;
1817 }
1818 angle = sorted[nextIndex];
1819 Segment* segment = angle->segment();
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001820 bool opp = base->fOperand ^ segment->fOperand;
1821 int maxWinding, oMaxWinding;
1822 int spanSign = segment->spanSign(angle);
1823 int oppoSign = segment->oppSign(angle);
1824 if (opp) {
1825 oMaxWinding = oWinding;
1826 oWinding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001827 maxWinding = winding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001828 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001829 winding -= oppoSign;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001830 }
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001831 } else {
1832 maxWinding = winding;
1833 winding -= spanSign;
caryclark@google.com729e1c42012-11-21 21:36:34 +00001834 oMaxWinding = oWinding;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001835 if (oppoSign) {
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001836 oWinding -= oppoSign;
1837 }
1838 }
1839 if (segment->windSum(angle) == SK_MinS32) {
1840 if (opp) {
1841 if (useInnerWinding(oMaxWinding, oWinding)) {
1842 oMaxWinding = oWinding;
1843 }
1844 if (oppoSign && useInnerWinding(maxWinding, winding)) {
1845 maxWinding = winding;
1846 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001847 (void) segment->markAndChaseWinding(angle, oMaxWinding, maxWinding);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001848 } else {
1849 if (useInnerWinding(maxWinding, winding)) {
1850 maxWinding = winding;
1851 }
1852 if (oppoSign && useInnerWinding(oMaxWinding, oWinding)) {
1853 oMaxWinding = oWinding;
1854 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001855 (void) segment->markAndChaseWinding(angle, maxWinding,
1856 binary ? oMaxWinding : 0);
caryclark@google.com7ba591e2012-11-20 14:21:54 +00001857 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001858 }
1859 } while (++nextIndex != lastIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001860 int minIndex = SkMin32(startIndex, endIndex);
caryclark@google.com6ec15262012-11-16 20:16:50 +00001861 return windSum(minIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001862 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001863
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001864 int crossedSpanX(const SkPoint& basePt, SkScalar& bestX, double& hitT, bool opp) const {
1865 int bestT = -1;
1866 SkScalar left = bounds().fLeft;
1867 SkScalar right = bounds().fRight;
1868 int end = 0;
1869 do {
1870 int start = end;
1871 end = nextSpan(start, 1);
1872 if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
1873 continue;
1874 }
1875 SkPoint edge[4];
1876 double startT = fTs[start].fT;
1877 double endT = fTs[end].fT;
1878 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
1879 // intersect ray starting at basePt with edge
1880 Intersections intersections;
1881 // FIXME: always use original and limit results to T values within
1882 // start t and end t.
1883 // OPTIMIZE: use specialty function that intersects ray with curve,
1884 // returning t values only for curve (we don't care about t on ray)
1885 int pts = (*HSegmentIntersect[fVerb])(edge, left, right, basePt.fY,
1886 false, intersections);
1887 if (pts == 0) {
1888 continue;
1889 }
1890 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1891 // if the intersection is edge on, wait for another one
1892 continue;
1893 }
1894 for (int index = 0; index < pts; ++index) {
1895 double foundT = intersections.fT[0][index];
1896 double testT = startT + (endT - startT) * foundT;
1897 SkScalar testX = (*SegmentXAtT[fVerb])(fPts, testT);
1898 if (bestX < testX && testX < basePt.fX) {
1899 if (fVerb > SkPath::kLine_Verb
1900 && !approximately_less_than_zero(foundT)
1901 && !approximately_greater_than_one(foundT)) {
1902 SkScalar dy = (*SegmentDYAtT[fVerb])(fPts, testT);
1903 if (approximately_zero(dy)) {
1904 continue;
1905 }
1906 }
1907 bestX = testX;
1908 bestT = foundT < 1 ? start : end;
1909 hitT = testT;
1910 }
1911 }
1912 } while (fTs[end].fT != 1);
1913 return bestT;
1914 }
1915
1916 int crossedSpanY(const SkPoint& basePt, SkScalar& bestY, double& hitT, bool opp) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001917 int bestT = -1;
1918 SkScalar top = bounds().fTop;
1919 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001920 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001921 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001922 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001923 end = nextSpan(start, 1);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001924 if ((opp ? fTs[start].fOppValue : fTs[start].fWindValue) == 0) {
caryclark@google.com47580692012-07-23 12:14:49 +00001925 continue;
1926 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001927 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001928 double startT = fTs[start].fT;
1929 double endT = fTs[end].fT;
1930 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001931 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001932 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001933 // FIXME: always use original and limit results to T values within
1934 // start t and end t.
1935 // OPTIMIZE: use specialty function that intersects ray with curve,
1936 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001937 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1938 false, intersections);
1939 if (pts == 0) {
1940 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001941 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001942 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1943 // if the intersection is edge on, wait for another one
1944 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001945 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001946 for (int index = 0; index < pts; ++index) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001947 double foundT = intersections.fT[0][index];
1948 double testT = startT + (endT - startT) * foundT;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001949 SkScalar testY = (*SegmentYAtT[fVerb])(fPts, testT);
1950 if (bestY < testY && testY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001951 if (fVerb > SkPath::kLine_Verb
1952 && !approximately_less_than_zero(foundT)
1953 && !approximately_greater_than_one(foundT)) {
1954 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1955 if (approximately_zero(dx)) {
1956 continue;
1957 }
1958 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00001959 bestY = testY;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001960 bestT = foundT < 1 ? start : end;
1961 hitT = testT;
1962 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001963 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001964 } while (fTs[end].fT != 1);
1965 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001966 }
caryclark@google.com18063442012-07-25 12:05:18 +00001967
caryclark@google.com4eeda372012-12-06 21:47:48 +00001968 void decrementSpan(Span* span) {
caryclark@google.com18063442012-07-25 12:05:18 +00001969 SkASSERT(span->fWindValue > 0);
1970 if (--(span->fWindValue) == 0) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00001971 if (!span->fOppValue && !span->fDone) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001972 span->fDone = true;
1973 ++fDoneSpans;
1974 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00001975 }
1976 }
1977
1978 bool bumpSpan(Span* span, int windDelta, int oppDelta) {
1979 SkASSERT(!span->fDone);
1980 span->fWindValue += windDelta;
1981 SkASSERT(span->fWindValue >= 0);
1982 span->fOppValue += oppDelta;
1983 SkASSERT(span->fOppValue >= 0);
1984 if (fXor) {
1985 span->fWindValue &= 1;
1986 }
1987 if (fOppXor) {
1988 span->fOppValue &= 1;
1989 }
1990 if (!span->fWindValue && !span->fOppValue) {
1991 span->fDone = true;
1992 ++fDoneSpans;
caryclark@google.com18063442012-07-25 12:05:18 +00001993 return true;
1994 }
1995 return false;
1996 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00001997
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00001998 // OPTIMIZE
1999 // when the edges are initially walked, they don't automatically get the prior and next
2000 // 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 +00002001 // and would additionally remove the need for similar checks in condition edges. It would
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00002002 // also allow intersection code to assume end of segment intersections (maybe?)
2003 bool complete() const {
2004 int count = fTs.count();
2005 return count > 1 && fTs[0].fT == 0 && fTs[--count].fT == 1;
2006 }
caryclark@google.com18063442012-07-25 12:05:18 +00002007
caryclark@google.com15fa1382012-05-07 20:49:36 +00002008 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002009 SkASSERT(fDoneSpans <= fTs.count());
2010 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002011 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002012
caryclark@google.comf839c032012-10-26 21:03:50 +00002013 bool done(int min) const {
2014 return fTs[min].fDone;
2015 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002016
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002017 bool done(const Angle* angle) const {
2018 return done(SkMin32(angle->start(), angle->end()));
caryclark@google.com47580692012-07-23 12:14:49 +00002019 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002020
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002021 /*
2022 The M and S variable name parts stand for the operators.
2023 Mi stands for Minuend (see wiki subtraction, analogous to difference)
2024 Su stands for Subtrahend
2025 The Opp variable name part designates that the value is for the Opposite operator.
2026 Opposite values result from combining coincident spans.
2027 */
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002028
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002029 Segment* findNextOp(SkTDArray<Span*>& chase, int& nextStart, int& nextEnd,
2030 bool& unsortable, ShapeOp op, const int xorMiMask, const int xorSuMask) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002031 const int startIndex = nextStart;
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002032 const int endIndex = nextEnd;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002033 SkASSERT(startIndex != endIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002034 const int count = fTs.count();
2035 SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
2036 const int step = SkSign32(endIndex - startIndex);
2037 const int end = nextExactSpan(startIndex, step);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002038 SkASSERT(end >= 0);
2039 Span* endSpan = &fTs[end];
2040 Segment* other;
2041 if (isSimple(end)) {
2042 // mark the smaller of startIndex, endIndex done, and all adjacent
2043 // spans with the same T value (but not 'other' spans)
2044 #if DEBUG_WINDING
2045 SkDebugf("%s simple\n", __FUNCTION__);
2046 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002047 int min = SkMin32(startIndex, endIndex);
2048 if (fTs[min].fDone) {
2049 return NULL;
2050 }
2051 markDoneBinary(min);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002052 other = endSpan->fOther;
2053 nextStart = endSpan->fOtherIndex;
2054 double startT = other->fTs[nextStart].fT;
2055 nextEnd = nextStart;
2056 do {
2057 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002058 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002059 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002060 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2061 return other;
2062 }
2063 // more than one viable candidate -- measure angles to find best
2064 SkTDArray<Angle> angles;
2065 SkASSERT(startIndex - endIndex != 0);
2066 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2067 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002068 buildAngles(end, angles, true);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002069 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002070 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002071 int angleCount = angles.count();
2072 int firstIndex = findStartingEdge(sorted, startIndex, end);
2073 SkASSERT(firstIndex >= 0);
2074 #if DEBUG_SORT
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002075 debugShowSort(__FUNCTION__, sorted, firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002076 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002077 if (!sortable) {
2078 unsortable = true;
2079 return NULL;
2080 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00002081 SkASSERT(sorted[firstIndex]->segment() == this);
2082 #if DEBUG_WINDING
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002083 SkDebugf("%s firstIndex=[%d] sign=%d\n", __FUNCTION__, firstIndex,
2084 sorted[firstIndex]->sign());
caryclark@google.com235f56a2012-09-14 14:19:30 +00002085 #endif
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002086 int sumMiWinding = updateWinding(endIndex, startIndex);
2087 int sumSuWinding = updateOppWinding(endIndex, startIndex);
2088 if (operand()) {
2089 SkTSwap<int>(sumMiWinding, sumSuWinding);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002090 }
2091 int nextIndex = firstIndex + 1;
2092 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2093 const Angle* foundAngle = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002094 bool foundDone = false;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002095 // iterate through the angle, and compute everyone's winding
caryclark@google.com235f56a2012-09-14 14:19:30 +00002096 Segment* nextSegment;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002097 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002098 SkASSERT(nextIndex != firstIndex);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002099 if (nextIndex == angleCount) {
2100 nextIndex = 0;
2101 }
2102 const Angle* nextAngle = sorted[nextIndex];
2103 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002104 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
2105 bool activeAngle = nextSegment->activeOp(xorMiMask, xorSuMask, nextAngle->start(),
2106 nextAngle->end(), op, sumMiWinding, sumSuWinding,
2107 maxWinding, sumWinding, oppMaxWinding, oppSumWinding);
2108 if (activeAngle && (!foundAngle || foundDone)) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002109 foundAngle = nextAngle;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002110 foundDone = nextSegment->done(nextAngle) && !nextSegment->tiny(nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002111 }
2112 if (nextSegment->done()) {
2113 continue;
2114 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002115 if (nextSegment->windSum(nextAngle) != SK_MinS32) {
2116 continue;
2117 }
2118 Span* last = nextSegment->markAngle(maxWinding, sumWinding, oppMaxWinding,
2119 oppSumWinding, activeAngle, nextAngle);
2120 if (last) {
2121 *chase.append() = last;
2122#if DEBUG_WINDING
2123 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2124 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2125#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002126 }
2127 } while (++nextIndex != lastIndex);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002128 markDoneBinary(SkMin32(startIndex, endIndex));
caryclark@google.com235f56a2012-09-14 14:19:30 +00002129 if (!foundAngle) {
2130 return NULL;
2131 }
2132 nextStart = foundAngle->start();
2133 nextEnd = foundAngle->end();
2134 nextSegment = foundAngle->segment();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00002135
caryclark@google.com235f56a2012-09-14 14:19:30 +00002136 #if DEBUG_WINDING
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002137 SkDebugf("%s from:[%d] to:[%d] start=%d end=%d\n",
2138 __FUNCTION__, debugID(), nextSegment->debugID(), nextStart, nextEnd);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002139 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00002140 return nextSegment;
2141 }
caryclark@google.com47580692012-07-23 12:14:49 +00002142
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002143 // so the span needs to contain the pairing info found here
2144 // this should include the winding computed for the edge, and
2145 // what edge it connects to, and whether it is discarded
2146 // (maybe discarded == abs(winding) > 1) ?
2147 // only need derivatives for duration of sorting, add a new struct
2148 // for pairings, remove extra spans that have zero length and
2149 // reference an unused other
2150 // for coincident, the last span on the other may be marked done
2151 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002152
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002153 // if loop is exhausted, contour may be closed.
2154 // FIXME: pass in close point so we can check for closure
2155
2156 // given a segment, and a sense of where 'inside' is, return the next
2157 // segment. If this segment has an intersection, or ends in multiple
2158 // segments, find the mate that continues the outside.
2159 // note that if there are multiples, but no coincidence, we can limit
2160 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002161
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002162 // mark found segments as done
2163
caryclark@google.com15fa1382012-05-07 20:49:36 +00002164 // start is the index of the beginning T of this edge
2165 // it is guaranteed to have an end which describes a non-zero length (?)
2166 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002167 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002168 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2169 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002170 const int startIndex = nextStart;
2171 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002172 int outerWinding = winding;
2173 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002174 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002175 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2176 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002177 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002178 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002179 outerWinding = innerWinding;
2180 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002181 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002182 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002183 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2184 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002185 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002186 int end = nextExactSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002187 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002188 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002189 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002190 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002191 // mark the smaller of startIndex, endIndex done, and all adjacent
2192 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002193 #if DEBUG_WINDING
2194 SkDebugf("%s simple\n", __FUNCTION__);
2195 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002196 int min = SkMin32(startIndex, endIndex);
2197 if (fTs[min].fDone) {
2198 return NULL;
2199 }
2200 markDone(min, outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002201 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002202 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002203 double startT = other->fTs[nextStart].fT;
2204 nextEnd = nextStart;
2205 do {
2206 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002207 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002208 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com495f8e42012-05-31 13:13:11 +00002209 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002210 return other;
2211 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002212 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002213 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002214 SkASSERT(startIndex - endIndex != 0);
2215 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002216 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002217 buildAngles(end, angles, false);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002218 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002219 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002220 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002221 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002222 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002223 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002224 debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002225 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002226 if (!sortable) {
2227 unsortable = true;
2228 return NULL;
2229 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002230 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002231 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002232 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002233 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002234 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002235 int nextIndex = firstIndex + 1;
2236 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2237 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002238 // FIXME: found done logic probably fails if there are more than 4
2239 // sorted angles. It should bias towards the first and last undone
2240 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002241 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002242 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002243 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002244 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002245 bool altFlipped = false;
2246 bool foundFlipped = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002247 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002248 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002249 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002250 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002251 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002252 nextIndex = 0;
2253 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002254 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002255 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002256 if (sumWinding) {
2257 lastNonZeroSum = sumWinding;
2258 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002259 nextSegment = nextAngle->segment();
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002260 bool nextDone = nextSegment->done(nextAngle);
2261 bool nextTiny = nextSegment->tiny(nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002262 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002263 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002264 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002265 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002266 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2267 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002268 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002269 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002270 if (!active) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002271 // FIXME: shouldn't this call mark and chase done ?
caryclark@google.com59823f72012-08-09 18:17:47 +00002272 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002273 // FIXME: shouldn't this call mark and chase winding ?
caryclark@google.com47580692012-07-23 12:14:49 +00002274 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002275 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002276 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002277 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002278 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002279 return NULL;
2280 }
caryclark@google.com47580692012-07-23 12:14:49 +00002281 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002282 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002283 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002284 foundFlipped = altFlipped;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002285 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002286 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002287 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002288
caryclark@google.com24bec792012-08-20 12:43:57 +00002289 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002290 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002291 if (foundAngle && foundDone2) {
2292 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002293 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002294 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002295 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002296 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002297 foundFlipped = altFlipped;
2298 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002299 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002300 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002301 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002302 }
2303 // if the winding is non-zero, nextAngle does not connect to
2304 // current chain. If we haven't done so already, mark the angle
2305 // as done, record the winding value, and mark connected unambiguous
2306 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002307 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002308 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002309 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002310 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002311 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002312 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002313 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002314 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002315 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002316 }
2317 if (last) {
2318 *chase.append() = last;
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002319 #if DEBUG_WINDING
2320 SkDebugf("%s chase.append id=%d\n", __FUNCTION__,
2321 last->fOther->fTs[last->fOtherIndex].fOther->debugID());
2322 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002323 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002324 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002325 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002326 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002327 if (!foundAngle) {
2328 return NULL;
2329 }
2330 nextStart = foundAngle->start();
2331 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002332 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002333 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002334 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2335 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002336 if (winding) {
2337 #if DEBUG_WINDING
2338 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2339 if (foundSum == SK_MinS32) {
2340 SkDebugf("?");
2341 } else {
2342 SkDebugf("%d", foundSum);
2343 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002344 SkDebugf("\n");
2345 #endif
2346 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002347 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002348 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002349 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002350 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002351 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002352 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002353
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002354 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002355 const int startIndex = nextStart;
2356 const int endIndex = nextEnd;
2357 SkASSERT(startIndex != endIndex);
2358 int count = fTs.count();
2359 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2360 : startIndex > 0);
2361 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002362 int end = nextExactSpan(startIndex, step);
caryclark@google.com24bec792012-08-20 12:43:57 +00002363 SkASSERT(end >= 0);
2364 Span* endSpan = &fTs[end];
2365 Segment* other;
caryclark@google.com24bec792012-08-20 12:43:57 +00002366 if (isSimple(end)) {
2367 #if DEBUG_WINDING
2368 SkDebugf("%s simple\n", __FUNCTION__);
2369 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002370 int min = SkMin32(startIndex, endIndex);
2371 if (fTs[min].fDone) {
2372 return NULL;
2373 }
2374 markDone(min, 1);
caryclark@google.com24bec792012-08-20 12:43:57 +00002375 other = endSpan->fOther;
2376 nextStart = endSpan->fOtherIndex;
2377 double startT = other->fTs[nextStart].fT;
2378 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002379 if ((approximately_less_than_zero(startT) && step < 0)
2380 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002381 step = -step;
2382 SkDEBUGCODE(firstLoop = false;)
2383 }
2384 do {
2385 nextEnd = nextStart;
2386 do {
2387 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002388 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002389 while (precisely_zero(startT - other->fTs[nextEnd].fT));
caryclark@google.com24bec792012-08-20 12:43:57 +00002390 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2391 break;
2392 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002393 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002394 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002395 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002396 SkDEBUGCODE(firstLoop = false;)
2397 step = -step;
2398 } while (true);
2399 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2400 return other;
2401 }
2402 SkTDArray<Angle> angles;
2403 SkASSERT(startIndex - endIndex != 0);
2404 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2405 addTwoAngles(startIndex, end, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002406 buildAngles(end, angles, false);
caryclark@google.com24bec792012-08-20 12:43:57 +00002407 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002408 bool sortable = SortAngles(angles, sorted);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002409 if (!sortable) {
2410 unsortable = true;
2411 return NULL;
2412 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002413 int angleCount = angles.count();
2414 int firstIndex = findStartingEdge(sorted, startIndex, end);
2415 SkASSERT(firstIndex >= 0);
2416 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002417 debugShowSort(__FUNCTION__, sorted, firstIndex, 0, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002418 #endif
2419 SkASSERT(sorted[firstIndex]->segment() == this);
2420 int nextIndex = firstIndex + 1;
2421 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2422 const Angle* nextAngle;
2423 Segment* nextSegment;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002424 bool foundAngle = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002425 do {
2426 if (nextIndex == angleCount) {
2427 nextIndex = 0;
2428 }
2429 nextAngle = sorted[nextIndex];
2430 nextSegment = nextAngle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002431 if (!nextSegment->done(nextAngle) || nextSegment->tiny(nextAngle)) {
2432 foundAngle = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00002433 break;
2434 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002435 } while (++nextIndex != lastIndex);
2436 markDone(SkMin32(startIndex, endIndex), 1);
2437 if (!foundAngle) {
2438 nextIndex = firstIndex + 1 == angleCount ? 0 : firstIndex + 1;
2439 nextAngle = sorted[nextIndex];
2440 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002441 nextStart = nextAngle->start();
2442 nextEnd = nextAngle->end();
2443 return nextSegment;
2444 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002445
2446 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2447 int angleCount = sorted.count();
2448 int firstIndex = -1;
2449 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2450 const Angle* angle = sorted[angleIndex];
2451 if (angle->segment() == this && angle->start() == end &&
2452 angle->end() == start) {
2453 firstIndex = angleIndex;
2454 break;
2455 }
2456 }
2457 return firstIndex;
2458 }
2459
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002460 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com4eeda372012-12-06 21:47:48 +00002461 void findTooCloseToCall() {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002462 int count = fTs.count();
2463 if (count < 3) { // require t=0, x, 1 at minimum
2464 return;
2465 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002466 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002467 int moCount;
2468 Span* match;
2469 Segment* mOther;
2470 do {
2471 match = &fTs[matchIndex];
2472 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002473 // FIXME: allow quads, cubics to be near coincident?
2474 if (mOther->fVerb == SkPath::kLine_Verb) {
2475 moCount = mOther->fTs.count();
2476 if (moCount >= 3) {
2477 break;
2478 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002479 }
2480 if (++matchIndex >= count) {
2481 return;
2482 }
2483 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002484 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002485 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002486 // look for a pair of nearby T values that map to the same (x,y) value
2487 // if found, see if the pair of other segments share a common point. If
2488 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002489 for (int index = matchIndex + 1; index < count; ++index) {
2490 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002491 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002492 continue;
2493 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002494 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002495 if (tOther->fVerb != SkPath::kLine_Verb) {
2496 continue; // FIXME: allow quads, cubics to be near coincident?
2497 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002498 int toCount = tOther->fTs.count();
2499 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002500 continue;
2501 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002502 const SkPoint* testPt = &xyAtT(test);
2503 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002504 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002505 moCount = toCount;
2506 match = test;
2507 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002508 matchPt = testPt;
2509 continue;
2510 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002511 int moStart = -1;
2512 int moEnd = -1;
2513 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002514 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002515 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002516 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002517 continue;
2518 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002519 if (moSpan.fOther == this) {
2520 if (moSpan.fOtherT == match->fT) {
2521 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002522 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002523 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002524 continue;
2525 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002526 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002527 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2528 moStart = -1;
2529 break;
2530 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002531 SkASSERT(moEnd == -1);
2532 moEnd = moIndex;
2533 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002534 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002535 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002536 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002537 continue;
2538 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002539 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002540 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002541 continue;
2542 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002543 int toStart = -1;
2544 int toEnd = -1;
2545 double toStartT, toEndT;
2546 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2547 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002548 if (toSpan.fDone) {
2549 continue;
2550 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002551 if (toSpan.fOther == this) {
2552 if (toSpan.fOtherT == test->fT) {
2553 toStart = toIndex;
2554 toStartT = toSpan.fT;
2555 }
2556 continue;
2557 }
2558 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002559 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2560 moStart = -1;
2561 break;
2562 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002563 SkASSERT(toEnd == -1);
2564 toEnd = toIndex;
2565 toEndT = toSpan.fT;
2566 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002567 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002568 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2569 if (toStart <= 0 || toEnd <= 0) {
2570 continue;
2571 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002572 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002573 continue;
2574 }
2575 // test to see if the segment between there and here is linear
2576 if (!mOther->isLinear(moStart, moEnd)
2577 || !tOther->isLinear(toStart, toEnd)) {
2578 continue;
2579 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002580 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002581 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002582 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002583 } else {
caryclark@google.com4eeda372012-12-06 21:47:48 +00002584 mOther->addTCoincident(moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002585 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002586 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002587 }
2588
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002589 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002590 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002591 // a) mark spans with either end unsortable as done, or
2592 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2593 // when encountering an unsortable span
2594
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002595 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2596 // and use more concise logic like the old edge walker code?
2597 // FIXME: this needs to deal with coincident edges
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002598 Segment* findTop(int& tIndex, int& endIndex, bool& unsortable, bool onlySortable) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002599 // iterate through T intersections and return topmost
2600 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002601 SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002602 int firstT = -1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002603 SkPoint topPt;
2604 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002605 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002606 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002607 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002608 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002609 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002610 const Span& span = fTs[index];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002611 if (onlySortable && (span.fUnsortableStart || lastUnsortable)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00002612 goto next;
2613 }
2614 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002615 const SkPoint& intercept = xyAtT(&span);
2616 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2617 && topPt.fX > intercept.fX)) {
2618 topPt = intercept;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002619 firstT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002620 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002621 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002622 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002623 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002624 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002625 }
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002626 SkASSERT(firstT >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002627 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002628 int step = 1;
2629 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002630 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002631 step = -1;
2632 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002633 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002634 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002635 // if the topmost T is not on end, or is three-way or more, find left
2636 // look for left-ness from tLeft to firstT (matching y of other)
2637 SkTDArray<Angle> angles;
2638 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002639 addTwoAngles(end, firstT, angles);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002640 buildAngles(firstT, angles, true);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002641 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002642 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002643 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00002644 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002645 #endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002646 if (onlySortable && !sortable) {
2647 unsortable = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002648 return NULL;
2649 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002650 // skip edges that have already been processed
2651 firstT = -1;
2652 Segment* leftSegment;
2653 do {
2654 const Angle* angle = sorted[++firstT];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002655 SkASSERT(!onlySortable || !angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002656 leftSegment = angle->segment();
2657 tIndex = angle->end();
2658 endIndex = angle->start();
2659 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002660 SkASSERT(!leftSegment->fTs[SkMin32(tIndex, endIndex)].fTiny);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002661 return leftSegment;
2662 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002663
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002664 // FIXME: not crazy about this
2665 // when the intersections are performed, the other index is into an
2666 // incomplete array. as the array grows, the indices become incorrect
2667 // while the following fixes the indices up again, it isn't smart about
2668 // skipping segments whose indices are already correct
2669 // assuming we leave the code that wrote the index in the first place
2670 void fixOtherTIndex() {
2671 int iCount = fTs.count();
2672 for (int i = 0; i < iCount; ++i) {
2673 Span& iSpan = fTs[i];
2674 double oT = iSpan.fOtherT;
2675 Segment* other = iSpan.fOther;
2676 int oCount = other->fTs.count();
2677 for (int o = 0; o < oCount; ++o) {
2678 Span& oSpan = other->fTs[o];
2679 if (oT == oSpan.fT && this == oSpan.fOther) {
2680 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002681 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002682 }
2683 }
2684 }
2685 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002686
caryclark@google.com4eeda372012-12-06 21:47:48 +00002687 void init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002688 fDoneSpans = 0;
2689 fOperand = operand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00002690 fXor = evenOdd;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002691 fPts = pts;
2692 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002693 }
2694
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002695 void initWinding(int start, int end, int winding, int oppWinding) {
2696 int local = spanSign(start, end);
2697 if (local * winding >= 0) {
2698 winding += local;
2699 }
2700 local = oppSign(start, end);
2701 if (local * oppWinding >= 0) {
2702 oppWinding += local;
2703 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002704 (void) markAndChaseWinding(start, end, winding, oppWinding);
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002705 }
2706
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002707 bool intersected() const {
2708 return fTs.count() > 0;
2709 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002710
2711 bool isConnected(int startIndex, int endIndex) const {
2712 return fTs[startIndex].fWindSum != SK_MinS32
2713 || fTs[endIndex].fWindSum != SK_MinS32;
2714 }
2715
caryclark@google.com235f56a2012-09-14 14:19:30 +00002716 bool isHorizontal() const {
2717 return fBounds.fTop == fBounds.fBottom;
2718 }
2719
caryclark@google.com15fa1382012-05-07 20:49:36 +00002720 bool isLinear(int start, int end) const {
2721 if (fVerb == SkPath::kLine_Verb) {
2722 return true;
2723 }
2724 if (fVerb == SkPath::kQuad_Verb) {
2725 SkPoint qPart[3];
2726 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2727 return QuadIsLinear(qPart);
2728 } else {
2729 SkASSERT(fVerb == SkPath::kCubic_Verb);
2730 SkPoint cPart[4];
2731 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2732 return CubicIsLinear(cPart);
2733 }
2734 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002735
2736 // OPTIMIZE: successive calls could start were the last leaves off
2737 // or calls could specialize to walk forwards or backwards
2738 bool isMissing(double startT) const {
2739 size_t tCount = fTs.count();
2740 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002741 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002742 return false;
2743 }
2744 }
2745 return true;
2746 }
2747
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002748 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002749 int count = fTs.count();
2750 if (count == 2) {
2751 return true;
2752 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002753 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002754 if (approximately_less_than_zero(t)) {
2755 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002756 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002757 if (approximately_greater_than_one(t)) {
2758 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002759 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002760 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002761 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002762
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002763 bool isVertical() const {
2764 return fBounds.fLeft == fBounds.fRight;
2765 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002766
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002767 SkScalar leftMost(int start, int end) const {
2768 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2769 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002770
caryclark@google.com495f8e42012-05-31 13:13:11 +00002771 // this span is excluded by the winding rule -- chase the ends
2772 // as long as they are unambiguous to mark connections as done
2773 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002774 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002775 int index = angle->start();
2776 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002777 return markAndChaseDone(index, endIndex, winding);
2778 }
2779
caryclark@google.com31143cf2012-11-09 22:14:19 +00002780 Span* markAndChaseDone(int index, int endIndex, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002781 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002782 int min = SkMin32(index, endIndex);
2783 markDone(min, winding);
2784 Span* last;
2785 Segment* other = this;
2786 while ((other = other->nextChase(index, step, min, last))) {
2787 other->markDone(min, winding);
2788 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002789 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002790 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002791
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002792 Span* markAndChaseDoneBinary(const Angle* angle, int winding, int oppWinding) {
2793 int index = angle->start();
2794 int endIndex = angle->end();
caryclark@google.com31143cf2012-11-09 22:14:19 +00002795 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002796 int min = SkMin32(index, endIndex);
2797 markDoneBinary(min, winding, oppWinding);
2798 Span* last;
2799 Segment* other = this;
2800 while ((other = other->nextChase(index, step, min, last))) {
2801 other->markDoneBinary(min, winding, oppWinding);
2802 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002803 return last;
2804 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002805
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002806 Span* markAndChaseDoneBinary(int index, int endIndex) {
2807 int step = SkSign32(endIndex - index);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002808 int min = SkMin32(index, endIndex);
2809 markDoneBinary(min);
2810 Span* last;
2811 Segment* other = this;
2812 while ((other = other->nextChase(index, step, min, last))) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00002813 if (other->done()) {
2814 return NULL;
2815 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00002816 other->markDoneBinary(min);
2817 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002818 return last;
2819 }
2820
caryclark@google.com4eeda372012-12-06 21:47:48 +00002821 Span* markAndChaseWinding(const Angle* angle, const int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002822 int index = angle->start();
2823 int endIndex = angle->end();
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00002824 int step = SkSign32(endIndex - index);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002825 int min = SkMin32(index, endIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002826 markWinding(min, winding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002827 Span* last;
2828 Segment* other = this;
2829 while ((other = other->nextChase(index, step, min, last))) {
2830 if (other->fTs[min].fWindSum != SK_MinS32) {
2831 SkASSERT(other->fTs[min].fWindSum == winding);
2832 return NULL;
2833 }
2834 other->markWinding(min, winding);
2835 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002836 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002837 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00002838
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002839 Span* markAndChaseWinding(int index, int endIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002840 int min = SkMin32(index, endIndex);
2841 int step = SkSign32(endIndex - index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002842 markWinding(min, winding, oppWinding);
caryclark@google.com4eeda372012-12-06 21:47:48 +00002843 Span* last;
2844 Segment* other = this;
2845 while ((other = other->nextChase(index, step, min, last))) {
2846 if (other->fTs[min].fWindSum != SK_MinS32) {
2847 SkASSERT(other->fTs[min].fWindSum == winding);
2848 return NULL;
2849 }
2850 other->markWinding(min, winding, oppWinding);
2851 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00002852 return last;
2853 }
2854
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002855 Span* markAndChaseWinding(const Angle* angle, int winding, int oppWinding) {
2856 int start = angle->start();
2857 int end = angle->end();
2858 return markAndChaseWinding(start, end, winding, oppWinding);
2859 }
2860
2861 Span* markAngle(int maxWinding, int sumWinding, int oppMaxWinding, int oppSumWinding,
2862 bool activeAngle, const Angle* angle) {
2863 SkASSERT(angle->segment() == this);
2864 if (useInnerWinding(maxWinding, sumWinding)) {
2865 maxWinding = sumWinding;
2866 }
2867 if (oppMaxWinding != oppSumWinding && useInnerWinding(oppMaxWinding, oppSumWinding)) {
2868 oppMaxWinding = oppSumWinding;
2869 }
2870 Span* last;
2871 if (activeAngle) {
2872 last = markAndChaseWinding(angle, maxWinding, oppMaxWinding);
2873 } else {
2874 last = markAndChaseDoneBinary(angle, maxWinding, oppMaxWinding);
2875 }
2876 return last;
2877 }
2878
caryclark@google.com495f8e42012-05-31 13:13:11 +00002879 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002880 // This may be called when the segment is already marked done. While this
2881 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002882 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002883 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002884 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002885 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002886 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002887 double referenceT = fTs[index].fT;
2888 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002889 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2890 markOneDone(__FUNCTION__, lesser, winding);
2891 }
2892 do {
2893 markOneDone(__FUNCTION__, index, winding);
2894 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00002895 }
2896
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002897 void markDoneBinary(int index, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002898 // SkASSERT(!done());
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00002899 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002900 double referenceT = fTs[index].fT;
2901 int lesser = index;
2902 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002903 markOneDoneBinary(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002904 }
2905 do {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002906 markOneDoneBinary(__FUNCTION__, index, winding, oppWinding);
2907 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2908 }
2909
2910 void markDoneBinary(int index) {
2911 double referenceT = fTs[index].fT;
2912 int lesser = index;
2913 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2914 markOneDoneBinary(__FUNCTION__, lesser);
2915 }
2916 do {
2917 markOneDoneBinary(__FUNCTION__, index);
caryclark@google.com31143cf2012-11-09 22:14:19 +00002918 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002919 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002920
caryclark@google.com24bec792012-08-20 12:43:57 +00002921 void markOneDone(const char* funName, int tIndex, int winding) {
2922 Span* span = markOneWinding(funName, tIndex, winding);
2923 if (!span) {
2924 return;
2925 }
2926 span->fDone = true;
2927 fDoneSpans++;
2928 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002929
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002930 void markOneDoneBinary(const char* funName, int tIndex) {
2931 Span* span = verifyOneWinding(funName, tIndex);
2932 if (!span) {
2933 return;
2934 }
2935 span->fDone = true;
2936 fDoneSpans++;
2937 }
2938
2939 void markOneDoneBinary(const char* funName, int tIndex, int winding, int oppWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00002940 Span* span = markOneWinding(funName, tIndex, winding, oppWinding);
2941 if (!span) {
2942 return;
2943 }
2944 span->fDone = true;
2945 fDoneSpans++;
2946 }
2947
caryclark@google.com24bec792012-08-20 12:43:57 +00002948 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2949 Span& span = fTs[tIndex];
2950 if (span.fDone) {
2951 return NULL;
2952 }
2953 #if DEBUG_MARK_DONE
2954 debugShowNewWinding(funName, span, winding);
2955 #endif
2956 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002957 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002958 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002959 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002960 span.fWindSum = winding;
2961 return &span;
2962 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002963
caryclark@google.com31143cf2012-11-09 22:14:19 +00002964 Span* markOneWinding(const char* funName, int tIndex, int winding, int oppWinding) {
2965 Span& span = fTs[tIndex];
2966 if (span.fDone) {
2967 return NULL;
2968 }
2969 #if DEBUG_MARK_DONE
2970 debugShowNewWinding(funName, span, winding, oppWinding);
2971 #endif
2972 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
2973 #ifdef SK_DEBUG
2974 SkASSERT(abs(winding) <= gDebugMaxWindSum);
2975 #endif
2976 span.fWindSum = winding;
2977 SkASSERT(span.fOppSum == SK_MinS32 || span.fOppSum == oppWinding);
2978 #ifdef SK_DEBUG
2979 SkASSERT(abs(oppWinding) <= gDebugMaxWindSum);
2980 #endif
2981 span.fOppSum = oppWinding;
2982 return &span;
2983 }
2984
caryclark@google.com7fce0de2012-11-29 14:31:50 +00002985 Span* verifyOneWinding(const char* funName, int tIndex) {
2986 Span& span = fTs[tIndex];
2987 if (span.fDone) {
2988 return NULL;
2989 }
2990 #if DEBUG_MARK_DONE
2991 debugShowNewWinding(funName, span, span.fWindSum, span.fOppSum);
2992 #endif
2993 SkASSERT(span.fWindSum != SK_MinS32);
2994 SkASSERT(span.fOppSum != SK_MinS32);
2995 return &span;
2996 }
2997
caryclark@google.comf839c032012-10-26 21:03:50 +00002998 // note that just because a span has one end that is unsortable, that's
2999 // not enough to mark it done. The other end may be sortable, allowing the
3000 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003001 void markUnsortable(int start, int end) {
3002 Span* span = &fTs[start];
3003 if (start < end) {
3004 span->fUnsortableStart = true;
3005 } else {
3006 --span;
3007 span->fUnsortableEnd = true;
3008 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003009 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003010 return;
3011 }
3012 span->fDone = true;
3013 fDoneSpans++;
3014 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003015
caryclark@google.com59823f72012-08-09 18:17:47 +00003016 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003017 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00003018 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003019 double referenceT = fTs[index].fT;
3020 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00003021 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3022 markOneWinding(__FUNCTION__, lesser, winding);
3023 }
3024 do {
3025 markOneWinding(__FUNCTION__, index, winding);
3026 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.com31143cf2012-11-09 22:14:19 +00003027 }
3028
3029 void markWinding(int index, int winding, int oppWinding) {
3030 // SkASSERT(!done());
caryclark@google.com4eeda372012-12-06 21:47:48 +00003031 SkASSERT(winding || oppWinding);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003032 double referenceT = fTs[index].fT;
3033 int lesser = index;
3034 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
3035 markOneWinding(__FUNCTION__, lesser, winding, oppWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003036 }
3037 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003038 markOneWinding(__FUNCTION__, index, winding, oppWinding);
3039 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
caryclark@google.comaf46cff2012-05-22 21:12:00 +00003040 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003041
caryclark@google.com2ddff932012-08-07 21:25:27 +00003042 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003043 int nextDoorWind = SK_MaxS32;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003044 int nextOppWind = SK_MaxS32;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003045 if (tIndex > 0) {
3046 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003047 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003048 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003049 nextOppWind = below.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003050 }
3051 }
3052 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
3053 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003054 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003055 nextDoorWind = above.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003056 nextOppWind = above.fOppValue;
caryclark@google.com0c803d02012-08-06 11:15:47 +00003057 }
3058 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003059 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
3060 const Span& below = fTs[tIndex - 1];
3061 nextDoorWind = below.fWindValue;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003062 nextOppWind = below.fOppValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003063 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003064 if (nextDoorWind != SK_MaxS32) {
3065 Span& newSpan = fTs[tIndex];
3066 newSpan.fWindValue = nextDoorWind;
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003067 newSpan.fOppValue = nextOppWind;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003068 if (!nextDoorWind && !nextOppWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00003069 newSpan.fDone = true;
3070 ++fDoneSpans;
3071 }
3072 }
3073 }
3074
caryclark@google.com9764cc62012-07-12 19:29:45 +00003075 // return span if when chasing, two or more radiating spans are not done
3076 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
3077 // candidate and the remaining spans have windValue == 0 (canceled by
3078 // coincidence). The coincident edges could either be removed altogether,
3079 // or this code could be more complicated in detecting this case. Worth it?
3080 bool multipleSpans(int end) const {
3081 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00003082 }
3083
caryclark@google.com4eeda372012-12-06 21:47:48 +00003084 Segment* nextChase(int& index, const int step, int& min, Span*& last) const {
3085 int end = nextExactSpan(index, step);
3086 SkASSERT(end >= 0);
3087 if (multipleSpans(end)) {
3088 last = &fTs[end];
3089 return NULL;
3090 }
3091 const Span& endSpan = fTs[end];
3092 Segment* other = endSpan.fOther;
3093 index = endSpan.fOtherIndex;
3094 int otherEnd = other->nextExactSpan(index, step);
3095 min = SkMin32(index, otherEnd);
3096 return other;
3097 }
3098
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003099 // This has callers for two different situations: one establishes the end
3100 // of the current span, and one establishes the beginning of the next span
3101 // (thus the name). When this is looking for the end of the current span,
3102 // coincidence is found when the beginning Ts contain -step and the end
3103 // contains step. When it is looking for the beginning of the next, the
3104 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003105 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003106 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003107 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00003108 int count = fTs.count();
3109 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00003110 while (step > 0 ? ++to < count : --to >= 0) {
3111 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003112 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003113 continue;
3114 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003115 return to;
3116 }
3117 return -1;
3118 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00003119
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003120 // FIXME
3121 // this returns at any difference in T, vs. a preset minimum. It may be
3122 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003123 // OPTIMIZATION splitting this into separate loops for up/down steps
3124 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003125 int nextExactSpan(int from, int step) const {
3126 const Span& fromSpan = fTs[from];
3127 int count = fTs.count();
3128 int to = from;
3129 while (step > 0 ? ++to < count : --to >= 0) {
3130 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00003131 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003132 continue;
3133 }
3134 return to;
3135 }
3136 return -1;
3137 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003138
caryclark@google.com235f56a2012-09-14 14:19:30 +00003139 bool operand() const {
3140 return fOperand;
3141 }
3142
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003143 int oppSign(const Angle* angle) const {
3144 SkASSERT(angle->segment() == this);
3145 return oppSign(angle->start(), angle->end());
3146 }
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003147
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003148 int oppSign(int startIndex, int endIndex) const {
3149 int result = startIndex < endIndex ? -fTs[startIndex].fOppValue
3150 : fTs[endIndex].fOppValue;
3151#if DEBUG_WIND_BUMP
3152 SkDebugf("%s oppSign=%d\n", __FUNCTION__, result);
3153#endif
3154 return result;
3155 }
3156
caryclark@google.com31143cf2012-11-09 22:14:19 +00003157 int oppSum(int tIndex) const {
3158 return fTs[tIndex].fOppSum;
3159 }
3160
3161 int oppSum(const Angle* angle) const {
3162 int lesser = SkMin32(angle->start(), angle->end());
3163 return fTs[lesser].fOppSum;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003164 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00003165
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003166 int oppValue(int tIndex) const {
3167 return fTs[tIndex].fOppValue;
3168 }
3169
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003170 int oppValue(const Angle* angle) const {
3171 int lesser = SkMin32(angle->start(), angle->end());
3172 return fTs[lesser].fOppValue;
3173 }
3174
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003175 const SkPoint* pts() const {
3176 return fPts;
3177 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003178
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003179 void reset() {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003180 init(NULL, (SkPath::Verb) -1, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003181 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
3182 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003183 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00003184
caryclark@google.com4eeda372012-12-06 21:47:48 +00003185 void setOppXor(bool isOppXor) {
3186 fOppXor = isOppXor;
3187 }
3188
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003189 void setUpWindings(int index, int endIndex, int& sumMiWinding, int& sumSuWinding,
3190 int& maxWinding, int& sumWinding, int& oppMaxWinding, int& oppSumWinding) {
3191 int deltaSum = spanSign(index, endIndex);
3192 int oppDeltaSum = oppSign(index, endIndex);
3193 if (operand()) {
3194 maxWinding = sumSuWinding;
3195 sumWinding = sumSuWinding -= deltaSum;
3196 oppMaxWinding = sumMiWinding;
3197 oppSumWinding = sumMiWinding -= oppDeltaSum;
3198 } else {
3199 maxWinding = sumMiWinding;
3200 sumWinding = sumMiWinding -= deltaSum;
3201 oppMaxWinding = sumSuWinding;
3202 oppSumWinding = sumSuWinding -= oppDeltaSum;
3203 }
3204 }
3205
caryclark@google.comf839c032012-10-26 21:03:50 +00003206 // This marks all spans unsortable so that this info is available for early
3207 // exclusion in find top and others. This could be optimized to only mark
3208 // adjacent spans that unsortable. However, this makes it difficult to later
3209 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003210 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003211 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003212 int angleCount = angles.count();
3213 int angleIndex;
3214 angleList.setReserve(angleCount);
3215 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003216 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003217 *angleList.append() = &angle;
3218 sortable &= !angle.unsortable();
3219 }
3220 if (sortable) {
3221 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3222 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3223 if (angles[angleIndex].unsortable()) {
3224 sortable = false;
3225 break;
3226 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003227 }
3228 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003229 if (!sortable) {
3230 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3231 Angle& angle = angles[angleIndex];
3232 angle.segment()->markUnsortable(angle.start(), angle.end());
3233 }
3234 }
3235 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003236 }
3237
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003238 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003239 const Span& span(int tIndex) const {
3240 return fTs[tIndex];
3241 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003242
caryclark@google.com235f56a2012-09-14 14:19:30 +00003243 int spanSign(const Angle* angle) const {
3244 SkASSERT(angle->segment() == this);
3245 return spanSign(angle->start(), angle->end());
3246 }
3247
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003248 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com31143cf2012-11-09 22:14:19 +00003249 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue
3250 : fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003251#if DEBUG_WIND_BUMP
3252 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3253#endif
3254 return result;
3255 }
3256
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003257 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003258 double t(int tIndex) const {
3259 return fTs[tIndex].fT;
3260 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003261
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003262 bool tiny(const Angle* angle) const {
3263 int start = angle->start();
3264 int end = angle->end();
caryclark@google.comf839c032012-10-26 21:03:50 +00003265 const Span& mSpan = fTs[SkMin32(start, end)];
3266 return mSpan.fTiny;
3267 }
3268
caryclark@google.com18063442012-07-25 12:05:18 +00003269 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3270 double start) {
3271 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003272 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003273 *outsideTs.append() = end;
3274 *outsideTs.append() = start;
3275 }
3276 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00003277
caryclark@google.com24bec792012-08-20 12:43:57 +00003278 void undoneSpan(int& start, int& end) {
3279 size_t tCount = fTs.count();
3280 size_t index;
3281 for (index = 0; index < tCount; ++index) {
3282 if (!fTs[index].fDone) {
3283 break;
3284 }
3285 }
3286 SkASSERT(index < tCount - 1);
3287 start = index;
3288 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003289 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003290 SkASSERT(index < tCount);
3291 SkASSERT(index < tCount);
3292 end = index;
3293 }
caryclark@google.com18063442012-07-25 12:05:18 +00003294
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003295 bool unsortable(int index) const {
3296 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3297 }
3298
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003299 void updatePts(const SkPoint pts[]) {
3300 fPts = pts;
3301 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003302
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003303 int updateOppWinding(int index, int endIndex) const {
3304 int lesser = SkMin32(index, endIndex);
3305 int oppWinding = oppSum(lesser);
3306 int oppSpanWinding = oppSign(index, endIndex);
3307 if (oppSpanWinding && useInnerWinding(oppWinding - oppSpanWinding, oppWinding)) {
3308 oppWinding -= oppSpanWinding;
3309 }
3310 return oppWinding;
3311 }
skia.committer@gmail.comc3d7d902012-11-30 02:01:24 +00003312
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003313 int updateOppWinding(const Angle* angle) const {
3314 int startIndex = angle->start();
3315 int endIndex = angle->end();
3316 return updateOppWinding(endIndex, startIndex);
3317 }
3318
3319 int updateOppWindingReverse(const Angle* angle) const {
3320 int startIndex = angle->start();
3321 int endIndex = angle->end();
3322 return updateOppWinding(startIndex, endIndex);
3323 }
3324
3325 int updateWinding(int index, int endIndex) const {
3326 int lesser = SkMin32(index, endIndex);
3327 int winding = windSum(lesser);
3328 int spanWinding = spanSign(index, endIndex);
caryclark@google.com9f3e9a52012-12-10 12:50:53 +00003329 if (winding && useInnerWinding(winding - spanWinding, winding)) {
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003330 winding -= spanWinding;
3331 }
3332 return winding;
3333 }
3334
3335 int updateWinding(const Angle* angle) const {
3336 int startIndex = angle->start();
3337 int endIndex = angle->end();
3338 return updateWinding(endIndex, startIndex);
3339 }
3340
3341 int updateWindingReverse(const Angle* angle) const {
3342 int startIndex = angle->start();
3343 int endIndex = angle->end();
3344 return updateWinding(startIndex, endIndex);
3345 }
3346
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003347 SkPath::Verb verb() const {
3348 return fVerb;
3349 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003350
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003351 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003352 return fTs[tIndex].fWindSum;
3353 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003354
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003355 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003356 int start = angle->start();
3357 int end = angle->end();
3358 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003359 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003360 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003361
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003362 int windValue(int tIndex) const {
3363 return fTs[tIndex].fWindValue;
3364 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003365
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003366 int windValue(const Angle* angle) const {
3367 int start = angle->start();
3368 int end = angle->end();
3369 int index = SkMin32(start, end);
3370 return windValue(index);
3371 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003372
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003373 SkScalar xAtT(const Span* span) const {
3374 return xyAtT(span).fX;
3375 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003376
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003377 const SkPoint& xyAtT(int index) const {
3378 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003379 }
3380
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003381 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003382 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003383 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003384 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003385 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003386 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003387 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003388 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003389 }
3390 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003391 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003392 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003393
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003394 SkScalar yAtT(int index) const {
3395 return yAtT(&fTs[index]);
3396 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003397
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003398 SkScalar yAtT(const Span* span) const {
3399 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003400 }
3401
caryclark@google.com4eeda372012-12-06 21:47:48 +00003402 void zeroCoincidentOpp(Span* oTest, int index) {
3403 Span* const test = &fTs[index];
3404 Span* end = test;
3405 do {
3406 end->fOppValue = 0;
3407 end = &fTs[++index];
3408 } while (approximately_negative(end->fT - test->fT));
3409 }
3410
3411 void zeroCoincidentOther(Span* test, const double tRatio, const double oEndT, int oIndex) {
3412 Span* const oTest = &fTs[oIndex];
3413 Span* oEnd = oTest;
3414 const double startT = test->fT;
3415 const double oStartT = oTest->fT;
3416 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
3417 while (!approximately_negative(oEndT - oEnd->fT)
3418 && approximately_negative(oEnd->fT - otherTMatch)) {
3419 oEnd->fOppValue = 0;
3420 oEnd = &fTs[++oIndex];
3421 }
3422 }
3423
3424 void zeroSpan(Span* span) {
3425 SkASSERT(span->fWindValue > 0 || span->fOppValue > 0);
caryclark@google.com6ec15262012-11-16 20:16:50 +00003426 span->fWindValue = 0;
caryclark@google.com729e1c42012-11-21 21:36:34 +00003427 span->fOppValue = 0;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003428 SkASSERT(!span->fDone);
3429 span->fDone = true;
3430 ++fDoneSpans;
caryclark@google.com6ec15262012-11-16 20:16:50 +00003431 }
3432
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003433#if DEBUG_DUMP
3434 void dump() const {
3435 const char className[] = "Segment";
3436 const int tab = 4;
3437 for (int i = 0; i < fTs.count(); ++i) {
3438 SkPoint out;
3439 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3440 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003441 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003442 tab + sizeof(className), className, fID,
3443 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003444 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003445 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003446 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003447 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003448 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003449 }
3450#endif
3451
caryclark@google.com47580692012-07-23 12:14:49 +00003452#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003453 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003454 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003455 for (int i = 0; i < fTs.count(); ++i) {
3456 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3457 return;
3458 }
3459 }
3460 SkASSERT(0);
3461 }
3462#endif
3463
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003464#if DEBUG_DUMP
3465 int debugID() const {
3466 return fID;
3467 }
3468#endif
3469
caryclark@google.com24bec792012-08-20 12:43:57 +00003470#if DEBUG_WINDING
3471 void debugShowSums() const {
3472 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3473 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3474 for (int i = 0; i < fTs.count(); ++i) {
3475 const Span& span = fTs[i];
3476 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3477 if (span.fWindSum == SK_MinS32) {
3478 SkDebugf("?");
3479 } else {
3480 SkDebugf("%d", span.fWindSum);
3481 }
3482 SkDebugf("]");
3483 }
3484 SkDebugf("\n");
3485 }
3486#endif
3487
caryclark@google.comcc905052012-07-25 20:59:42 +00003488#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003489 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003490 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com4eeda372012-12-06 21:47:48 +00003491 int lastWind = -1;
3492 int lastOpp = -1;
3493 double lastT = -1;
3494 int i;
3495 for (i = 0; i < fTs.count(); ++i) {
3496 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
3497 || lastOpp != fTs[i].fOppValue;
3498 if (change && lastWind >= 0) {
3499 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3500 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3501 }
3502 if (change) {
3503 SkDebugf(" [o=%d", fTs[i].fOther->fID);
3504 lastWind = fTs[i].fWindValue;
3505 lastOpp = fTs[i].fOppValue;
3506 lastT = fTs[i].fT;
3507 } else {
3508 SkDebugf(",%d", fTs[i].fOther->fID);
3509 }
3510 }
3511 if (i <= 0) {
3512 return;
3513 }
3514 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
3515 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
3516 if (fOperand) {
3517 SkDebugf(" operand");
3518 }
3519 if (done()) {
3520 SkDebugf(" done");
caryclark@google.com47580692012-07-23 12:14:49 +00003521 }
3522 SkDebugf("\n");
3523 }
3524#endif
3525
caryclark@google.com027de222012-07-12 12:52:50 +00003526#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003527 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003528 if (done()) {
3529 return;
3530 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003531#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3532 int lastId = -1;
3533 double lastT = -1;
3534#endif
caryclark@google.com027de222012-07-12 12:52:50 +00003535 for (int i = 0; i < fTs.count(); ++i) {
3536 if (fTs[i].fDone) {
3537 continue;
3538 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003539#if DEBUG_ACTIVE_SPANS_SHORT_FORM
3540 if (lastId == fID && lastT == fTs[i].fT) {
3541 continue;
3542 }
3543 lastId = fID;
3544 lastT = fTs[i].fT;
3545#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003546 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003547 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3548 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3549 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3550 }
3551 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003552 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003553 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003554 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003555 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3556 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3557 if (fTs[i].fWindSum == SK_MinS32) {
3558 SkDebugf("?");
3559 } else {
3560 SkDebugf("%d", fTs[i].fWindSum);
3561 }
caryclark@google.com4eeda372012-12-06 21:47:48 +00003562 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003563 }
3564 }
skia.committer@gmail.comb0a327e2012-11-21 02:02:25 +00003565
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003566 // This isn't useful yet -- but leaving it in for now in case i think of something
3567 // to use it for
3568 void validateActiveSpans() const {
3569 if (done()) {
3570 return;
3571 }
3572 int tCount = fTs.count();
3573 for (int index = 0; index < tCount; ++index) {
3574 if (fTs[index].fDone) {
3575 continue;
3576 }
3577 // count number of connections which are not done
3578 int first = index;
3579 double baseT = fTs[index].fT;
3580 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3581 --first;
3582 }
3583 int last = index;
3584 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3585 ++last;
3586 }
3587 int connections = 0;
3588 connections += first > 0 && !fTs[first - 1].fDone;
3589 for (int test = first; test <= last; ++test) {
3590 connections += !fTs[test].fDone;
3591 const Segment* other = fTs[test].fOther;
3592 int oIndex = fTs[test].fOtherIndex;
3593 connections += !other->fTs[oIndex].fDone;
3594 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3595 }
3596 // SkASSERT(!(connections & 1));
3597 }
3598 }
caryclark@google.com027de222012-07-12 12:52:50 +00003599#endif
3600
caryclark@google.com0c803d02012-08-06 11:15:47 +00003601#if DEBUG_MARK_DONE
3602 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3603 const SkPoint& pt = xyAtT(&span);
3604 SkDebugf("%s id=%d", fun, fID);
3605 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3606 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3607 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3608 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003609 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3610 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3611 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3612 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003613 if (span.fWindSum == SK_MinS32) {
3614 SkDebugf("?");
3615 } else {
3616 SkDebugf("%d", span.fWindSum);
3617 }
3618 SkDebugf(" windValue=%d\n", span.fWindValue);
3619 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003620
3621 void debugShowNewWinding(const char* fun, const Span& span, int winding, int oppWinding) {
3622 const SkPoint& pt = xyAtT(&span);
3623 SkDebugf("%s id=%d", fun, fID);
3624 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3625 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3626 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3627 }
3628 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3629 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3630 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d newOppSum=%d oppSum=",
3631 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
3632 winding, oppWinding);
3633 if (span.fOppSum == SK_MinS32) {
3634 SkDebugf("?");
3635 } else {
3636 SkDebugf("%d", span.fOppSum);
3637 }
3638 SkDebugf(" windSum=");
3639 if (span.fWindSum == SK_MinS32) {
3640 SkDebugf("?");
3641 } else {
3642 SkDebugf("%d", span.fWindSum);
3643 }
3644 SkDebugf(" windValue=%d\n", span.fWindValue);
3645 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00003646#endif
3647
caryclark@google.com47580692012-07-23 12:14:49 +00003648#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003649 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com31143cf2012-11-09 22:14:19 +00003650 const int contourWinding, const int oppContourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003651 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003652 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003653 int lastSum = contourWinding;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003654 int oppLastSum = oppContourWinding;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003655 const Angle* firstAngle = angles[first];
3656 int windSum = lastSum - spanSign(firstAngle);
3657 int oppoSign = oppSign(firstAngle);
3658 int oppWindSum = oppLastSum - oppoSign;
caryclark@google.com31143cf2012-11-09 22:14:19 +00003659 SkDebugf("%s %s contourWinding=%d oppContourWinding=%d sign=%d\n", fun, __FUNCTION__,
3660 contourWinding, oppContourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003661 int index = first;
3662 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003663 do {
3664 const Angle& angle = *angles[index];
3665 const Segment& segment = *angle.segment();
3666 int start = angle.start();
3667 int end = angle.end();
3668 const Span& sSpan = segment.fTs[start];
3669 const Span& eSpan = segment.fTs[end];
3670 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com31143cf2012-11-09 22:14:19 +00003671 bool opp = segment.fOperand ^ fOperand;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003672 if (!firstTime) {
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003673 oppoSign = segment.oppSign(&angle);
caryclark@google.com31143cf2012-11-09 22:14:19 +00003674 if (opp) {
3675 oppLastSum = oppWindSum;
3676 oppWindSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003677 if (oppoSign) {
3678 lastSum = windSum;
3679 windSum -= oppoSign;
3680 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003681 } else {
3682 lastSum = windSum;
3683 windSum -= segment.spanSign(&angle);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003684 if (oppoSign) {
3685 oppLastSum = oppWindSum;
3686 oppWindSum -= oppoSign;
3687 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003688 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00003689 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003690 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 +00003691 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003692 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003693 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003694 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3695 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3696 mSpan.fWindValue);
3697 if (mSpan.fWindSum == SK_MinS32) {
3698 SkDebugf("?");
3699 } else {
3700 SkDebugf("%d", mSpan.fWindSum);
3701 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003702 int last, wind;
3703 if (opp) {
3704 last = oppLastSum;
3705 wind = oppWindSum;
3706 } else {
3707 last = lastSum;
3708 wind = windSum;
3709 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003710 if (!oppoSign) {
skia.committer@gmail.comb3b6a602012-11-15 02:01:17 +00003711 SkDebugf(" %d->%d (max=%d)", last, wind,
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003712 useInnerWinding(last, wind) ? wind : last);
3713 } else {
3714 SkDebugf(" %d->%d (%d->%d)", last, wind, opp ? lastSum : oppLastSum,
3715 opp ? windSum : oppWindSum);
3716 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00003717 SkDebugf(" done=%d tiny=%d opp=%d\n", mSpan.fDone, mSpan.fTiny, opp);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003718#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003719 angle.debugShow(segment.xyAtT(&sSpan));
3720#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003721 ++index;
3722 if (index == angles.count()) {
3723 index = 0;
3724 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003725 if (firstTime) {
3726 firstTime = false;
3727 }
caryclark@google.com47580692012-07-23 12:14:49 +00003728 } while (index != first);
3729 }
caryclark@google.com7fce0de2012-11-29 14:31:50 +00003730
3731 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first) {
3732 const Angle* firstAngle = angles[first];
3733 const Segment* segment = firstAngle->segment();
3734 int winding = segment->updateWinding(firstAngle);
3735 int oppWinding = segment->updateOppWinding(firstAngle);
3736 debugShowSort(fun, angles, first, winding, oppWinding);
3737 }
3738
caryclark@google.com47580692012-07-23 12:14:49 +00003739#endif
3740
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003741#if DEBUG_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003742 static char as_digit(int value) {
3743 return value < 0 ? '?' : value <= 9 ? '0' + value : '+';
3744 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003745#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003746
caryclark@google.com729e1c42012-11-21 21:36:34 +00003747#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003748 int debugShowWindingValues(int slotCount, int ofInterest) const {
3749 if (!(1 << fID & ofInterest)) {
3750 return 0;
3751 }
3752 int sum = 0;
3753 SkTDArray<char> slots;
3754 slots.setCount(slotCount * 2);
3755 memset(slots.begin(), ' ', slotCount * 2);
3756 for (int i = 0; i < fTs.count(); ++i) {
3757 // if (!(1 << fTs[i].fOther->fID & ofInterest)) {
3758 // continue;
3759 // }
3760 sum += fTs[i].fWindValue;
3761 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
3762 sum += fTs[i].fOppValue;
3763 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
3764 }
3765 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
3766 slots.begin() + slotCount);
3767 return sum;
3768 }
caryclark@google.com729e1c42012-11-21 21:36:34 +00003769#endif
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003770
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003771private:
3772 const SkPoint* fPts;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003773 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003774 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com4eeda372012-12-06 21:47:48 +00003775 // OPTIMIZATION: could pack donespans, verb, operand, xor into 1 int-sized value
caryclark@google.com24bec792012-08-20 12:43:57 +00003776 int fDoneSpans; // quick check that segment is finished
caryclark@google.com4eeda372012-12-06 21:47:48 +00003777 // OPTIMIZATION: force the following to be byte-sized
3778 SkPath::Verb fVerb;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003779 bool fOperand;
caryclark@google.com4eeda372012-12-06 21:47:48 +00003780 bool fXor; // set if original contour had even-odd fill
3781 bool fOppXor; // set if opposite operand had even-odd fill
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003782#if DEBUG_DUMP
3783 int fID;
3784#endif
3785};
3786
caryclark@google.comb9738012012-07-03 19:53:30 +00003787class Contour;
3788
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003789struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003790 Contour* fContours[2];
3791 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003792 double fTs[2][2];
3793};
3794
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003795class Contour {
3796public:
3797 Contour() {
3798 reset();
3799#if DEBUG_DUMP
3800 fID = ++gContourID;
3801#endif
3802 }
3803
3804 bool operator<(const Contour& rh) const {
3805 return fBounds.fTop == rh.fBounds.fTop
3806 ? fBounds.fLeft < rh.fBounds.fLeft
3807 : fBounds.fTop < rh.fBounds.fTop;
3808 }
3809
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003810 void addCoincident(int index, Contour* other, int otherIndex,
3811 const Intersections& ts, bool swap) {
3812 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.com57cff8d2012-11-14 21:14:56 +00003813 coincidence.fContours[0] = this; // FIXME: no need to store
caryclark@google.comb9738012012-07-03 19:53:30 +00003814 coincidence.fContours[1] = other;
3815 coincidence.fSegments[0] = index;
3816 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003817 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3818 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3819 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3820 coincidence.fTs[swap][0] = ts.fT[0][0];
3821 coincidence.fTs[swap][1] = ts.fT[0][1];
3822 coincidence.fTs[!swap][0] = ts.fT[1][0];
3823 coincidence.fTs[!swap][1] = ts.fT[1][1];
3824 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3825 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3826 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3827 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3828 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3829 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3830 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003831 }
3832
3833 void addCross(const Contour* crosser) {
3834#ifdef DEBUG_CROSS
3835 for (int index = 0; index < fCrosses.count(); ++index) {
3836 SkASSERT(fCrosses[index] != crosser);
3837 }
3838#endif
3839 *fCrosses.append() = crosser;
3840 }
3841
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003842 void addCubic(const SkPoint pts[4]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003843 fSegments.push_back().addCubic(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003844 fContainsCurves = true;
3845 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003846
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003847 int addLine(const SkPoint pts[2]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003848 fSegments.push_back().addLine(pts, fOperand, fXor);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003849 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003850 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003851
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003852 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3853 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3854 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003855
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003856 int addQuad(const SkPoint pts[3]) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003857 fSegments.push_back().addQuad(pts, fOperand, fXor);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003858 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003859 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003860 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003861
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003862 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3863 containsIntercepts();
3864 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3865 }
3866
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003867 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003868 return fBounds;
3869 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003870
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003871 void complete() {
3872 setBounds();
3873 fContainsIntercepts = false;
3874 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003875
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003876 void containsIntercepts() {
3877 fContainsIntercepts = true;
3878 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003879
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003880 const Segment* crossedSegmentX(const SkPoint& basePt, SkScalar& bestX,
3881 int &tIndex, double& hitT, bool opp) {
3882 int segmentCount = fSegments.count();
3883 const Segment* bestSegment = NULL;
3884 for (int test = 0; test < segmentCount; ++test) {
3885 Segment* testSegment = &fSegments[test];
3886 const SkRect& bounds = testSegment->bounds();
3887 if (bounds.fRight <= bestX) {
3888 continue;
3889 }
3890 if (bounds.fLeft >= basePt.fX) {
3891 continue;
3892 }
3893 if (bounds.fTop > basePt.fY) {
3894 continue;
3895 }
3896 if (bounds.fBottom < basePt.fY) {
3897 continue;
3898 }
3899 if (bounds.fTop == bounds.fBottom) {
3900 continue;
3901 }
3902 double testHitT;
3903 int testT = testSegment->crossedSpanX(basePt, bestX, testHitT, opp);
3904 if (testT >= 0) {
3905 bestSegment = testSegment;
3906 tIndex = testT;
3907 hitT = testHitT;
3908 }
3909 }
3910 return bestSegment;
3911 }
3912
3913 const Segment* crossedSegmentY(const SkPoint& basePt, SkScalar& bestY,
3914 int &tIndex, double& hitT, bool opp) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003915 int segmentCount = fSegments.count();
3916 const Segment* bestSegment = NULL;
3917 for (int test = 0; test < segmentCount; ++test) {
3918 Segment* testSegment = &fSegments[test];
3919 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003920 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003921 continue;
3922 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003923 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003924 continue;
3925 }
3926 if (bounds.fLeft > basePt.fX) {
3927 continue;
3928 }
3929 if (bounds.fRight < basePt.fX) {
3930 continue;
3931 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003932 if (bounds.fLeft == bounds.fRight) {
3933 continue;
3934 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003935 double testHitT;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00003936 int testT = testSegment->crossedSpanY(basePt, bestY, testHitT, opp);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003937 if (testT >= 0) {
3938 bestSegment = testSegment;
3939 tIndex = testT;
3940 hitT = testHitT;
3941 }
3942 }
3943 return bestSegment;
3944 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003945
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003946 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003947 for (int index = 0; index < fCrosses.count(); ++index) {
3948 if (fCrosses[index] == crosser) {
3949 return true;
3950 }
3951 }
3952 return false;
3953 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003954
caryclark@google.comf839c032012-10-26 21:03:50 +00003955 const SkPoint& end() const {
3956 const Segment& segment = fSegments.back();
3957 return segment.pts()[segment.verb()];
3958 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003959
caryclark@google.com4eeda372012-12-06 21:47:48 +00003960 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003961 int segmentCount = fSegments.count();
3962 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003963 fSegments[sIndex].findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003964 }
3965 }
3966
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003967 void fixOtherTIndex() {
3968 int segmentCount = fSegments.count();
3969 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3970 fSegments[sIndex].fixOtherTIndex();
3971 }
3972 }
skia.committer@gmail.com453995e2012-11-10 02:01:26 +00003973
caryclark@google.com31143cf2012-11-09 22:14:19 +00003974 bool operand() const {
3975 return fOperand;
3976 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003977
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003978 void reset() {
3979 fSegments.reset();
3980 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003981 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003982 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003983
caryclark@google.com7ba591e2012-11-20 14:21:54 +00003984 void resolveCoincidence(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003985 int count = fCoincidences.count();
3986 for (int index = 0; index < count; ++index) {
3987 Coincidence& coincidence = fCoincidences[index];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003988 SkASSERT(coincidence.fContours[0] == this);
caryclark@google.comb9738012012-07-03 19:53:30 +00003989 int thisIndex = coincidence.fSegments[0];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003990 Segment& thisOne = fSegments[thisIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00003991 Contour* otherContour = coincidence.fContours[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00003992 int otherIndex = coincidence.fSegments[1];
caryclark@google.comb9738012012-07-03 19:53:30 +00003993 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00003994 if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00003995 continue;
3996 }
caryclark@google.com47580692012-07-23 12:14:49 +00003997 #if DEBUG_CONCIDENT
3998 thisOne.debugShowTs();
3999 other.debugShowTs();
4000 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004001 double startT = coincidence.fTs[0][0];
4002 double endT = coincidence.fTs[0][1];
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004003 bool cancelers = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004004 if (startT > endT) {
4005 SkTSwap<double>(startT, endT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004006 cancelers ^= true; // FIXME: just assign true
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004007 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004008 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004009 double oStartT = coincidence.fTs[1][0];
4010 double oEndT = coincidence.fTs[1][1];
4011 if (oStartT > oEndT) {
4012 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004013 cancelers ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004014 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004015 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com4eeda372012-12-06 21:47:48 +00004016 bool opp = fOperand ^ otherContour->fOperand;
caryclark@google.com57cff8d2012-11-14 21:14:56 +00004017 if (cancelers && !opp) {
4018 // make sure startT and endT have t entries
caryclark@google.com2ddff932012-08-07 21:25:27 +00004019 if (startT > 0 || oEndT < 1
4020 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
4021 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004022 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004023 if (oStartT > 0 || endT < 1
4024 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
4025 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004026 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004027 if (!thisOne.done() && !other.done()) {
4028 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
4029 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004030 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004031 if (startT > 0 || oStartT > 0
4032 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004033 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004034 }
caryclark@google.com200c2112012-08-03 15:05:04 +00004035 if (endT < 1 || oEndT < 1
4036 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00004037 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00004038 }
caryclark@google.com0d3d09e2012-12-10 14:50:04 +00004039 if (!thisOne.done() && !other.done()) {
4040 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
4041 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004042 }
caryclark@google.com47580692012-07-23 12:14:49 +00004043 #if DEBUG_CONCIDENT
4044 thisOne.debugShowTs();
4045 other.debugShowTs();
4046 #endif
caryclark@google.com729e1c42012-11-21 21:36:34 +00004047 #if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004048 debugShowWindingValues(contourList);
4049 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004050 }
4051 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004052
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004053 const SkTArray<Segment>& segments() {
4054 return fSegments;
4055 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004056
caryclark@google.com235f56a2012-09-14 14:19:30 +00004057 void setOperand(bool isOp) {
4058 fOperand = isOp;
4059 }
skia.committer@gmail.comc7b4be72012-12-11 02:01:20 +00004060
caryclark@google.com4eeda372012-12-06 21:47:48 +00004061 void setOppXor(bool isOppXor) {
4062 fOppXor = isOppXor;
4063 int segmentCount = fSegments.count();
4064 for (int test = 0; test < segmentCount; ++test) {
4065 fSegments[test].setOppXor(isOppXor);
4066 }
4067 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004068
caryclark@google.com235f56a2012-09-14 14:19:30 +00004069 void setXor(bool isXor) {
4070 fXor = isXor;
4071 }
4072
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004073 void sortSegments() {
4074 int segmentCount = fSegments.count();
4075 fSortedSegments.setReserve(segmentCount);
4076 for (int test = 0; test < segmentCount; ++test) {
4077 *fSortedSegments.append() = &fSegments[test];
4078 }
4079 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
4080 fFirstSorted = 0;
4081 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004082
caryclark@google.comf839c032012-10-26 21:03:50 +00004083 const SkPoint& start() const {
4084 return fSegments.front().pts()[0];
4085 }
4086
4087 void toPath(PathWrapper& path) const {
4088 int segmentCount = fSegments.count();
4089 const SkPoint& pt = fSegments.front().pts()[0];
4090 path.deferredMove(pt);
4091 for (int test = 0; test < segmentCount; ++test) {
4092 fSegments[test].addCurveTo(0, 1, path, true);
4093 }
4094 path.close();
4095 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00004096
caryclark@google.comf839c032012-10-26 21:03:50 +00004097 void toPartialBackward(PathWrapper& path) const {
4098 int segmentCount = fSegments.count();
4099 for (int test = segmentCount - 1; test >= 0; --test) {
4100 fSegments[test].addCurveTo(1, 0, path, true);
4101 }
4102 }
4103
4104 void toPartialForward(PathWrapper& path) const {
4105 int segmentCount = fSegments.count();
4106 for (int test = 0; test < segmentCount; ++test) {
4107 fSegments[test].addCurveTo(0, 1, path, true);
4108 }
4109 }
4110
4111#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00004112 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
4113 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00004114 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00004115 // segments' top, and not a true sort, so it could be ameniable to regular
4116 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004117 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00004118 int segmentCount = fSegments.count();
4119 SkASSERT(segmentCount > 0);
4120 int best = -1;
4121 Segment* bestSegment = NULL;
4122 while (++best < segmentCount) {
4123 Segment* testSegment = &fSegments[best];
4124 if (testSegment->done()) {
4125 continue;
4126 }
4127 bestSegment = testSegment;
4128 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004129 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004130 if (!bestSegment) {
4131 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004132 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004133 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004134 for (int test = best + 1; test < segmentCount; ++test) {
4135 Segment* testSegment = &fSegments[test];
4136 if (testSegment->done()) {
4137 continue;
4138 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004139 if (testSegment->bounds().fTop > bestTop) {
4140 continue;
4141 }
4142 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00004143 if (bestTop > testTop) {
4144 bestTop = testTop;
4145 bestSegment = testSegment;
4146 }
4147 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004148 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00004149 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004150 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004151#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004152
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004153 // FIXME: get rid of allowTies logic if we don't need it
4154 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY, bool allowTies) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004155 int segmentCount = fSortedSegments.count();
4156 SkASSERT(segmentCount > 0);
4157 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00004158 int sortedIndex = fFirstSorted;
4159 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
4160 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004161 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004162 if (sortedIndex == fFirstSorted) {
4163 ++fFirstSorted;
4164 }
4165 continue;
4166 }
4167 SkPoint testXY;
4168 testSegment->activeLeftTop(testXY);
4169 if (testXY.fY < topLeft.fY) {
4170 continue;
4171 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004172 if (testXY.fY == topLeft.fY && ( /* allowTies ? testXY.fX < topLeft.fX : */
4173 testXY.fX <= topLeft.fX)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00004174 continue;
4175 }
4176 if (bestXY.fY < testXY.fY) {
4177 continue;
4178 }
4179 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004180 continue;
4181 }
4182 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00004183 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004184 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004185 return bestSegment;
4186 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004187
caryclark@google.com24bec792012-08-20 12:43:57 +00004188 Segment* undoneSegment(int& start, int& end) {
4189 int segmentCount = fSegments.count();
4190 for (int test = 0; test < segmentCount; ++test) {
4191 Segment* testSegment = &fSegments[test];
4192 if (testSegment->done()) {
4193 continue;
4194 }
4195 testSegment->undoneSpan(start, end);
4196 return testSegment;
4197 }
4198 return NULL;
4199 }
4200
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004201 int updateSegment(int index, const SkPoint* pts) {
4202 Segment& segment = fSegments[index];
4203 segment.updatePts(pts);
4204 return segment.verb() + 1;
4205 }
4206
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004207#if DEBUG_TEST
4208 SkTArray<Segment>& debugSegments() {
4209 return fSegments;
4210 }
4211#endif
4212
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004213#if DEBUG_DUMP
4214 void dump() {
4215 int i;
4216 const char className[] = "Contour";
4217 const int tab = 4;
4218 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
4219 for (i = 0; i < fSegments.count(); ++i) {
4220 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
4221 className, i);
4222 fSegments[i].dump();
4223 }
4224 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
4225 tab + sizeof(className), className,
4226 fBounds.fLeft, fBounds.fTop,
4227 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004228 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
4229 className, fContainsIntercepts);
4230 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
4231 className, fContainsCurves);
4232 }
4233#endif
4234
caryclark@google.com027de222012-07-12 12:52:50 +00004235#if DEBUG_ACTIVE_SPANS
4236 void debugShowActiveSpans() {
4237 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004238 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00004239 }
4240 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004241
4242 void validateActiveSpans() {
4243 for (int index = 0; index < fSegments.count(); ++index) {
4244 fSegments[index].validateActiveSpans();
4245 }
4246 }
caryclark@google.com027de222012-07-12 12:52:50 +00004247#endif
4248
caryclark@google.com729e1c42012-11-21 21:36:34 +00004249#if DEBUG_SHOW_WINDING
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004250 int debugShowWindingValues(int totalSegments, int ofInterest) {
4251 int count = fSegments.count();
4252 int sum = 0;
4253 for (int index = 0; index < count; ++index) {
4254 sum += fSegments[index].debugShowWindingValues(totalSegments, ofInterest);
4255 }
4256 // SkDebugf("%s sum=%d\n", __FUNCTION__, sum);
4257 return sum;
4258 }
4259
4260 static void debugShowWindingValues(SkTDArray<Contour*>& contourList) {
4261 // int ofInterest = 1 << 1 | 1 << 5 | 1 << 9 | 1 << 13;
4262 // int ofInterest = 1 << 4 | 1 << 8 | 1 << 12 | 1 << 16;
4263 int ofInterest = 1 << 5 | 1 << 8;
4264 int total = 0;
4265 int index;
4266 for (index = 0; index < contourList.count(); ++index) {
4267 total += contourList[index]->segments().count();
4268 }
4269 int sum = 0;
4270 for (index = 0; index < contourList.count(); ++index) {
4271 sum += contourList[index]->debugShowWindingValues(total, ofInterest);
4272 }
4273 // SkDebugf("%s total=%d\n", __FUNCTION__, sum);
4274 }
4275#endif
4276
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004277protected:
4278 void setBounds() {
4279 int count = fSegments.count();
4280 if (count == 0) {
4281 SkDebugf("%s empty contour\n", __FUNCTION__);
4282 SkASSERT(0);
4283 // FIXME: delete empty contour?
4284 return;
4285 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004286 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004287 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004288 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004289 }
4290 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004291
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004292private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004293 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004294 SkTDArray<Segment*> fSortedSegments;
4295 int fFirstSorted;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004296 SkTDArray<Coincidence> fCoincidences;
4297 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004298 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004299 bool fContainsIntercepts;
4300 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004301 bool fOperand; // true for the second argument to a binary operator
4302 bool fXor;
caryclark@google.com4eeda372012-12-06 21:47:48 +00004303 bool fOppXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004304#if DEBUG_DUMP
4305 int fID;
4306#endif
4307};
4308
4309class EdgeBuilder {
4310public:
4311
caryclark@google.comf839c032012-10-26 21:03:50 +00004312EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
4313 : fPath(path.nativePath())
4314 , fContours(contours)
4315{
4316 init();
4317}
4318
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004319EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00004320 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004321 , fContours(contours)
4322{
caryclark@google.comf839c032012-10-26 21:03:50 +00004323 init();
4324}
4325
4326void init() {
4327 fCurrentContour = NULL;
4328 fOperand = false;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004329 fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004330#if DEBUG_DUMP
4331 gContourID = 0;
4332 gSegmentID = 0;
4333#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00004334 fSecondHalf = preFetch();
4335}
4336
4337void addOperand(const SkPath& path) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00004338 SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
4339 fPathVerbs.pop();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004340 fPath = &path;
caryclark@google.com729e1c42012-11-21 21:36:34 +00004341 fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004342 preFetch();
4343}
4344
4345void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004346 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004347 complete();
4348 if (fCurrentContour && !fCurrentContour->segments().count()) {
4349 fContours.pop_back();
4350 }
4351 // correct pointers in contours since fReducePts may have moved as it grew
4352 int cIndex = 0;
4353 int extraCount = fExtra.count();
4354 SkASSERT(extraCount == 0 || fExtra[0] == -1);
4355 int eIndex = 0;
4356 int rIndex = 0;
4357 while (++eIndex < extraCount) {
4358 int offset = fExtra[eIndex];
4359 if (offset < 0) {
4360 ++cIndex;
4361 continue;
4362 }
4363 fCurrentContour = &fContours[cIndex];
4364 rIndex += fCurrentContour->updateSegment(offset - 1,
4365 &fReducePts[rIndex]);
4366 }
4367 fExtra.reset(); // we're done with this
4368}
4369
4370ShapeOpMask xorMask() const {
caryclark@google.com729e1c42012-11-21 21:36:34 +00004371 return fXorMask[fOperand];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004372}
4373
4374protected:
4375
4376void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004377 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004378 fCurrentContour->complete();
4379 fCurrentContour = NULL;
4380 }
4381}
4382
caryclark@google.com235f56a2012-09-14 14:19:30 +00004383// FIXME:remove once we can access path pts directly
4384int preFetch() {
4385 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004386 SkPoint pts[4];
4387 SkPath::Verb verb;
4388 do {
4389 verb = iter.next(pts);
4390 *fPathVerbs.append() = verb;
4391 if (verb == SkPath::kMove_Verb) {
4392 *fPathPts.append() = pts[0];
4393 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
4394 fPathPts.append(verb, &pts[1]);
4395 }
4396 } while (verb != SkPath::kDone_Verb);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004397 return fPathVerbs.count() - 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004398}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004399
caryclark@google.com235f56a2012-09-14 14:19:30 +00004400void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004401 SkPath::Verb reducedVerb;
4402 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00004403 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004404 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004405 const SkPoint* finalCurveStart = NULL;
4406 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00004407 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004408 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
4409 switch (verb) {
4410 case SkPath::kMove_Verb:
4411 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004412 if (!fCurrentContour) {
4413 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004414 fCurrentContour->setOperand(fOperand);
caryclark@google.com729e1c42012-11-21 21:36:34 +00004415 fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004416 *fExtra.append() = -1; // start new contour
4417 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004418 finalCurveEnd = pointsPtr++;
caryclark@google.com31143cf2012-11-09 22:14:19 +00004419 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004420 case SkPath::kLine_Verb:
4421 // skip degenerate points
4422 if (pointsPtr[-1].fX != pointsPtr[0].fX
4423 || pointsPtr[-1].fY != pointsPtr[0].fY) {
4424 fCurrentContour->addLine(&pointsPtr[-1]);
4425 }
4426 break;
4427 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00004428
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004429 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
4430 if (reducedVerb == 0) {
4431 break; // skip degenerate points
4432 }
4433 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00004434 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004435 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004436 break;
4437 }
4438 fCurrentContour->addQuad(&pointsPtr[-1]);
4439 break;
4440 case SkPath::kCubic_Verb:
4441 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
4442 if (reducedVerb == 0) {
4443 break; // skip degenerate points
4444 }
4445 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004446 *fExtra.append() =
4447 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004448 break;
4449 }
4450 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004451 *fExtra.append() =
4452 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004453 break;
4454 }
4455 fCurrentContour->addCubic(&pointsPtr[-1]);
4456 break;
4457 case SkPath::kClose_Verb:
4458 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004459 if (finalCurveStart && finalCurveEnd
4460 && *finalCurveStart != *finalCurveEnd) {
4461 *fReducePts.append() = *finalCurveStart;
4462 *fReducePts.append() = *finalCurveEnd;
4463 *fExtra.append() =
4464 fCurrentContour->addLine(fReducePts.end() - 2);
4465 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004466 complete();
caryclark@google.com31143cf2012-11-09 22:14:19 +00004467 goto nextVerb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004468 default:
4469 SkDEBUGFAIL("bad verb");
4470 return;
4471 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004472 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004473 pointsPtr += verb;
4474 SkASSERT(fCurrentContour);
caryclark@google.com31143cf2012-11-09 22:14:19 +00004475 nextVerb:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004476 if (verbPtr == endOfFirstHalf) {
4477 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004478 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004479 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004480}
4481
4482private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004483 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004484 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004485 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004486 Contour* fCurrentContour;
4487 SkTArray<Contour>& fContours;
4488 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004489 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com729e1c42012-11-21 21:36:34 +00004490 ShapeOpMask fXorMask[2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004491 int fSecondHalf;
4492 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004493};
4494
4495class Work {
4496public:
4497 enum SegmentType {
4498 kHorizontalLine_Segment = -1,
4499 kVerticalLine_Segment = 0,
4500 kLine_Segment = SkPath::kLine_Verb,
4501 kQuad_Segment = SkPath::kQuad_Verb,
4502 kCubic_Segment = SkPath::kCubic_Verb,
4503 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004504
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004505 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4506 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4507 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004508
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004509 // FIXME: does it make sense to write otherIndex now if we're going to
4510 // fix it up later?
4511 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004512 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004513 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004514
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004515 // Avoid collapsing t values that are close to the same since
4516 // we walk ts to describe consecutive intersections. Since a pair of ts can
4517 // be nearly equal, any problems caused by this should be taken care
4518 // of later.
4519 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004520 int addT(double newT, const Work& other) {
4521 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004522 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004523
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004524 bool advance() {
4525 return ++fIndex < fLast;
4526 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004527
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004528 SkScalar bottom() const {
4529 return bounds().fBottom;
4530 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004531
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004532 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004533 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004534 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004535
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004536 const SkPoint* cubic() const {
4537 return fCubic;
4538 }
4539
4540 void init(Contour* contour) {
4541 fContour = contour;
4542 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004543 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004544 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004545
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004546 bool isAdjacent(const Work& next) {
4547 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4548 }
4549
4550 bool isFirstLast(const Work& next) {
4551 return fContour == next.fContour && fIndex == 0
4552 && next.fIndex == fLast - 1;
4553 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004554
4555 SkScalar left() const {
4556 return bounds().fLeft;
4557 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004558
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004559 void promoteToCubic() {
4560 fCubic[0] = pts()[0];
4561 fCubic[2] = pts()[1];
4562 fCubic[3] = pts()[2];
4563 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4564 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4565 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4566 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4567 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004568
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004569 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004570 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004571 }
4572
4573 SkScalar right() const {
4574 return bounds().fRight;
4575 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004576
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004577 ptrdiff_t segmentIndex() const {
4578 return fIndex;
4579 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004580
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004581 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004582 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004583 SegmentType type = (SegmentType) segment.verb();
4584 if (type != kLine_Segment) {
4585 return type;
4586 }
4587 if (segment.isHorizontal()) {
4588 return kHorizontalLine_Segment;
4589 }
4590 if (segment.isVertical()) {
4591 return kVerticalLine_Segment;
4592 }
4593 return kLine_Segment;
4594 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004595
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004596 bool startAfter(const Work& after) {
4597 fIndex = after.fIndex;
4598 return advance();
4599 }
4600
4601 SkScalar top() const {
4602 return bounds().fTop;
4603 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004604
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004605 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004606 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004607 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004608
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004609 SkScalar x() const {
4610 return bounds().fLeft;
4611 }
4612
4613 bool xFlipped() const {
4614 return x() != pts()[0].fX;
4615 }
4616
4617 SkScalar y() const {
4618 return bounds().fTop;
4619 }
4620
4621 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004622 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004623 }
4624
4625protected:
4626 Contour* fContour;
4627 SkPoint fCubic[4];
4628 int fIndex;
4629 int fLast;
4630};
4631
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004632#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004633static void debugShowLineIntersection(int pts, const Work& wt,
4634 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004635 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004636 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004637 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4638 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4639 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4640 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004641 return;
4642 }
4643 SkPoint wtOutPt, wnOutPt;
4644 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4645 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004646 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 +00004647 __FUNCTION__,
4648 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4649 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4650 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004651 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004652 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004653 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004654 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4655 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4656 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004657 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4658 }
4659 SkDebugf("\n");
4660}
4661
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004662static void debugShowQuadLineIntersection(int pts, const Work& wt,
4663 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4664 if (!pts) {
4665 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004666 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004667 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4668 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004669 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004670 return;
4671 }
4672 SkPoint wtOutPt, wnOutPt;
4673 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4674 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4675 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4676 __FUNCTION__,
4677 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4678 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4679 wtOutPt.fX, wtOutPt.fY);
4680 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004681 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4682 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004683 }
4684 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4685 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4686 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4687 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004688 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4689 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004690 }
4691 SkDebugf("\n");
4692}
4693
caryclark@google.coma461ff02012-10-11 12:54:23 +00004694static void debugShowQuadIntersection(int pts, const Work& wt,
4695 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4696 if (!pts) {
4697 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4698 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4699 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004700 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004701 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004702 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004703 return;
4704 }
4705 SkPoint wtOutPt, wnOutPt;
4706 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4707 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4708 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4709 __FUNCTION__,
4710 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4711 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4712 wtOutPt.fX, wtOutPt.fY);
4713 if (pts == 2) {
4714 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4715 }
4716 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4717 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4718 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4719 wnOutPt.fX, wnOutPt.fY);
4720 if (pts == 2) {
4721 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004722 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004723 SkDebugf("\n");
4724}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004725#else
4726static void debugShowLineIntersection(int , const Work& ,
4727 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004728}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004729
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004730static void debugShowQuadLineIntersection(int , const Work& ,
4731 const Work& , const double [2], const double [2]) {
4732}
4733
caryclark@google.coma461ff02012-10-11 12:54:23 +00004734static void debugShowQuadIntersection(int , const Work& ,
4735 const Work& , const double [2], const double [2]) {
4736}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004737#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004738
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004739static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004740
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004741 if (test != next) {
4742 if (test->bounds().fBottom < next->bounds().fTop) {
4743 return false;
4744 }
4745 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4746 return true;
4747 }
4748 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004749 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004750 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004751 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004752 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004753 Work wn;
4754 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004755 if (test == next && !wn.startAfter(wt)) {
4756 continue;
4757 }
4758 do {
4759 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4760 continue;
4761 }
4762 int pts;
4763 Intersections ts;
4764 bool swap = false;
4765 switch (wt.segmentType()) {
4766 case Work::kHorizontalLine_Segment:
4767 swap = true;
4768 switch (wn.segmentType()) {
4769 case Work::kHorizontalLine_Segment:
4770 case Work::kVerticalLine_Segment:
4771 case Work::kLine_Segment: {
4772 pts = HLineIntersect(wn.pts(), wt.left(),
4773 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004774 debugShowLineIntersection(pts, wt, wn,
4775 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004776 break;
4777 }
4778 case Work::kQuad_Segment: {
4779 pts = HQuadIntersect(wn.pts(), wt.left(),
4780 wt.right(), wt.y(), wt.xFlipped(), ts);
4781 break;
4782 }
4783 case Work::kCubic_Segment: {
4784 pts = HCubicIntersect(wn.pts(), wt.left(),
4785 wt.right(), wt.y(), wt.xFlipped(), ts);
4786 break;
4787 }
4788 default:
4789 SkASSERT(0);
4790 }
4791 break;
4792 case Work::kVerticalLine_Segment:
4793 swap = true;
4794 switch (wn.segmentType()) {
4795 case Work::kHorizontalLine_Segment:
4796 case Work::kVerticalLine_Segment:
4797 case Work::kLine_Segment: {
4798 pts = VLineIntersect(wn.pts(), wt.top(),
4799 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004800 debugShowLineIntersection(pts, wt, wn,
4801 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004802 break;
4803 }
4804 case Work::kQuad_Segment: {
4805 pts = VQuadIntersect(wn.pts(), wt.top(),
4806 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4807 break;
4808 }
4809 case Work::kCubic_Segment: {
4810 pts = VCubicIntersect(wn.pts(), wt.top(),
4811 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4812 break;
4813 }
4814 default:
4815 SkASSERT(0);
4816 }
4817 break;
4818 case Work::kLine_Segment:
4819 switch (wn.segmentType()) {
4820 case Work::kHorizontalLine_Segment:
4821 pts = HLineIntersect(wt.pts(), wn.left(),
4822 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004823 debugShowLineIntersection(pts, wt, wn,
4824 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004825 break;
4826 case Work::kVerticalLine_Segment:
4827 pts = VLineIntersect(wt.pts(), wn.top(),
4828 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004829 debugShowLineIntersection(pts, wt, wn,
4830 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004831 break;
4832 case Work::kLine_Segment: {
4833 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4834 debugShowLineIntersection(pts, wt, wn,
4835 ts.fT[1], ts.fT[0]);
4836 break;
4837 }
4838 case Work::kQuad_Segment: {
4839 swap = true;
4840 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004841 debugShowQuadLineIntersection(pts, wn, wt,
4842 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004843 break;
4844 }
4845 case Work::kCubic_Segment: {
4846 swap = true;
4847 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4848 break;
4849 }
4850 default:
4851 SkASSERT(0);
4852 }
4853 break;
4854 case Work::kQuad_Segment:
4855 switch (wn.segmentType()) {
4856 case Work::kHorizontalLine_Segment:
4857 pts = HQuadIntersect(wt.pts(), wn.left(),
4858 wn.right(), wn.y(), wn.xFlipped(), ts);
4859 break;
4860 case Work::kVerticalLine_Segment:
4861 pts = VQuadIntersect(wt.pts(), wn.top(),
4862 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4863 break;
4864 case Work::kLine_Segment: {
4865 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004866 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004867 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004868 break;
4869 }
4870 case Work::kQuad_Segment: {
4871 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004872 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004873 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004874 break;
4875 }
4876 case Work::kCubic_Segment: {
4877 wt.promoteToCubic();
4878 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4879 break;
4880 }
4881 default:
4882 SkASSERT(0);
4883 }
4884 break;
4885 case Work::kCubic_Segment:
4886 switch (wn.segmentType()) {
4887 case Work::kHorizontalLine_Segment:
4888 pts = HCubicIntersect(wt.pts(), wn.left(),
4889 wn.right(), wn.y(), wn.xFlipped(), ts);
4890 break;
4891 case Work::kVerticalLine_Segment:
4892 pts = VCubicIntersect(wt.pts(), wn.top(),
4893 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4894 break;
4895 case Work::kLine_Segment: {
4896 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4897 break;
4898 }
4899 case Work::kQuad_Segment: {
4900 wn.promoteToCubic();
4901 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4902 break;
4903 }
4904 case Work::kCubic_Segment: {
4905 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4906 break;
4907 }
4908 default:
4909 SkASSERT(0);
4910 }
4911 break;
4912 default:
4913 SkASSERT(0);
4914 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004915 if (!foundCommonContour && pts > 0) {
4916 test->addCross(next);
4917 next->addCross(test);
4918 foundCommonContour = true;
4919 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004920 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004921 if (pts == 2) {
4922 if (wn.segmentType() <= Work::kLine_Segment
4923 && wt.segmentType() <= Work::kLine_Segment) {
4924 wt.addCoincident(wn, ts, swap);
4925 continue;
4926 }
4927 if (wn.segmentType() == Work::kQuad_Segment
4928 && wt.segmentType() == Work::kQuad_Segment
4929 && ts.coincidentUsed() == 2) {
4930 wt.addCoincident(wn, ts, swap);
4931 continue;
4932 }
4933
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004934 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004935 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004936 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4937 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004938 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4939 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004940 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4941 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004942 }
4943 } while (wn.advance());
4944 } while (wt.advance());
4945 return true;
4946}
4947
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004948// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004949// see if coincidence is formed by clipping non-concident segments
caryclark@google.com4eeda372012-12-06 21:47:48 +00004950static void coincidenceCheck(SkTDArray<Contour*>& contourList, int total) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004951 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004952 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004953 Contour* contour = contourList[cIndex];
caryclark@google.com7ba591e2012-11-20 14:21:54 +00004954 contour->resolveCoincidence(contourList);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004955 }
4956 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4957 Contour* contour = contourList[cIndex];
caryclark@google.com4eeda372012-12-06 21:47:48 +00004958 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004959 }
4960}
4961
caryclark@google.come7bd5f42012-12-13 19:47:53 +00004962static int contourRangeCheckX(SkTDArray<Contour*>& contourList, double mid,
4963 const Segment* current, int index, int endIndex, bool opp) {
4964 const SkPoint& basePt = current->xyAtT(endIndex);
4965 int contourCount = contourList.count();
4966 SkScalar bestX = SK_ScalarMin;
4967 const Segment* test = NULL;
4968 int tIndex;
4969 double tHit;
4970 bool crossOpp;
4971 for (int cTest = 0; cTest < contourCount; ++cTest) {
4972 Contour* contour = contourList[cTest];
4973 bool testOpp = contour->operand() ^ current->operand() ^ opp;
4974 if (basePt.fX < contour->bounds().fLeft) {
4975 continue;
4976 }
4977 if (bestX > contour->bounds().fRight) {
4978 continue;
4979 }
4980 const Segment* next = contour->crossedSegmentX(basePt, bestX, tIndex, tHit, testOpp);
4981 if (next) {
4982 test = next;
4983 crossOpp = testOpp;
4984 }
4985 }
4986 if (!test) {
4987 return 0;
4988 }
4989 if (approximately_zero(tHit - test->t(tIndex))) { // if we hit the end of a span, disregard
4990 return SK_MinS32;
4991 }
4992 int winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
4993 SkASSERT(winding != SK_MinS32);
4994 int windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
4995#if DEBUG_WINDING
4996 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4997 windValue);
4998#endif
4999 // see if a + change in T results in a +/- change in X (compute x'(T))
5000 SkScalar dy = (*SegmentDYAtT[test->verb()])(test->pts(), tHit);
5001 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dy)) {
5002 const SkPoint* pts = test->pts();
5003 dy = pts[2].fY - pts[1].fY - dy;
5004 }
5005#if DEBUG_WINDING
5006 SkDebugf("%s dy=%1.9g\n", __FUNCTION__, dy);
5007#endif
5008 SkASSERT(dy != 0);
5009 if (winding * dy > 0) { // if same signs, result is negative
5010 winding += dy > 0 ? -windValue : windValue;
5011#if DEBUG_WINDING
5012 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
5013#endif
5014 }
5015 return winding;
5016}
5017
5018static int contourRangeCheckY(SkTDArray<Contour*>& contourList, double mid,
5019 const Segment* current, int index, int endIndex, bool opp) {
5020 const SkPoint& basePt = current->xyAtT(endIndex);
5021 int contourCount = contourList.count();
5022 SkScalar bestY = SK_ScalarMin;
5023 const Segment* test = NULL;
5024 int tIndex;
5025 double tHit;
5026 bool crossOpp;
5027 for (int cTest = 0; cTest < contourCount; ++cTest) {
5028 Contour* contour = contourList[cTest];
5029 bool testOpp = contour->operand() ^ current->operand() ^ opp;
5030 if (basePt.fY < contour->bounds().fTop) {
5031 continue;
5032 }
5033 if (bestY > contour->bounds().fBottom) {
5034 continue;
5035 }
5036 const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
5037 if (next) {
5038 test = next;
5039 crossOpp = testOpp;
5040 }
5041 }
5042 if (!test) {
5043 return 0;
5044 }
5045 if (approximately_zero(tHit - test->t(tIndex))) { // if we hit the end of a span, disregard
5046 return SK_MinS32;
5047 }
5048 int winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
5049 SkASSERT(winding != SK_MinS32);
5050 int windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
5051#if DEBUG_WINDING
5052 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
5053 windValue);
5054#endif
5055 // see if a + change in T results in a +/- change in X (compute x'(T))
5056 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
5057 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
5058 const SkPoint* pts = test->pts();
5059 dx = pts[2].fX - pts[1].fX - dx;
5060 }
5061#if DEBUG_WINDING
5062 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
5063#endif
5064 SkASSERT(dx != 0);
5065 if (winding * dx > 0) { // if same signs, result is negative
5066 winding += dx > 0 ? -windValue : windValue;
5067#if DEBUG_WINDING
5068 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
5069#endif
5070 }
5071 return winding;
5072}
5073
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005074// project a ray from the top of the contour up and see if it hits anything
5075// note: when we compute line intersections, we keep track of whether
5076// two contours touch, so we need only look at contours not touching this one.
5077// OPTIMIZATION: sort contourList vertically to avoid linear walk
5078static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com31143cf2012-11-09 22:14:19 +00005079 const Segment* current, int index, int endIndex, bool opp) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005080 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005081 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005082 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00005083 const Segment* test = NULL;
5084 int tIndex;
5085 double tHit;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005086 bool crossOpp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005087 for (int cTest = 0; cTest < contourCount; ++cTest) {
5088 Contour* contour = contourList[cTest];
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005089 bool testOpp = contour->operand() ^ current->operand() ^ opp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005090 if (basePt.fY < contour->bounds().fTop) {
5091 continue;
5092 }
5093 if (bestY > contour->bounds().fBottom) {
5094 continue;
5095 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005096 const Segment* next = contour->crossedSegmentY(basePt, bestY, tIndex, tHit, testOpp);
caryclark@google.com47580692012-07-23 12:14:49 +00005097 if (next) {
5098 test = next;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005099 crossOpp = testOpp;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005100 }
caryclark@google.com47580692012-07-23 12:14:49 +00005101 }
5102 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00005103 return 0;
5104 }
5105 int winding, windValue;
5106 // If the ray hit the end of a span, we need to construct the wheel of
5107 // angles to find the span closest to the ray -- even if there are just
5108 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005109 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00005110 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00005111 SkTDArray<Angle> angles;
5112 int end = test->nextSpan(tIndex, 1);
5113 if (end < 0) {
5114 end = test->nextSpan(tIndex, -1);
5115 }
5116 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00005117 SkASSERT(angles.count() > 0);
5118 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
5119#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00005120 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00005121#endif
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005122 return SK_MinS32;
caryclark@google.com59823f72012-08-09 18:17:47 +00005123 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005124 test->buildAngles(tIndex, angles, false);
caryclark@google.com47580692012-07-23 12:14:49 +00005125 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00005126 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00005127 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00005128 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00005129 // hour after 6 o'clock
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005130 bool sortable = Segment::SortAngles(angles, sorted);
5131 if (!sortable) {
5132 return SK_MinS32;
5133 }
caryclark@google.com47580692012-07-23 12:14:49 +00005134#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005135 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00005136#endif
5137 // walk the sorted angle fan to find the lowest angle
5138 // above the base point. Currently, the first angle in the sorted array
5139 // is 12 noon or an earlier hour (the next counterclockwise)
5140 int count = sorted.count();
5141 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005142 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00005143 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005144 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00005145 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00005146 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005147 if (angle->unsortable()) {
5148 continue;
5149 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005150 if (baseMatches && angle->isHorizontal()) {
5151 continue;
5152 }
5153 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00005154 test = angle->segment();
5155 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
5156 const SkPoint* pts = test->pts();
5157 indexDx = pts[2].fX - pts[1].fX - indexDx;
5158 }
caryclark@google.com47580692012-07-23 12:14:49 +00005159 if (indexDx < 0) {
5160 left = index;
5161 } else if (indexDx > 0) {
5162 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00005163 int previous = index - 1;
5164 if (previous < 0) {
5165 previous = count - 1;
5166 }
5167 const Angle* prev = sorted[previous];
5168 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
5169#if DEBUG_SORT
5170 SkDebugf("%s use prev\n", __FUNCTION__);
5171#endif
5172 right = previous;
5173 }
caryclark@google.com47580692012-07-23 12:14:49 +00005174 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005175 } else {
5176 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005177 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005178 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005179 if ((left < 0 || right < 0) && mid >= 0) {
5180 angle = sorted[mid];
5181 Segment* midSeg = angle->segment();
5182 int end = angle->end();
5183 if (midSeg->unsortable(end)) {
5184 return SK_MinS32;
5185 }
5186 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00005187 if (left < 0 && right < 0) {
5188 left = mid;
5189 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005190 if (left < 0 && right < 0) {
5191 SkASSERT(0);
5192 return SK_MinS32; // unsortable
5193 }
caryclark@google.com47580692012-07-23 12:14:49 +00005194 if (left < 0) {
5195 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005196 } else if (left >= 0 && mid >= 0 && right >= 0
5197 && sorted[mid]->sign() == sorted[right]->sign()) {
5198 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005199 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005200 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00005201 test = angle->segment();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005202 winding = crossOpp ? test->oppSum(angle) : test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00005203 SkASSERT(winding != SK_MinS32);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005204 windValue = crossOpp ? test->oppValue(angle) : test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00005205#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005206 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
5207 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00005208#endif
5209 } else {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005210 winding = crossOpp ? test->oppSum(tIndex) : test->windSum(tIndex);
5211 if (winding == SK_MinS32) {
5212 return SK_MinS32; // unsortable
5213 }
5214 windValue = crossOpp ? test->oppValue(tIndex) : test->windValue(tIndex);
caryclark@google.com47580692012-07-23 12:14:49 +00005215#if DEBUG_WINDING
5216 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
5217 windValue);
5218#endif
5219 }
5220 // see if a + change in T results in a +/- change in X (compute x'(T))
5221 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00005222 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
5223 const SkPoint* pts = test->pts();
5224 dx = pts[2].fX - pts[1].fX - dx;
5225 }
caryclark@google.com47580692012-07-23 12:14:49 +00005226#if DEBUG_WINDING
5227 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
5228#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00005229 SkASSERT(dx != 0);
5230 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00005231 winding += dx > 0 ? -windValue : windValue;
5232#if DEBUG_WINDING
5233 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
5234#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005235 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005236 return winding;
5237}
rmistry@google.comd6176b02012-08-23 18:14:13 +00005238
caryclark@google.com24bec792012-08-20 12:43:57 +00005239static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
5240 int contourCount = contourList.count();
5241 Segment* result;
5242 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5243 Contour* contour = contourList[cIndex];
5244 result = contour->undoneSegment(start, end);
5245 if (result) {
5246 return result;
5247 }
5248 }
5249 return NULL;
5250}
5251
5252
5253
caryclark@google.com31143cf2012-11-09 22:14:19 +00005254static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005255 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005256 Span* span;
5257 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005258 const Span& backPtr = span->fOther->span(span->fOtherIndex);
5259 Segment* segment = backPtr.fOther;
5260 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005261 SkTDArray<Angle> angles;
5262 int done = 0;
5263 if (segment->activeAngle(tIndex, done, angles)) {
5264 Angle* last = angles.end() - 1;
5265 tIndex = last->start();
5266 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005267 #if TRY_ROTATE
5268 *chase.insert(0) = span;
5269 #else
5270 *chase.append() = span;
5271 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005272 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005273 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005274 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00005275 continue;
5276 }
5277 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005278 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00005279#if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005280 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00005281#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005282 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005283 continue;
5284 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00005285 // find first angle, initialize winding to computed fWindSum
5286 int firstIndex = -1;
5287 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005288 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005289 do {
5290 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005291 segment = angle->segment();
5292 winding = segment->windSum(angle);
5293 } while (winding == SK_MinS32);
5294 int spanWinding = segment->spanSign(angle->start(), angle->end());
5295 #if DEBUG_WINDING
caryclark@google.com31143cf2012-11-09 22:14:19 +00005296 SkDebugf("%s winding=%d spanWinding=%d\n",
5297 __FUNCTION__, winding, spanWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00005298 #endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005299 // turn span winding into contour winding
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005300 if (spanWinding * winding < 0) {
5301 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005302 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005303 #if DEBUG_SORT
caryclark@google.com31143cf2012-11-09 22:14:19 +00005304 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding, 0);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005305 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005306 // we care about first sign and whether wind sum indicates this
5307 // edge is inside or outside. Maybe need to pass span winding
5308 // or first winding or something into this function?
5309 // advance to first undone angle, then return it and winding
5310 // (to set whether edges are active or not)
5311 int nextIndex = firstIndex + 1;
5312 int angleCount = sorted.count();
5313 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005314 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00005315 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00005316 do {
5317 SkASSERT(nextIndex != firstIndex);
5318 if (nextIndex == angleCount) {
5319 nextIndex = 0;
5320 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005321 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00005322 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005323 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00005324 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005325 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00005326 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
5327 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005328 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005329 tIndex = angle->start();
5330 endIndex = angle->end();
5331 int lesser = SkMin32(tIndex, endIndex);
5332 const Span& nextSpan = segment->span(lesser);
5333 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005334#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00005335 // FIXME: this be wrong. assign startWinding if edge is in
5336 // same direction. If the direction is opposite, winding to
5337 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00005338 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005339 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00005340 }
caryclark@google.com59823f72012-08-09 18:17:47 +00005341 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00005342#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005343 break;
5344 }
5345 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005346 #if TRY_ROTATE
5347 *chase.insert(0) = span;
5348 #else
5349 *chase.append() = span;
5350 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00005351 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005352 }
5353 return NULL;
5354}
5355
caryclark@google.com027de222012-07-12 12:52:50 +00005356#if DEBUG_ACTIVE_SPANS
5357static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005358 int index;
5359 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00005360 contourList[index]->debugShowActiveSpans();
5361 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005362 for (index = 0; index < contourList.count(); ++ index) {
5363 contourList[index]->validateActiveSpans();
5364 }
caryclark@google.com027de222012-07-12 12:52:50 +00005365}
5366#endif
5367
caryclark@google.com27c449a2012-07-27 18:26:38 +00005368static bool windingIsActive(int winding, int spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005369 // FIXME: !spanWinding test must be superflorous, true?
caryclark@google.com27c449a2012-07-27 18:26:38 +00005370 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
5371 && (!winding || !spanWinding || winding == -spanWinding);
5372}
5373
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005374static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005375 int& endIndex, SkPoint& topLeft, bool& unsortable, bool allowTies, bool onlySortable) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005376 Segment* result;
5377 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00005378 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005379 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00005380 Segment* topStart = NULL;
5381 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
5382 Contour* contour = contourList[cIndex];
5383 const Bounds& bounds = contour->bounds();
5384 if (bounds.fBottom < topLeft.fY) {
5385 continue;
5386 }
5387 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
5388 continue;
5389 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005390 Segment* test = contour->topSortableSegment(topLeft, bestXY, allowTies);
caryclark@google.comf839c032012-10-26 21:03:50 +00005391 if (test) {
5392 topStart = test;
5393 }
5394 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005395 if (!topStart) {
5396 return NULL;
5397 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005398 topLeft = bestXY;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005399 result = topStart->findTop(index, endIndex, unsortable, onlySortable);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005400 } while (!result);
5401 return result;
5402}
caryclark@google.com31143cf2012-11-09 22:14:19 +00005403
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005404static int updateWindings(const Segment* current, int index, int endIndex, int& spanWinding) {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005405 int lesser = SkMin32(index, endIndex);
5406 spanWinding = current->spanSign(index, endIndex);
5407 int winding = current->windSum(lesser);
5408 bool inner = useInnerWinding(winding - spanWinding, winding);
5409#if DEBUG_WINDING
5410 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
5411 " inner=%d result=%d\n",
5412 __FUNCTION__, current->debugID(), current->t(lesser),
5413 spanWinding, winding, SkSign32(index - endIndex),
5414 useInnerWinding(winding - spanWinding, winding),
5415 inner ? winding - spanWinding : winding);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005416#endif
caryclark@google.com31143cf2012-11-09 22:14:19 +00005417 if (inner) {
5418 winding -= spanWinding;
5419 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005420 return winding;
5421}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005422
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005423static Segment* findSortableTopOld(SkTDArray<Contour*>& contourList, bool& firstContour, int& index,
5424 int& endIndex, SkPoint& topLeft, int& contourWinding, bool& unsortable) {
5425 Segment* current;
5426 bool allowTies = true;
5427 do {
5428 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, allowTies,
5429 true);
5430 if (!current) {
5431 break;
5432 }
5433 if (firstContour) {
5434 contourWinding = 0;
5435 firstContour = false;
5436 break;
5437 }
5438 int sumWinding = current->windSum(SkMin32(index, endIndex));
5439 // FIXME: don't I have to adjust windSum to get contourWinding?
5440 if (sumWinding == SK_MinS32) {
5441 sumWinding = current->computeSum(index, endIndex, false);
5442 }
5443 if (sumWinding == SK_MinS32) {
5444 contourWinding = innerContourCheck(contourList, current, index, endIndex, false);
5445 allowTies = false;
5446 } else {
5447 contourWinding = sumWinding;
5448 int spanWinding = current->spanSign(index, endIndex);
5449 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
5450 if (inner) {
5451 contourWinding -= spanWinding;
5452 }
5453#if DEBUG_WINDING
5454 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n",
5455 __FUNCTION__, sumWinding, spanWinding, SkSign32(index - endIndex),
5456 inner, contourWinding);
5457#endif
5458 }
5459 } while (contourWinding == SK_MinS32);
5460 if (contourWinding != SK_MinS32) {
5461#if DEBUG_WINDING
5462 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
5463#endif
5464 return current;
5465 }
5466 // the simple upward projection of the unresolved points hit unsortable angles
5467 // shoot rays at right angles to the segment to find its winding, ignoring angle cases
5468 topLeft.fX = topLeft.fY = SK_ScalarMin;
5469 do {
5470 current = findSortableTop(contourList, index, endIndex, topLeft, unsortable, allowTies,
5471 false);
5472 SkASSERT(current); // FIXME: return to caller that path cannot be simplified (yet)
5473 // find bounds
5474 Bounds bounds;
5475 bounds.setPoint(current->xyAtT(index));
5476 bounds.add(current->xyAtT(endIndex));
5477 SkScalar width = bounds.width();
5478 SkScalar height = bounds.height();
5479 int (*rangeChecker)(SkTDArray<Contour*>& contourList, double mid,
5480 const Segment* current, int index, int endIndex, bool opp);
5481 if (width > height) {
5482 if (approximately_negative(width)) {
5483 continue; // edge is too small to resolve meaningfully
5484 }
5485 rangeChecker = contourRangeCheckY;
5486 } else {
5487 if (approximately_negative(height)) {
5488 continue; // edge is too small to resolve meaningfully
5489 }
5490 rangeChecker = contourRangeCheckX;
5491 }
5492 double test = 1;
5493 do {
5494 contourWinding = (*rangeChecker)(contourList, test, current, index, endIndex, false);
5495 if (contourWinding != SK_MinS32) {
5496 return current;
5497 }
5498 test /= 2;
5499 } while (!approximately_negative(test));
5500 } while (true);
5501 return current;
5502}
5503
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005504// Each segment may have an inside or an outside. Segments contained within
5505// winding may have insides on either side, and form a contour that should be
5506// ignored. Segments that are coincident with opposing direction segments may
5507// have outsides on either side, and should also disappear.
5508// 'Normal' segments will have one inside and one outside. Subsequent connections
5509// when winding should follow the intersection direction. If more than one edge
5510// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005511 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00005512 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005513// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005514static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005515 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005516 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005517 bool topUnsortable = false;
5518 bool firstRetry = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00005519 bool closable = true;
5520 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00005521 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005522 int index, endIndex;
caryclark@google.com31143cf2012-11-09 22:14:19 +00005523 // iterates while top is unsortable
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005524 int contourWinding;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005525 Segment* current = findSortableTopOld(contourList, firstContour, index, endIndex, topLeft,
5526 contourWinding, topUnsortable);
5527 if (!current) {
5528 if (topUnsortable) {
5529 topUnsortable = false;
5530 SkASSERT(!firstRetry);
5531 firstRetry = true;
5532 topLeft.fX = topLeft.fY = SK_ScalarMin;
5533 continue;
caryclark@google.com200c2112012-08-03 15:05:04 +00005534 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005535 break;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005536 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005537 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00005538 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005539 // FIXME: needs work. While it works in limited situations, it does
5540 // not always compute winding correctly. Active should be removed and instead
5541 // the initial winding should be correctly passed in so that if the
5542 // inner contour is wound the same way, it never finds an accumulated
5543 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00005544 // other than zero when resolving sorted angles.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005545 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005546 do {
caryclark@google.com31143cf2012-11-09 22:14:19 +00005547 bool active = windingIsActive(winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005548 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00005549 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
5550 __FUNCTION__, active ? "true" : "false",
5551 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005552 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005553 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005554 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005555 int nextStart = index;
5556 int nextEnd = endIndex;
5557 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005558 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005559 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005560 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005561 && current->verb() != SkPath::kLine_Verb
5562 && !simple.isClosed()) {
5563 current->addCurveTo(index, endIndex, simple, true);
5564 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005565 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005566 break;
5567 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005568 #if DEBUG_FLOW
5569 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5570 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
5571 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
5572 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005573 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005574 current = next;
5575 index = nextStart;
5576 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005577 } while (!simple.isClosed()
5578 && ((active && !unsortable) || !current->done()));
5579 if (active) {
5580 if (!simple.isClosed()) {
5581 SkASSERT(unsortable);
5582 int min = SkMin32(index, endIndex);
5583 if (!current->done(min)) {
5584 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005585 current->markDone(min, winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00005586 }
5587 closable = false;
5588 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005589 simple.close();
5590 }
caryclark@google.com31143cf2012-11-09 22:14:19 +00005591 current = findChase(chaseArray, index, endIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005592 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00005593 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00005594 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005595 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00005596 break;
5597 }
caryclark@google.com57cff8d2012-11-14 21:14:56 +00005598 winding = updateWindings(current, index, endIndex, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005599 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005600 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005601 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005602}
5603
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005604// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005605static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005606 Segment* current;
5607 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005608 bool unsortable = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005609 bool closable = true;
caryclark@google.com24bec792012-08-20 12:43:57 +00005610 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005611 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005612 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005613 int nextStart = start;
5614 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005615 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005616 if (!next) {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005617 if (!unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00005618 && current->verb() != SkPath::kLine_Verb
5619 && !simple.isClosed()) {
5620 current->addCurveTo(start, end, simple, true);
5621 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005622 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005623 break;
5624 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005625 #if DEBUG_FLOW
5626 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
5627 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
5628 current->xyAtT(end).fX, current->xyAtT(end).fY);
5629 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005630 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005631 current = next;
5632 start = nextStart;
5633 end = nextEnd;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005634 } while (!simple.isClosed() && (!unsortable || !current->done()));
5635 if (!simple.isClosed()) {
5636 SkASSERT(unsortable);
5637 int min = SkMin32(start, end);
5638 if (!current->done(min)) {
5639 current->addCurveTo(start, end, simple, true);
5640 current->markDone(min, 1);
5641 }
5642 closable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005643 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005644 simple.close();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005645 #if DEBUG_ACTIVE_SPANS
5646 debugShowActiveSpans(contourList);
5647 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005648 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005649 return closable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005650}
5651
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005652static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5653 int contourCount = contourList.count();
5654 for (int cTest = 0; cTest < contourCount; ++cTest) {
5655 Contour* contour = contourList[cTest];
5656 contour->fixOtherTIndex();
5657 }
5658}
5659
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005660static void sortSegments(SkTDArray<Contour*>& contourList) {
5661 int contourCount = contourList.count();
5662 for (int cTest = 0; cTest < contourCount; ++cTest) {
5663 Contour* contour = contourList[cTest];
5664 contour->sortSegments();
5665 }
5666}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005667
caryclark@google.com4eeda372012-12-06 21:47:48 +00005668static void makeContourList(SkTArray<Contour>& contours, SkTDArray<Contour*>& list,
5669 bool evenOdd, bool oppEvenOdd) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005670 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005671 if (count == 0) {
5672 return;
5673 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005674 for (int index = 0; index < count; ++index) {
caryclark@google.com4eeda372012-12-06 21:47:48 +00005675 Contour& contour = contours[index];
5676 contour.setOppXor(contour.operand() ? evenOdd : oppEvenOdd);
5677 *list.append() = &contour;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005678 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005679 QSort<Contour>(list.begin(), list.end() - 1);
5680}
5681
caryclark@google.comf839c032012-10-26 21:03:50 +00005682static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005683 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005684}
5685
caryclark@google.comf839c032012-10-26 21:03:50 +00005686 /*
5687 check start and end of each contour
5688 if not the same, record them
5689 match them up
5690 connect closest
5691 reassemble contour pieces into new path
5692 */
5693static void assemble(const PathWrapper& path, PathWrapper& simple) {
5694#if DEBUG_PATH_CONSTRUCTION
5695 SkDebugf("%s\n", __FUNCTION__);
5696#endif
5697 SkTArray<Contour> contours;
5698 EdgeBuilder builder(path, contours);
5699 builder.finish();
5700 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005701 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005702 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005703 for (outer = 0; outer < count; ++outer) {
5704 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005705 const SkPoint& eStart = eContour.start();
5706 const SkPoint& eEnd = eContour.end();
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005707#if DEBUG_ASSEMBLE
5708 SkDebugf("%s contour", __FUNCTION__);
5709 if (!approximatelyEqual(eStart, eEnd)) {
5710 SkDebugf("[%d]", runs.count());
5711 } else {
5712 SkDebugf(" ");
5713 }
5714 SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
5715 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
5716#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005717 if (approximatelyEqual(eStart, eEnd)) {
5718 eContour.toPath(simple);
5719 continue;
5720 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005721 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005722 }
5723 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005724 if (count == 0) {
5725 return;
5726 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005727 SkTDArray<int> sLink, eLink;
5728 sLink.setCount(count);
5729 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005730 SkTDArray<double> sBest, eBest;
5731 sBest.setCount(count);
5732 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005733 int rIndex;
5734 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005735 outer = runs[rIndex];
5736 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005737 const SkPoint& oStart = oContour.start();
5738 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005739 double dx = oEnd.fX - oStart.fX;
5740 double dy = oEnd.fY - oStart.fY;
5741 double dist = dx * dx + dy * dy;
5742 sBest[rIndex] = eBest[rIndex] = dist;
5743 sLink[rIndex] = eLink[rIndex] = rIndex;
5744 }
5745 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5746 outer = runs[rIndex];
5747 const Contour& oContour = contours[outer];
5748 const SkPoint& oStart = oContour.start();
5749 const SkPoint& oEnd = oContour.end();
5750 double bestStartDist = sBest[rIndex];
5751 double bestEndDist = eBest[rIndex];
5752 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5753 int inner = runs[iIndex];
5754 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005755 const SkPoint& iStart = iContour.start();
5756 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005757 double dx = iStart.fX - oStart.fX;
5758 double dy = iStart.fY - oStart.fY;
5759 double dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005760 if (bestStartDist > dist && sBest[iIndex] > dist) {
5761 sBest[iIndex] = bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005762 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005763 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005764 }
5765 dx = iEnd.fX - oStart.fX;
5766 dy = iEnd.fY - oStart.fY;
5767 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005768 if (bestStartDist > dist && eBest[iIndex] > dist) {
5769 eBest[iIndex] = bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005770 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005771 eLink[iIndex] = rIndex;
5772 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005773 dx = iStart.fX - oEnd.fX;
5774 dy = iStart.fY - oEnd.fY;
5775 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005776 if (bestEndDist > dist && sBest[iIndex] > dist) {
5777 sBest[iIndex] = bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005778 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005779 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005780 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005781 dx = iEnd.fX - oEnd.fX;
5782 dy = iEnd.fY - oEnd.fY;
5783 dist = dx * dx + dy * dy;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005784 if (bestEndDist > dist && eBest[iIndex] > dist) {
5785 eBest[iIndex] = bestEndDist = dist;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005786 eLink[iIndex] = ~rIndex;
5787 eLink[rIndex] = ~iIndex;
5788 }
5789 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005790 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005791#if DEBUG_ASSEMBLE
5792 for (rIndex = 0; rIndex < count; ++rIndex) {
5793 int s = sLink[rIndex];
5794 int e = eLink[rIndex];
5795 SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
5796 s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
caryclark@google.comf839c032012-10-26 21:03:50 +00005797 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005798#endif
5799 rIndex = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00005800 do {
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005801 bool forward = true;
5802 bool first = true;
5803 int sIndex = sLink[rIndex];
5804 SkASSERT(sIndex != INT_MAX);
5805 sLink[rIndex] = INT_MAX;
5806 int eIndex;
5807 if (sIndex < 0) {
5808 eIndex = sLink[~sIndex];
5809 sLink[~sIndex] = INT_MAX;
5810 } else {
5811 eIndex = eLink[sIndex];
5812 eLink[sIndex] = INT_MAX;
5813 }
5814 SkASSERT(eIndex != INT_MAX);
5815#if DEBUG_ASSEMBLE
5816 SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
5817 sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
5818 eIndex < 0 ? ~eIndex : eIndex);
5819#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005820 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005821 outer = runs[rIndex];
5822 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005823 if (first) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005824 first = false;
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005825 const SkPoint* startPtr = &contour.start();
caryclark@google.comf839c032012-10-26 21:03:50 +00005826 simple.deferredMove(startPtr[0]);
5827 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005828 if (forward) {
5829 contour.toPartialForward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00005830 } else {
5831 contour.toPartialBackward(simple);
caryclark@google.comf839c032012-10-26 21:03:50 +00005832 }
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005833#if DEBUG_ASSEMBLE
5834 SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
5835 eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
5836 sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
5837#endif
5838 if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005839 simple.close();
caryclark@google.comf839c032012-10-26 21:03:50 +00005840 break;
5841 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005842 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005843 eIndex = eLink[rIndex];
5844 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005845 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005846 if (eIndex >= 0) {
5847 SkASSERT(sLink[eIndex] == rIndex);
5848 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005849 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005850 SkASSERT(eLink[~eIndex] == ~rIndex);
5851 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005852 }
5853 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005854 eIndex = sLink[rIndex];
5855 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005856 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005857 if (eIndex >= 0) {
5858 SkASSERT(eLink[eIndex] == rIndex);
5859 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005860 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005861 SkASSERT(sLink[~eIndex] == ~rIndex);
5862 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005863 }
5864 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005865 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005866 if (rIndex < 0) {
5867 forward ^= 1;
5868 rIndex = ~rIndex;
5869 }
5870 } while (true);
5871 for (rIndex = 0; rIndex < count; ++rIndex) {
5872 if (sLink[rIndex] != INT_MAX) {
5873 break;
5874 }
5875 }
5876 } while (rIndex < count);
caryclark@google.come7bd5f42012-12-13 19:47:53 +00005877#if DEBUG_ASSEMBLE
5878 for (rIndex = 0; rIndex < count; ++rIndex) {
5879 SkASSERT(sLink[rIndex] == INT_MAX);
5880 SkASSERT(eLink[rIndex] == INT_MAX);
5881 }
5882#endif
caryclark@google.comf839c032012-10-26 21:03:50 +00005883}
5884
5885void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005886 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005887 result.reset();
5888 result.setFillType(SkPath::kEvenOdd_FillType);
5889 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005890
5891 // turn path into list of segments
5892 SkTArray<Contour> contours;
5893 // FIXME: add self-intersecting cubics' T values to segment
5894 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005895 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005896 SkTDArray<Contour*> contourList;
caryclark@google.com4eeda372012-12-06 21:47:48 +00005897 makeContourList(contours, contourList, false, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005898 Contour** currentPtr = contourList.begin();
5899 if (!currentPtr) {
5900 return;
5901 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005902 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005903 // find all intersections between segments
5904 do {
5905 Contour** nextPtr = currentPtr;
5906 Contour* current = *currentPtr++;
5907 Contour* next;
5908 do {
5909 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005910 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005911 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005912 // eat through coincident edges
caryclark@google.com4eeda372012-12-06 21:47:48 +00005913 coincidenceCheck(contourList, 0);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005914 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005915 sortSegments(contourList);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005916#if DEBUG_ACTIVE_SPANS
5917 debugShowActiveSpans(contourList);
5918#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005919 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005920 if (builder.xorMask() == kWinding_Mask
5921 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005922 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005923 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005924 SkPath temp;
5925 temp.setFillType(SkPath::kEvenOdd_FillType);
5926 PathWrapper assembled(temp);
5927 assemble(simple, assembled);
5928 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005929 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005930}
5931