blob: ced59bab055d04bd17a3d412a2d413f75321c861 [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.coma461ff02012-10-11 12:54:23 +000028#define PRECISE_T_SORT 1
caryclark@google.comfb51afb2012-10-19 15:54:16 +000029#define SORTABLE_CONTOURS 0 // set to 1 for old code that works most of the time
caryclark@google.comf839c032012-10-26 21:03:50 +000030#define PIN_ADD_T 0
caryclark@google.com0b7da432012-10-31 19:00:20 +000031#define TRY_ROTATE 1
caryclark@google.coma461ff02012-10-11 12:54:23 +000032
caryclark@google.com47580692012-07-23 12:14:49 +000033#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comf839c032012-10-26 21:03:50 +000034#define FORCE_RELEASE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000035
caryclark@google.comfb51afb2012-10-19 15:54:16 +000036#if FORCE_RELEASE || defined SK_RELEASE // set force release to 1 for multiple thread -- no debugging
caryclark@google.com47580692012-07-23 12:14:49 +000037
38const bool gRunTestsInOneThread = false;
39
40#define DEBUG_ACTIVE_SPANS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000041#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.com47580692012-07-23 12:14:49 +000042#define DEBUG_ADD_T_PAIR 0
caryclark@google.comc899ad92012-08-23 15:24:42 +000043#define DEBUG_ANGLE 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.com8dcf1142012-07-02 20:27:02 +000046#define DEBUG_MARK_DONE 0
caryclark@google.comf839c032012-10-26 21:03:50 +000047#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com47580692012-07-23 12:14:49 +000048#define DEBUG_SORT 0
caryclark@google.comafe56de2012-07-24 18:11:03 +000049#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000050#define DEBUG_WINDING 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000051
52#else
53
caryclark@google.com47580692012-07-23 12:14:49 +000054const bool gRunTestsInOneThread = true;
caryclark@google.comfa0588f2012-04-26 21:01:06 +000055
caryclark@google.comc91dfe42012-10-16 12:06:27 +000056#define DEBUG_ACTIVE_SPANS 1
caryclark@google.com6aea33f2012-10-09 14:11:58 +000057#define DEBUG_ADD_INTERSECTING_TS 1
58#define DEBUG_ADD_T_PAIR 1
caryclark@google.com3350c3c2012-08-24 15:24:36 +000059#define DEBUG_ANGLE 1
60#define DEBUG_CONCIDENT 1
caryclark@google.com534aa5b2012-08-02 20:08:21 +000061#define DEBUG_CROSS 0
caryclark@google.com3350c3c2012-08-24 15:24:36 +000062#define DEBUG_MARK_DONE 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000063#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com47580692012-07-23 12:14:49 +000064#define DEBUG_SORT 1
caryclark@google.comafe56de2012-07-24 18:11:03 +000065#define DEBUG_WIND_BUMP 0
caryclark@google.com47580692012-07-23 12:14:49 +000066#define DEBUG_WINDING 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000067
68#endif
69
caryclark@google.com6aea33f2012-10-09 14:11:58 +000070#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
caryclark@google.com027de222012-07-12 12:52:50 +000071
caryclark@google.comfa0588f2012-04-26 21:01:06 +000072#if DEBUG_DUMP
73static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000074// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000075static int gContourID;
76static int gSegmentID;
77#endif
78
caryclark@google.com8dcf1142012-07-02 20:27:02 +000079#ifndef DEBUG_TEST
80#define DEBUG_TEST 0
81#endif
82
caryclark@google.com32546db2012-08-31 20:55:07 +000083#define MAKE_CONST_LINE(line, pts) \
84 const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
85#define MAKE_CONST_QUAD(quad, pts) \
86 const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
87 {pts[2].fX, pts[2].fY}}
88#define MAKE_CONST_CUBIC(cubic, pts) \
89 const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
90 {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
91
caryclark@google.comfa0588f2012-04-26 21:01:06 +000092static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
93 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +000094 MAKE_CONST_LINE(aLine, a);
95 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +000096 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
97}
98
99static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
100 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000101 MAKE_CONST_QUAD(aQuad, a);
102 MAKE_CONST_LINE(bLine, b);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000103 return intersect(aQuad, bLine, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000104}
105
caryclark@google.com32546db2012-08-31 20:55:07 +0000106static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000107 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000108 MAKE_CONST_CUBIC(aCubic, a);
109 MAKE_CONST_LINE(bLine, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000110 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
111}
112
113static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
114 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000115 MAKE_CONST_QUAD(aQuad, a);
116 MAKE_CONST_QUAD(bQuad, b);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000117#define TRY_QUARTIC_SOLUTION 1
118#if TRY_QUARTIC_SOLUTION
119 intersect2(aQuad, bQuad, intersections);
120#else
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000121 intersect(aQuad, bQuad, intersections);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000122#endif
caryclark@google.com32546db2012-08-31 20:55:07 +0000123 return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000124}
125
126static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
127 Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000128 MAKE_CONST_CUBIC(aCubic, a);
129 MAKE_CONST_CUBIC(bCubic, b);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000130 intersect(aCubic, bCubic, intersections);
131 return intersections.fUsed;
132}
133
134static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
135 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000136 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000137 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
138}
139
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000140static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
141 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000142 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000143 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
144}
145
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000146static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
147 SkScalar y, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000148 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000149 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
150}
151
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000152static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
153 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000154 MAKE_CONST_LINE(aLine, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000155 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
156}
157
158static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
159 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000160 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000161 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
162}
163
164static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
165 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000166 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000167 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000168}
169
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000170static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
171 SkScalar , SkScalar , bool , Intersections& ) = {
172 NULL,
173 VLineIntersect,
174 VQuadIntersect,
175 VCubicIntersect
176};
177
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000178static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000179 MAKE_CONST_LINE(line, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000180 double x, y;
181 xy_at_t(line, t, x, y);
182 out->fX = SkDoubleToScalar(x);
183 out->fY = SkDoubleToScalar(y);
184}
185
186static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000187 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000188 double x, y;
189 xy_at_t(quad, t, x, y);
190 out->fX = SkDoubleToScalar(x);
191 out->fY = SkDoubleToScalar(y);
192}
193
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000194static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
195 MAKE_CONST_QUAD(quad, a);
196 xy_at_t(quad, t, out->x, out->y);
197}
198
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000199static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000200 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000201 double x, y;
202 xy_at_t(cubic, t, x, y);
203 out->fX = SkDoubleToScalar(x);
204 out->fY = SkDoubleToScalar(y);
205}
206
207static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
208 NULL,
209 LineXYAtT,
210 QuadXYAtT,
211 CubicXYAtT
212};
213
214static SkScalar LineXAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000215 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000216 double x;
217 xy_at_t(aLine, t, x, *(double*) 0);
218 return SkDoubleToScalar(x);
219}
220
221static SkScalar QuadXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000222 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000223 double x;
224 xy_at_t(quad, t, x, *(double*) 0);
225 return SkDoubleToScalar(x);
226}
227
228static SkScalar CubicXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000229 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000230 double x;
231 xy_at_t(cubic, t, x, *(double*) 0);
232 return SkDoubleToScalar(x);
233}
234
235static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
236 NULL,
237 LineXAtT,
238 QuadXAtT,
239 CubicXAtT
240};
241
242static SkScalar LineYAtT(const SkPoint a[2], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000243 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000244 double y;
245 xy_at_t(aLine, t, *(double*) 0, y);
246 return SkDoubleToScalar(y);
247}
248
249static SkScalar QuadYAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000250 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000251 double y;
252 xy_at_t(quad, t, *(double*) 0, y);
253 return SkDoubleToScalar(y);
254}
255
256static SkScalar CubicYAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000257 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000258 double y;
259 xy_at_t(cubic, t, *(double*) 0, y);
260 return SkDoubleToScalar(y);
261}
262
263static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
264 NULL,
265 LineYAtT,
266 QuadYAtT,
267 CubicYAtT
268};
269
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000270static SkScalar LineDXAtT(const SkPoint a[2], double ) {
271 return a[1].fX - a[0].fX;
272}
273
274static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000275 MAKE_CONST_QUAD(quad, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000276 double x;
277 dxdy_at_t(quad, t, x, *(double*) 0);
278 return SkDoubleToScalar(x);
279}
280
281static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000282 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000283 double x;
284 dxdy_at_t(cubic, t, x, *(double*) 0);
285 return SkDoubleToScalar(x);
286}
287
288static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
289 NULL,
290 LineDXAtT,
291 QuadDXAtT,
292 CubicDXAtT
293};
294
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000295static void LineSubDivide(const SkPoint a[2], double startT, double endT,
296 SkPoint sub[2]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000297 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000298 _Line dst;
299 sub_divide(aLine, startT, endT, dst);
300 sub[0].fX = SkDoubleToScalar(dst[0].x);
301 sub[0].fY = SkDoubleToScalar(dst[0].y);
302 sub[1].fX = SkDoubleToScalar(dst[1].x);
303 sub[1].fY = SkDoubleToScalar(dst[1].y);
304}
305
306static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
307 SkPoint sub[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000308 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000309 Quadratic dst;
310 sub_divide(aQuad, startT, endT, dst);
311 sub[0].fX = SkDoubleToScalar(dst[0].x);
312 sub[0].fY = SkDoubleToScalar(dst[0].y);
313 sub[1].fX = SkDoubleToScalar(dst[1].x);
314 sub[1].fY = SkDoubleToScalar(dst[1].y);
315 sub[2].fX = SkDoubleToScalar(dst[2].x);
316 sub[2].fY = SkDoubleToScalar(dst[2].y);
317}
318
319static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
320 SkPoint sub[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000321 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000322 Cubic dst;
323 sub_divide(aCubic, startT, endT, dst);
324 sub[0].fX = SkDoubleToScalar(dst[0].x);
325 sub[0].fY = SkDoubleToScalar(dst[0].y);
326 sub[1].fX = SkDoubleToScalar(dst[1].x);
327 sub[1].fY = SkDoubleToScalar(dst[1].y);
328 sub[2].fX = SkDoubleToScalar(dst[2].x);
329 sub[2].fY = SkDoubleToScalar(dst[2].y);
330 sub[3].fX = SkDoubleToScalar(dst[3].x);
331 sub[3].fY = SkDoubleToScalar(dst[3].y);
332}
333
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000334static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
335 SkPoint []) = {
336 NULL,
337 LineSubDivide,
338 QuadSubDivide,
339 CubicSubDivide
340};
341
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000342static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000343 _Line sub) {
344 MAKE_CONST_LINE(aLine, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000345 _Line dst;
346 sub_divide(aLine, startT, endT, dst);
347 sub[0] = dst[0];
348 sub[1] = dst[1];
349}
350
351static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000352 Quadratic sub) {
353 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000354 Quadratic dst;
355 sub_divide(aQuad, startT, endT, dst);
356 sub[0] = dst[0];
357 sub[1] = dst[1];
358 sub[2] = dst[2];
359}
360
361static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
caryclark@google.com32546db2012-08-31 20:55:07 +0000362 Cubic sub) {
363 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000364 Cubic dst;
365 sub_divide(aCubic, startT, endT, dst);
366 sub[0] = dst[0];
367 sub[1] = dst[1];
368 sub[2] = dst[2];
369 sub[3] = dst[3];
370}
371
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000372#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000373static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
374 SkRect& bounds) {
375 SkPoint dst[3];
376 QuadSubDivide(a, startT, endT, dst);
377 bounds.fLeft = bounds.fRight = dst[0].fX;
378 bounds.fTop = bounds.fBottom = dst[0].fY;
379 for (int index = 1; index < 3; ++index) {
380 bounds.growToInclude(dst[index].fX, dst[index].fY);
381 }
382}
383
384static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
385 SkRect& bounds) {
386 SkPoint dst[4];
387 CubicSubDivide(a, startT, endT, dst);
388 bounds.fLeft = bounds.fRight = dst[0].fX;
389 bounds.fTop = bounds.fBottom = dst[0].fY;
390 for (int index = 1; index < 4; ++index) {
391 bounds.growToInclude(dst[index].fX, dst[index].fY);
392 }
393}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000394#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395
caryclark@google.com15fa1382012-05-07 20:49:36 +0000396static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000397 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000398 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000399 Quadratic dst;
400 int order = reduceOrder(aQuad, dst);
caryclark@google.com24bec792012-08-20 12:43:57 +0000401 if (order == 2) { // quad became line
402 for (int index = 0; index < order; ++index) {
403 SkPoint* pt = reducePts.append();
404 pt->fX = SkDoubleToScalar(dst[index].x);
405 pt->fY = SkDoubleToScalar(dst[index].y);
406 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000407 }
408 return (SkPath::Verb) (order - 1);
409}
410
411static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
412 SkTDArray<SkPoint>& reducePts) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000413 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000414 Cubic dst;
415 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.com24bec792012-08-20 12:43:57 +0000416 if (order == 2 || order == 3) { // cubic became line or quad
417 for (int index = 0; index < order; ++index) {
418 SkPoint* pt = reducePts.append();
419 pt->fX = SkDoubleToScalar(dst[index].x);
420 pt->fY = SkDoubleToScalar(dst[index].y);
421 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000422 }
423 return (SkPath::Verb) (order - 1);
424}
425
caryclark@google.com15fa1382012-05-07 20:49:36 +0000426static bool QuadIsLinear(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000427 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000428 return isLinear(aQuad, 0, 2);
429}
430
431static bool CubicIsLinear(const SkPoint a[4]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000432 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000433 return isLinear(aCubic, 0, 3);
434}
435
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000436static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000437 MAKE_CONST_LINE(aLine, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000438 double x[2];
439 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000440 xy_at_t(aLine, endT, x[1], *(double*) 0);
441 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000442}
443
444static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000445 MAKE_CONST_QUAD(aQuad, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000446 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000447}
448
449static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000450 MAKE_CONST_CUBIC(aCubic, a);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000451 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000452}
453
454static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
455 NULL,
456 LineLeftMost,
457 QuadLeftMost,
458 CubicLeftMost
459};
460
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000461#if 0 // currently unused
caryclark@google.com235f56a2012-09-14 14:19:30 +0000462static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
463 Intersections& intersections) {
464 MAKE_CONST_QUAD(aQuad, a);
465 MAKE_CONST_LINE(bLine, b);
466 return intersectRay(aQuad, bLine, intersections);
467}
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000468#endif
469
470static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
471 Intersections& intersections) {
472 MAKE_CONST_QUAD(aQuad, a);
473 return intersectRay(aQuad, bLine, intersections);
474}
caryclark@google.com235f56a2012-09-14 14:19:30 +0000475
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000476class Segment;
477
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000478struct Span {
479 Segment* fOther;
480 mutable SkPoint fPt; // lazily computed as needed
481 double fT;
482 double fOtherT; // value at fOther[fOtherIndex].fT
483 int fOtherIndex; // can't be used during intersection
484 int fWindSum; // accumulated from contours surrounding this one
485 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
486 int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
487 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000488 bool fUnsortableStart; // set when start is part of an unsortable pair
489 bool fUnsortableEnd; // set when end is part of an unsortable pair
caryclark@google.comf839c032012-10-26 21:03:50 +0000490 bool fTiny; // if set, span may still be considered once for edge following
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000491};
492
caryclark@google.com15fa1382012-05-07 20:49:36 +0000493// sorting angles
494// given angles of {dx dy ddx ddy dddx dddy} sort them
495class Angle {
496public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000497 // FIXME: this is bogus for quads and cubics
498 // if the quads and cubics' line from end pt to ctrl pt are coincident,
499 // there's no obvious way to determine the curve ordering from the
500 // derivatives alone. In particular, if one quadratic's coincident tangent
501 // is longer than the other curve, the final control point can place the
502 // longer curve on either side of the shorter one.
503 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
504 // may provide some help, but nothing has been figured out yet.
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000505
caryclark@google.com32546db2012-08-31 20:55:07 +0000506 /*(
507 for quads and cubics, set up a parameterized line (e.g. LineParameters )
508 for points [0] to [1]. See if point [2] is on that line, or on one side
509 or the other. If it both quads' end points are on the same side, choose
510 the shorter tangent. If the tangents are equal, choose the better second
511 tangent angle
skia.committer@gmail.com4f55d392012-09-01 02:00:58 +0000512
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000513 maybe I could set up LineParameters lazily
caryclark@google.com32546db2012-08-31 20:55:07 +0000514 */
caryclark@google.com15fa1382012-05-07 20:49:36 +0000515 bool operator<(const Angle& rh) const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000516 double y = dy();
517 double ry = rh.dy();
518 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
519 return y < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000520 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000521 double x = dx();
522 double rx = rh.dx();
523 if (y == 0 && ry == 0 && x * rx < 0) {
524 return x < rx;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000525 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000526 double x_ry = x * ry;
527 double rx_y = rx * y;
528 double cmp = x_ry - rx_y;
caryclark@google.comc899ad92012-08-23 15:24:42 +0000529 if (!approximately_zero(cmp)) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000530 return cmp < 0;
531 }
caryclark@google.comd1688742012-09-18 20:08:37 +0000532 if (approximately_zero(x_ry) && approximately_zero(rx_y)
533 && !approximately_zero_squared(cmp)) {
534 return cmp < 0;
535 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000536 // at this point, the initial tangent line is coincident
537 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
538 // FIXME: running demo will trigger this assertion
539 // (don't know if commenting out will trigger further assertion or not)
540 // commenting it out allows demo to run in release, though
541 // SkASSERT(fSide != rh.fSide);
542 return fSide < rh.fSide;
543 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000544 // see if either curve can be lengthened and try the tangent compare again
545 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
546 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
547 Angle longer = *this;
548 Angle rhLonger = rh;
549 if (longer.lengthen() | rhLonger.lengthen()) {
550 return longer < rhLonger;
551 }
caryclark@google.coma461ff02012-10-11 12:54:23 +0000552 // what if we extend in the other direction?
553 longer = *this;
554 rhLonger = rh;
555 if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
556 return longer < rhLonger;
557 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000558 }
caryclark@google.com185c7c42012-10-19 18:26:24 +0000559 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y))
560 || (rh.fVerb == SkPath::kLine_Verb && approximately_zero(rx) && approximately_zero(ry))) {
561 // See general unsortable comment below. This case can happen when
562 // one line has a non-zero change in t but no change in x and y.
563 fUnsortable = true;
564 rh.fUnsortable = true;
565 return this < &rh; // even with no solution, return a stable sort
566 }
caryclark@google.com235f56a2012-09-14 14:19:30 +0000567 SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
568 SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
skia.committer@gmail.comc1ad0222012-09-19 02:01:47 +0000569 // FIXME: until I can think of something better, project a ray from the
caryclark@google.comd1688742012-09-18 20:08:37 +0000570 // end of the shorter tangent to midway between the end points
571 // through both curves and use the resulting angle to sort
caryclark@google.com235f56a2012-09-14 14:19:30 +0000572 // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
573 double len = fTangent1.normalSquared();
574 double rlen = rh.fTangent1.normalSquared();
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000575 _Line ray;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000576 Intersections i, ri;
caryclark@google.comd1688742012-09-18 20:08:37 +0000577 int roots, rroots;
578 bool flip = false;
579 do {
580 const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
581 double midX = (q[0].x + q[2].x) / 2;
582 double midY = (q[0].y + q[2].y) / 2;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000583 ray[0] = q[1];
584 ray[1].x = midX;
585 ray[1].y = midY;
caryclark@google.comd1688742012-09-18 20:08:37 +0000586 SkASSERT(ray[0] != ray[1]);
587 roots = QuadRayIntersect(fPts, ray, i);
588 rroots = QuadRayIntersect(rh.fPts, ray, ri);
589 } while ((roots == 0 || rroots == 0) && (flip ^= true));
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000590 if (roots == 0 || rroots == 0) {
591 // FIXME: we don't have a solution in this case. The interim solution
592 // is to mark the edges as unsortable, exclude them from this and
593 // future computations, and allow the returned path to be fragmented
594 fUnsortable = true;
595 rh.fUnsortable = true;
596 return this < &rh; // even with no solution, return a stable sort
597 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000598 _Point loc;
599 double best = SK_ScalarInfinity;
600 double dx, dy, dist;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000601 int index;
602 for (index = 0; index < roots; ++index) {
603 QuadXYAtT(fPts, i.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000604 dx = loc.x - ray[0].x;
605 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000606 dist = dx * dx + dy * dy;
607 if (best > dist) {
608 best = dist;
609 }
610 }
611 for (index = 0; index < rroots; ++index) {
612 QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000613 dx = loc.x - ray[0].x;
614 dy = loc.y - ray[0].y;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000615 dist = dx * dx + dy * dy;
616 if (best > dist) {
617 return fSide < 0;
618 }
619 }
620 return fSide > 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000621 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000622
caryclark@google.com47580692012-07-23 12:14:49 +0000623 double dx() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000624 return fTangent1.dx();
caryclark@google.com47580692012-07-23 12:14:49 +0000625 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000626
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000627 double dy() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000628 return fTangent1.dy();
caryclark@google.com7db7c6b2012-07-27 21:22:25 +0000629 }
630
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000631 int end() const {
632 return fEnd;
633 }
634
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000635 bool isHorizontal() const {
caryclark@google.com235f56a2012-09-14 14:19:30 +0000636 return dy() == 0 && fVerb == SkPath::kLine_Verb;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000637 }
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000638
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000639 bool lengthen() {
640 int newEnd = fEnd;
641 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
642 fEnd = newEnd;
643 setSpans();
644 return true;
645 }
646 return false;
647 }
648
caryclark@google.coma461ff02012-10-11 12:54:23 +0000649 bool reverseLengthen() {
650 if (fReversed) {
651 return false;
652 }
653 int newEnd = fStart;
654 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
655 fEnd = newEnd;
656 fReversed = true;
657 setSpans();
658 return true;
659 }
660 return false;
661 }
662
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000663 void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000664 int start, int end, const SkTDArray<Span>& spans) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000665 fSegment = segment;
666 fStart = start;
667 fEnd = end;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000668 fPts = orig;
669 fVerb = verb;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000670 fSpans = &spans;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000671 fReversed = false;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000672 fUnsortable = false;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000673 setSpans();
674 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000675
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000676 void setSpans() {
677 double startT = (*fSpans)[fStart].fT;
678 double endT = (*fSpans)[fEnd].fT;
679 switch (fVerb) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000680 case SkPath::kLine_Verb:
681 _Line l;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000682 LineSubDivideHD(fPts, startT, endT, l);
caryclark@google.com32546db2012-08-31 20:55:07 +0000683 // OPTIMIZATION: for pure line compares, we never need fTangent1.c
684 fTangent1.lineEndPoints(l);
caryclark@google.comf839c032012-10-26 21:03:50 +0000685 fUnsortable = dx() == 0 && dy() == 0;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000686 fSide = 0;
caryclark@google.com32546db2012-08-31 20:55:07 +0000687 break;
688 case SkPath::kQuad_Verb:
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000689 QuadSubDivideHD(fPts, startT, endT, fQ);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000690 fTangent1.quadEndPoints(fQ, 0, 1);
691 fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000692 break;
693 case SkPath::kCubic_Verb:
694 Cubic c;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000695 CubicSubDivideHD(fPts, startT, endT, c);
caryclark@google.com32546db2012-08-31 20:55:07 +0000696 fTangent1.cubicEndPoints(c, 0, 1);
caryclark@google.com235f56a2012-09-14 14:19:30 +0000697 fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
caryclark@google.com32546db2012-08-31 20:55:07 +0000698 break;
caryclark@google.com235f56a2012-09-14 14:19:30 +0000699 default:
700 SkASSERT(0);
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000701 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000702 if (fUnsortable) {
703 return;
704 }
705 SkASSERT(fStart != fEnd);
706 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro?
707 for (int index = fStart; index != fEnd; index += step) {
708 if ((*fSpans)[index].fUnsortableStart) {
709 fUnsortable = true;
710 return;
711 }
712 if (index != fStart && (*fSpans)[index].fUnsortableEnd) {
713 fUnsortable = true;
714 return;
715 }
716 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +0000717 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000718
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000719 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000720 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000721 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000722
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000723 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000724 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000725 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000726
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000727 const SkTDArray<Span>* spans() const {
728 return fSpans;
729 }
730
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000731 int start() const {
732 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000733 }
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +0000734
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000735 bool unsortable() const {
736 return fUnsortable;
737 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000738
caryclark@google.comc899ad92012-08-23 15:24:42 +0000739#if DEBUG_ANGLE
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000740 const SkPoint* pts() const {
741 return fPts;
742 }
743
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000744 SkPath::Verb verb() const {
745 return fVerb;
746 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +0000747
caryclark@google.comc899ad92012-08-23 15:24:42 +0000748 void debugShow(const SkPoint& a) const {
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000749 SkDebugf(" d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
caryclark@google.comc899ad92012-08-23 15:24:42 +0000750 }
751#endif
752
caryclark@google.com15fa1382012-05-07 20:49:36 +0000753private:
caryclark@google.com235f56a2012-09-14 14:19:30 +0000754 const SkPoint* fPts;
755 Quadratic fQ;
756 SkPath::Verb fVerb;
757 double fSide;
758 LineParameters fTangent1;
caryclark@google.com6aea33f2012-10-09 14:11:58 +0000759 const SkTDArray<Span>* fSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000760 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000761 int fStart;
762 int fEnd;
caryclark@google.coma461ff02012-10-11 12:54:23 +0000763 bool fReversed;
caryclark@google.comc91dfe42012-10-16 12:06:27 +0000764 mutable bool fUnsortable; // this alone is editable by the less than operator
caryclark@google.com15fa1382012-05-07 20:49:36 +0000765};
766
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000767// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000768struct Bounds : public SkRect {
769 static bool Intersects(const Bounds& a, const Bounds& b) {
770 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
771 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
772 }
773
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000774 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
775 if (left < fLeft) {
776 fLeft = left;
777 }
778 if (top < fTop) {
779 fTop = top;
780 }
781 if (right > fRight) {
782 fRight = right;
783 }
784 if (bottom > fBottom) {
785 fBottom = bottom;
786 }
787 }
788
789 void add(const Bounds& toAdd) {
790 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
791 }
792
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000793 bool isEmpty() {
794 return fLeft > fRight || fTop > fBottom
caryclark@google.com235f56a2012-09-14 14:19:30 +0000795 || (fLeft == fRight && fTop == fBottom)
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000796 || isnan(fLeft) || isnan(fRight)
797 || isnan(fTop) || isnan(fBottom);
798 }
799
800 void setCubicBounds(const SkPoint a[4]) {
801 _Rect dRect;
caryclark@google.com32546db2012-08-31 20:55:07 +0000802 MAKE_CONST_CUBIC(cubic, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000803 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000804 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
805 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000806 }
807
808 void setQuadBounds(const SkPoint a[3]) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000809 MAKE_CONST_QUAD(quad, a);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000810 _Rect dRect;
811 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000812 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
813 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000814 }
815};
816
caryclark@google.com2ddff932012-08-07 21:25:27 +0000817static bool useInnerWinding(int outerWinding, int innerWinding) {
818 SkASSERT(outerWinding != innerWinding);
819 int absOut = abs(outerWinding);
820 int absIn = abs(innerWinding);
821 bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
822 if (outerWinding * innerWinding < 0) {
823#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +0000824 SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +0000825 outerWinding, innerWinding, result ? "true" : "false");
826#endif
827 }
828 return result;
829}
830
caryclark@google.com235f56a2012-09-14 14:19:30 +0000831static const bool opLookup[][2][2] = {
832 // ==0 !=0
833 // b a b a
834 {{true , false}, {false, true }}, // a - b
835 {{false, false}, {true , true }}, // a & b
836 {{true , true }, {false, false}}, // a | b
837 {{true , true }, {true , true }}, // a ^ b
838};
839
840static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
841 return opLookup[op][otherNonZero][angleIsOp];
842}
843
caryclark@google.comf839c032012-10-26 21:03:50 +0000844
845// wrap path to keep track of whether the contour is initialized and non-empty
846class PathWrapper {
847public:
848 PathWrapper(SkPath& path)
849 : fPathPtr(&path)
850 {
851 init();
852 }
853
854 void close() {
855 if (!fHasMove) {
856 return;
857 }
858 bool callClose = isClosed();
859 lineTo();
860 if (fEmpty) {
861 return;
862 }
863 if (callClose) {
864 #if DEBUG_PATH_CONSTRUCTION
865 SkDebugf("path.close();\n");
866 #endif
867 fPathPtr->close();
868 }
869 init();
870 }
871
872 void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
873 lineTo();
874 moveTo();
875#if DEBUG_PATH_CONSTRUCTION
876 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
877 pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
878#endif
879 fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3.fX, pt3.fY);
880 fDefer[0] = fDefer[1] = pt3;
881 fEmpty = false;
882 }
883
884 void deferredLine(const SkPoint& pt) {
885 if (pt == fDefer[1]) {
886 return;
887 }
888 if (changedSlopes(pt)) {
889 lineTo();
890 fDefer[0] = fDefer[1];
891 }
892 fDefer[1] = pt;
893 }
894
895 void deferredMove(const SkPoint& pt) {
896 fMoved = true;
897 fHasMove = true;
898 fEmpty = true;
899 fDefer[0] = fDefer[1] = pt;
900 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000901
caryclark@google.comf839c032012-10-26 21:03:50 +0000902 void deferredMoveLine(const SkPoint& pt) {
903 if (!fHasMove) {
904 deferredMove(pt);
905 }
906 deferredLine(pt);
907 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000908
caryclark@google.comf839c032012-10-26 21:03:50 +0000909 bool hasMove() const {
910 return fHasMove;
911 }
912
913 void init() {
914 fEmpty = true;
915 fHasMove = false;
916 fMoved = false;
917 }
918
919 bool isClosed() const {
920 return !fEmpty && fFirstPt == fDefer[1];
921 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000922
caryclark@google.comf839c032012-10-26 21:03:50 +0000923 void lineTo() {
924 if (fDefer[0] == fDefer[1]) {
925 return;
926 }
927 moveTo();
928 fEmpty = false;
929#if DEBUG_PATH_CONSTRUCTION
930 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
931#endif
932 fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
933 fDefer[0] = fDefer[1];
934 }
935
936 const SkPath* nativePath() const {
937 return fPathPtr;
938 }
939
940 void quadTo(const SkPoint& pt1, const SkPoint& pt2) {
941 lineTo();
942 moveTo();
943#if DEBUG_PATH_CONSTRUCTION
944 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
945 pt1.fX, pt1.fY, pt2.fX, pt2.fY);
946#endif
947 fPathPtr->quadTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY);
948 fDefer[0] = fDefer[1] = pt2;
949 fEmpty = false;
950 }
951
952protected:
953 bool changedSlopes(const SkPoint& pt) const {
954 if (fDefer[0] == fDefer[1]) {
955 return false;
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +0000956 }
caryclark@google.comf839c032012-10-26 21:03:50 +0000957 SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
958 SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
959 SkScalar lineDx = pt.fX - fDefer[1].fX;
960 SkScalar lineDy = pt.fY - fDefer[1].fY;
961 return deferDx * lineDy != deferDy * lineDx;
962 }
963
964 void moveTo() {
965 if (!fMoved) {
966 return;
967 }
968 fFirstPt = fDefer[0];
969#if DEBUG_PATH_CONSTRUCTION
970 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
971#endif
972 fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
973 fMoved = false;
974 }
975
976private:
977 SkPath* fPathPtr;
978 SkPoint fDefer[2];
979 SkPoint fFirstPt;
980 bool fEmpty;
981 bool fHasMove;
982 bool fMoved;
983};
984
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000985class Segment {
986public:
987 Segment() {
988#if DEBUG_DUMP
989 fID = ++gSegmentID;
990#endif
991 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +0000992
caryclark@google.comfb51afb2012-10-19 15:54:16 +0000993 bool operator<(const Segment& rh) const {
994 return fBounds.fTop < rh.fBounds.fTop;
995 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000996
caryclark@google.com9764cc62012-07-12 19:29:45 +0000997 bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
998 if (activeAngleInner(index, done, angles)) {
999 return true;
1000 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001001 double referenceT = fTs[index].fT;
1002 int lesser = index;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001003 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001004 if (activeAngleOther(lesser, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001005 return true;
1006 }
1007 }
1008 do {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001009 if (activeAngleOther(index, done, angles)) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001010 return true;
1011 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001012 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001013 return false;
1014 }
1015
caryclark@google.com9764cc62012-07-12 19:29:45 +00001016 bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001017 Span* span = &fTs[index];
1018 Segment* other = span->fOther;
1019 int oIndex = span->fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00001020 return other->activeAngleInner(oIndex, done, angles);
1021 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001022
caryclark@google.com9764cc62012-07-12 19:29:45 +00001023 bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001024 int next = nextExactSpan(index, 1);
caryclark@google.com9764cc62012-07-12 19:29:45 +00001025 if (next > 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001026 const Span& upSpan = fTs[index];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001027 if (upSpan.fWindValue) {
1028 addAngle(angles, index, next);
caryclark@google.comf839c032012-10-26 21:03:50 +00001029 if (upSpan.fDone || upSpan.fUnsortableEnd) {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001030 done++;
1031 } else if (upSpan.fWindSum != SK_MinS32) {
1032 return true;
1033 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001034 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001035 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00001036 int prev = nextExactSpan(index, -1);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001037 // edge leading into junction
caryclark@google.com9764cc62012-07-12 19:29:45 +00001038 if (prev >= 0) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00001039 const Span& downSpan = fTs[prev];
caryclark@google.com210acaf2012-07-12 21:05:13 +00001040 if (downSpan.fWindValue) {
1041 addAngle(angles, index, prev);
1042 if (downSpan.fDone) {
1043 done++;
1044 } else if (downSpan.fWindSum != SK_MinS32) {
1045 return true;
1046 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00001047 }
1048 }
1049 return false;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001050 }
1051
caryclark@google.comf839c032012-10-26 21:03:50 +00001052 void activeLeftTop(SkPoint& result) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001053 SkASSERT(!done());
1054 int count = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001055 result.fY = SK_ScalarMax;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001056 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00001057 bool lastUnsortable = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001058 for (int index = 0; index < count; ++index) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001059 const Span& span = fTs[index];
1060 if (span.fUnsortableStart | lastUnsortable) {
1061 goto next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001062 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001063 if (!span.fDone | !lastDone) {
1064 const SkPoint& xy = xyAtT(index);
1065 if (result.fY < xy.fY) {
1066 goto next;
1067 }
1068 if (result.fY == xy.fY && result.fX < xy.fX) {
1069 goto next;
1070 }
1071 result = xy;
1072 }
1073 next:
1074 lastDone = span.fDone;
1075 lastUnsortable = span.fUnsortableEnd;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001076 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001077 SkASSERT(result.fY < SK_ScalarMax);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001078 }
1079
1080 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001081 SkASSERT(start != end);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001082 Angle* angle = angles.append();
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001083#if DEBUG_ANGLE
1084 if (angles.count() > 1) {
1085 SkPoint angle0Pt, newPt;
1086 (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
1087 (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
1088 (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
1089 SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
1090 SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
1091 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001092#endif
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001093 angle->set(fPts, fVerb, this, start, end, fTs);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001094 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001095
caryclark@google.com2ddff932012-08-07 21:25:27 +00001096 void addCancelOutsides(double tStart, double oStart, Segment& other,
caryclark@google.comcc905052012-07-25 20:59:42 +00001097 double oEnd) {
1098 int tIndex = -1;
1099 int tCount = fTs.count();
1100 int oIndex = -1;
1101 int oCount = other.fTs.count();
caryclark@google.comcc905052012-07-25 20:59:42 +00001102 do {
1103 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001104 } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001105 int tIndexStart = tIndex;
1106 do {
1107 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001108 } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
caryclark@google.comcc905052012-07-25 20:59:42 +00001109 int oIndexStart = oIndex;
1110 double nextT;
1111 do {
1112 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001113 } while (nextT < 1 && approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001114 double oNextT;
1115 do {
1116 oNextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001117 } while (oNextT < 1 && approximately_negative(oNextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001118 // at this point, spans before and after are at:
1119 // fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
1120 // if tIndexStart == 0, no prior span
1121 // if nextT == 1, no following span
rmistry@google.comd6176b02012-08-23 18:14:13 +00001122
caryclark@google.comcc905052012-07-25 20:59:42 +00001123 // advance the span with zero winding
1124 // if the following span exists (not past the end, non-zero winding)
1125 // connect the two edges
1126 if (!fTs[tIndexStart].fWindValue) {
1127 if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
1128 #if DEBUG_CONCIDENT
1129 SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1130 __FUNCTION__, fID, other.fID, tIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001131 fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
1132 xyAtT(tIndexStart).fY);
caryclark@google.comcc905052012-07-25 20:59:42 +00001133 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001134 addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001135 }
1136 if (nextT < 1 && fTs[tIndex].fWindValue) {
1137 #if DEBUG_CONCIDENT
1138 SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1139 __FUNCTION__, fID, other.fID, tIndex,
1140 fTs[tIndex].fT, xyAtT(tIndex).fX,
1141 xyAtT(tIndex).fY);
1142 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00001143 addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001144 }
1145 } else {
1146 SkASSERT(!other.fTs[oIndexStart].fWindValue);
1147 if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
1148 #if DEBUG_CONCIDENT
1149 SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1150 __FUNCTION__, fID, other.fID, oIndexStart - 1,
caryclark@google.com27c449a2012-07-27 18:26:38 +00001151 other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
1152 other.xyAtT(oIndexStart).fY);
1153 other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
caryclark@google.comcc905052012-07-25 20:59:42 +00001154 #endif
caryclark@google.comcc905052012-07-25 20:59:42 +00001155 }
1156 if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
1157 #if DEBUG_CONCIDENT
1158 SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
1159 __FUNCTION__, fID, other.fID, oIndex,
1160 other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
1161 other.xyAtT(oIndex).fY);
1162 other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
1163 #endif
1164 }
1165 }
1166 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001167
caryclark@google.comcc905052012-07-25 20:59:42 +00001168 void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
1169 double oEnd) {
1170 // walk this to outsideTs[0]
1171 // walk other to outsideTs[1]
1172 // if either is > 0, add a pointer to the other, copying adjacent winding
1173 int tIndex = -1;
1174 int oIndex = -1;
1175 double tStart = outsideTs[0];
1176 double oStart = outsideTs[1];
1177 do {
1178 ++tIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001179 } while (!approximately_negative(tStart - fTs[tIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001180 do {
1181 ++oIndex;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001182 } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001183 if (tIndex > 0 || oIndex > 0) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001184 addTPair(tStart, other, oStart, false);
caryclark@google.comcc905052012-07-25 20:59:42 +00001185 }
1186 tStart = fTs[tIndex].fT;
1187 oStart = other.fTs[oIndex].fT;
1188 do {
1189 double nextT;
1190 do {
1191 nextT = fTs[++tIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001192 } while (approximately_negative(nextT - tStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001193 tStart = nextT;
1194 do {
1195 nextT = other.fTs[++oIndex].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001196 } while (approximately_negative(nextT - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001197 oStart = nextT;
1198 if (tStart == 1 && oStart == 1) {
1199 break;
1200 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00001201 addTPair(tStart, other, oStart, false);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001202 } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
caryclark@google.comcc905052012-07-25 20:59:42 +00001203 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001204
caryclark@google.com235f56a2012-09-14 14:19:30 +00001205 void addCubic(const SkPoint pts[4], bool operand) {
1206 init(pts, SkPath::kCubic_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001207 fBounds.setCubicBounds(pts);
1208 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001209
caryclark@google.comf839c032012-10-26 21:03:50 +00001210 /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001211 SkPoint edge[4];
caryclark@google.comf839c032012-10-26 21:03:50 +00001212 const SkPoint* ePtr;
1213 int lastT = fTs.count() - 1;
1214 if (lastT < 0 || (start == 0 && end == lastT) || (start == lastT && end == 0)) {
1215 ePtr = fPts;
1216 } else {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001217 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.comf839c032012-10-26 21:03:50 +00001218 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
1219 ePtr = edge;
1220 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001221 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001222 bool reverse = ePtr == fPts && start != 0;
1223 if (reverse) {
1224 path.deferredMoveLine(ePtr[fVerb]);
1225 switch (fVerb) {
1226 case SkPath::kLine_Verb:
1227 path.deferredLine(ePtr[0]);
1228 break;
1229 case SkPath::kQuad_Verb:
1230 path.quadTo(ePtr[1], ePtr[0]);
1231 break;
1232 case SkPath::kCubic_Verb:
1233 path.cubicTo(ePtr[2], ePtr[1], ePtr[0]);
1234 break;
1235 default:
1236 SkASSERT(0);
1237 }
1238 // return ePtr[0];
1239 } else {
1240 path.deferredMoveLine(ePtr[0]);
1241 switch (fVerb) {
1242 case SkPath::kLine_Verb:
1243 path.deferredLine(ePtr[1]);
1244 break;
1245 case SkPath::kQuad_Verb:
1246 path.quadTo(ePtr[1], ePtr[2]);
1247 break;
1248 case SkPath::kCubic_Verb:
1249 path.cubicTo(ePtr[1], ePtr[2], ePtr[3]);
1250 break;
1251 default:
1252 SkASSERT(0);
1253 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001254 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001255 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001256 // return ePtr[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001257 }
1258
caryclark@google.com235f56a2012-09-14 14:19:30 +00001259 void addLine(const SkPoint pts[2], bool operand) {
1260 init(pts, SkPath::kLine_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001261 fBounds.set(pts, 2);
1262 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001263
caryclark@google.comf839c032012-10-26 21:03:50 +00001264#if 0
1265 const SkPoint& addMoveTo(int tIndex, PathWrapper& path, bool active) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001266 const SkPoint& pt = xyAtT(tIndex);
1267 if (active) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001268 path.deferredMove(pt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001269 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001270 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001271 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001272#endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001273
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001274 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001275 void addOtherT(int index, double otherT, int otherIndex) {
1276 Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00001277 #if PIN_ADD_T
caryclark@google.com185c7c42012-10-19 18:26:24 +00001278 if (precisely_less_than_zero(otherT)) {
1279 otherT = 0;
1280 } else if (precisely_greater_than_one(otherT)) {
1281 otherT = 1;
1282 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001283 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001284 span.fOtherT = otherT;
1285 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001286 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001287
caryclark@google.com235f56a2012-09-14 14:19:30 +00001288 void addQuad(const SkPoint pts[3], bool operand) {
1289 init(pts, SkPath::kQuad_Verb, operand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001290 fBounds.setQuadBounds(pts);
1291 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001292
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001293 // Defer all coincident edge processing until
1294 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001295
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001296// no need to be tricky; insert in normal T order
1297// resolve overlapping ts when considering coincidence later
1298
1299 // add non-coincident intersection. Resulting edges are sorted in T.
1300 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001301 // FIXME: in the pathological case where there is a ton of intercepts,
1302 // binary search?
1303 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001304 size_t tCount = fTs.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00001305 #if PIN_ADD_T
caryclark@google.comc899ad92012-08-23 15:24:42 +00001306 // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
caryclark@google.com185c7c42012-10-19 18:26:24 +00001307 if (precisely_less_than_zero(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001308 newT = 0;
caryclark@google.com185c7c42012-10-19 18:26:24 +00001309 } else if (precisely_greater_than_one(newT)) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00001310 newT = 1;
1311 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001312 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001313 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001314 // OPTIMIZATION: if there are three or more identical Ts, then
1315 // the fourth and following could be further insertion-sorted so
1316 // that all the edges are clockwise or counterclockwise.
1317 // This could later limit segment tests to the two adjacent
1318 // neighbors, although it doesn't help with determining which
1319 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001320 if (newT < fTs[index].fT) {
1321 insertedAt = index;
1322 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001323 }
1324 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001325 Span* span;
1326 if (insertedAt >= 0) {
1327 span = fTs.insert(insertedAt);
1328 } else {
1329 insertedAt = tCount;
1330 span = fTs.append();
1331 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001332 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001333 span->fOther = other;
caryclark@google.com27c449a2012-07-27 18:26:38 +00001334 span->fPt.fX = SK_ScalarNaN;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001335 span->fWindSum = SK_MinS32;
1336 span->fWindValue = 1;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001337 span->fWindValueOpp = 0;
caryclark@google.comf839c032012-10-26 21:03:50 +00001338 span->fTiny = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001339 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001340 ++fDoneSpans;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001341 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001342 span->fUnsortableStart = false;
1343 span->fUnsortableEnd = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00001344 if (span - fTs.begin() > 0 && !span[-1].fDone
1345 && !precisely_negative(newT - span[-1].fT)
1346 // && approximately_negative(newT - span[-1].fT)
1347 && xyAtT(&span[-1]) == xyAtT(span)) {
1348 span[-1].fTiny = true;
1349 span[-1].fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001350 if (approximately_negative(newT - span[-1].fT)) {
1351 if (approximately_greater_than_one(newT)) {
1352 span[-1].fUnsortableStart = true;
1353 span[-2].fUnsortableEnd = true;
1354 }
1355 if (approximately_less_than_zero(span[-1].fT)) {
1356 span->fUnsortableStart = true;
1357 span[-1].fUnsortableEnd = true;
1358 }
1359 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001360 ++fDoneSpans;
1361 }
1362 if (fTs.end() - span > 1 && !span->fDone
1363 && !precisely_negative(span[1].fT - newT)
1364 // && approximately_negative(span[1].fT - newT)
1365 && xyAtT(&span[1]) == xyAtT(span)) {
1366 span->fTiny = true;
1367 span->fDone = true;
caryclark@google.com0b7da432012-10-31 19:00:20 +00001368 if (approximately_negative(span[1].fT - newT)) {
1369 if (approximately_greater_than_one(span[1].fT)) {
1370 span->fUnsortableStart = true;
1371 span[-1].fUnsortableEnd = true;
1372 }
1373 if (approximately_less_than_zero(newT)) {
1374 span[1].fUnsortableStart = true;
1375 span->fUnsortableEnd = true;
1376 }
1377 }
caryclark@google.comf839c032012-10-26 21:03:50 +00001378 ++fDoneSpans;
1379 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001380 return insertedAt;
1381 }
1382
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001383 // set spans from start to end to decrement by one
1384 // note this walks other backwards
1385 // FIMXE: there's probably an edge case that can be constructed where
1386 // two span in one segment are separated by float epsilon on one span but
1387 // not the other, if one segment is very small. For this
1388 // case the counts asserted below may or may not be enough to separate the
caryclark@google.com2ddff932012-08-07 21:25:27 +00001389 // spans. Even if the counts work out, what if the spans aren't correctly
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001390 // sorted? It feels better in such a case to match the span's other span
1391 // pointer since both coincident segments must contain the same spans.
1392 void addTCancel(double startT, double endT, Segment& other,
1393 double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001394 SkASSERT(!approximately_negative(endT - startT));
1395 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001396 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001397 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001398 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001399 ++index;
1400 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001401 int oIndex = other.fTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001402 while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001403 ;
caryclark@google.com59823f72012-08-09 18:17:47 +00001404 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001405 Span* test = &fTs[index];
1406 Span* oTest = &other.fTs[oIndex];
caryclark@google.com18063442012-07-25 12:05:18 +00001407 SkTDArray<double> outsideTs;
1408 SkTDArray<double> oOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001409 do {
1410 bool decrement = test->fWindValue && oTest->fWindValue;
caryclark@google.comcc905052012-07-25 20:59:42 +00001411 bool track = test->fWindValue || oTest->fWindValue;
caryclark@google.com200c2112012-08-03 15:05:04 +00001412 double testT = test->fT;
1413 double oTestT = oTest->fT;
1414 Span* span = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001415 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001416 if (decrement) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00001417 if (binary) {
1418 --(span->fWindValueOpp);
1419 } else {
1420 decrementSpan(span);
1421 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001422 } else if (track && span->fT < 1 && oTestT < 1) {
1423 TrackOutside(outsideTs, span->fT, oTestT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001424 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001425 span = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001426 } while (approximately_negative(span->fT - testT));
caryclark@google.com200c2112012-08-03 15:05:04 +00001427 Span* oSpan = oTest;
caryclark@google.com59823f72012-08-09 18:17:47 +00001428 double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
1429 double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
1430 SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001431 while (approximately_negative(otherTMatchStart - oSpan->fT)
1432 && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001433 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001434 SkASSERT(originalWindValue == oSpan->fWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001435 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001436 if (decrement) {
caryclark@google.com200c2112012-08-03 15:05:04 +00001437 other.decrementSpan(oSpan);
1438 } else if (track && oSpan->fT < 1 && testT < 1) {
1439 TrackOutside(oOutsideTs, oSpan->fT, testT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001440 }
1441 if (!oIndex) {
1442 break;
1443 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001444 oSpan = &other.fTs[--oIndex];
rmistry@google.comd6176b02012-08-23 18:14:13 +00001445 }
caryclark@google.com200c2112012-08-03 15:05:04 +00001446 test = span;
1447 oTest = oSpan;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001448 } while (!approximately_negative(endT - test->fT));
1449 SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
caryclark@google.com18063442012-07-25 12:05:18 +00001450 // FIXME: determine if canceled edges need outside ts added
caryclark@google.comcc905052012-07-25 20:59:42 +00001451 if (!done() && outsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001452 double tStart = outsideTs[0];
1453 double oStart = outsideTs[1];
1454 addCancelOutsides(tStart, oStart, other, oEndT);
1455 int count = outsideTs.count();
1456 if (count > 2) {
1457 double tStart = outsideTs[count - 2];
1458 double oStart = outsideTs[count - 1];
1459 addCancelOutsides(tStart, oStart, other, oEndT);
1460 }
caryclark@google.com18063442012-07-25 12:05:18 +00001461 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001462 if (!other.done() && oOutsideTs.count()) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00001463 double tStart = oOutsideTs[0];
1464 double oStart = oOutsideTs[1];
1465 other.addCancelOutsides(tStart, oStart, *this, endT);
caryclark@google.com18063442012-07-25 12:05:18 +00001466 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001467 }
1468
1469 // set spans from start to end to increment the greater by one and decrement
1470 // the lesser
caryclark@google.com235f56a2012-09-14 14:19:30 +00001471 void addTCoincident(const bool isXor, double startT, double endT,
1472 Segment& other, double oStartT, double oEndT) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001473 SkASSERT(!approximately_negative(endT - startT));
1474 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com235f56a2012-09-14 14:19:30 +00001475 bool binary = fOperand != other.fOperand;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001476 int index = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001477 while (!approximately_negative(startT - fTs[index].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001478 ++index;
1479 }
1480 int oIndex = 0;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001481 while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001482 ++oIndex;
1483 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001484 double tRatio = (oEndT - oStartT) / (endT - startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001485 Span* test = &fTs[index];
1486 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001487 SkTDArray<double> outsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001488 SkTDArray<double> xOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001489 SkTDArray<double> oOutsideTs;
caryclark@google.comcc905052012-07-25 20:59:42 +00001490 SkTDArray<double> oxOutsideTs;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001491 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001492 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com235f56a2012-09-14 14:19:30 +00001493 bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
1494 bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001495 Span* end = test;
1496 double startT = end->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001497 int startIndex = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001498 double oStartT = oTest->fT;
caryclark@google.comcc905052012-07-25 20:59:42 +00001499 int oStartIndex = oIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001500 do {
caryclark@google.comb9738012012-07-03 19:53:30 +00001501 if (transfer) {
1502 if (decrementOther) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001503 #ifdef SK_DEBUG
caryclark@google.com59823f72012-08-09 18:17:47 +00001504 SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
caryclark@google.com03f97062012-08-21 13:13:52 +00001505 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001506 if (binary) {
1507 ++(end->fWindValueOpp);
1508 } else {
1509 ++(end->fWindValue);
1510 }
caryclark@google.com18063442012-07-25 12:05:18 +00001511 } else if (decrementSpan(end)) {
1512 TrackOutside(outsideTs, end->fT, oStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001513 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001514 } else if (oTest->fWindValue) {
1515 SkASSERT(!decrementOther);
1516 if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
1517 TrackOutside(xOutsideTs, end->fT, oStartT);
1518 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001519 }
1520 end = &fTs[++index];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001521 } while (approximately_negative(end->fT - test->fT));
caryclark@google.com59823f72012-08-09 18:17:47 +00001522 // because of the order in which coincidences are resolved, this and other
1523 // may not have the same intermediate points. Compute the corresponding
1524 // intermediate T values (using this as the master, other as the follower)
1525 // and walk other conditionally -- hoping that it catches up in the end
1526 double otherTMatch = (test->fT - startT) * tRatio + oStartT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001527 Span* oEnd = oTest;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001528 while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00001529 if (transfer) {
caryclark@google.com24bec792012-08-20 12:43:57 +00001530 if (decrementThis) {
caryclark@google.com03f97062012-08-21 13:13:52 +00001531 #ifdef SK_DEBUG
1532 SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
1533 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001534 if (binary) {
1535 ++(oEnd->fWindValueOpp);
1536 } else {
1537 ++(oEnd->fWindValue);
1538 }
caryclark@google.com18063442012-07-25 12:05:18 +00001539 } else if (other.decrementSpan(oEnd)) {
1540 TrackOutside(oOutsideTs, oEnd->fT, startT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001541 }
caryclark@google.comcc905052012-07-25 20:59:42 +00001542 } else if (test->fWindValue) {
1543 SkASSERT(!decrementOther);
1544 if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
1545 SkASSERT(0); // track for later?
1546 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001547 }
1548 oEnd = &other.fTs[++oIndex];
caryclark@google.com59823f72012-08-09 18:17:47 +00001549 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001550 test = end;
1551 oTest = oEnd;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001552 } while (!approximately_negative(endT - test->fT));
1553 SkASSERT(approximately_negative(oTest->fT - oEndT));
1554 SkASSERT(approximately_negative(oEndT - oTest->fT));
caryclark@google.comcc905052012-07-25 20:59:42 +00001555 if (!done()) {
1556 if (outsideTs.count()) {
1557 addCoinOutsides(outsideTs, other, oEndT);
1558 }
1559 if (xOutsideTs.count()) {
1560 addCoinOutsides(xOutsideTs, other, oEndT);
1561 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001562 }
1563 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001564 other.addCoinOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001565 }
1566 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001567
caryclark@google.comcc905052012-07-25 20:59:42 +00001568 // FIXME: this doesn't prevent the same span from being added twice
1569 // fix in caller, assert here?
caryclark@google.com2ddff932012-08-07 21:25:27 +00001570 void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001571 int tCount = fTs.count();
1572 for (int tIndex = 0; tIndex < tCount; ++tIndex) {
1573 const Span& span = fTs[tIndex];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001574 if (!approximately_negative(span.fT - t)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001575 break;
1576 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001577 if (approximately_negative(span.fT - t) && span.fOther == &other
1578 && approximately_equal(span.fOtherT, otherT)) {
caryclark@google.comcc905052012-07-25 20:59:42 +00001579#if DEBUG_ADD_T_PAIR
1580 SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
1581 __FUNCTION__, fID, t, other.fID, otherT);
1582#endif
1583 return;
1584 }
1585 }
caryclark@google.com47580692012-07-23 12:14:49 +00001586#if DEBUG_ADD_T_PAIR
1587 SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
1588 __FUNCTION__, fID, t, other.fID, otherT);
1589#endif
caryclark@google.comb9738012012-07-03 19:53:30 +00001590 int insertedAt = addT(t, &other);
1591 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001592 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +00001593 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com2ddff932012-08-07 21:25:27 +00001594 matchWindingValue(insertedAt, t, borrowWind);
1595 other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001596 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001597
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001598 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001599 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001600 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1601 addAngle(angles, end, start);
1602 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001603 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001604 int step = SkSign32(end - start);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001605 int tIndex = nextExactSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001606 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001607 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001608 }
1609 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001610
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001611 const Bounds& bounds() const {
1612 return fBounds;
1613 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001614
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001615 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001616 double referenceT = fTs[index].fT;
1617 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001618 #if PRECISE_T_SORT
1619 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
1620 buildAnglesInner(lesser, angles);
1621 }
1622 do {
1623 buildAnglesInner(index, angles);
1624 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
1625 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001626 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001627 buildAnglesInner(lesser, angles);
1628 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001629 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001630 buildAnglesInner(index, angles);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00001631 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00001632 #endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001633 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001634
1635 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1636 Span* span = &fTs[index];
1637 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001638 // if there is only one live crossing, and no coincidence, continue
1639 // in the same direction
1640 // if there is coincidence, the only choice may be to reverse direction
1641 // find edge on either side of intersection
1642 int oIndex = span->fOtherIndex;
1643 // if done == -1, prior span has already been processed
1644 int step = 1;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001645 #if PRECISE_T_SORT
1646 int next = other->nextExactSpan(oIndex, step);
1647 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001648 int next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001649 #endif
1650 if (next < 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001651 step = -step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001652 #if PRECISE_T_SORT
1653 next = other->nextExactSpan(oIndex, step);
1654 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001655 next = other->nextSpan(oIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001656 #endif
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001657 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001658 // add candidate into and away from junction
1659 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001660 }
1661
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001662 // figure out if the segment's ascending T goes clockwise or not
1663 // not enough context to write this as shown
1664 // instead, add all segments meeting at the top
1665 // sort them using buildAngleList
1666 // find the first in the sort
1667 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001668 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001669 SkASSERT(0); // incomplete
1670 return false;
1671 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001672
caryclark@google.com6aea33f2012-10-09 14:11:58 +00001673 // FIXME may not need this at all
1674 // FIXME once I figure out the logic, merge this and too close to call
1675 // NOTE not sure if tiny triangles can ever form at the edge, so until we
1676 // see one, only worry about triangles that happen away from 0 and 1
1677 void collapseTriangles(bool isXor) {
1678 if (fTs.count() < 3) { // require t=0, x, 1 at minimum
1679 return;
1680 }
1681 int lastIndex = 1;
1682 double lastT;
1683 while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
1684 ++lastIndex;
1685 }
1686 if (approximately_greater_than_one(lastT)) {
1687 return;
1688 }
1689 int matchIndex = lastIndex;
1690 do {
1691 Span& match = fTs[++matchIndex];
1692 double matchT = match.fT;
1693 if (approximately_greater_than_one(matchT)) {
1694 return;
1695 }
1696 if (matchT == lastT) {
1697 goto nextSpan;
1698 }
1699 if (approximately_negative(matchT - lastT)) {
1700 Span& last = fTs[lastIndex];
1701 Segment* lOther = last.fOther;
1702 double lT = last.fOtherT;
1703 if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
1704 goto nextSpan;
1705 }
1706 Segment* mOther = match.fOther;
1707 double mT = match.fOtherT;
1708 if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
1709 goto nextSpan;
1710 }
1711 // add new point to connect adjacent spurs
1712 int count = lOther->fTs.count();
1713 for (int index = 0; index < count; ++index) {
1714 Span& span = lOther->fTs[index];
1715 if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
1716 goto nextSpan;
1717 }
1718 }
1719 mOther->addTPair(mT, *lOther, lT, false);
1720 // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
1721 }
1722 nextSpan:
1723 lastIndex = matchIndex;
1724 lastT = matchT;
1725 } while (true);
1726 }
1727
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001728 int computeSum(int startIndex, int endIndex) {
1729 SkTDArray<Angle> angles;
1730 addTwoAngles(startIndex, endIndex, angles);
1731 buildAngles(endIndex, angles);
caryclark@google.comd1688742012-09-18 20:08:37 +00001732 // OPTIMIZATION: check all angles to see if any have computed wind sum
1733 // before sorting (early exit if none)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001734 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001735 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00001736#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00001737 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00001738#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001739 if (!sortable) {
1740 return SK_MinS32;
1741 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001742 int angleCount = angles.count();
1743 const Angle* angle;
1744 const Segment* base;
1745 int winding;
1746 int firstIndex = 0;
1747 do {
1748 angle = sorted[firstIndex];
1749 base = angle->segment();
1750 winding = base->windSum(angle);
1751 if (winding != SK_MinS32) {
1752 break;
1753 }
1754 if (++firstIndex == angleCount) {
1755 return SK_MinS32;
1756 }
1757 } while (true);
1758 // turn winding into contourWinding
caryclark@google.com2ddff932012-08-07 21:25:27 +00001759 int spanWinding = base->spanSign(angle);
1760 bool inner = useInnerWinding(winding + spanWinding, winding);
1761 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00001762 SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
caryclark@google.com59823f72012-08-09 18:17:47 +00001763 spanWinding, winding, angle->sign(), inner,
caryclark@google.com2ddff932012-08-07 21:25:27 +00001764 inner ? winding + spanWinding : winding);
1765 #endif
1766 if (inner) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001767 winding += spanWinding;
1768 }
1769 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00001770 base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001771 #endif
1772 int nextIndex = firstIndex + 1;
1773 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001774 winding -= base->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001775 do {
1776 if (nextIndex == angleCount) {
1777 nextIndex = 0;
1778 }
1779 angle = sorted[nextIndex];
1780 Segment* segment = angle->segment();
1781 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00001782 winding -= segment->spanSign(angle);
caryclark@google.com200c2112012-08-03 15:05:04 +00001783 if (segment->windSum(angle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00001784 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001785 maxWinding = winding;
1786 }
caryclark@google.com59823f72012-08-09 18:17:47 +00001787 segment->markAndChaseWinding(angle, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001788 }
1789 } while (++nextIndex != lastIndex);
1790 return windSum(SkMin32(startIndex, endIndex));
1791 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001792
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001793 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001794 int bestT = -1;
1795 SkScalar top = bounds().fTop;
1796 SkScalar bottom = bounds().fBottom;
caryclark@google.com210acaf2012-07-12 21:05:13 +00001797 int end = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001798 do {
caryclark@google.com210acaf2012-07-12 21:05:13 +00001799 int start = end;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001800 end = nextSpan(start, 1);
caryclark@google.com47580692012-07-23 12:14:49 +00001801 if (fTs[start].fWindValue == 0) {
1802 continue;
1803 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001804 SkPoint edge[4];
caryclark@google.com24bec792012-08-20 12:43:57 +00001805 double startT = fTs[start].fT;
1806 double endT = fTs[end].fT;
1807 (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001808 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001809 Intersections intersections;
caryclark@google.comd1688742012-09-18 20:08:37 +00001810 // FIXME: always use original and limit results to T values within
1811 // start t and end t.
1812 // OPTIMIZE: use specialty function that intersects ray with curve,
1813 // returning t values only for curve (we don't care about t on ray)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001814 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1815 false, intersections);
1816 if (pts == 0) {
1817 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001818 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001819 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1820 // if the intersection is edge on, wait for another one
1821 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001822 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001823 for (int index = 0; index < pts; ++index) {
1824 SkPoint pt;
1825 double foundT = intersections.fT[0][index];
1826 double testT = startT + (endT - startT) * foundT;
1827 (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
1828 if (bestY < pt.fY && pt.fY < basePt.fY) {
caryclark@google.comd1688742012-09-18 20:08:37 +00001829 if (fVerb > SkPath::kLine_Verb
1830 && !approximately_less_than_zero(foundT)
1831 && !approximately_greater_than_one(foundT)) {
1832 SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
1833 if (approximately_zero(dx)) {
1834 continue;
1835 }
1836 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001837 bestY = pt.fY;
1838 bestT = foundT < 1 ? start : end;
1839 hitT = testT;
1840 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001841 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001842 } while (fTs[end].fT != 1);
1843 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001844 }
caryclark@google.com18063442012-07-25 12:05:18 +00001845
caryclark@google.com534aa5b2012-08-02 20:08:21 +00001846 bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
1847 // if a segment is connected to this one, consider it crossing
1848 int tIndex;
1849 if (fPts[0].fX == basePt.fX) {
1850 tIndex = 0;
1851 do {
1852 const Span& sSpan = fTs[tIndex];
1853 const Segment* sOther = sSpan.fOther;
1854 if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
1855 continue;
1856 }
1857 if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
1858 : sOther->fBounds.fRight > basePt.fX) {
1859 return true;
1860 }
1861 } while (fTs[++tIndex].fT == 0);
1862 }
1863 if (fPts[fVerb].fX == basePt.fX) {
1864 tIndex = fTs.count() - 1;
1865 do {
1866 const Span& eSpan = fTs[tIndex];
1867 const Segment* eOther = eSpan.fOther;
1868 if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
1869 continue;
1870 }
1871 if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
1872 : eOther->fBounds.fRight > basePt.fX) {
1873 return true;
1874 }
1875 } while (fTs[--tIndex].fT == 1);
1876 }
1877 return false;
1878 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001879
caryclark@google.com18063442012-07-25 12:05:18 +00001880 bool decrementSpan(Span* span) {
1881 SkASSERT(span->fWindValue > 0);
1882 if (--(span->fWindValue) == 0) {
caryclark@google.comf839c032012-10-26 21:03:50 +00001883 if (!span->fDone) {
1884 span->fDone = true;
1885 ++fDoneSpans;
1886 }
caryclark@google.com18063442012-07-25 12:05:18 +00001887 return true;
1888 }
1889 return false;
1890 }
1891
caryclark@google.com15fa1382012-05-07 20:49:36 +00001892 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001893 SkASSERT(fDoneSpans <= fTs.count());
1894 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001895 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00001896
caryclark@google.comf839c032012-10-26 21:03:50 +00001897 bool done(int min) const {
1898 return fTs[min].fDone;
1899 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001900
caryclark@google.com47580692012-07-23 12:14:49 +00001901 bool done(const Angle& angle) const {
caryclark@google.comf839c032012-10-26 21:03:50 +00001902 return done(SkMin32(angle.start(), angle.end()));
caryclark@google.com47580692012-07-23 12:14:49 +00001903 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00001904
caryclark@google.com235f56a2012-09-14 14:19:30 +00001905 Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00001906 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001907 bool& unsortable, ShapeOp op,
caryclark@google.com235f56a2012-09-14 14:19:30 +00001908 const int aXorMask, const int bXorMask) {
1909 const int startIndex = nextStart;
1910 const int endIndex = nextEnd;
1911 int outerWinding = winding;
1912 int innerWinding = winding + spanWinding;
1913 #if DEBUG_WINDING
1914 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
1915 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
1916 #endif
1917 if (useInnerWinding(outerWinding, innerWinding)) {
1918 outerWinding = innerWinding;
1919 }
1920 SkASSERT(startIndex != endIndex);
1921 int count = fTs.count();
1922 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1923 : startIndex > 0);
1924 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001925 #if PRECISE_T_SORT
1926 int end = nextExactSpan(startIndex, step);
1927 #else
caryclark@google.com235f56a2012-09-14 14:19:30 +00001928 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00001929 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001930 SkASSERT(end >= 0);
1931 Span* endSpan = &fTs[end];
1932 Segment* other;
1933 if (isSimple(end)) {
1934 // mark the smaller of startIndex, endIndex done, and all adjacent
1935 // spans with the same T value (but not 'other' spans)
1936 #if DEBUG_WINDING
1937 SkDebugf("%s simple\n", __FUNCTION__);
1938 #endif
1939 markDone(SkMin32(startIndex, endIndex), outerWinding);
1940 other = endSpan->fOther;
1941 nextStart = endSpan->fOtherIndex;
1942 double startT = other->fTs[nextStart].fT;
1943 nextEnd = nextStart;
1944 do {
1945 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00001946 }
1947 #if PRECISE_T_SORT
1948 while (precisely_zero(startT - other->fTs[nextEnd].fT));
1949 #else
1950 while (approximately_zero(startT - other->fTs[nextEnd].fT));
1951 #endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00001952 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
1953 return other;
1954 }
1955 // more than one viable candidate -- measure angles to find best
1956 SkTDArray<Angle> angles;
1957 SkASSERT(startIndex - endIndex != 0);
1958 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
1959 addTwoAngles(startIndex, end, angles);
1960 buildAngles(end, angles);
1961 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001962 bool sortable = SortAngles(angles, sorted);
caryclark@google.com235f56a2012-09-14 14:19:30 +00001963 int angleCount = angles.count();
1964 int firstIndex = findStartingEdge(sorted, startIndex, end);
1965 SkASSERT(firstIndex >= 0);
1966 #if DEBUG_SORT
1967 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
1968 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00001969 if (!sortable) {
1970 unsortable = true;
1971 return NULL;
1972 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00001973 SkASSERT(sorted[firstIndex]->segment() == this);
1974 #if DEBUG_WINDING
1975 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
1976 #endif
1977 int aSumWinding = winding;
1978 int bSumWinding = winding;
1979 bool angleIsOp = sorted[firstIndex]->segment()->operand();
1980 int angleSpan = spanSign(sorted[firstIndex]);
1981 if (angleIsOp) {
1982 bSumWinding -= angleSpan;
1983 } else {
1984 aSumWinding -= angleSpan;
1985 }
1986 int nextIndex = firstIndex + 1;
1987 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1988 const Angle* foundAngle = NULL;
1989 // FIXME: found done logic probably fails if there are more than 4
1990 // sorted angles. It should bias towards the first and last undone
1991 // edges -- but not sure that it won't choose a middle (incorrect)
1992 // edge if one is undone
1993 bool foundDone = false;
1994 bool foundDone2 = false;
1995 // iterate through the angle, and compute everyone's winding
1996 bool altFlipped = false;
1997 bool foundFlipped = false;
1998 int foundMax = SK_MinS32;
1999 int foundSum = SK_MinS32;
2000 Segment* nextSegment;
2001 int lastNonZeroSum = winding;
2002 do {
2003 if (nextIndex == angleCount) {
2004 nextIndex = 0;
2005 }
2006 const Angle* nextAngle = sorted[nextIndex];
2007 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002008 bool nextDone = nextSegment->done(*nextAngle);
2009 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com235f56a2012-09-14 14:19:30 +00002010 angleIsOp = nextSegment->operand();
2011 int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
2012 int maxWinding = sumWinding;
2013 if (sumWinding) {
2014 lastNonZeroSum = sumWinding;
2015 }
2016 sumWinding -= nextSegment->spanSign(nextAngle);
2017 int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
2018 bool otherNonZero;
2019 if (angleIsOp) {
2020 bSumWinding = sumWinding;
2021 otherNonZero = aSumWinding & aXorMask;
2022 } else {
2023 aSumWinding = sumWinding;
2024 otherNonZero = bSumWinding & bXorMask;
2025 }
2026 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002027 #if 0 && DEBUG_WINDING
caryclark@google.com235f56a2012-09-14 14:19:30 +00002028 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
2029 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2030 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
2031 #endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002032
caryclark@google.com235f56a2012-09-14 14:19:30 +00002033 if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
2034 if (!active) {
2035 markDone(SkMin32(startIndex, endIndex), outerWinding);
2036 // FIXME: seems like a bug that this isn't calling userInnerWinding
2037 nextSegment->markWinding(SkMin32(nextAngle->start(),
2038 nextAngle->end()), maxWinding);
2039 #if DEBUG_WINDING
2040 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
2041 #endif
2042 return NULL;
2043 }
2044 if (!foundAngle || foundDone) {
2045 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002046 foundDone = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002047 foundFlipped = altFlipped;
2048 foundMax = maxWinding;
2049 }
2050 continue;
2051 }
2052 if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
2053 && activeOp(angleIsOp, otherNonZero, op)) {
2054 #if DEBUG_WINDING
2055 if (foundAngle && foundDone2) {
2056 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
2057 }
2058 #endif
2059 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002060 foundDone2 = nextDone && !nextTiny;
caryclark@google.com235f56a2012-09-14 14:19:30 +00002061 foundFlipped = altFlipped;
2062 foundSum = sumWinding;
2063 }
2064 if (nextSegment->done()) {
2065 continue;
2066 }
2067 // if the winding is non-zero, nextAngle does not connect to
2068 // current chain. If we haven't done so already, mark the angle
2069 // as done, record the winding value, and mark connected unambiguous
2070 // segments as well.
2071 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
2072 if (useInnerWinding(maxWinding, sumWinding)) {
2073 maxWinding = sumWinding;
2074 }
2075 Span* last;
2076 if (foundAngle) {
2077 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
2078 } else {
2079 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
2080 }
2081 if (last) {
2082 *chase.append() = last;
2083 }
2084 }
2085 } while (++nextIndex != lastIndex);
2086 markDone(SkMin32(startIndex, endIndex), outerWinding);
2087 if (!foundAngle) {
2088 return NULL;
2089 }
2090 nextStart = foundAngle->start();
2091 nextEnd = foundAngle->end();
2092 nextSegment = foundAngle->segment();
2093 int flipped = foundFlipped ? -1 : 1;
2094 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2095 SkMin32(nextStart, nextEnd));
2096 if (winding) {
2097 #if DEBUG_WINDING
2098 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2099 if (foundSum == SK_MinS32) {
2100 SkDebugf("?");
2101 } else {
2102 SkDebugf("%d", foundSum);
2103 }
2104 SkDebugf(" foundMax=");
2105 if (foundMax == SK_MinS32) {
2106 SkDebugf("?");
2107 } else {
2108 SkDebugf("%d", foundMax);
2109 }
2110 SkDebugf("\n");
2111 #endif
2112 winding = foundSum;
2113 }
2114 #if DEBUG_WINDING
2115 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
2116 #endif
2117 return nextSegment;
2118 }
caryclark@google.com47580692012-07-23 12:14:49 +00002119
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002120 // so the span needs to contain the pairing info found here
2121 // this should include the winding computed for the edge, and
2122 // what edge it connects to, and whether it is discarded
2123 // (maybe discarded == abs(winding) > 1) ?
2124 // only need derivatives for duration of sorting, add a new struct
2125 // for pairings, remove extra spans that have zero length and
2126 // reference an unused other
2127 // for coincident, the last span on the other may be marked done
2128 // (always?)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002129
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002130 // if loop is exhausted, contour may be closed.
2131 // FIXME: pass in close point so we can check for closure
2132
2133 // given a segment, and a sense of where 'inside' is, return the next
2134 // segment. If this segment has an intersection, or ends in multiple
2135 // segments, find the mate that continues the outside.
2136 // note that if there are multiples, but no coincidence, we can limit
2137 // choices to connections in the correct direction
rmistry@google.comd6176b02012-08-23 18:14:13 +00002138
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002139 // mark found segments as done
2140
caryclark@google.com15fa1382012-05-07 20:49:36 +00002141 // start is the index of the beginning T of this edge
2142 // it is guaranteed to have an end which describes a non-zero length (?)
2143 // winding -1 means ccw, 1 means cw
caryclark@google.com24bec792012-08-20 12:43:57 +00002144 Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002145 int& nextStart, int& nextEnd, int& winding, int& spanWinding,
2146 bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002147 const int startIndex = nextStart;
2148 const int endIndex = nextEnd;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002149 int outerWinding = winding;
2150 int innerWinding = winding + spanWinding;
caryclark@google.come21cb182012-07-23 21:26:31 +00002151 #if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002152 SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
2153 __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
caryclark@google.come21cb182012-07-23 21:26:31 +00002154 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002155 if (useInnerWinding(outerWinding, innerWinding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002156 outerWinding = innerWinding;
2157 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002158 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002159 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002160 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2161 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002162 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002163 #if PRECISE_T_SORT
2164 int end = nextExactSpan(startIndex, step);
2165 #else
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002166 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002167 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002168 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002169 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002170 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002171 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002172 // mark the smaller of startIndex, endIndex done, and all adjacent
2173 // spans with the same T value (but not 'other' spans)
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002174 #if DEBUG_WINDING
2175 SkDebugf("%s simple\n", __FUNCTION__);
2176 #endif
caryclark@google.com59823f72012-08-09 18:17:47 +00002177 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002178 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002179 nextStart = endSpan->fOtherIndex;
caryclark@google.com18063442012-07-25 12:05:18 +00002180 double startT = other->fTs[nextStart].fT;
2181 nextEnd = nextStart;
2182 do {
2183 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002184 }
2185 #if PRECISE_T_SORT
2186 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2187 #else
2188 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2189 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00002190 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00002191 return other;
2192 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002193 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00002194 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002195 SkASSERT(startIndex - endIndex != 0);
2196 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002197 addTwoAngles(startIndex, end, angles);
2198 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002199 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002200 bool sortable = SortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002201 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002202 int firstIndex = findStartingEdge(sorted, startIndex, end);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002203 SkASSERT(firstIndex >= 0);
caryclark@google.com47580692012-07-23 12:14:49 +00002204 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002205 debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com47580692012-07-23 12:14:49 +00002206 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002207 if (!sortable) {
2208 unsortable = true;
2209 return NULL;
2210 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002211 SkASSERT(sorted[firstIndex]->segment() == this);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002212 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002213 SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
caryclark@google.com0e08a192012-07-13 21:07:52 +00002214 #endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00002215 int sumWinding = winding - spanSign(sorted[firstIndex]);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002216 int nextIndex = firstIndex + 1;
2217 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2218 const Angle* foundAngle = NULL;
caryclark@google.com24bec792012-08-20 12:43:57 +00002219 // FIXME: found done logic probably fails if there are more than 4
2220 // sorted angles. It should bias towards the first and last undone
2221 // edges -- but not sure that it won't choose a middle (incorrect)
rmistry@google.comd6176b02012-08-23 18:14:13 +00002222 // edge if one is undone
caryclark@google.com47580692012-07-23 12:14:49 +00002223 bool foundDone = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00002224 bool foundDone2 = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002225 // iterate through the angle, and compute everyone's winding
caryclark@google.com24bec792012-08-20 12:43:57 +00002226 bool altFlipped = false;
2227 bool foundFlipped = false;
2228 int foundMax = SK_MinS32;
2229 int foundSum = SK_MinS32;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002230 Segment* nextSegment;
caryclark@google.com24bec792012-08-20 12:43:57 +00002231 int lastNonZeroSum = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002232 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002233 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002234 nextIndex = 0;
2235 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002236 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.come21cb182012-07-23 21:26:31 +00002237 int maxWinding = sumWinding;
caryclark@google.com24bec792012-08-20 12:43:57 +00002238 if (sumWinding) {
2239 lastNonZeroSum = sumWinding;
2240 }
caryclark@google.comafe56de2012-07-24 18:11:03 +00002241 nextSegment = nextAngle->segment();
caryclark@google.comf839c032012-10-26 21:03:50 +00002242 bool nextDone = nextSegment->done(*nextAngle);
2243 bool nextTiny = nextSegment->tiny(*nextAngle);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002244 sumWinding -= nextSegment->spanSign(nextAngle);
caryclark@google.com24bec792012-08-20 12:43:57 +00002245 altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
caryclark@google.comd1688742012-09-18 20:08:37 +00002246 #if 0 && DEBUG_WINDING
caryclark@google.com03f97062012-08-21 13:13:52 +00002247 SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
caryclark@google.com24bec792012-08-20 12:43:57 +00002248 SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
2249 nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002250 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002251 if (!sumWinding) {
caryclark@google.com5c286d32012-07-13 11:57:28 +00002252 if (!active) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002253 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00002254 // FIXME: seems like a bug that this isn't calling userInnerWinding
caryclark@google.com47580692012-07-23 12:14:49 +00002255 nextSegment->markWinding(SkMin32(nextAngle->start(),
caryclark@google.com59823f72012-08-09 18:17:47 +00002256 nextAngle->end()), maxWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002257 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002258 SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
caryclark@google.com0e08a192012-07-13 21:07:52 +00002259 #endif
caryclark@google.com5c286d32012-07-13 11:57:28 +00002260 return NULL;
2261 }
caryclark@google.com47580692012-07-23 12:14:49 +00002262 if (!foundAngle || foundDone) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002263 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002264 foundDone = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002265 foundFlipped = altFlipped;
2266 foundMax = maxWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002267 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002268 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002269 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002270
caryclark@google.com24bec792012-08-20 12:43:57 +00002271 if (!maxWinding && (!foundAngle || foundDone2)) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00002272 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002273 if (foundAngle && foundDone2) {
2274 SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002275 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002276 #endif
caryclark@google.com0e08a192012-07-13 21:07:52 +00002277 foundAngle = nextAngle;
caryclark@google.comf839c032012-10-26 21:03:50 +00002278 foundDone2 = nextDone && !nextTiny;
caryclark@google.com24bec792012-08-20 12:43:57 +00002279 foundFlipped = altFlipped;
2280 foundSum = sumWinding;
caryclark@google.com0e08a192012-07-13 21:07:52 +00002281 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002282 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002283 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002284 }
2285 // if the winding is non-zero, nextAngle does not connect to
2286 // current chain. If we haven't done so already, mark the angle
2287 // as done, record the winding value, and mark connected unambiguous
2288 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002289 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002290 if (useInnerWinding(maxWinding, sumWinding)) {
caryclark@google.come21cb182012-07-23 21:26:31 +00002291 maxWinding = sumWinding;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002292 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002293 Span* last;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00002294 if (foundAngle) {
caryclark@google.com59823f72012-08-09 18:17:47 +00002295 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002296 } else {
caryclark@google.com59823f72012-08-09 18:17:47 +00002297 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002298 }
2299 if (last) {
2300 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002301 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002302 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002303 } while (++nextIndex != lastIndex);
caryclark@google.com59823f72012-08-09 18:17:47 +00002304 markDone(SkMin32(startIndex, endIndex), outerWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002305 if (!foundAngle) {
2306 return NULL;
2307 }
2308 nextStart = foundAngle->start();
2309 nextEnd = foundAngle->end();
caryclark@google.comafe56de2012-07-24 18:11:03 +00002310 nextSegment = foundAngle->segment();
caryclark@google.com24bec792012-08-20 12:43:57 +00002311 int flipped = foundFlipped ? -1 : 1;
caryclark@google.comafe56de2012-07-24 18:11:03 +00002312 spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
2313 SkMin32(nextStart, nextEnd));
caryclark@google.com24bec792012-08-20 12:43:57 +00002314 if (winding) {
2315 #if DEBUG_WINDING
2316 SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
2317 if (foundSum == SK_MinS32) {
2318 SkDebugf("?");
2319 } else {
2320 SkDebugf("%d", foundSum);
2321 }
2322 SkDebugf(" foundMax=");
2323 if (foundMax == SK_MinS32) {
2324 SkDebugf("?");
2325 } else {
2326 SkDebugf("%d", foundMax);
2327 }
2328 SkDebugf("\n");
2329 #endif
2330 winding = foundSum;
caryclark@google.com27c449a2012-07-27 18:26:38 +00002331 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00002332 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00002333 SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
caryclark@google.com27c449a2012-07-27 18:26:38 +00002334 #endif
caryclark@google.comafe56de2012-07-24 18:11:03 +00002335 return nextSegment;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002336 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002337
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002338 Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002339 const int startIndex = nextStart;
2340 const int endIndex = nextEnd;
2341 SkASSERT(startIndex != endIndex);
2342 int count = fTs.count();
2343 SkASSERT(startIndex < endIndex ? startIndex < count - 1
2344 : startIndex > 0);
2345 int step = SkSign32(endIndex - startIndex);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002346 #if PRECISE_T_SORT
2347 int end = nextExactSpan(startIndex, step);
2348 #else
caryclark@google.com24bec792012-08-20 12:43:57 +00002349 int end = nextSpan(startIndex, step);
caryclark@google.coma461ff02012-10-11 12:54:23 +00002350 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002351 SkASSERT(end >= 0);
2352 Span* endSpan = &fTs[end];
2353 Segment* other;
2354 markDone(SkMin32(startIndex, endIndex), 1);
2355 if (isSimple(end)) {
2356 #if DEBUG_WINDING
2357 SkDebugf("%s simple\n", __FUNCTION__);
2358 #endif
2359 other = endSpan->fOther;
2360 nextStart = endSpan->fOtherIndex;
2361 double startT = other->fTs[nextStart].fT;
2362 SkDEBUGCODE(bool firstLoop = true;)
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002363 if ((approximately_less_than_zero(startT) && step < 0)
2364 || (approximately_greater_than_one(startT) && step > 0)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002365 step = -step;
2366 SkDEBUGCODE(firstLoop = false;)
2367 }
2368 do {
2369 nextEnd = nextStart;
2370 do {
2371 nextEnd += step;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002372 }
2373 #if PRECISE_T_SORT
2374 while (precisely_zero(startT - other->fTs[nextEnd].fT));
2375 #else
2376 while (approximately_zero(startT - other->fTs[nextEnd].fT));
2377 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002378 if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
2379 break;
2380 }
caryclark@google.com03f97062012-08-21 13:13:52 +00002381 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002382 SkASSERT(firstLoop);
caryclark@google.com03f97062012-08-21 13:13:52 +00002383 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002384 SkDEBUGCODE(firstLoop = false;)
2385 step = -step;
2386 } while (true);
2387 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
2388 return other;
2389 }
2390 SkTDArray<Angle> angles;
2391 SkASSERT(startIndex - endIndex != 0);
2392 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
2393 addTwoAngles(startIndex, end, angles);
2394 buildAngles(end, angles);
2395 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002396 bool sortable = SortAngles(angles, sorted);
caryclark@google.com24bec792012-08-20 12:43:57 +00002397 int angleCount = angles.count();
2398 int firstIndex = findStartingEdge(sorted, startIndex, end);
2399 SkASSERT(firstIndex >= 0);
2400 #if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00002401 debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
caryclark@google.com24bec792012-08-20 12:43:57 +00002402 #endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002403 if (!sortable) {
2404 unsortable = true;
2405 return NULL;
2406 }
caryclark@google.com24bec792012-08-20 12:43:57 +00002407 SkASSERT(sorted[firstIndex]->segment() == this);
2408 int nextIndex = firstIndex + 1;
2409 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2410 const Angle* nextAngle;
2411 Segment* nextSegment;
2412 do {
2413 if (nextIndex == angleCount) {
2414 nextIndex = 0;
2415 }
2416 nextAngle = sorted[nextIndex];
2417 nextSegment = nextAngle->segment();
2418 if (!nextSegment->done(*nextAngle)) {
2419 break;
2420 }
2421 if (++nextIndex == lastIndex) {
2422 return NULL;
2423 }
2424 } while (true);
2425 nextStart = nextAngle->start();
2426 nextEnd = nextAngle->end();
2427 return nextSegment;
2428 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002429
2430 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
2431 int angleCount = sorted.count();
2432 int firstIndex = -1;
2433 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
2434 const Angle* angle = sorted[angleIndex];
2435 if (angle->segment() == this && angle->start() == end &&
2436 angle->end() == start) {
2437 firstIndex = angleIndex;
2438 break;
2439 }
2440 }
2441 return firstIndex;
2442 }
2443
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002444 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com235f56a2012-09-14 14:19:30 +00002445 void findTooCloseToCall(bool isXor) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002446 int count = fTs.count();
2447 if (count < 3) { // require t=0, x, 1 at minimum
2448 return;
2449 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002450 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002451 int moCount;
2452 Span* match;
2453 Segment* mOther;
2454 do {
2455 match = &fTs[matchIndex];
2456 mOther = match->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002457 // FIXME: allow quads, cubics to be near coincident?
2458 if (mOther->fVerb == SkPath::kLine_Verb) {
2459 moCount = mOther->fTs.count();
2460 if (moCount >= 3) {
2461 break;
2462 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002463 }
2464 if (++matchIndex >= count) {
2465 return;
2466 }
2467 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00002468 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002469 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002470 // look for a pair of nearby T values that map to the same (x,y) value
2471 // if found, see if the pair of other segments share a common point. If
2472 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00002473 for (int index = matchIndex + 1; index < count; ++index) {
2474 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002475 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002476 continue;
2477 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002478 Segment* tOther = test->fOther;
caryclark@google.comc899ad92012-08-23 15:24:42 +00002479 if (tOther->fVerb != SkPath::kLine_Verb) {
2480 continue; // FIXME: allow quads, cubics to be near coincident?
2481 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002482 int toCount = tOther->fTs.count();
2483 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002484 continue;
2485 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002486 const SkPoint* testPt = &xyAtT(test);
2487 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002488 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002489 moCount = toCount;
2490 match = test;
2491 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002492 matchPt = testPt;
2493 continue;
2494 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002495 int moStart = -1;
2496 int moEnd = -1;
2497 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002498 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002499 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002500 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002501 continue;
2502 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002503 if (moSpan.fOther == this) {
2504 if (moSpan.fOtherT == match->fT) {
2505 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002506 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002507 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002508 continue;
2509 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002510 if (moSpan.fOther == tOther) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002511 if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
2512 moStart = -1;
2513 break;
2514 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002515 SkASSERT(moEnd == -1);
2516 moEnd = moIndex;
2517 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002518 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002519 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002520 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002521 continue;
2522 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002523 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002524 if (approximately_equal(moStartT, moEndT)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002525 continue;
2526 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002527 int toStart = -1;
2528 int toEnd = -1;
2529 double toStartT, toEndT;
2530 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
2531 Span& toSpan = tOther->fTs[toIndex];
caryclark@google.comc899ad92012-08-23 15:24:42 +00002532 if (toSpan.fDone) {
2533 continue;
2534 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002535 if (toSpan.fOther == this) {
2536 if (toSpan.fOtherT == test->fT) {
2537 toStart = toIndex;
2538 toStartT = toSpan.fT;
2539 }
2540 continue;
2541 }
2542 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
caryclark@google.comc899ad92012-08-23 15:24:42 +00002543 if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
2544 moStart = -1;
2545 break;
2546 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002547 SkASSERT(toEnd == -1);
2548 toEnd = toIndex;
2549 toEndT = toSpan.fT;
2550 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002551 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002552 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
2553 if (toStart <= 0 || toEnd <= 0) {
2554 continue;
2555 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002556 if (approximately_equal(toStartT, toEndT)) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002557 continue;
2558 }
2559 // test to see if the segment between there and here is linear
2560 if (!mOther->isLinear(moStart, moEnd)
2561 || !tOther->isLinear(toStart, toEnd)) {
2562 continue;
2563 }
caryclark@google.comc899ad92012-08-23 15:24:42 +00002564 bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002565 if (flipped) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002566 mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002567 } else {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002568 // FIXME: this is bogus for multiple ops
2569 // the xorMask needs to be accumulated from the union of the two
2570 // edges -- which means that the segment must have its own copy of the mask
2571 mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002572 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002573 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002574 }
2575
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002576 // start here;
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00002577 // either:
caryclark@google.comc91dfe42012-10-16 12:06:27 +00002578 // a) mark spans with either end unsortable as done, or
2579 // b) rewrite findTop / findTopSegment / findTopContour to iterate further
2580 // when encountering an unsortable span
2581
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002582 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
2583 // and use more concise logic like the old edge walker code?
2584 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002585 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002586 // iterate through T intersections and return topmost
2587 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002588 SkASSERT(!done());
2589 int firstT;
2590 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002591 SkPoint topPt;
2592 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002593 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002594 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002595 bool lastDone = true;
caryclark@google.comf839c032012-10-26 21:03:50 +00002596 bool lastUnsortable = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002597 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002598 const Span& span = fTs[index];
caryclark@google.comf839c032012-10-26 21:03:50 +00002599 if (span.fUnsortableStart | lastUnsortable) {
2600 goto next;
2601 }
2602 if (!span.fDone | !lastDone) {
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002603 const SkPoint& intercept = xyAtT(&span);
2604 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
2605 && topPt.fX > intercept.fX)) {
2606 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002607 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002608 } else if (topPt == intercept) {
2609 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002610 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002611 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002612 next:
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002613 lastDone = span.fDone;
caryclark@google.comf839c032012-10-26 21:03:50 +00002614 lastUnsortable = span.fUnsortableEnd;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002615 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002616 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002617 int step = 1;
2618 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002619 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002620 step = -1;
2621 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002622 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002623 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002624 // if the topmost T is not on end, or is three-way or more, find left
2625 // look for left-ness from tLeft to firstT (matching y of other)
2626 SkTDArray<Angle> angles;
2627 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002628 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002629 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002630 SkTDArray<Angle*> sorted;
caryclark@google.comf839c032012-10-26 21:03:50 +00002631 bool sortable = SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00002632 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00002633 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00002634 #endif
caryclark@google.comf839c032012-10-26 21:03:50 +00002635 if (!sortable) {
2636 return NULL;
2637 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002638 // skip edges that have already been processed
2639 firstT = -1;
2640 Segment* leftSegment;
2641 do {
2642 const Angle* angle = sorted[++firstT];
caryclark@google.comf839c032012-10-26 21:03:50 +00002643 SkASSERT(!angle->unsortable());
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002644 leftSegment = angle->segment();
2645 tIndex = angle->end();
2646 endIndex = angle->start();
2647 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002648 return leftSegment;
2649 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002650
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002651 // FIXME: not crazy about this
2652 // when the intersections are performed, the other index is into an
2653 // incomplete array. as the array grows, the indices become incorrect
2654 // while the following fixes the indices up again, it isn't smart about
2655 // skipping segments whose indices are already correct
2656 // assuming we leave the code that wrote the index in the first place
2657 void fixOtherTIndex() {
2658 int iCount = fTs.count();
2659 for (int i = 0; i < iCount; ++i) {
2660 Span& iSpan = fTs[i];
2661 double oT = iSpan.fOtherT;
2662 Segment* other = iSpan.fOther;
2663 int oCount = other->fTs.count();
2664 for (int o = 0; o < oCount; ++o) {
2665 Span& oSpan = other->fTs[o];
2666 if (oT == oSpan.fT && this == oSpan.fOther) {
2667 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002668 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002669 }
2670 }
2671 }
2672 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002673
caryclark@google.com495f8e42012-05-31 13:13:11 +00002674 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.com59823f72012-08-09 18:17:47 +00002675 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002676 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002677 SkASSERT(end >= 0);
2678 if (multipleSpans(end)) {
2679 return &fTs[end];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002680 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002681 const Span& endSpan = fTs[end];
2682 Segment* other = endSpan.fOther;
2683 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002684 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com59823f72012-08-09 18:17:47 +00002685 Span* last = other->innerChaseDone(index, step, winding);
2686 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002687 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002688 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002689
caryclark@google.com59823f72012-08-09 18:17:47 +00002690 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002691 int end = nextExactSpan(index, step);
caryclark@google.com9764cc62012-07-12 19:29:45 +00002692 SkASSERT(end >= 0);
2693 if (multipleSpans(end)) {
2694 return &fTs[end];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002695 }
2696 const Span& endSpan = fTs[end];
2697 Segment* other = endSpan.fOther;
2698 index = endSpan.fOtherIndex;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002699 int otherEnd = other->nextExactSpan(index, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002700 int min = SkMin32(index, otherEnd);
2701 if (other->fTs[min].fWindSum != SK_MinS32) {
caryclark@google.com0e08a192012-07-13 21:07:52 +00002702 SkASSERT(other->fTs[min].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002703 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002704 }
caryclark@google.com59823f72012-08-09 18:17:47 +00002705 Span* last = other->innerChaseWinding(index, step, winding);
2706 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002707 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002708 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002709
caryclark@google.com235f56a2012-09-14 14:19:30 +00002710 void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
2711 fDoneSpans = 0;
2712 fOperand = operand;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002713 fPts = pts;
2714 fVerb = verb;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002715 }
2716
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002717 bool intersected() const {
2718 return fTs.count() > 0;
2719 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00002720
2721 bool isConnected(int startIndex, int endIndex) const {
2722 return fTs[startIndex].fWindSum != SK_MinS32
2723 || fTs[endIndex].fWindSum != SK_MinS32;
2724 }
2725
caryclark@google.com235f56a2012-09-14 14:19:30 +00002726 bool isHorizontal() const {
2727 return fBounds.fTop == fBounds.fBottom;
2728 }
2729
caryclark@google.com15fa1382012-05-07 20:49:36 +00002730 bool isLinear(int start, int end) const {
2731 if (fVerb == SkPath::kLine_Verb) {
2732 return true;
2733 }
2734 if (fVerb == SkPath::kQuad_Verb) {
2735 SkPoint qPart[3];
2736 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
2737 return QuadIsLinear(qPart);
2738 } else {
2739 SkASSERT(fVerb == SkPath::kCubic_Verb);
2740 SkPoint cPart[4];
2741 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
2742 return CubicIsLinear(cPart);
2743 }
2744 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002745
2746 // OPTIMIZE: successive calls could start were the last leaves off
2747 // or calls could specialize to walk forwards or backwards
2748 bool isMissing(double startT) const {
2749 size_t tCount = fTs.count();
2750 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002751 if (approximately_zero(startT - fTs[index].fT)) {
caryclark@google.comb9738012012-07-03 19:53:30 +00002752 return false;
2753 }
2754 }
2755 return true;
2756 }
2757
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002758 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002759 int count = fTs.count();
2760 if (count == 2) {
2761 return true;
2762 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002763 double t = fTs[end].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002764 if (approximately_less_than_zero(t)) {
2765 return !approximately_less_than_zero(fTs[1].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002766 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002767 if (approximately_greater_than_one(t)) {
2768 return !approximately_greater_than_one(fTs[count - 2].fT);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002769 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002770 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002771 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002772
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002773 bool isVertical() const {
2774 return fBounds.fLeft == fBounds.fRight;
2775 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002776
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002777 SkScalar leftMost(int start, int end) const {
2778 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
2779 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002780
caryclark@google.com495f8e42012-05-31 13:13:11 +00002781 // this span is excluded by the winding rule -- chase the ends
2782 // as long as they are unambiguous to mark connections as done
2783 // and give them the same winding value
caryclark@google.com59823f72012-08-09 18:17:47 +00002784 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002785 int index = angle->start();
2786 int endIndex = angle->end();
2787 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002788 Span* last = innerChaseDone(index, step, winding);
2789 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002790 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002791 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002792
caryclark@google.com59823f72012-08-09 18:17:47 +00002793 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002794 int index = angle->start();
2795 int endIndex = angle->end();
2796 int min = SkMin32(index, endIndex);
2797 int step = SkSign32(endIndex - index);
caryclark@google.com59823f72012-08-09 18:17:47 +00002798 Span* last = innerChaseWinding(index, step, winding);
2799 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002800 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002801 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002802
caryclark@google.com495f8e42012-05-31 13:13:11 +00002803 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002804 // This may be called when the segment is already marked done. While this
2805 // wastes time, it shouldn't do any more than spin through the T spans.
rmistry@google.comd6176b02012-08-23 18:14:13 +00002806 // OPTIMIZATION: abort on first done found (assuming that this code is
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002807 // always called to mark segments done).
caryclark@google.com59823f72012-08-09 18:17:47 +00002808 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002809 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002810 SkASSERT(winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002811 double referenceT = fTs[index].fT;
2812 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002813 #if PRECISE_T_SORT
2814 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2815 markOneDone(__FUNCTION__, lesser, winding);
2816 }
2817 do {
2818 markOneDone(__FUNCTION__, index, winding);
2819 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2820 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002821 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002822 markOneDone(__FUNCTION__, lesser, winding);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002823 }
2824 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002825 markOneDone(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002826 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002827 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002828 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00002829
caryclark@google.com24bec792012-08-20 12:43:57 +00002830 void markOneDone(const char* funName, int tIndex, int winding) {
2831 Span* span = markOneWinding(funName, tIndex, winding);
2832 if (!span) {
2833 return;
2834 }
2835 span->fDone = true;
2836 fDoneSpans++;
2837 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002838
caryclark@google.com24bec792012-08-20 12:43:57 +00002839 Span* markOneWinding(const char* funName, int tIndex, int winding) {
2840 Span& span = fTs[tIndex];
2841 if (span.fDone) {
2842 return NULL;
2843 }
2844 #if DEBUG_MARK_DONE
2845 debugShowNewWinding(funName, span, winding);
2846 #endif
2847 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
caryclark@google.com03f97062012-08-21 13:13:52 +00002848 #ifdef SK_DEBUG
caryclark@google.com24bec792012-08-20 12:43:57 +00002849 SkASSERT(abs(winding) <= gDebugMaxWindSum);
caryclark@google.com03f97062012-08-21 13:13:52 +00002850 #endif
caryclark@google.com24bec792012-08-20 12:43:57 +00002851 span.fWindSum = winding;
2852 return &span;
2853 }
skia.committer@gmail.com24c29d92012-10-20 02:01:23 +00002854
caryclark@google.comf839c032012-10-26 21:03:50 +00002855 // note that just because a span has one end that is unsortable, that's
2856 // not enough to mark it done. The other end may be sortable, allowing the
2857 // span to be added.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002858 void markUnsortable(int start, int end) {
2859 Span* span = &fTs[start];
2860 if (start < end) {
2861 span->fUnsortableStart = true;
2862 } else {
2863 --span;
2864 span->fUnsortableEnd = true;
2865 }
caryclark@google.comf839c032012-10-26 21:03:50 +00002866 if (!span->fUnsortableStart || !span->fUnsortableEnd || span->fDone) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002867 return;
2868 }
2869 span->fDone = true;
2870 fDoneSpans++;
2871 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002872
caryclark@google.com59823f72012-08-09 18:17:47 +00002873 void markWinding(int index, int winding) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00002874 // SkASSERT(!done());
caryclark@google.com24bec792012-08-20 12:43:57 +00002875 SkASSERT(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002876 double referenceT = fTs[index].fT;
2877 int lesser = index;
caryclark@google.coma461ff02012-10-11 12:54:23 +00002878 #if PRECISE_T_SORT
2879 while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
2880 markOneWinding(__FUNCTION__, lesser, winding);
2881 }
2882 do {
2883 markOneWinding(__FUNCTION__, index, winding);
2884 } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
2885 #else
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002886 while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
caryclark@google.com24bec792012-08-20 12:43:57 +00002887 markOneWinding(__FUNCTION__, lesser, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002888 }
2889 do {
caryclark@google.com24bec792012-08-20 12:43:57 +00002890 markOneWinding(__FUNCTION__, index, winding);
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002891 } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
caryclark@google.coma461ff02012-10-11 12:54:23 +00002892 #endif
caryclark@google.comaf46cff2012-05-22 21:12:00 +00002893 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002894
caryclark@google.com2ddff932012-08-07 21:25:27 +00002895 void matchWindingValue(int tIndex, double t, bool borrowWind) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002896 int nextDoorWind = SK_MaxS32;
2897 if (tIndex > 0) {
2898 const Span& below = fTs[tIndex - 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002899 if (approximately_negative(t - below.fT)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002900 nextDoorWind = below.fWindValue;
2901 }
2902 }
2903 if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
2904 const Span& above = fTs[tIndex + 1];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002905 if (approximately_negative(above.fT - t)) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002906 nextDoorWind = above.fWindValue;
2907 }
2908 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00002909 if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
2910 const Span& below = fTs[tIndex - 1];
2911 nextDoorWind = below.fWindValue;
2912 }
caryclark@google.com0c803d02012-08-06 11:15:47 +00002913 if (nextDoorWind != SK_MaxS32) {
2914 Span& newSpan = fTs[tIndex];
2915 newSpan.fWindValue = nextDoorWind;
caryclark@google.comf839c032012-10-26 21:03:50 +00002916 if (!nextDoorWind && !newSpan.fDone) {
caryclark@google.com0c803d02012-08-06 11:15:47 +00002917 newSpan.fDone = true;
2918 ++fDoneSpans;
2919 }
2920 }
2921 }
2922
caryclark@google.com9764cc62012-07-12 19:29:45 +00002923 // return span if when chasing, two or more radiating spans are not done
2924 // OPTIMIZATION: ? multiple spans is detected when there is only one valid
2925 // candidate and the remaining spans have windValue == 0 (canceled by
2926 // coincidence). The coincident edges could either be removed altogether,
2927 // or this code could be more complicated in detecting this case. Worth it?
2928 bool multipleSpans(int end) const {
2929 return end > 0 && end < fTs.count() - 1;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002930 }
2931
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002932 // This has callers for two different situations: one establishes the end
2933 // of the current span, and one establishes the beginning of the next span
2934 // (thus the name). When this is looking for the end of the current span,
2935 // coincidence is found when the beginning Ts contain -step and the end
2936 // contains step. When it is looking for the beginning of the next, the
2937 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002938 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002939 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002940 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00002941 int count = fTs.count();
2942 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00002943 while (step > 0 ? ++to < count : --to >= 0) {
2944 const Span& span = fTs[to];
caryclark@google.com3350c3c2012-08-24 15:24:36 +00002945 if (approximately_zero(span.fT - fromSpan.fT)) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002946 continue;
2947 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002948 return to;
2949 }
2950 return -1;
2951 }
skia.committer@gmail.com439cb512012-10-10 02:01:30 +00002952
caryclark@google.coma461ff02012-10-11 12:54:23 +00002953#if PRECISE_T_SORT
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002954 // FIXME
2955 // this returns at any difference in T, vs. a preset minimum. It may be
2956 // that all callers to nextSpan should use this instead.
caryclark@google.comfb51afb2012-10-19 15:54:16 +00002957 // OPTIMIZATION splitting this into separate loops for up/down steps
2958 // would allow using precisely_negative instead of precisely_zero
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002959 int nextExactSpan(int from, int step) const {
2960 const Span& fromSpan = fTs[from];
2961 int count = fTs.count();
2962 int to = from;
2963 while (step > 0 ? ++to < count : --to >= 0) {
2964 const Span& span = fTs[to];
caryclark@google.coma461ff02012-10-11 12:54:23 +00002965 if (precisely_zero(span.fT - fromSpan.fT)) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00002966 continue;
2967 }
2968 return to;
2969 }
2970 return -1;
2971 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00002972#endif
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00002973
caryclark@google.com235f56a2012-09-14 14:19:30 +00002974 bool operand() const {
2975 return fOperand;
2976 }
2977
2978 int oppSign(int startIndex, int endIndex) const {
2979 int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
2980 fTs[endIndex].fWindValueOpp;
2981#if DEBUG_WIND_BUMP
2982 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
2983#endif
2984 return result;
2985 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00002986
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002987 const SkPoint* pts() const {
2988 return fPts;
2989 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002990
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002991 void reset() {
caryclark@google.com235f56a2012-09-14 14:19:30 +00002992 init(NULL, (SkPath::Verb) -1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002993 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
2994 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002995 }
2996
caryclark@google.comf839c032012-10-26 21:03:50 +00002997 // This marks all spans unsortable so that this info is available for early
2998 // exclusion in find top and others. This could be optimized to only mark
2999 // adjacent spans that unsortable. However, this makes it difficult to later
3000 // determine starting points for edge detection in find top and the like.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003001 static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003002 bool sortable = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003003 int angleCount = angles.count();
3004 int angleIndex;
3005 angleList.setReserve(angleCount);
3006 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003007 Angle& angle = angles[angleIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00003008 *angleList.append() = &angle;
3009 sortable &= !angle.unsortable();
3010 }
3011 if (sortable) {
3012 QSort<Angle>(angleList.begin(), angleList.end() - 1);
3013 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3014 if (angles[angleIndex].unsortable()) {
3015 sortable = false;
3016 break;
3017 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003018 }
3019 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003020 if (!sortable) {
3021 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
3022 Angle& angle = angles[angleIndex];
3023 angle.segment()->markUnsortable(angle.start(), angle.end());
3024 }
3025 }
3026 return sortable;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003027 }
3028
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003029 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003030 const Span& span(int tIndex) const {
3031 return fTs[tIndex];
3032 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003033
caryclark@google.com235f56a2012-09-14 14:19:30 +00003034 int spanSign(const Angle* angle) const {
3035 SkASSERT(angle->segment() == this);
3036 return spanSign(angle->start(), angle->end());
3037 }
3038
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003039 int spanSign(int startIndex, int endIndex) const {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003040 int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003041 fTs[endIndex].fWindValue;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003042#if DEBUG_WIND_BUMP
3043 SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
3044#endif
3045 return result;
3046 }
3047
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003048 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003049 double t(int tIndex) const {
3050 return fTs[tIndex].fT;
3051 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003052
caryclark@google.comf839c032012-10-26 21:03:50 +00003053 bool tiny(const Angle& angle) const {
3054 int start = angle.start();
3055 int end = angle.end();
3056 const Span& mSpan = fTs[SkMin32(start, end)];
3057 return mSpan.fTiny;
3058 }
3059
caryclark@google.com18063442012-07-25 12:05:18 +00003060 static void TrackOutside(SkTDArray<double>& outsideTs, double end,
3061 double start) {
3062 int outCount = outsideTs.count();
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003063 if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
caryclark@google.com18063442012-07-25 12:05:18 +00003064 *outsideTs.append() = end;
3065 *outsideTs.append() = start;
3066 }
3067 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003068
caryclark@google.com24bec792012-08-20 12:43:57 +00003069 void undoneSpan(int& start, int& end) {
3070 size_t tCount = fTs.count();
3071 size_t index;
3072 for (index = 0; index < tCount; ++index) {
3073 if (!fTs[index].fDone) {
3074 break;
3075 }
3076 }
3077 SkASSERT(index < tCount - 1);
3078 start = index;
3079 double startT = fTs[index].fT;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003080 while (approximately_negative(fTs[++index].fT - startT))
caryclark@google.com24bec792012-08-20 12:43:57 +00003081 SkASSERT(index < tCount);
3082 SkASSERT(index < tCount);
3083 end = index;
3084 }
caryclark@google.com18063442012-07-25 12:05:18 +00003085
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003086 bool unsortable(int index) const {
3087 return fTs[index].fUnsortableStart || fTs[index].fUnsortableEnd;
3088 }
3089
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003090 void updatePts(const SkPoint pts[]) {
3091 fPts = pts;
3092 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003093
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003094 SkPath::Verb verb() const {
3095 return fVerb;
3096 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003097
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003098 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003099 return fTs[tIndex].fWindSum;
3100 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003101
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003102 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00003103 int start = angle->start();
3104 int end = angle->end();
3105 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00003106 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00003107 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003108
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003109 int windValue(int tIndex) const {
3110 return fTs[tIndex].fWindValue;
3111 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003112
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003113 int windValue(const Angle* angle) const {
3114 int start = angle->start();
3115 int end = angle->end();
3116 int index = SkMin32(start, end);
3117 return windValue(index);
3118 }
3119
3120 SkScalar xAtT(const Span* span) const {
3121 return xyAtT(span).fX;
3122 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003123
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003124 const SkPoint& xyAtT(int index) const {
3125 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003126 }
3127
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003128 const SkPoint& xyAtT(const Span* span) const {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003129 if (SkScalarIsNaN(span->fPt.fX)) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003130 if (span->fT == 0) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003131 span->fPt = fPts[0];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003132 } else if (span->fT == 1) {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003133 span->fPt = fPts[fVerb];
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003134 } else {
caryclark@google.com27c449a2012-07-27 18:26:38 +00003135 (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00003136 }
3137 }
caryclark@google.com27c449a2012-07-27 18:26:38 +00003138 return span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003139 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003140
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003141 SkScalar yAtT(int index) const {
3142 return yAtT(&fTs[index]);
3143 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003144
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003145 SkScalar yAtT(const Span* span) const {
3146 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003147 }
3148
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003149#if DEBUG_DUMP
3150 void dump() const {
3151 const char className[] = "Segment";
3152 const int tab = 4;
3153 for (int i = 0; i < fTs.count(); ++i) {
3154 SkPoint out;
3155 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
3156 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003157 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003158 tab + sizeof(className), className, fID,
3159 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003160 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003161 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003162 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003163 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00003164 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003165 }
3166#endif
3167
caryclark@google.com47580692012-07-23 12:14:49 +00003168#if DEBUG_CONCIDENT
caryclark@google.comcc905052012-07-25 20:59:42 +00003169 // assert if pair has not already been added
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003170 void debugAddTPair(double t, const Segment& other, double otherT) const {
caryclark@google.comcc905052012-07-25 20:59:42 +00003171 for (int i = 0; i < fTs.count(); ++i) {
3172 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
3173 return;
3174 }
3175 }
3176 SkASSERT(0);
3177 }
3178#endif
3179
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003180#if DEBUG_DUMP
3181 int debugID() const {
3182 return fID;
3183 }
3184#endif
3185
caryclark@google.com24bec792012-08-20 12:43:57 +00003186#if DEBUG_WINDING
3187 void debugShowSums() const {
3188 SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
3189 fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
3190 for (int i = 0; i < fTs.count(); ++i) {
3191 const Span& span = fTs[i];
3192 SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
3193 if (span.fWindSum == SK_MinS32) {
3194 SkDebugf("?");
3195 } else {
3196 SkDebugf("%d", span.fWindSum);
3197 }
3198 SkDebugf("]");
3199 }
3200 SkDebugf("\n");
3201 }
3202#endif
3203
caryclark@google.comcc905052012-07-25 20:59:42 +00003204#if DEBUG_CONCIDENT
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003205 void debugShowTs() const {
caryclark@google.com24bec792012-08-20 12:43:57 +00003206 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com47580692012-07-23 12:14:49 +00003207 for (int i = 0; i < fTs.count(); ++i) {
caryclark@google.com200c2112012-08-03 15:05:04 +00003208 SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
caryclark@google.com47580692012-07-23 12:14:49 +00003209 fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
3210 }
3211 SkDebugf("\n");
3212 }
3213#endif
3214
caryclark@google.com027de222012-07-12 12:52:50 +00003215#if DEBUG_ACTIVE_SPANS
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003216 void debugShowActiveSpans() const {
caryclark@google.com027de222012-07-12 12:52:50 +00003217 if (done()) {
3218 return;
3219 }
3220 for (int i = 0; i < fTs.count(); ++i) {
3221 if (fTs[i].fDone) {
3222 continue;
3223 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003224 SkDebugf("%s id=%d", __FUNCTION__, fID);
caryclark@google.com027de222012-07-12 12:52:50 +00003225 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3226 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3227 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3228 }
3229 const Span* span = &fTs[i];
rmistry@google.comd6176b02012-08-23 18:14:13 +00003230 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
caryclark@google.com0c803d02012-08-06 11:15:47 +00003231 xAtT(span), yAtT(span));
caryclark@google.com027de222012-07-12 12:52:50 +00003232 const Segment* other = fTs[i].fOther;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003233 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
3234 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
3235 if (fTs[i].fWindSum == SK_MinS32) {
3236 SkDebugf("?");
3237 } else {
3238 SkDebugf("%d", fTs[i].fWindSum);
3239 }
3240 SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
caryclark@google.com027de222012-07-12 12:52:50 +00003241 }
3242 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003243
3244 // This isn't useful yet -- but leaving it in for now in case i think of something
3245 // to use it for
3246 void validateActiveSpans() const {
3247 if (done()) {
3248 return;
3249 }
3250 int tCount = fTs.count();
3251 for (int index = 0; index < tCount; ++index) {
3252 if (fTs[index].fDone) {
3253 continue;
3254 }
3255 // count number of connections which are not done
3256 int first = index;
3257 double baseT = fTs[index].fT;
3258 while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
3259 --first;
3260 }
3261 int last = index;
3262 while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
3263 ++last;
3264 }
3265 int connections = 0;
3266 connections += first > 0 && !fTs[first - 1].fDone;
3267 for (int test = first; test <= last; ++test) {
3268 connections += !fTs[test].fDone;
3269 const Segment* other = fTs[test].fOther;
3270 int oIndex = fTs[test].fOtherIndex;
3271 connections += !other->fTs[oIndex].fDone;
3272 connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
3273 }
3274 // SkASSERT(!(connections & 1));
3275 }
3276 }
caryclark@google.com027de222012-07-12 12:52:50 +00003277#endif
3278
caryclark@google.com0c803d02012-08-06 11:15:47 +00003279#if DEBUG_MARK_DONE
3280 void debugShowNewWinding(const char* fun, const Span& span, int winding) {
3281 const SkPoint& pt = xyAtT(&span);
3282 SkDebugf("%s id=%d", fun, fID);
3283 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
3284 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
3285 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
3286 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003287 SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
3288 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
3289 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) newWindSum=%d windSum=",
3290 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, winding);
caryclark@google.com0c803d02012-08-06 11:15:47 +00003291 if (span.fWindSum == SK_MinS32) {
3292 SkDebugf("?");
3293 } else {
3294 SkDebugf("%d", span.fWindSum);
3295 }
3296 SkDebugf(" windValue=%d\n", span.fWindValue);
3297 }
3298#endif
3299
caryclark@google.com47580692012-07-23 12:14:49 +00003300#if DEBUG_SORT
caryclark@google.com03f97062012-08-21 13:13:52 +00003301 void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003302 const int contourWinding) const {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003303 SkASSERT(angles[first]->segment() == this);
caryclark@google.com200c2112012-08-03 15:05:04 +00003304 SkASSERT(angles.count() > 1);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003305 int lastSum = contourWinding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003306 int windSum = lastSum - spanSign(angles[first]);
caryclark@google.com03f97062012-08-21 13:13:52 +00003307 SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
caryclark@google.com2ddff932012-08-07 21:25:27 +00003308 contourWinding, spanSign(angles[first]));
caryclark@google.comafe56de2012-07-24 18:11:03 +00003309 int index = first;
3310 bool firstTime = true;
caryclark@google.com47580692012-07-23 12:14:49 +00003311 do {
3312 const Angle& angle = *angles[index];
3313 const Segment& segment = *angle.segment();
3314 int start = angle.start();
3315 int end = angle.end();
3316 const Span& sSpan = segment.fTs[start];
3317 const Span& eSpan = segment.fTs[end];
3318 const Span& mSpan = segment.fTs[SkMin32(start, end)];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003319 if (!firstTime) {
caryclark@google.comafe56de2012-07-24 18:11:03 +00003320 lastSum = windSum;
caryclark@google.com2ddff932012-08-07 21:25:27 +00003321 windSum -= segment.spanSign(&angle);
caryclark@google.comafe56de2012-07-24 18:11:03 +00003322 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003323 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 +00003324 " sign=%d windValue=%d windSum=",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003325 __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE *** " : "",
caryclark@google.comc91dfe42012-10-16 12:06:27 +00003326 segment.fID, kLVerbStr[segment.fVerb],
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003327 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
3328 segment.xAtT(&eSpan), segment.yAtT(&eSpan), angle.sign(),
3329 mSpan.fWindValue);
3330 if (mSpan.fWindSum == SK_MinS32) {
3331 SkDebugf("?");
3332 } else {
3333 SkDebugf("%d", mSpan.fWindSum);
3334 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003335 SkDebugf(" winding: %d->%d (max=%d) done=%d tiny=%d\n", lastSum, windSum,
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003336 useInnerWinding(lastSum, windSum) ? windSum : lastSum,
caryclark@google.comf839c032012-10-26 21:03:50 +00003337 mSpan.fDone, mSpan.fTiny);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003338#if false && DEBUG_ANGLE
caryclark@google.comc899ad92012-08-23 15:24:42 +00003339 angle.debugShow(segment.xyAtT(&sSpan));
3340#endif
caryclark@google.com47580692012-07-23 12:14:49 +00003341 ++index;
3342 if (index == angles.count()) {
3343 index = 0;
3344 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003345 if (firstTime) {
3346 firstTime = false;
3347 }
caryclark@google.com47580692012-07-23 12:14:49 +00003348 } while (index != first);
3349 }
3350#endif
3351
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003352#if DEBUG_WINDING
3353 bool debugVerifyWinding(int start, int end, int winding) const {
3354 const Span& span = fTs[SkMin32(start, end)];
3355 int spanWinding = span.fWindSum;
3356 if (spanWinding == SK_MinS32) {
3357 return true;
3358 }
3359 int spanSign = SkSign32(start - end);
3360 int signedVal = spanSign * span.fWindValue;
3361 if (signedVal < 0) {
3362 spanWinding -= signedVal;
3363 }
3364 return span.fWindSum == winding;
3365 }
3366#endif
3367
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003368private:
3369 const SkPoint* fPts;
3370 SkPath::Verb fVerb;
3371 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003372 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.com24bec792012-08-20 12:43:57 +00003373 int fDoneSpans; // quick check that segment is finished
caryclark@google.com235f56a2012-09-14 14:19:30 +00003374 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003375#if DEBUG_DUMP
3376 int fID;
3377#endif
3378};
3379
caryclark@google.comb9738012012-07-03 19:53:30 +00003380class Contour;
3381
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003382struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00003383 Contour* fContours[2];
3384 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003385 double fTs[2][2];
caryclark@google.com235f56a2012-09-14 14:19:30 +00003386 bool fXor;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003387};
3388
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003389class Contour {
3390public:
3391 Contour() {
3392 reset();
3393#if DEBUG_DUMP
3394 fID = ++gContourID;
3395#endif
3396 }
3397
3398 bool operator<(const Contour& rh) const {
3399 return fBounds.fTop == rh.fBounds.fTop
3400 ? fBounds.fLeft < rh.fBounds.fLeft
3401 : fBounds.fTop < rh.fBounds.fTop;
3402 }
3403
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003404 void addCoincident(int index, Contour* other, int otherIndex,
3405 const Intersections& ts, bool swap) {
3406 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00003407 coincidence.fContours[0] = this;
3408 coincidence.fContours[1] = other;
3409 coincidence.fSegments[0] = index;
3410 coincidence.fSegments[1] = otherIndex;
caryclark@google.com32546db2012-08-31 20:55:07 +00003411 if (fSegments[index].verb() == SkPath::kLine_Verb &&
3412 other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
3413 // FIXME: coincident lines use legacy Ts instead of coincident Ts
3414 coincidence.fTs[swap][0] = ts.fT[0][0];
3415 coincidence.fTs[swap][1] = ts.fT[0][1];
3416 coincidence.fTs[!swap][0] = ts.fT[1][0];
3417 coincidence.fTs[!swap][1] = ts.fT[1][1];
3418 } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
3419 other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
3420 coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
3421 coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
3422 coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
3423 coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
3424 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003425 coincidence.fXor = fOperand == other->fOperand ? fXor : true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003426 }
3427
3428 void addCross(const Contour* crosser) {
3429#ifdef DEBUG_CROSS
3430 for (int index = 0; index < fCrosses.count(); ++index) {
3431 SkASSERT(fCrosses[index] != crosser);
3432 }
3433#endif
3434 *fCrosses.append() = crosser;
3435 }
3436
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003437 void addCubic(const SkPoint pts[4]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003438 fSegments.push_back().addCubic(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003439 fContainsCurves = true;
3440 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003441
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003442 int addLine(const SkPoint pts[2]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003443 fSegments.push_back().addLine(pts, fOperand);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003444 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003445 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003446
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003447 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
3448 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
3449 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003450
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003451 int addQuad(const SkPoint pts[3]) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003452 fSegments.push_back().addQuad(pts, fOperand);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003453 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003454 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003455 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003456
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003457 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
3458 containsIntercepts();
3459 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
3460 }
3461
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003462 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003463 return fBounds;
3464 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003465
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003466 void collapseTriangles() {
3467 int segmentCount = fSegments.count();
3468 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3469 fSegments[sIndex].collapseTriangles(fXor);
3470 }
3471 }
3472
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003473 void complete() {
3474 setBounds();
3475 fContainsIntercepts = false;
3476 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003477
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003478 void containsIntercepts() {
3479 fContainsIntercepts = true;
3480 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003481
rmistry@google.comd6176b02012-08-23 18:14:13 +00003482 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003483 int &tIndex, double& hitT) {
3484 int segmentCount = fSegments.count();
3485 const Segment* bestSegment = NULL;
3486 for (int test = 0; test < segmentCount; ++test) {
3487 Segment* testSegment = &fSegments[test];
3488 const SkRect& bounds = testSegment->bounds();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003489 if (bounds.fBottom <= bestY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003490 continue;
3491 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003492 if (bounds.fTop >= basePt.fY) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003493 continue;
3494 }
3495 if (bounds.fLeft > basePt.fX) {
3496 continue;
3497 }
3498 if (bounds.fRight < basePt.fX) {
3499 continue;
3500 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003501 if (bounds.fLeft == bounds.fRight) {
3502 continue;
3503 }
3504 #if 0
3505 bool leftHalf = bounds.fLeft == basePt.fX;
3506 bool rightHalf = bounds.fRight == basePt.fX;
3507 if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
3508 basePt, leftHalf, rightHalf)) {
3509 continue;
3510 }
3511 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003512 double testHitT;
3513 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
3514 if (testT >= 0) {
3515 bestSegment = testSegment;
3516 tIndex = testT;
3517 hitT = testHitT;
3518 }
3519 }
3520 return bestSegment;
3521 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003522
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003523 bool crosses(const Contour* crosser) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003524 for (int index = 0; index < fCrosses.count(); ++index) {
3525 if (fCrosses[index] == crosser) {
3526 return true;
3527 }
3528 }
3529 return false;
3530 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003531
caryclark@google.comf839c032012-10-26 21:03:50 +00003532 const SkPoint& end() const {
3533 const Segment& segment = fSegments.back();
3534 return segment.pts()[segment.verb()];
3535 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003536
caryclark@google.com235f56a2012-09-14 14:19:30 +00003537 void findTooCloseToCall() {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003538 int segmentCount = fSegments.count();
3539 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
caryclark@google.com235f56a2012-09-14 14:19:30 +00003540 fSegments[sIndex].findTooCloseToCall(fXor);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003541 }
3542 }
3543
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003544 void fixOtherTIndex() {
3545 int segmentCount = fSegments.count();
3546 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
3547 fSegments[sIndex].fixOtherTIndex();
3548 }
3549 }
3550
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003551 void reset() {
3552 fSegments.reset();
3553 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00003554 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003555 }
caryclark@google.comb9738012012-07-03 19:53:30 +00003556
caryclark@google.com235f56a2012-09-14 14:19:30 +00003557 // FIXME: for binary ops, need to keep both ops winding contributions separately
3558 // in edge array
3559 void resolveCoincidence() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003560 int count = fCoincidences.count();
3561 for (int index = 0; index < count; ++index) {
3562 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00003563 Contour* thisContour = coincidence.fContours[0];
3564 Contour* otherContour = coincidence.fContours[1];
3565 int thisIndex = coincidence.fSegments[0];
3566 int otherIndex = coincidence.fSegments[1];
3567 Segment& thisOne = thisContour->fSegments[thisIndex];
3568 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com47580692012-07-23 12:14:49 +00003569 #if DEBUG_CONCIDENT
3570 thisOne.debugShowTs();
3571 other.debugShowTs();
3572 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003573 double startT = coincidence.fTs[0][0];
3574 double endT = coincidence.fTs[0][1];
caryclark@google.com32546db2012-08-31 20:55:07 +00003575 bool opposite = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003576 if (startT > endT) {
3577 SkTSwap<double>(startT, endT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003578 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003579 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003580 SkASSERT(!approximately_negative(endT - startT));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003581 double oStartT = coincidence.fTs[1][0];
3582 double oEndT = coincidence.fTs[1][1];
3583 if (oStartT > oEndT) {
3584 SkTSwap<double>(oStartT, oEndT);
caryclark@google.com32546db2012-08-31 20:55:07 +00003585 opposite ^= true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003586 }
caryclark@google.com3350c3c2012-08-24 15:24:36 +00003587 SkASSERT(!approximately_negative(oEndT - oStartT));
caryclark@google.com32546db2012-08-31 20:55:07 +00003588 if (opposite) {
caryclark@google.comb9738012012-07-03 19:53:30 +00003589 // make sure startT and endT have t entries
caryclark@google.com32546db2012-08-31 20:55:07 +00003590 SkASSERT(opposite);
caryclark@google.com2ddff932012-08-07 21:25:27 +00003591 if (startT > 0 || oEndT < 1
3592 || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
3593 thisOne.addTPair(startT, other, oEndT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003594 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00003595 if (oStartT > 0 || endT < 1
3596 || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
3597 other.addTPair(oStartT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003598 }
3599 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003600 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00003601 if (startT > 0 || oStartT > 0
3602 || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003603 thisOne.addTPair(startT, other, oStartT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003604 }
caryclark@google.com200c2112012-08-03 15:05:04 +00003605 if (endT < 1 || oEndT < 1
3606 || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
caryclark@google.com2ddff932012-08-07 21:25:27 +00003607 other.addTPair(oEndT, thisOne, endT, true);
caryclark@google.comb9738012012-07-03 19:53:30 +00003608 }
caryclark@google.com235f56a2012-09-14 14:19:30 +00003609 thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003610 }
caryclark@google.com47580692012-07-23 12:14:49 +00003611 #if DEBUG_CONCIDENT
3612 thisOne.debugShowTs();
3613 other.debugShowTs();
3614 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003615 }
3616 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003617
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003618 const SkTArray<Segment>& segments() {
3619 return fSegments;
3620 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00003621
caryclark@google.com235f56a2012-09-14 14:19:30 +00003622 void setOperand(bool isOp) {
3623 fOperand = isOp;
3624 }
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00003625
caryclark@google.com235f56a2012-09-14 14:19:30 +00003626 void setXor(bool isXor) {
3627 fXor = isXor;
3628 }
3629
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003630#if !SORTABLE_CONTOURS
3631 void sortSegments() {
3632 int segmentCount = fSegments.count();
3633 fSortedSegments.setReserve(segmentCount);
3634 for (int test = 0; test < segmentCount; ++test) {
3635 *fSortedSegments.append() = &fSegments[test];
3636 }
3637 QSort<Segment>(fSortedSegments.begin(), fSortedSegments.end() - 1);
3638 fFirstSorted = 0;
3639 }
3640#endif
3641
caryclark@google.comf839c032012-10-26 21:03:50 +00003642 const SkPoint& start() const {
3643 return fSegments.front().pts()[0];
3644 }
3645
3646 void toPath(PathWrapper& path) const {
3647 int segmentCount = fSegments.count();
3648 const SkPoint& pt = fSegments.front().pts()[0];
3649 path.deferredMove(pt);
3650 for (int test = 0; test < segmentCount; ++test) {
3651 fSegments[test].addCurveTo(0, 1, path, true);
3652 }
3653 path.close();
3654 }
skia.committer@gmail.com549c93e2012-10-27 02:01:15 +00003655
caryclark@google.comf839c032012-10-26 21:03:50 +00003656 void toPartialBackward(PathWrapper& path) const {
3657 int segmentCount = fSegments.count();
3658 for (int test = segmentCount - 1; test >= 0; --test) {
3659 fSegments[test].addCurveTo(1, 0, path, true);
3660 }
3661 }
3662
3663 void toPartialForward(PathWrapper& path) const {
3664 int segmentCount = fSegments.count();
3665 for (int test = 0; test < segmentCount; ++test) {
3666 fSegments[test].addCurveTo(0, 1, path, true);
3667 }
3668 }
3669
3670#if 0 // FIXME: obsolete, remove
caryclark@google.com15fa1382012-05-07 20:49:36 +00003671 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
3672 // we need to sort and walk edges in y, but that on the surface opens the
rmistry@google.comd6176b02012-08-23 18:14:13 +00003673 // same can of worms as before. But then, this is a rough sort based on
caryclark@google.com15fa1382012-05-07 20:49:36 +00003674 // segments' top, and not a true sort, so it could be ameniable to regular
3675 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003676 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00003677 int segmentCount = fSegments.count();
3678 SkASSERT(segmentCount > 0);
3679 int best = -1;
3680 Segment* bestSegment = NULL;
3681 while (++best < segmentCount) {
3682 Segment* testSegment = &fSegments[best];
3683 if (testSegment->done()) {
3684 continue;
3685 }
3686 bestSegment = testSegment;
3687 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003688 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00003689 if (!bestSegment) {
3690 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003691 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003692 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003693 for (int test = best + 1; test < segmentCount; ++test) {
3694 Segment* testSegment = &fSegments[test];
3695 if (testSegment->done()) {
3696 continue;
3697 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003698 if (testSegment->bounds().fTop > bestTop) {
3699 continue;
3700 }
3701 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00003702 if (bestTop > testTop) {
3703 bestTop = testTop;
3704 bestSegment = testSegment;
3705 }
3706 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003707 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00003708 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003709 }
caryclark@google.comf839c032012-10-26 21:03:50 +00003710#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003711
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003712#if !SORTABLE_CONTOURS
caryclark@google.comf839c032012-10-26 21:03:50 +00003713 Segment* topSortableSegment(const SkPoint& topLeft, SkPoint& bestXY) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003714 int segmentCount = fSortedSegments.count();
3715 SkASSERT(segmentCount > 0);
3716 Segment* bestSegment = NULL;
caryclark@google.comf839c032012-10-26 21:03:50 +00003717 int sortedIndex = fFirstSorted;
3718 for ( ; sortedIndex < segmentCount; ++sortedIndex) {
3719 Segment* testSegment = fSortedSegments[sortedIndex];
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003720 if (testSegment->done()) {
caryclark@google.comf839c032012-10-26 21:03:50 +00003721 if (sortedIndex == fFirstSorted) {
3722 ++fFirstSorted;
3723 }
3724 continue;
3725 }
3726 SkPoint testXY;
3727 testSegment->activeLeftTop(testXY);
3728 if (testXY.fY < topLeft.fY) {
3729 continue;
3730 }
3731 if (testXY.fY == topLeft.fY && testXY.fX < topLeft.fX) {
3732 continue;
3733 }
3734 if (bestXY.fY < testXY.fY) {
3735 continue;
3736 }
3737 if (bestXY.fY == testXY.fY && bestXY.fX < testXY.fX) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003738 continue;
3739 }
3740 bestSegment = testSegment;
caryclark@google.comf839c032012-10-26 21:03:50 +00003741 bestXY = testXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003742 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003743 return bestSegment;
3744 }
3745#endif
3746
caryclark@google.com24bec792012-08-20 12:43:57 +00003747 Segment* undoneSegment(int& start, int& end) {
3748 int segmentCount = fSegments.count();
3749 for (int test = 0; test < segmentCount; ++test) {
3750 Segment* testSegment = &fSegments[test];
3751 if (testSegment->done()) {
3752 continue;
3753 }
3754 testSegment->undoneSpan(start, end);
3755 return testSegment;
3756 }
3757 return NULL;
3758 }
3759
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003760 int updateSegment(int index, const SkPoint* pts) {
3761 Segment& segment = fSegments[index];
3762 segment.updatePts(pts);
3763 return segment.verb() + 1;
3764 }
3765
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003766#if DEBUG_TEST
3767 SkTArray<Segment>& debugSegments() {
3768 return fSegments;
3769 }
3770#endif
3771
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003772#if DEBUG_DUMP
3773 void dump() {
3774 int i;
3775 const char className[] = "Contour";
3776 const int tab = 4;
3777 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
3778 for (i = 0; i < fSegments.count(); ++i) {
3779 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
3780 className, i);
3781 fSegments[i].dump();
3782 }
3783 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
3784 tab + sizeof(className), className,
3785 fBounds.fLeft, fBounds.fTop,
3786 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003787 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
3788 className, fContainsIntercepts);
3789 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
3790 className, fContainsCurves);
3791 }
3792#endif
3793
caryclark@google.com027de222012-07-12 12:52:50 +00003794#if DEBUG_ACTIVE_SPANS
3795 void debugShowActiveSpans() {
3796 for (int index = 0; index < fSegments.count(); ++index) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00003797 fSegments[index].debugShowActiveSpans();
caryclark@google.com027de222012-07-12 12:52:50 +00003798 }
3799 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00003800
3801 void validateActiveSpans() {
3802 for (int index = 0; index < fSegments.count(); ++index) {
3803 fSegments[index].validateActiveSpans();
3804 }
3805 }
caryclark@google.com027de222012-07-12 12:52:50 +00003806#endif
3807
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003808protected:
3809 void setBounds() {
3810 int count = fSegments.count();
3811 if (count == 0) {
3812 SkDebugf("%s empty contour\n", __FUNCTION__);
3813 SkASSERT(0);
3814 // FIXME: delete empty contour?
3815 return;
3816 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003817 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003818 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003819 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003820 }
3821 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003822
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003823private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003824 SkTArray<Segment> fSegments;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00003825#if !SORTABLE_CONTOURS
3826 SkTDArray<Segment*> fSortedSegments;
3827 int fFirstSorted;
3828#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003829 SkTDArray<Coincidence> fCoincidences;
3830 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003831 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003832 bool fContainsIntercepts;
3833 bool fContainsCurves;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003834 bool fOperand; // true for the second argument to a binary operator
3835 bool fXor;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003836#if DEBUG_DUMP
3837 int fID;
3838#endif
3839};
3840
3841class EdgeBuilder {
3842public:
3843
caryclark@google.comf839c032012-10-26 21:03:50 +00003844EdgeBuilder(const PathWrapper& path, SkTArray<Contour>& contours)
3845 : fPath(path.nativePath())
3846 , fContours(contours)
3847{
3848 init();
3849}
3850
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003851EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.com235f56a2012-09-14 14:19:30 +00003852 : fPath(&path)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003853 , fContours(contours)
3854{
caryclark@google.comf839c032012-10-26 21:03:50 +00003855 init();
3856}
3857
3858void init() {
3859 fCurrentContour = NULL;
3860 fOperand = false;
3861 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003862#if DEBUG_DUMP
3863 gContourID = 0;
3864 gSegmentID = 0;
3865#endif
caryclark@google.com235f56a2012-09-14 14:19:30 +00003866 fSecondHalf = preFetch();
3867}
3868
3869void addOperand(const SkPath& path) {
3870 fPath = &path;
caryclark@google.comf839c032012-10-26 21:03:50 +00003871 fXorMask = (fPath->getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003872 preFetch();
3873}
3874
3875void finish() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003876 walk();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003877 complete();
3878 if (fCurrentContour && !fCurrentContour->segments().count()) {
3879 fContours.pop_back();
3880 }
3881 // correct pointers in contours since fReducePts may have moved as it grew
3882 int cIndex = 0;
3883 int extraCount = fExtra.count();
3884 SkASSERT(extraCount == 0 || fExtra[0] == -1);
3885 int eIndex = 0;
3886 int rIndex = 0;
3887 while (++eIndex < extraCount) {
3888 int offset = fExtra[eIndex];
3889 if (offset < 0) {
3890 ++cIndex;
3891 continue;
3892 }
3893 fCurrentContour = &fContours[cIndex];
3894 rIndex += fCurrentContour->updateSegment(offset - 1,
3895 &fReducePts[rIndex]);
3896 }
3897 fExtra.reset(); // we're done with this
3898}
3899
3900ShapeOpMask xorMask() const {
3901 return fXorMask;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003902}
3903
3904protected:
3905
3906void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00003907 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003908 fCurrentContour->complete();
3909 fCurrentContour = NULL;
3910 }
3911}
3912
caryclark@google.com235f56a2012-09-14 14:19:30 +00003913// FIXME:remove once we can access path pts directly
3914int preFetch() {
3915 SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003916 SkPoint pts[4];
3917 SkPath::Verb verb;
3918 do {
3919 verb = iter.next(pts);
3920 *fPathVerbs.append() = verb;
3921 if (verb == SkPath::kMove_Verb) {
3922 *fPathPts.append() = pts[0];
3923 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
3924 fPathPts.append(verb, &pts[1]);
3925 }
3926 } while (verb != SkPath::kDone_Verb);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003927 return fPathVerbs.count();
3928}
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003929
caryclark@google.com235f56a2012-09-14 14:19:30 +00003930void walk() {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003931 SkPath::Verb reducedVerb;
3932 uint8_t* verbPtr = fPathVerbs.begin();
caryclark@google.com235f56a2012-09-14 14:19:30 +00003933 uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003934 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003935 const SkPoint* finalCurveStart = NULL;
3936 const SkPoint* finalCurveEnd = NULL;
caryclark@google.com235f56a2012-09-14 14:19:30 +00003937 SkPath::Verb verb;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003938 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
3939 switch (verb) {
3940 case SkPath::kMove_Verb:
3941 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003942 if (!fCurrentContour) {
3943 fCurrentContour = fContours.push_back_n(1);
caryclark@google.com235f56a2012-09-14 14:19:30 +00003944 fCurrentContour->setOperand(fOperand);
3945 fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003946 *fExtra.append() = -1; // start new contour
3947 }
caryclark@google.com59823f72012-08-09 18:17:47 +00003948 finalCurveEnd = pointsPtr++;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003949 continue;
3950 case SkPath::kLine_Verb:
3951 // skip degenerate points
3952 if (pointsPtr[-1].fX != pointsPtr[0].fX
3953 || pointsPtr[-1].fY != pointsPtr[0].fY) {
3954 fCurrentContour->addLine(&pointsPtr[-1]);
3955 }
3956 break;
3957 case SkPath::kQuad_Verb:
rmistry@google.comd6176b02012-08-23 18:14:13 +00003958
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003959 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
3960 if (reducedVerb == 0) {
3961 break; // skip degenerate points
3962 }
3963 if (reducedVerb == 1) {
rmistry@google.comd6176b02012-08-23 18:14:13 +00003964 *fExtra.append() =
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003965 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003966 break;
3967 }
3968 fCurrentContour->addQuad(&pointsPtr[-1]);
3969 break;
3970 case SkPath::kCubic_Verb:
3971 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
3972 if (reducedVerb == 0) {
3973 break; // skip degenerate points
3974 }
3975 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003976 *fExtra.append() =
3977 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003978 break;
3979 }
3980 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003981 *fExtra.append() =
3982 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003983 break;
3984 }
3985 fCurrentContour->addCubic(&pointsPtr[-1]);
3986 break;
3987 case SkPath::kClose_Verb:
3988 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00003989 if (finalCurveStart && finalCurveEnd
3990 && *finalCurveStart != *finalCurveEnd) {
3991 *fReducePts.append() = *finalCurveStart;
3992 *fReducePts.append() = *finalCurveEnd;
3993 *fExtra.append() =
3994 fCurrentContour->addLine(fReducePts.end() - 2);
3995 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003996 complete();
3997 continue;
3998 default:
3999 SkDEBUGFAIL("bad verb");
4000 return;
4001 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004002 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004003 pointsPtr += verb;
4004 SkASSERT(fCurrentContour);
caryclark@google.com235f56a2012-09-14 14:19:30 +00004005 if (verbPtr == endOfFirstHalf) {
4006 fOperand = true;
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +00004007 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004008 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004009}
4010
4011private:
caryclark@google.com235f56a2012-09-14 14:19:30 +00004012 const SkPath* fPath;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004013 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004014 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004015 Contour* fCurrentContour;
4016 SkTArray<Contour>& fContours;
4017 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004018 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.com235f56a2012-09-14 14:19:30 +00004019 ShapeOpMask fXorMask;
4020 int fSecondHalf;
4021 bool fOperand;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004022};
4023
4024class Work {
4025public:
4026 enum SegmentType {
4027 kHorizontalLine_Segment = -1,
4028 kVerticalLine_Segment = 0,
4029 kLine_Segment = SkPath::kLine_Verb,
4030 kQuad_Segment = SkPath::kQuad_Verb,
4031 kCubic_Segment = SkPath::kCubic_Verb,
4032 };
rmistry@google.comd6176b02012-08-23 18:14:13 +00004033
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004034 void addCoincident(Work& other, const Intersections& ts, bool swap) {
4035 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
4036 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004037
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004038 // FIXME: does it make sense to write otherIndex now if we're going to
4039 // fix it up later?
4040 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004041 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004042 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004043
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004044 // Avoid collapsing t values that are close to the same since
4045 // we walk ts to describe consecutive intersections. Since a pair of ts can
4046 // be nearly equal, any problems caused by this should be taken care
4047 // of later.
4048 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004049 int addT(double newT, const Work& other) {
4050 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004051 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004052
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004053 bool advance() {
4054 return ++fIndex < fLast;
4055 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004056
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004057 SkScalar bottom() const {
4058 return bounds().fBottom;
4059 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004060
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004061 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004062 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004063 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004064
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004065 const SkPoint* cubic() const {
4066 return fCubic;
4067 }
4068
4069 void init(Contour* contour) {
4070 fContour = contour;
4071 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004072 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004073 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00004074
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00004075 bool isAdjacent(const Work& next) {
4076 return fContour == next.fContour && fIndex + 1 == next.fIndex;
4077 }
4078
4079 bool isFirstLast(const Work& next) {
4080 return fContour == next.fContour && fIndex == 0
4081 && next.fIndex == fLast - 1;
4082 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004083
4084 SkScalar left() const {
4085 return bounds().fLeft;
4086 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004087
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004088 void promoteToCubic() {
4089 fCubic[0] = pts()[0];
4090 fCubic[2] = pts()[1];
4091 fCubic[3] = pts()[2];
4092 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
4093 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
4094 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
4095 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
4096 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004097
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004098 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004099 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004100 }
4101
4102 SkScalar right() const {
4103 return bounds().fRight;
4104 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004105
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004106 ptrdiff_t segmentIndex() const {
4107 return fIndex;
4108 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004109
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004110 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004111 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004112 SegmentType type = (SegmentType) segment.verb();
4113 if (type != kLine_Segment) {
4114 return type;
4115 }
4116 if (segment.isHorizontal()) {
4117 return kHorizontalLine_Segment;
4118 }
4119 if (segment.isVertical()) {
4120 return kVerticalLine_Segment;
4121 }
4122 return kLine_Segment;
4123 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004124
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004125 bool startAfter(const Work& after) {
4126 fIndex = after.fIndex;
4127 return advance();
4128 }
4129
4130 SkScalar top() const {
4131 return bounds().fTop;
4132 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004133
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004134 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004135 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004136 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004137
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004138 SkScalar x() const {
4139 return bounds().fLeft;
4140 }
4141
4142 bool xFlipped() const {
4143 return x() != pts()[0].fX;
4144 }
4145
4146 SkScalar y() const {
4147 return bounds().fTop;
4148 }
4149
4150 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004151 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004152 }
4153
4154protected:
4155 Contour* fContour;
4156 SkPoint fCubic[4];
4157 int fIndex;
4158 int fLast;
4159};
4160
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004161#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004162static void debugShowLineIntersection(int pts, const Work& wt,
4163 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004164 return;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004165 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004166 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
4167 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4168 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
4169 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004170 return;
4171 }
4172 SkPoint wtOutPt, wnOutPt;
4173 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4174 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004175 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 +00004176 __FUNCTION__,
4177 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4178 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
4179 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004180 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004181 }
caryclark@google.coma461ff02012-10-11 12:54:23 +00004182 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004183 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4184 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4185 if (pts == 2) {
caryclark@google.coma461ff02012-10-11 12:54:23 +00004186 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
4187 }
4188 SkDebugf("\n");
4189}
4190
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004191static void debugShowQuadLineIntersection(int pts, const Work& wt,
4192 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4193 if (!pts) {
4194 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
caryclark@google.com0b7da432012-10-31 19:00:20 +00004195 " (%1.9g,%1.9g %1.9g,%1.9g)\n",
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004196 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
4197 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004198 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004199 return;
4200 }
4201 SkPoint wtOutPt, wnOutPt;
4202 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4203 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4204 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4205 __FUNCTION__,
4206 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4207 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4208 wtOutPt.fX, wtOutPt.fY);
4209 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004210 QuadXYAtT(wt.pts(), wtTs[1], &wtOutPt);
4211 SkDebugf(" wtTs[1]=%1.9g (%1.9g,%1.9g)", wtTs[1], wtOutPt.fX, wtOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004212 }
4213 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4214 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4215 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
4216 if (pts == 2) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004217 LineXYAtT(wn.pts(), wnTs[1], &wnOutPt);
4218 SkDebugf(" wnTs[1]=%1.9g (%1.9g,%1.9g)", wnTs[1], wnOutPt.fX, wnOutPt.fY);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004219 }
4220 SkDebugf("\n");
4221}
4222
caryclark@google.coma461ff02012-10-11 12:54:23 +00004223static void debugShowQuadIntersection(int pts, const Work& wt,
4224 const Work& wn, const double wtTs[2], const double wnTs[2]) {
4225 if (!pts) {
4226 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
4227 " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
4228 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +00004229 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
caryclark@google.coma461ff02012-10-11 12:54:23 +00004230 wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004231 wn.pts()[2].fX, wn.pts()[2].fY );
caryclark@google.coma461ff02012-10-11 12:54:23 +00004232 return;
4233 }
4234 SkPoint wtOutPt, wnOutPt;
4235 QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
4236 QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
4237 SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4238 __FUNCTION__,
4239 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
4240 wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
4241 wtOutPt.fX, wtOutPt.fY);
4242 if (pts == 2) {
4243 SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
4244 }
4245 SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
4246 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
4247 wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
4248 wnOutPt.fX, wnOutPt.fY);
4249 if (pts == 2) {
4250 SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004251 }
caryclark@google.comb9738012012-07-03 19:53:30 +00004252 SkDebugf("\n");
4253}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004254#else
4255static void debugShowLineIntersection(int , const Work& ,
4256 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004257}
caryclark@google.coma461ff02012-10-11 12:54:23 +00004258
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004259static void debugShowQuadLineIntersection(int , const Work& ,
4260 const Work& , const double [2], const double [2]) {
4261}
4262
caryclark@google.coma461ff02012-10-11 12:54:23 +00004263static void debugShowQuadIntersection(int , const Work& ,
4264 const Work& , const double [2], const double [2]) {
4265}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004266#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004267
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00004268static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004269
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004270 if (test != next) {
4271 if (test->bounds().fBottom < next->bounds().fTop) {
4272 return false;
4273 }
4274 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
4275 return true;
4276 }
4277 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004278 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004279 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004280 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004281 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004282 Work wn;
4283 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004284 if (test == next && !wn.startAfter(wt)) {
4285 continue;
4286 }
4287 do {
4288 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
4289 continue;
4290 }
4291 int pts;
4292 Intersections ts;
4293 bool swap = false;
4294 switch (wt.segmentType()) {
4295 case Work::kHorizontalLine_Segment:
4296 swap = true;
4297 switch (wn.segmentType()) {
4298 case Work::kHorizontalLine_Segment:
4299 case Work::kVerticalLine_Segment:
4300 case Work::kLine_Segment: {
4301 pts = HLineIntersect(wn.pts(), wt.left(),
4302 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004303 debugShowLineIntersection(pts, wt, wn,
4304 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004305 break;
4306 }
4307 case Work::kQuad_Segment: {
4308 pts = HQuadIntersect(wn.pts(), wt.left(),
4309 wt.right(), wt.y(), wt.xFlipped(), ts);
4310 break;
4311 }
4312 case Work::kCubic_Segment: {
4313 pts = HCubicIntersect(wn.pts(), wt.left(),
4314 wt.right(), wt.y(), wt.xFlipped(), ts);
4315 break;
4316 }
4317 default:
4318 SkASSERT(0);
4319 }
4320 break;
4321 case Work::kVerticalLine_Segment:
4322 swap = true;
4323 switch (wn.segmentType()) {
4324 case Work::kHorizontalLine_Segment:
4325 case Work::kVerticalLine_Segment:
4326 case Work::kLine_Segment: {
4327 pts = VLineIntersect(wn.pts(), wt.top(),
4328 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004329 debugShowLineIntersection(pts, wt, wn,
4330 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004331 break;
4332 }
4333 case Work::kQuad_Segment: {
4334 pts = VQuadIntersect(wn.pts(), wt.top(),
4335 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4336 break;
4337 }
4338 case Work::kCubic_Segment: {
4339 pts = VCubicIntersect(wn.pts(), wt.top(),
4340 wt.bottom(), wt.x(), wt.yFlipped(), ts);
4341 break;
4342 }
4343 default:
4344 SkASSERT(0);
4345 }
4346 break;
4347 case Work::kLine_Segment:
4348 switch (wn.segmentType()) {
4349 case Work::kHorizontalLine_Segment:
4350 pts = HLineIntersect(wt.pts(), wn.left(),
4351 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004352 debugShowLineIntersection(pts, wt, wn,
4353 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004354 break;
4355 case Work::kVerticalLine_Segment:
4356 pts = VLineIntersect(wt.pts(), wn.top(),
4357 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004358 debugShowLineIntersection(pts, wt, wn,
4359 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004360 break;
4361 case Work::kLine_Segment: {
4362 pts = LineIntersect(wt.pts(), wn.pts(), ts);
4363 debugShowLineIntersection(pts, wt, wn,
4364 ts.fT[1], ts.fT[0]);
4365 break;
4366 }
4367 case Work::kQuad_Segment: {
4368 swap = true;
4369 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004370 debugShowQuadLineIntersection(pts, wn, wt,
4371 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004372 break;
4373 }
4374 case Work::kCubic_Segment: {
4375 swap = true;
4376 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
4377 break;
4378 }
4379 default:
4380 SkASSERT(0);
4381 }
4382 break;
4383 case Work::kQuad_Segment:
4384 switch (wn.segmentType()) {
4385 case Work::kHorizontalLine_Segment:
4386 pts = HQuadIntersect(wt.pts(), wn.left(),
4387 wn.right(), wn.y(), wn.xFlipped(), ts);
4388 break;
4389 case Work::kVerticalLine_Segment:
4390 pts = VQuadIntersect(wt.pts(), wn.top(),
4391 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4392 break;
4393 case Work::kLine_Segment: {
4394 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004395 debugShowQuadLineIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004396 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004397 break;
4398 }
4399 case Work::kQuad_Segment: {
4400 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
caryclark@google.coma461ff02012-10-11 12:54:23 +00004401 debugShowQuadIntersection(pts, wt, wn,
caryclark@google.com0b7da432012-10-31 19:00:20 +00004402 ts.fT[0], ts.fT[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004403 break;
4404 }
4405 case Work::kCubic_Segment: {
4406 wt.promoteToCubic();
4407 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
4408 break;
4409 }
4410 default:
4411 SkASSERT(0);
4412 }
4413 break;
4414 case Work::kCubic_Segment:
4415 switch (wn.segmentType()) {
4416 case Work::kHorizontalLine_Segment:
4417 pts = HCubicIntersect(wt.pts(), wn.left(),
4418 wn.right(), wn.y(), wn.xFlipped(), ts);
4419 break;
4420 case Work::kVerticalLine_Segment:
4421 pts = VCubicIntersect(wt.pts(), wn.top(),
4422 wn.bottom(), wn.x(), wn.yFlipped(), ts);
4423 break;
4424 case Work::kLine_Segment: {
4425 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
4426 break;
4427 }
4428 case Work::kQuad_Segment: {
4429 wn.promoteToCubic();
4430 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
4431 break;
4432 }
4433 case Work::kCubic_Segment: {
4434 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
4435 break;
4436 }
4437 default:
4438 SkASSERT(0);
4439 }
4440 break;
4441 default:
4442 SkASSERT(0);
4443 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004444 if (!foundCommonContour && pts > 0) {
4445 test->addCross(next);
4446 next->addCross(test);
4447 foundCommonContour = true;
4448 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004449 // in addition to recording T values, record matching segment
caryclark@google.com32546db2012-08-31 20:55:07 +00004450 if (pts == 2) {
4451 if (wn.segmentType() <= Work::kLine_Segment
4452 && wt.segmentType() <= Work::kLine_Segment) {
4453 wt.addCoincident(wn, ts, swap);
4454 continue;
4455 }
4456 if (wn.segmentType() == Work::kQuad_Segment
4457 && wt.segmentType() == Work::kQuad_Segment
4458 && ts.coincidentUsed() == 2) {
4459 wt.addCoincident(wn, ts, swap);
4460 continue;
4461 }
4462
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00004463 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004464 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004465 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
4466 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004467 int testTAt = wt.addT(ts.fT[swap][pt], wn);
4468 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004469 wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
4470 wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004471 }
4472 } while (wn.advance());
4473 } while (wt.advance());
4474 return true;
4475}
4476
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004477// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004478// see if coincidence is formed by clipping non-concident segments
caryclark@google.com235f56a2012-09-14 14:19:30 +00004479static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004480 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00004481 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004482 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004483 contour->resolveCoincidence();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004484 }
4485 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4486 Contour* contour = contourList[cIndex];
caryclark@google.com235f56a2012-09-14 14:19:30 +00004487 contour->findTooCloseToCall();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004488 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004489#if 0
4490 // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
4491 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4492 Contour* contour = contourList[cIndex];
4493 contour->collapseTriangles();
4494 }
4495#endif
caryclark@google.coma833b5c2012-04-30 19:38:50 +00004496}
4497
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004498// project a ray from the top of the contour up and see if it hits anything
4499// note: when we compute line intersections, we keep track of whether
4500// two contours touch, so we need only look at contours not touching this one.
4501// OPTIMIZATION: sort contourList vertically to avoid linear walk
4502static int innerContourCheck(SkTDArray<Contour*>& contourList,
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004503 const Segment* current, int index, int endIndex) {
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004504 const SkPoint& basePt = current->xyAtT(endIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004505 int contourCount = contourList.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004506 SkScalar bestY = SK_ScalarMin;
caryclark@google.com47580692012-07-23 12:14:49 +00004507 const Segment* test = NULL;
4508 int tIndex;
4509 double tHit;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004510 // bool checkCrosses = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004511 for (int cTest = 0; cTest < contourCount; ++cTest) {
4512 Contour* contour = contourList[cTest];
4513 if (basePt.fY < contour->bounds().fTop) {
4514 continue;
4515 }
4516 if (bestY > contour->bounds().fBottom) {
4517 continue;
4518 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004519#if 0
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004520 // even though the contours crossed, if spans cancel through concidence,
4521 // the contours may be not have any span links to chase, and the current
4522 // segment may be isolated. Detect this by seeing if current has
4523 // uninitialized wind sums. If so, project a ray instead of relying on
4524 // previously found intersections.
4525 if (baseContour == contour) {
4526 continue;
4527 }
4528 if (checkCrosses && baseContour->crosses(contour)) {
4529 if (current->isConnected(index, endIndex)) {
4530 continue;
4531 }
4532 checkCrosses = false;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004533 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004534#endif
caryclark@google.com47580692012-07-23 12:14:49 +00004535 const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
4536 if (next) {
4537 test = next;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004538 }
caryclark@google.com47580692012-07-23 12:14:49 +00004539 }
4540 if (!test) {
caryclark@google.com47580692012-07-23 12:14:49 +00004541 return 0;
4542 }
4543 int winding, windValue;
4544 // If the ray hit the end of a span, we need to construct the wheel of
4545 // angles to find the span closest to the ray -- even if there are just
4546 // two spokes on the wheel.
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004547 const Angle* angle = NULL;
caryclark@google.com3350c3c2012-08-24 15:24:36 +00004548 if (approximately_zero(tHit - test->t(tIndex))) {
caryclark@google.com47580692012-07-23 12:14:49 +00004549 SkTDArray<Angle> angles;
4550 int end = test->nextSpan(tIndex, 1);
4551 if (end < 0) {
4552 end = test->nextSpan(tIndex, -1);
4553 }
4554 test->addTwoAngles(end, tIndex, angles);
caryclark@google.com59823f72012-08-09 18:17:47 +00004555 SkASSERT(angles.count() > 0);
4556 if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
4557#if DEBUG_SORT
caryclark@google.com24bec792012-08-20 12:43:57 +00004558 SkDebugf("%s early return\n", __FUNCTION__);
caryclark@google.com59823f72012-08-09 18:17:47 +00004559#endif
4560 return 0;
4561 }
caryclark@google.com47580692012-07-23 12:14:49 +00004562 test->buildAngles(tIndex, angles);
4563 SkTDArray<Angle*> sorted;
rmistry@google.comd6176b02012-08-23 18:14:13 +00004564 // OPTIMIZATION: call a sort that, if base point is the leftmost,
caryclark@google.com47580692012-07-23 12:14:49 +00004565 // returns the first counterclockwise hour before 6 o'clock,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004566 // or if the base point is rightmost, returns the first clockwise
caryclark@google.com47580692012-07-23 12:14:49 +00004567 // hour after 6 o'clock
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004568 (void) Segment::SortAngles(angles, sorted);
caryclark@google.com47580692012-07-23 12:14:49 +00004569#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004570 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com47580692012-07-23 12:14:49 +00004571#endif
4572 // walk the sorted angle fan to find the lowest angle
4573 // above the base point. Currently, the first angle in the sorted array
4574 // is 12 noon or an earlier hour (the next counterclockwise)
4575 int count = sorted.count();
4576 int left = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004577 int mid = -1;
caryclark@google.com47580692012-07-23 12:14:49 +00004578 int right = -1;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004579 bool baseMatches = test->yAtT(tIndex) == basePt.fY;
caryclark@google.com47580692012-07-23 12:14:49 +00004580 for (int index = 0; index < count; ++index) {
caryclark@google.com59823f72012-08-09 18:17:47 +00004581 angle = sorted[index];
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004582 if (angle->unsortable()) {
4583 continue;
4584 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004585 if (baseMatches && angle->isHorizontal()) {
4586 continue;
4587 }
4588 double indexDx = angle->dx();
caryclark@google.comd1688742012-09-18 20:08:37 +00004589 test = angle->segment();
4590 if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
4591 const SkPoint* pts = test->pts();
4592 indexDx = pts[2].fX - pts[1].fX - indexDx;
4593 }
caryclark@google.com47580692012-07-23 12:14:49 +00004594 if (indexDx < 0) {
4595 left = index;
4596 } else if (indexDx > 0) {
4597 right = index;
caryclark@google.com59823f72012-08-09 18:17:47 +00004598 int previous = index - 1;
4599 if (previous < 0) {
4600 previous = count - 1;
4601 }
4602 const Angle* prev = sorted[previous];
4603 if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
4604#if DEBUG_SORT
4605 SkDebugf("%s use prev\n", __FUNCTION__);
4606#endif
4607 right = previous;
4608 }
caryclark@google.com47580692012-07-23 12:14:49 +00004609 break;
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004610 } else {
4611 mid = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004612 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004613 }
caryclark@google.com7db7c6b2012-07-27 21:22:25 +00004614 if (left < 0 && right < 0) {
4615 left = mid;
4616 }
caryclark@google.com47580692012-07-23 12:14:49 +00004617 SkASSERT(left >= 0 || right >= 0);
4618 if (left < 0) {
4619 left = right;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004620 } else if (left >= 0 && mid >= 0 && right >= 0
4621 && sorted[mid]->sign() == sorted[right]->sign()) {
4622 left = right;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004623 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004624 angle = sorted[left];
caryclark@google.com47580692012-07-23 12:14:49 +00004625 test = angle->segment();
4626 winding = test->windSum(angle);
caryclark@google.come21cb182012-07-23 21:26:31 +00004627 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004628 windValue = test->windValue(angle);
caryclark@google.com47580692012-07-23 12:14:49 +00004629#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004630 SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
4631 windValue, angle->sign());
caryclark@google.com47580692012-07-23 12:14:49 +00004632#endif
4633 } else {
4634 winding = test->windSum(tIndex);
caryclark@google.come21cb182012-07-23 21:26:31 +00004635 SkASSERT(winding != SK_MinS32);
caryclark@google.com47580692012-07-23 12:14:49 +00004636 windValue = test->windValue(tIndex);
4637#if DEBUG_WINDING
4638 SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
4639 windValue);
4640#endif
4641 }
4642 // see if a + change in T results in a +/- change in X (compute x'(T))
4643 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
caryclark@google.comd1688742012-09-18 20:08:37 +00004644 if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
4645 const SkPoint* pts = test->pts();
4646 dx = pts[2].fX - pts[1].fX - dx;
4647 }
caryclark@google.com47580692012-07-23 12:14:49 +00004648#if DEBUG_WINDING
4649 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
4650#endif
caryclark@google.com2ddff932012-08-07 21:25:27 +00004651 SkASSERT(dx != 0);
4652 if (winding * dx > 0) { // if same signs, result is negative
caryclark@google.com47580692012-07-23 12:14:49 +00004653 winding += dx > 0 ? -windValue : windValue;
4654#if DEBUG_WINDING
4655 SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
4656#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004657 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004658 return winding;
4659}
rmistry@google.comd6176b02012-08-23 18:14:13 +00004660
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004661#if SORTABLE_CONTOURS
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004662// OPTIMIZATION: not crazy about linear search here to find top active y.
4663// seems like we should break down and do the sort, or maybe sort each
rmistry@google.comd6176b02012-08-23 18:14:13 +00004664// contours' segments?
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004665// Once the segment array is built, there's no reason I can think of not to
4666// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004667// FIXME: return the contour found to pass to inner contour check
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004668static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004669 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004670 int cIndex = 0;
4671 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004672 SkScalar bestY = SK_ScalarMax;
4673 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004674 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004675 contour = contourList[cIndex];
4676 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004677 } while (!topStart && ++cIndex < contourCount);
4678 if (!topStart) {
4679 return NULL;
4680 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004681 while (++cIndex < contourCount) {
4682 contour = contourList[cIndex];
4683 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004684 continue;
4685 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004686 SkScalar testY = SK_ScalarMax;
4687 Segment* test = contour->topSegment(testY);
4688 if (!test || bestY <= testY) {
4689 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004690 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004691 topStart = test;
4692 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004693 }
4694 return topStart;
4695}
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004696#endif
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004697
caryclark@google.com24bec792012-08-20 12:43:57 +00004698static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
4699 int contourCount = contourList.count();
4700 Segment* result;
4701 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4702 Contour* contour = contourList[cIndex];
4703 result = contour->undoneSegment(start, end);
4704 if (result) {
4705 return result;
4706 }
4707 }
4708 return NULL;
4709}
4710
4711
4712
caryclark@google.come21cb182012-07-23 21:26:31 +00004713static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
4714 int contourWinding) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004715 while (chase.count()) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004716 Span* span;
4717 chase.pop(&span);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004718 const Span& backPtr = span->fOther->span(span->fOtherIndex);
4719 Segment* segment = backPtr.fOther;
4720 tIndex = backPtr.fOtherIndex;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004721 SkTDArray<Angle> angles;
4722 int done = 0;
4723 if (segment->activeAngle(tIndex, done, angles)) {
4724 Angle* last = angles.end() - 1;
4725 tIndex = last->start();
4726 endIndex = last->end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00004727 #if TRY_ROTATE
4728 *chase.insert(0) = span;
4729 #else
4730 *chase.append() = span;
4731 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004732 return last->segment();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004733 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004734 if (done == angles.count()) {
caryclark@google.com9764cc62012-07-12 19:29:45 +00004735 continue;
4736 }
4737 SkTDArray<Angle*> sorted;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004738 bool sortable = Segment::SortAngles(angles, sorted);
caryclark@google.com03f97062012-08-21 13:13:52 +00004739#if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004740 sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
caryclark@google.com03f97062012-08-21 13:13:52 +00004741#endif
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004742 if (!sortable) {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004743 continue;
4744 }
caryclark@google.com9764cc62012-07-12 19:29:45 +00004745 // find first angle, initialize winding to computed fWindSum
4746 int firstIndex = -1;
4747 const Angle* angle;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004748 int winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004749 do {
4750 angle = sorted[++firstIndex];
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004751 segment = angle->segment();
4752 winding = segment->windSum(angle);
4753 } while (winding == SK_MinS32);
4754 int spanWinding = segment->spanSign(angle->start(), angle->end());
4755 #if DEBUG_WINDING
4756 SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
4757 __FUNCTION__, winding, spanWinding, contourWinding);
caryclark@google.com47580692012-07-23 12:14:49 +00004758 #endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004759 // turn swinding into contourWinding
4760 if (spanWinding * winding < 0) {
4761 winding += spanWinding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004762 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004763 #if DEBUG_SORT
rmistry@google.comd6176b02012-08-23 18:14:13 +00004764 segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004765 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004766 // we care about first sign and whether wind sum indicates this
4767 // edge is inside or outside. Maybe need to pass span winding
4768 // or first winding or something into this function?
4769 // advance to first undone angle, then return it and winding
4770 // (to set whether edges are active or not)
4771 int nextIndex = firstIndex + 1;
4772 int angleCount = sorted.count();
4773 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004774 angle = sorted[firstIndex];
caryclark@google.com2ddff932012-08-07 21:25:27 +00004775 winding -= angle->segment()->spanSign(angle);
caryclark@google.com9764cc62012-07-12 19:29:45 +00004776 do {
4777 SkASSERT(nextIndex != firstIndex);
4778 if (nextIndex == angleCount) {
4779 nextIndex = 0;
4780 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004781 angle = sorted[nextIndex];
caryclark@google.com9764cc62012-07-12 19:29:45 +00004782 segment = angle->segment();
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004783 int maxWinding = winding;
caryclark@google.com2ddff932012-08-07 21:25:27 +00004784 winding -= segment->spanSign(angle);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004785 #if DEBUG_SORT
caryclark@google.com2ddff932012-08-07 21:25:27 +00004786 SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
4787 segment->debugID(), maxWinding, winding, angle->sign());
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004788 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004789 tIndex = angle->start();
4790 endIndex = angle->end();
4791 int lesser = SkMin32(tIndex, endIndex);
4792 const Span& nextSpan = segment->span(lesser);
4793 if (!nextSpan.fDone) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004794#if 1
caryclark@google.com9764cc62012-07-12 19:29:45 +00004795 // FIXME: this be wrong. assign startWinding if edge is in
4796 // same direction. If the direction is opposite, winding to
4797 // assign is flipped sign or +/- 1?
caryclark@google.com59823f72012-08-09 18:17:47 +00004798 if (useInnerWinding(maxWinding, winding)) {
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004799 maxWinding = winding;
caryclark@google.com9764cc62012-07-12 19:29:45 +00004800 }
caryclark@google.com59823f72012-08-09 18:17:47 +00004801 segment->markWinding(lesser, maxWinding);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004802#endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004803 break;
4804 }
4805 } while (++nextIndex != lastIndex);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004806 #if TRY_ROTATE
4807 *chase.insert(0) = span;
4808 #else
4809 *chase.append() = span;
4810 #endif
caryclark@google.com9764cc62012-07-12 19:29:45 +00004811 return segment;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004812 }
4813 return NULL;
4814}
4815
caryclark@google.com027de222012-07-12 12:52:50 +00004816#if DEBUG_ACTIVE_SPANS
4817static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004818 int index;
4819 for (index = 0; index < contourList.count(); ++ index) {
caryclark@google.com027de222012-07-12 12:52:50 +00004820 contourList[index]->debugShowActiveSpans();
4821 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00004822 for (index = 0; index < contourList.count(); ++ index) {
4823 contourList[index]->validateActiveSpans();
4824 }
caryclark@google.com027de222012-07-12 12:52:50 +00004825}
4826#endif
4827
caryclark@google.com27c449a2012-07-27 18:26:38 +00004828static bool windingIsActive(int winding, int spanWinding) {
4829 return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
4830 && (!winding || !spanWinding || winding == -spanWinding);
4831}
4832
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004833#if !SORTABLE_CONTOURS
4834static Segment* findSortableTop(SkTDArray<Contour*>& contourList, int& index,
caryclark@google.comf839c032012-10-26 21:03:50 +00004835 int& endIndex, SkPoint& topLeft) {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004836 Segment* result;
4837 do {
caryclark@google.comf839c032012-10-26 21:03:50 +00004838 SkPoint bestXY = {SK_ScalarMax, SK_ScalarMax};
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004839 int contourCount = contourList.count();
caryclark@google.comf839c032012-10-26 21:03:50 +00004840 Segment* topStart = NULL;
4841 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
4842 Contour* contour = contourList[cIndex];
4843 const Bounds& bounds = contour->bounds();
4844 if (bounds.fBottom < topLeft.fY) {
4845 continue;
4846 }
4847 if (bounds.fBottom == topLeft.fY && bounds.fRight < topLeft.fX) {
4848 continue;
4849 }
4850 Segment* test = contour->topSortableSegment(topLeft, bestXY);
4851 if (test) {
4852 topStart = test;
4853 }
4854 }
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004855 if (!topStart) {
4856 return NULL;
4857 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004858 topLeft = bestXY;
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004859 result = topStart->findTop(index, endIndex);
4860 } while (!result);
4861 return result;
4862}
4863#endif
4864
caryclark@google.comfa0588f2012-04-26 21:01:06 +00004865// Each segment may have an inside or an outside. Segments contained within
4866// winding may have insides on either side, and form a contour that should be
4867// ignored. Segments that are coincident with opposing direction segments may
4868// have outsides on either side, and should also disappear.
4869// 'Normal' segments will have one inside and one outside. Subsequent connections
4870// when winding should follow the intersection direction. If more than one edge
4871// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004872 // since we start with leftmost top edge, we'll traverse through a
rmistry@google.comd6176b02012-08-23 18:14:13 +00004873 // smaller angle counterclockwise to get to the next edge.
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004874// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00004875static bool bridgeWinding(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004876 bool firstContour = true;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004877 bool unsortable = false;
caryclark@google.comf839c032012-10-26 21:03:50 +00004878 bool closable = true;
4879 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
caryclark@google.com15fa1382012-05-07 20:49:36 +00004880 do {
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004881#if SORTABLE_CONTOURS // old way
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004882 Segment* topStart = findTopContour(contourList);
caryclark@google.com15fa1382012-05-07 20:49:36 +00004883 if (!topStart) {
4884 break;
caryclark@google.comcc905052012-07-25 20:59:42 +00004885 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00004886 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00004887 // follow edges to intersection by changing the index by direction.
4888 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00004889 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004890#else // new way: iterate while top is unsortable
4891 int index, endIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00004892 Segment* current = findSortableTop(contourList, index, endIndex, topLeft);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00004893 if (!current) {
4894 break;
4895 }
4896#endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004897 int contourWinding;
4898 if (firstContour) {
4899 contourWinding = 0;
4900 firstContour = false;
4901 } else {
caryclark@google.com200c2112012-08-03 15:05:04 +00004902 int sumWinding = current->windSum(SkMin32(index, endIndex));
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004903 // FIXME: don't I have to adjust windSum to get contourWinding?
caryclark@google.com200c2112012-08-03 15:05:04 +00004904 if (sumWinding == SK_MinS32) {
4905 sumWinding = current->computeSum(index, endIndex);
4906 }
4907 if (sumWinding == SK_MinS32) {
4908 contourWinding = innerContourCheck(contourList, current,
4909 index, endIndex);
4910 } else {
4911 contourWinding = sumWinding;
4912 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004913 bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
4914 if (inner) {
caryclark@google.com200c2112012-08-03 15:05:04 +00004915 contourWinding -= spanWinding;
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004916 }
caryclark@google.com2ddff932012-08-07 21:25:27 +00004917#if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004918 SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
rmistry@google.comd6176b02012-08-23 18:14:13 +00004919 sumWinding, spanWinding, SkSign32(index - endIndex),
caryclark@google.com59823f72012-08-09 18:17:47 +00004920 inner, contourWinding);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004921#endif
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004922 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004923#if DEBUG_WINDING
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004924 // SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004925 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
4926#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004927 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004928 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00004929 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004930 // FIXME: needs work. While it works in limited situations, it does
4931 // not always compute winding correctly. Active should be removed and instead
4932 // the initial winding should be correctly passed in so that if the
4933 // inner contour is wound the same way, it never finds an accumulated
4934 // winding of zero. Inside 'find next', we need to look for transitions
rmistry@google.comd6176b02012-08-23 18:14:13 +00004935 // other than zero when resolving sorted angles.
caryclark@google.com27c449a2012-07-27 18:26:38 +00004936 bool active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004937 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00004938 do {
caryclark@google.com0e08a192012-07-13 21:07:52 +00004939 #if DEBUG_WINDING
caryclark@google.come21cb182012-07-23 21:26:31 +00004940 SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
4941 __FUNCTION__, active ? "true" : "false",
4942 winding, spanWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004943 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004944 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004945 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00004946 int nextStart = index;
4947 int nextEnd = endIndex;
4948 Segment* next = current->findNextWinding(chaseArray, active,
caryclark@google.comc91dfe42012-10-16 12:06:27 +00004949 nextStart, nextEnd, winding, spanWinding, unsortable);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004950 if (!next) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00004951 if (active && !unsortable && simple.hasMove()
caryclark@google.comf839c032012-10-26 21:03:50 +00004952 && current->verb() != SkPath::kLine_Verb
4953 && !simple.isClosed()) {
4954 current->addCurveTo(index, endIndex, simple, true);
4955 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00004956 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004957 break;
4958 }
caryclark@google.comf839c032012-10-26 21:03:50 +00004959 current->addCurveTo(index, endIndex, simple, active);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004960 current = next;
4961 index = nextStart;
4962 endIndex = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00004963 } while (!simple.isClosed()
4964 && ((active && !unsortable) || !current->done()));
4965 if (active) {
4966 if (!simple.isClosed()) {
4967 SkASSERT(unsortable);
4968 int min = SkMin32(index, endIndex);
4969 if (!current->done(min)) {
4970 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com0b7da432012-10-31 19:00:20 +00004971 current->markDone(SkMin32(index, endIndex), winding ? winding : spanWinding);
caryclark@google.comf839c032012-10-26 21:03:50 +00004972 }
4973 closable = false;
4974 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004975 simple.close();
4976 }
caryclark@google.come21cb182012-07-23 21:26:31 +00004977 current = findChase(chaseArray, index, endIndex, contourWinding);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004978 #if DEBUG_ACTIVE_SPANS
caryclark@google.com027de222012-07-12 12:52:50 +00004979 debugShowActiveSpans(contourList);
caryclark@google.com0e08a192012-07-13 21:07:52 +00004980 #endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004981 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00004982 break;
4983 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004984 int lesser = SkMin32(index, endIndex);
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004985 spanWinding = current->spanSign(index, endIndex);
4986 winding = current->windSum(lesser);
caryclark@google.com2ddff932012-08-07 21:25:27 +00004987 bool inner = useInnerWinding(winding - spanWinding, winding);
4988 #if DEBUG_WINDING
caryclark@google.com24bec792012-08-20 12:43:57 +00004989 SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
caryclark@google.com59823f72012-08-09 18:17:47 +00004990 " inner=%d result=%d\n",
caryclark@google.com2ddff932012-08-07 21:25:27 +00004991 __FUNCTION__, current->debugID(), current->t(lesser),
4992 spanWinding, winding, SkSign32(index - endIndex),
4993 useInnerWinding(winding - spanWinding, winding),
caryclark@google.com2ddff932012-08-07 21:25:27 +00004994 inner ? winding - spanWinding : winding);
4995 #endif
4996 if (inner) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00004997 winding -= spanWinding;
4998 }
caryclark@google.com534aa5b2012-08-02 20:08:21 +00004999 active = windingIsActive(winding, spanWinding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00005000 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005001 } while (true);
caryclark@google.comf839c032012-10-26 21:03:50 +00005002 return closable;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005003}
5004
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005005// returns true if all edges were processed
caryclark@google.comf839c032012-10-26 21:03:50 +00005006static bool bridgeXor(SkTDArray<Contour*>& contourList, PathWrapper& simple) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005007 Segment* current;
5008 int start, end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005009 bool unsortable = false;
caryclark@google.com24bec792012-08-20 12:43:57 +00005010 while ((current = findUndone(contourList, start, end))) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005011 do {
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005012 SkASSERT(unsortable || !current->done());
caryclark@google.com24bec792012-08-20 12:43:57 +00005013 int nextStart = start;
5014 int nextEnd = end;
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005015 Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
caryclark@google.com24bec792012-08-20 12:43:57 +00005016 if (!next) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005017 if (simple.hasMove()
5018 && current->verb() != SkPath::kLine_Verb
5019 && !simple.isClosed()) {
5020 current->addCurveTo(start, end, simple, true);
5021 SkASSERT(simple.isClosed());
caryclark@google.comc899ad92012-08-23 15:24:42 +00005022 }
caryclark@google.com24bec792012-08-20 12:43:57 +00005023 break;
5024 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005025 current->addCurveTo(start, end, simple, true);
caryclark@google.com24bec792012-08-20 12:43:57 +00005026 current = next;
5027 start = nextStart;
5028 end = nextEnd;
caryclark@google.comf839c032012-10-26 21:03:50 +00005029 } while (!simple.isClosed());
5030 // FIXME: add unsortable test
5031 if (simple.hasMove()) {
caryclark@google.com24bec792012-08-20 12:43:57 +00005032 simple.close();
5033 }
caryclark@google.com6aea33f2012-10-09 14:11:58 +00005034 #if DEBUG_ACTIVE_SPANS
5035 debugShowActiveSpans(contourList);
5036 #endif
rmistry@google.comd6176b02012-08-23 18:14:13 +00005037 }
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005038 return !unsortable;
caryclark@google.com24bec792012-08-20 12:43:57 +00005039}
5040
caryclark@google.comb45a1b42012-05-18 20:50:33 +00005041static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
5042 int contourCount = contourList.count();
5043 for (int cTest = 0; cTest < contourCount; ++cTest) {
5044 Contour* contour = contourList[cTest];
5045 contour->fixOtherTIndex();
5046 }
5047}
5048
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005049#if !SORTABLE_CONTOURS
5050static void sortSegments(SkTDArray<Contour*>& contourList) {
5051 int contourCount = contourList.count();
5052 for (int cTest = 0; cTest < contourCount; ++cTest) {
5053 Contour* contour = contourList[cTest];
5054 contour->sortSegments();
5055 }
5056}
5057#endif
5058
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005059static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005060 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005061 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005062 if (count == 0) {
5063 return;
5064 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005065 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005066 *list.append() = &contours[index];
5067 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005068 QSort<Contour>(list.begin(), list.end() - 1);
5069}
5070
caryclark@google.comf839c032012-10-26 21:03:50 +00005071static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005072 return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005073}
5074
caryclark@google.comf839c032012-10-26 21:03:50 +00005075 /*
5076 check start and end of each contour
5077 if not the same, record them
5078 match them up
5079 connect closest
5080 reassemble contour pieces into new path
5081 */
5082static void assemble(const PathWrapper& path, PathWrapper& simple) {
5083#if DEBUG_PATH_CONSTRUCTION
5084 SkDebugf("%s\n", __FUNCTION__);
5085#endif
5086 SkTArray<Contour> contours;
5087 EdgeBuilder builder(path, contours);
5088 builder.finish();
5089 int count = contours.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005090 int outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005091 SkTDArray<int> runs; // indices of partial contours
caryclark@google.com0b7da432012-10-31 19:00:20 +00005092 for (outer = 0; outer < count; ++outer) {
5093 const Contour& eContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005094 const SkPoint& eStart = eContour.start();
5095 const SkPoint& eEnd = eContour.end();
5096 if (approximatelyEqual(eStart, eEnd)) {
5097 eContour.toPath(simple);
5098 continue;
5099 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005100 *runs.append() = outer;
caryclark@google.comf839c032012-10-26 21:03:50 +00005101 }
5102 count = runs.count();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005103 if (count == 0) {
5104 return;
5105 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005106 SkTDArray<int> sLink, eLink;
5107 sLink.setCount(count);
5108 eLink.setCount(count);
caryclark@google.com0b7da432012-10-31 19:00:20 +00005109 SkTDArray<double> sBest, eBest;
5110 sBest.setCount(count);
5111 eBest.setCount(count);
caryclark@google.comf839c032012-10-26 21:03:50 +00005112 int rIndex;
5113 for (rIndex = 0; rIndex < count; ++rIndex) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005114 outer = runs[rIndex];
5115 const Contour& oContour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005116 const SkPoint& oStart = oContour.start();
5117 const SkPoint& oEnd = oContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005118 double dx = oEnd.fX - oStart.fX;
5119 double dy = oEnd.fY - oStart.fY;
5120 double dist = dx * dx + dy * dy;
5121 sBest[rIndex] = eBest[rIndex] = dist;
5122 sLink[rIndex] = eLink[rIndex] = rIndex;
5123 }
5124 for (rIndex = 0; rIndex < count - 1; ++rIndex) {
5125 outer = runs[rIndex];
5126 const Contour& oContour = contours[outer];
5127 const SkPoint& oStart = oContour.start();
5128 const SkPoint& oEnd = oContour.end();
5129 double bestStartDist = sBest[rIndex];
5130 double bestEndDist = eBest[rIndex];
5131 for (int iIndex = rIndex + 1; iIndex < count; ++iIndex) {
5132 int inner = runs[iIndex];
5133 const Contour& iContour = contours[inner];
caryclark@google.comf839c032012-10-26 21:03:50 +00005134 const SkPoint& iStart = iContour.start();
5135 const SkPoint& iEnd = iContour.end();
caryclark@google.com0b7da432012-10-31 19:00:20 +00005136 double dx = iStart.fX - oStart.fX;
5137 double dy = iStart.fY - oStart.fY;
5138 double dist = dx * dx + dy * dy;
5139 if (bestStartDist > dist) {
5140 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005141 sLink[rIndex] = ~iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005142 sLink[iIndex] = ~rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005143 }
5144 dx = iEnd.fX - oStart.fX;
5145 dy = iEnd.fY - oStart.fY;
5146 dist = dx * dx + dy * dy;
5147 if (bestStartDist > dist) {
5148 bestStartDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005149 sLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005150 eLink[iIndex] = rIndex;
5151 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005152 dx = iStart.fX - oEnd.fX;
5153 dy = iStart.fY - oEnd.fY;
5154 dist = dx * dx + dy * dy;
5155 if (bestEndDist > dist) {
5156 bestEndDist = dist;
caryclark@google.comf839c032012-10-26 21:03:50 +00005157 sLink[iIndex] = rIndex;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005158 eLink[rIndex] = iIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005159 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005160 dx = iEnd.fX - oEnd.fX;
5161 dy = iEnd.fY - oEnd.fY;
5162 dist = dx * dx + dy * dy;
5163 if (bestEndDist > dist) {
5164 bestEndDist = dist;
5165 eLink[iIndex] = ~rIndex;
5166 eLink[rIndex] = ~iIndex;
5167 }
5168 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005169 }
5170 rIndex = 0;
5171 bool forward = true;
5172 bool first = true;
5173 const SkPoint* startPtr;
5174 int sIndex = sLink[rIndex];
caryclark@google.com0b7da432012-10-31 19:00:20 +00005175 SkASSERT(sIndex != INT_MAX);
5176 sLink[rIndex] = INT_MAX;
5177 int eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005178 if (sIndex < 0) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005179 eIndex = sLink[~sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005180 sLink[~sIndex] = INT_MAX;
5181 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005182 eIndex = eLink[sIndex];
caryclark@google.comf839c032012-10-26 21:03:50 +00005183 eLink[sIndex] = INT_MAX;
5184 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005185 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005186 do {
5187 do {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005188 outer = runs[rIndex];
5189 const Contour& contour = contours[outer];
caryclark@google.comf839c032012-10-26 21:03:50 +00005190 if (first) {
5191 startPtr = &contour.start();
5192 first = false;
5193 simple.deferredMove(startPtr[0]);
5194 }
5195 const SkPoint* endPtr;
5196 if (forward) {
5197 contour.toPartialForward(simple);
5198 endPtr = &contour.end();
5199 } else {
5200 contour.toPartialBackward(simple);
5201 endPtr = &contour.start();
5202 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005203 if (sIndex == eIndex) {
caryclark@google.comf839c032012-10-26 21:03:50 +00005204 simple.close();
5205 first = forward = true;
5206 break;
5207 }
caryclark@google.comf839c032012-10-26 21:03:50 +00005208 if (forward) {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005209 eIndex = eLink[rIndex];
5210 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005211 eLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005212 if (eIndex >= 0) {
5213 SkASSERT(sLink[eIndex] == rIndex);
5214 sLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005215 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005216 SkASSERT(eLink[~eIndex] == ~rIndex);
5217 eLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005218 }
5219 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005220 eIndex = sLink[rIndex];
5221 SkASSERT(eIndex != INT_MAX);
caryclark@google.comf839c032012-10-26 21:03:50 +00005222 sLink[rIndex] = INT_MAX;
caryclark@google.com0b7da432012-10-31 19:00:20 +00005223 if (eIndex >= 0) {
5224 SkASSERT(eLink[eIndex] == rIndex);
5225 eLink[eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005226 } else {
caryclark@google.com0b7da432012-10-31 19:00:20 +00005227 SkASSERT(sLink[~eIndex] == ~rIndex);
5228 sLink[~eIndex] = INT_MAX;
caryclark@google.comf839c032012-10-26 21:03:50 +00005229 }
5230 }
caryclark@google.com0b7da432012-10-31 19:00:20 +00005231 rIndex = eIndex;
caryclark@google.comf839c032012-10-26 21:03:50 +00005232 if (rIndex < 0) {
5233 forward ^= 1;
5234 rIndex = ~rIndex;
5235 }
5236 } while (true);
5237 for (rIndex = 0; rIndex < count; ++rIndex) {
5238 if (sLink[rIndex] != INT_MAX) {
5239 break;
5240 }
5241 }
5242 } while (rIndex < count);
5243 SkASSERT(first);
5244}
5245
5246void simplifyx(const SkPath& path, SkPath& result) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005247 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.comf839c032012-10-26 21:03:50 +00005248 result.reset();
5249 result.setFillType(SkPath::kEvenOdd_FillType);
5250 PathWrapper simple(result);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005251
5252 // turn path into list of segments
5253 SkTArray<Contour> contours;
5254 // FIXME: add self-intersecting cubics' T values to segment
5255 EdgeBuilder builder(path, contours);
caryclark@google.com235f56a2012-09-14 14:19:30 +00005256 builder.finish();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005257 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005258 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005259 Contour** currentPtr = contourList.begin();
5260 if (!currentPtr) {
5261 return;
5262 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005263 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005264 // find all intersections between segments
5265 do {
5266 Contour** nextPtr = currentPtr;
5267 Contour* current = *currentPtr++;
5268 Contour* next;
5269 do {
5270 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00005271 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00005272 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00005273 // eat through coincident edges
caryclark@google.com235f56a2012-09-14 14:19:30 +00005274 coincidenceCheck(contourList);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00005275 fixOtherTIndex(contourList);
caryclark@google.comfb51afb2012-10-19 15:54:16 +00005276#if !SORTABLE_CONTOURS
5277 sortSegments(contourList);
5278#endif
caryclark@google.com0b7da432012-10-31 19:00:20 +00005279#if DEBUG_ACTIVE_SPANS
5280 debugShowActiveSpans(contourList);
5281#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005282 // construct closed contours
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005283 if (builder.xorMask() == kWinding_Mask
5284 ? !bridgeWinding(contourList, simple)
skia.committer@gmail.com20c301b2012-10-17 02:01:13 +00005285 : !bridgeXor(contourList, simple))
caryclark@google.comc91dfe42012-10-16 12:06:27 +00005286 { // if some edges could not be resolved, assemble remaining fragments
caryclark@google.comf839c032012-10-26 21:03:50 +00005287 SkPath temp;
5288 temp.setFillType(SkPath::kEvenOdd_FillType);
5289 PathWrapper assembled(temp);
5290 assemble(simple, assembled);
5291 result = *assembled.nativePath();
caryclark@google.com24bec792012-08-20 12:43:57 +00005292 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00005293}
5294