blob: fc53ac2335465a4c1c5c8589083db2641ccf89fe [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001#include "GrPath.h"
2
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +00003GrPath::GrPath() {
4 fConvexHint = kNone_ConvexHint;
5}
reed@google.comac10a2d2010-12-22 21:39:39 +00006
reed@google.comc9218432011-01-25 19:05:12 +00007GrPath::GrPath(const GrPath& src) : INHERITED() {
bsalomon@google.comd302f142011-03-03 13:54:13 +00008 GrPath::Iter iter(src);
9 this->resetFromIter(&iter);
reed@google.comac10a2d2010-12-22 21:39:39 +000010}
11
12GrPath::GrPath(GrPathIter& iter) {
13 this->resetFromIter(&iter);
14}
15
16GrPath::~GrPath() {
17}
18
bsalomon@google.comd302f142011-03-03 13:54:13 +000019bool GrPath::operator ==(const GrPath& path) const {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000020 if (fCmds.count() != path.fCmds.count() ||
bsalomon@google.comd302f142011-03-03 13:54:13 +000021 fPts.count() != path.fPts.count()) {
22 return false;
23 }
24
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000025 for (int v = 0; v < fCmds.count(); ++v) {
26 if (fCmds[v] != path.fCmds[v]) {
bsalomon@google.comd302f142011-03-03 13:54:13 +000027 return false;
28 }
29 }
30
31 for (int p = 0; p < fPts.count(); ++p) {
32 if (fPts[p] != path.fPts[p]) {
33 return false;
34 }
35 }
36 return true;
37}
38
reed@google.comac10a2d2010-12-22 21:39:39 +000039void GrPath::ensureMoveTo() {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000040 if (fCmds.isEmpty() || this->wasLastVerb(kClose_PathCmd)) {
41 *fCmds.append() = kMove_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +000042 fPts.append()->set(0, 0);
43 }
44}
45
46void GrPath::moveTo(GrScalar x, GrScalar y) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000047 if (this->wasLastVerb(kMove_PathCmd)) {
reed@google.comac10a2d2010-12-22 21:39:39 +000048 // overwrite prev kMove value
49 fPts[fPts.count() - 1].set(x, y);
50 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000051 *fCmds.append() = kMove_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +000052 fPts.append()->set(x, y);
53 }
54}
55
56void GrPath::lineTo(GrScalar x, GrScalar y) {
57 this->ensureMoveTo();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000058 *fCmds.append() = kLine_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +000059 fPts.append()->set(x, y);
60}
61
62void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) {
63 this->ensureMoveTo();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000064 *fCmds.append() = kQuadratic_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +000065 fPts.append()->set(x0, y0);
66 fPts.append()->set(x1, y1);
67}
68
69void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
70 GrScalar x2, GrScalar y2) {
71 this->ensureMoveTo();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000072 *fCmds.append() = kCubic_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +000073 fPts.append()->set(x0, y0);
74 fPts.append()->set(x1, y1);
75 fPts.append()->set(x2, y2);
76}
77
78void GrPath::close() {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000079 if (!fCmds.isEmpty() && !this->wasLastVerb(kClose_PathCmd)) {
reed@google.comac10a2d2010-12-22 21:39:39 +000080 // should we allow kMove followed by kClose?
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000081 *fCmds.append() = kClose_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +000082 }
83}
84
85///////////////////////////////////////////////////////////////////////////////
86
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +000087static bool check_two_vecs(const GrVec& prevVec,
88 const GrVec& currVec,
89 GrScalar turnDir,
90 int* xDir,
91 int* yDir,
92 int* flipX,
93 int* flipY) {
94 if (currVec.fX * *xDir < 0) {
95 ++*flipX;
96 if (*flipX > 2) {
97 return false;
98 }
99 *xDir = -*xDir;
100 }
101 if (currVec.fY * *yDir < 0) {
102 ++*flipY;
103 if (*flipY > 2) {
104 return false;
105 }
106 *yDir = -*yDir;
107 }
108 GrScalar d = prevVec.cross(currVec);
109 return (d * turnDir) >= 0;
110}
111
112static void init_from_two_vecs(const GrVec& firstVec,
113 const GrVec& secondVec,
114 GrScalar* turnDir,
115 int* xDir, int* yDir) {
116 *turnDir = firstVec.cross(secondVec);
117 if (firstVec.fX > 0) {
118 *xDir = 1;
119 } else if (firstVec.fX < 0) {
120 *xDir = -1;
121 } else {
122 *xDir = 0;
123 }
124 if (firstVec.fY > 0) {
125 *yDir = 1;
126 } else if (firstVec.fY < 0) {
127 *yDir = -1;
128 } else {
129 *yDir = 0;
130 }
131}
132
reed@google.comac10a2d2010-12-22 21:39:39 +0000133void GrPath::resetFromIter(GrPathIter* iter) {
134 fPts.reset();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000135 fCmds.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +0000136
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000137 fConvexHint = iter->convexHint();
138
139 // first point of the subpath
140 GrPoint firstPt;
141 // first edge of the subpath
142 GrVec firstVec;
143 // vec of most recently processed edge, that wasn't degenerate
144 GrVec previousVec;
145 // most recently processed point
146 GrPoint previousPt;
147
148 // sign indicates whether we're bending left or right
149 GrScalar turnDir;
150 // number of times the direction has flipped in x or y
151
152 // we track which direction we are moving in x/y and the
153 // number of times it changes.
154 int xDir;
155 int yDir;
156 int flipX;
157 int flipY;
158
159 // counts number of sub path pts that didn't add a degenerate edge.
160 int subPathPts = 0;
161
162 int numSubPaths = 0;
163 iter->rewind();
164 GrPathCmd cmd;
reed@google.comac10a2d2010-12-22 21:39:39 +0000165 GrPoint pts[4];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000166 bool subPathClosed;
167 do {
168 cmd = iter->next(pts);
169 // If the convexity test is ever updated to handle multiple subpaths
170 // the loop has to be adjusted to handle moving to a new subpath without
171 // closing the previous one. Currently the implicit closing vectors for a
172 // filled path would never be examined.
reed@google.comac10a2d2010-12-22 21:39:39 +0000173 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000174 case kMove_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000175 this->moveTo(pts[0].fX, pts[0].fY);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000176 subPathPts = 0;
177 subPathClosed = false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000178 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000179 case kLine_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000180 this->lineTo(pts[1].fX, pts[1].fY);
181 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000182 case kQuadratic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000183 this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
184 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000185 case kCubic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000186 this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
187 pts[3].fX, pts[3].fY);
188 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000189 case kClose_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000190 this->close();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000191 subPathClosed = true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000192 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000193 case kEnd_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000194 break;
195 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000196 int n = NumPathCmdPoints(cmd);
197 if (0 == subPathPts && n > 0) {
198 previousPt = pts[0];
199 firstPt = previousPt;
200 flipX = 0;
201 flipY = 0;
202 turnDir = 0;
203 subPathPts = 1;
204 ++numSubPaths;
205 }
206 // either we skip the first pt because it is redundant with
207 // last point of the previous subpath cmd or we just ate it
208 // in the above if.
209 int consumed = 1;
210 if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) {
211 while (consumed < n) {
212 GrAssert(pts[consumed-1] == previousPt);
213 GrVec vec;
214 vec.setBetween(previousPt, pts[consumed]);
215 if (vec.fX || vec.fY) {
216 if (subPathPts >= 2) {
217 if (0 == turnDir) {
218 firstVec = previousVec;
219 init_from_two_vecs(firstVec, vec,
220 &turnDir, &xDir, &yDir);
221 // here we aren't checking whether the x/y dirs
222 // change between the first and second edge. It
223 // gets covered when the path is closed.
224 } else {
225 if (!check_two_vecs(previousVec, vec, turnDir,
226 &xDir, &yDir,
227 &flipX, &flipY)) {
228 fConvexHint = kConcave_ConvexHint;
229 break;
230 }
231 }
232 }
233 previousVec = vec;
234 previousPt = pts[consumed];
235 ++subPathPts;
236 }
237 ++consumed;
238 }
239 if (subPathPts > 2 && (kClose_PathCmd == cmd ||
240 (!subPathClosed && kEnd_PathCmd == cmd ))) {
241 // if an additional vector is needed to close the loop check
242 // that it validates against the previous vector.
243 GrVec vec;
244 vec.setBetween(previousPt, firstPt);
245 if (vec.fX || vec.fY) {
246 if (!check_two_vecs(previousVec, vec, turnDir,
247 &xDir, &yDir, &flipX, &flipY)) {
248 fConvexHint = kConcave_ConvexHint;
249 break;
250 }
251 previousVec = vec;
252 }
253 // check that closing vector validates against the first vector.
254 if (!check_two_vecs(previousVec, firstVec, turnDir,
255 &xDir, &yDir, &flipX, &flipY)) {
256 fConvexHint = kConcave_ConvexHint;
257 break;
258 }
259 }
260 }
261 } while (cmd != kEnd_PathCmd);
262 if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) {
263 fConvexHint = kConvex_ConvexHint;
264 } else {
265 bool recurse = false;
266 if (recurse) {
267 this->resetFromIter(iter);
268 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000269 }
270}
271
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000272void GrPath::ConvexUnitTest() {
273 GrPath testPath;
274 GrPath::Iter testIter;
275
276 GrPath pt;
277 pt.moveTo(0, 0);
278 pt.close();
279
280 testIter.reset(pt);
281 testPath.resetFromIter(&testIter);
282 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
283
284 GrPath line;
285 line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
286 line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
287 line.close();
288
289 testIter.reset(line);
290 testPath.resetFromIter(&testIter);
291 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
292
293 GrPath triLeft;
294 triLeft.moveTo(0, 0);
295 triLeft.lineTo(1, 0);
296 triLeft.lineTo(1, 1);
297 triLeft.close();
298
299 testIter.reset(triLeft);
300 testPath.resetFromIter(&testIter);
301 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
302
303 GrPath triRight;
304 triRight.moveTo(0, 0);
305 triRight.lineTo(-1, 0);
306 triRight.lineTo(1, 1);
307 triRight.close();
308
309 testIter.reset(triRight);
310 testPath.resetFromIter(&testIter);
311 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
312
313 GrPath square;
314 square.moveTo(0, 0);
315 square.lineTo(1, 0);
316 square.lineTo(1, 1);
317 square.lineTo(0, 1);
318 square.close();
319
320 testIter.reset(square);
321 testPath.resetFromIter(&testIter);
322 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
323
324 GrPath redundantSquare;
325 square.moveTo(0, 0);
326 square.lineTo(0, 0);
327 square.lineTo(0, 0);
328 square.lineTo(1, 0);
329 square.lineTo(1, 0);
330 square.lineTo(1, 0);
331 square.lineTo(1, 1);
332 square.lineTo(1, 1);
333 square.lineTo(1, 1);
334 square.lineTo(0, 1);
335 square.lineTo(0, 1);
336 square.lineTo(0, 1);
337 square.close();
338
339 testIter.reset(redundantSquare);
340 testPath.resetFromIter(&testIter);
341 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
342
343 GrPath bowTie;
344 bowTie.moveTo(0, 0);
345 bowTie.lineTo(0, 0);
346 bowTie.lineTo(0, 0);
347 bowTie.lineTo(1, 1);
348 bowTie.lineTo(1, 1);
349 bowTie.lineTo(1, 1);
350 bowTie.lineTo(1, 0);
351 bowTie.lineTo(1, 0);
352 bowTie.lineTo(1, 0);
353 bowTie.lineTo(0, 1);
354 bowTie.lineTo(0, 1);
355 bowTie.lineTo(0, 1);
356 bowTie.close();
357
358 testIter.reset(bowTie);
359 testPath.resetFromIter(&testIter);
360 GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
361
362 GrPath spiral;
363 spiral.moveTo(0, 0);
364 spiral.lineTo(1, 0);
365 spiral.lineTo(1, 1);
366 spiral.lineTo(0, 1);
367 spiral.lineTo(0,.5);
368 spiral.lineTo(.5,.5);
369 spiral.lineTo(.5,.75);
370 spiral.close();
371
372 testIter.reset(spiral);
373 testPath.resetFromIter(&testIter);
374 GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
375
376 GrPath dent;
377 dent.moveTo(0, 0);
378 dent.lineTo(1, 1);
379 dent.lineTo(0, 1);
380 dent.lineTo(-.5,2);
381 dent.lineTo(-2, 1);
382 dent.close();
383
384 testIter.reset(dent);
385 testPath.resetFromIter(&testIter);
386 GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
387}
reed@google.comac10a2d2010-12-22 21:39:39 +0000388///////////////////////////////////////////////////////////////////////////////
389
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000390GrPath::Iter::Iter() : fPath(NULL) {
391}
392
393GrPath::Iter::Iter(const GrPath& path) : fPath(&path) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000394 this->rewind();
395}
396
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000397GrPathCmd GrPath::Iter::next(GrPoint points[]) {
398 if (fCmdIndex == fPath->fCmds.count()) {
399 GrAssert(fPtIndex == fPath->fPts.count());
400 return kEnd_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +0000401 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000402 GrAssert(fCmdIndex < fPath->fCmds.count());
reed@google.comac10a2d2010-12-22 21:39:39 +0000403 }
404
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000405 GrPathCmd cmd = fPath->fCmds[fCmdIndex++];
406 const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex;
reed@google.comac10a2d2010-12-22 21:39:39 +0000407
408 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000409 case kMove_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000410 if (points) {
411 points[0] = srcPts[0];
412 }
413 fLastPt = srcPts[0];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000414 GrAssert(fPtIndex <= fPath->fPts.count() + 1);
reed@google.comac10a2d2010-12-22 21:39:39 +0000415 fPtIndex += 1;
416 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000417 case kLine_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000418 if (points) {
419 points[0] = fLastPt;
420 points[1] = srcPts[0];
421 }
422 fLastPt = srcPts[0];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000423 GrAssert(fPtIndex <= fPath->fPts.count() + 1);
reed@google.comac10a2d2010-12-22 21:39:39 +0000424 fPtIndex += 1;
425 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000426 case kQuadratic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000427 if (points) {
428 points[0] = fLastPt;
429 points[1] = srcPts[0];
430 points[2] = srcPts[1];
431 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000432 fLastPt = srcPts[1];
433 GrAssert(fPtIndex <= fPath->fPts.count() + 2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000434 fPtIndex += 2;
435 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000436 case kCubic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000437 if (points) {
438 points[0] = fLastPt;
439 points[1] = srcPts[0];
440 points[2] = srcPts[1];
441 points[3] = srcPts[2];
442 }
443 fLastPt = srcPts[2];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000444 GrAssert(fPtIndex <= fPath->fPts.count() + 3);
reed@google.comac10a2d2010-12-22 21:39:39 +0000445 fPtIndex += 3;
446 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000447 case kClose_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000448 break;
449 default:
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000450 GrAssert(!"unknown grpath cmd");
reed@google.comac10a2d2010-12-22 21:39:39 +0000451 break;
452 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000453 return cmd;
reed@google.comac10a2d2010-12-22 21:39:39 +0000454}
455
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000456GrConvexHint GrPath::Iter::convexHint() const {
457 return fPath->getConvexHint();
reed@google.comac10a2d2010-12-22 21:39:39 +0000458}
459
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000460GrPathCmd GrPath::Iter::next() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000461 return this->next(NULL);
462}
463
464void GrPath::Iter::rewind() {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000465 this->reset(*fPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000466}
467
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000468void GrPath::Iter::reset(const GrPath& path) {
469 fPath = &path;
470 fCmdIndex = fPtIndex = 0;
471}
reed@google.comac10a2d2010-12-22 21:39:39 +0000472
473