blob: 019b2fd5c4ad0e56101da6abb534d62c030c42c9 [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
reed@google.com6f8f2922011-03-04 22:27:10 +000087void GrPath::offset(GrScalar tx, GrScalar ty) {
88 if (!tx && !ty) {
89 return; // nothing to do
90 }
91
92 GrPoint* iter = fPts.begin();
93 GrPoint* stop = fPts.end();
94 while (iter < stop) {
95 iter->offset(tx, ty);
96 ++iter;
97 }
98}
99
100///////////////////////////////////////////////////////////////////////////////
101
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000102static bool check_two_vecs(const GrVec& prevVec,
103 const GrVec& currVec,
104 GrScalar turnDir,
105 int* xDir,
106 int* yDir,
reed@google.com6f8f2922011-03-04 22:27:10 +0000107 int* flipX,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000108 int* flipY) {
109 if (currVec.fX * *xDir < 0) {
110 ++*flipX;
111 if (*flipX > 2) {
112 return false;
113 }
114 *xDir = -*xDir;
115 }
116 if (currVec.fY * *yDir < 0) {
117 ++*flipY;
118 if (*flipY > 2) {
119 return false;
120 }
121 *yDir = -*yDir;
122 }
123 GrScalar d = prevVec.cross(currVec);
124 return (d * turnDir) >= 0;
125}
126
127static void init_from_two_vecs(const GrVec& firstVec,
128 const GrVec& secondVec,
129 GrScalar* turnDir,
130 int* xDir, int* yDir) {
131 *turnDir = firstVec.cross(secondVec);
132 if (firstVec.fX > 0) {
133 *xDir = 1;
134 } else if (firstVec.fX < 0) {
135 *xDir = -1;
136 } else {
137 *xDir = 0;
138 }
139 if (firstVec.fY > 0) {
140 *yDir = 1;
141 } else if (firstVec.fY < 0) {
142 *yDir = -1;
143 } else {
144 *yDir = 0;
145 }
146}
147
reed@google.comac10a2d2010-12-22 21:39:39 +0000148void GrPath::resetFromIter(GrPathIter* iter) {
149 fPts.reset();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000150 fCmds.reset();
reed@google.comac10a2d2010-12-22 21:39:39 +0000151
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000152 fConvexHint = iter->convexHint();
153
154 // first point of the subpath
155 GrPoint firstPt;
156 // first edge of the subpath
157 GrVec firstVec;
158 // vec of most recently processed edge, that wasn't degenerate
159 GrVec previousVec;
160 // most recently processed point
161 GrPoint previousPt;
162
163 // sign indicates whether we're bending left or right
164 GrScalar turnDir;
165 // number of times the direction has flipped in x or y
166
167 // we track which direction we are moving in x/y and the
168 // number of times it changes.
169 int xDir;
170 int yDir;
171 int flipX;
172 int flipY;
173
174 // counts number of sub path pts that didn't add a degenerate edge.
175 int subPathPts = 0;
176
177 int numSubPaths = 0;
178 iter->rewind();
179 GrPathCmd cmd;
reed@google.comac10a2d2010-12-22 21:39:39 +0000180 GrPoint pts[4];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000181 bool subPathClosed;
182 do {
183 cmd = iter->next(pts);
184 // If the convexity test is ever updated to handle multiple subpaths
185 // the loop has to be adjusted to handle moving to a new subpath without
186 // closing the previous one. Currently the implicit closing vectors for a
187 // filled path would never be examined.
reed@google.comac10a2d2010-12-22 21:39:39 +0000188 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000189 case kMove_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000190 this->moveTo(pts[0].fX, pts[0].fY);
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000191 subPathPts = 0;
192 subPathClosed = false;
reed@google.comac10a2d2010-12-22 21:39:39 +0000193 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000194 case kLine_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000195 this->lineTo(pts[1].fX, pts[1].fY);
196 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000197 case kQuadratic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000198 this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
199 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000200 case kCubic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000201 this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
202 pts[3].fX, pts[3].fY);
203 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000204 case kClose_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000205 this->close();
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000206 subPathClosed = true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000207 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000208 case kEnd_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000209 break;
210 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000211 int n = NumPathCmdPoints(cmd);
212 if (0 == subPathPts && n > 0) {
213 previousPt = pts[0];
214 firstPt = previousPt;
215 flipX = 0;
216 flipY = 0;
217 turnDir = 0;
218 subPathPts = 1;
219 ++numSubPaths;
220 }
221 // either we skip the first pt because it is redundant with
222 // last point of the previous subpath cmd or we just ate it
223 // in the above if.
224 int consumed = 1;
225 if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) {
226 while (consumed < n) {
227 GrAssert(pts[consumed-1] == previousPt);
228 GrVec vec;
229 vec.setBetween(previousPt, pts[consumed]);
230 if (vec.fX || vec.fY) {
231 if (subPathPts >= 2) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000232 if (0 == turnDir) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000233 firstVec = previousVec;
reed@google.com6f8f2922011-03-04 22:27:10 +0000234 init_from_two_vecs(firstVec, vec,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000235 &turnDir, &xDir, &yDir);
236 // here we aren't checking whether the x/y dirs
237 // change between the first and second edge. It
238 // gets covered when the path is closed.
239 } else {
240 if (!check_two_vecs(previousVec, vec, turnDir,
241 &xDir, &yDir,
242 &flipX, &flipY)) {
243 fConvexHint = kConcave_ConvexHint;
244 break;
245 }
246 }
247 }
248 previousVec = vec;
249 previousPt = pts[consumed];
250 ++subPathPts;
251 }
252 ++consumed;
253 }
reed@google.com6f8f2922011-03-04 22:27:10 +0000254 if (subPathPts > 2 && (kClose_PathCmd == cmd ||
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000255 (!subPathClosed && kEnd_PathCmd == cmd ))) {
256 // if an additional vector is needed to close the loop check
257 // that it validates against the previous vector.
258 GrVec vec;
259 vec.setBetween(previousPt, firstPt);
260 if (vec.fX || vec.fY) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000261 if (!check_two_vecs(previousVec, vec, turnDir,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000262 &xDir, &yDir, &flipX, &flipY)) {
263 fConvexHint = kConcave_ConvexHint;
264 break;
265 }
266 previousVec = vec;
267 }
268 // check that closing vector validates against the first vector.
reed@google.com6f8f2922011-03-04 22:27:10 +0000269 if (!check_two_vecs(previousVec, firstVec, turnDir,
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000270 &xDir, &yDir, &flipX, &flipY)) {
271 fConvexHint = kConcave_ConvexHint;
272 break;
273 }
274 }
275 }
276 } while (cmd != kEnd_PathCmd);
277 if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) {
278 fConvexHint = kConvex_ConvexHint;
279 } else {
280 bool recurse = false;
281 if (recurse) {
282 this->resetFromIter(iter);
283 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000284 }
285}
286
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000287void GrPath::ConvexUnitTest() {
288 GrPath testPath;
289 GrPath::Iter testIter;
290
291 GrPath pt;
292 pt.moveTo(0, 0);
293 pt.close();
294
295 testIter.reset(pt);
296 testPath.resetFromIter(&testIter);
297 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
298
299 GrPath line;
300 line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
301 line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
302 line.close();
303
304 testIter.reset(line);
305 testPath.resetFromIter(&testIter);
306 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
307
308 GrPath triLeft;
309 triLeft.moveTo(0, 0);
310 triLeft.lineTo(1, 0);
311 triLeft.lineTo(1, 1);
312 triLeft.close();
313
314 testIter.reset(triLeft);
315 testPath.resetFromIter(&testIter);
316 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
317
318 GrPath triRight;
319 triRight.moveTo(0, 0);
320 triRight.lineTo(-1, 0);
321 triRight.lineTo(1, 1);
322 triRight.close();
323
324 testIter.reset(triRight);
325 testPath.resetFromIter(&testIter);
326 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
reed@google.com6f8f2922011-03-04 22:27:10 +0000327
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000328 GrPath square;
329 square.moveTo(0, 0);
330 square.lineTo(1, 0);
331 square.lineTo(1, 1);
332 square.lineTo(0, 1);
333 square.close();
334
335 testIter.reset(square);
336 testPath.resetFromIter(&testIter);
337 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
338
339 GrPath redundantSquare;
340 square.moveTo(0, 0);
341 square.lineTo(0, 0);
342 square.lineTo(0, 0);
343 square.lineTo(1, 0);
344 square.lineTo(1, 0);
345 square.lineTo(1, 0);
346 square.lineTo(1, 1);
347 square.lineTo(1, 1);
348 square.lineTo(1, 1);
349 square.lineTo(0, 1);
350 square.lineTo(0, 1);
351 square.lineTo(0, 1);
352 square.close();
reed@google.com6f8f2922011-03-04 22:27:10 +0000353
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000354 testIter.reset(redundantSquare);
355 testPath.resetFromIter(&testIter);
356 GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
357
358 GrPath bowTie;
359 bowTie.moveTo(0, 0);
360 bowTie.lineTo(0, 0);
361 bowTie.lineTo(0, 0);
362 bowTie.lineTo(1, 1);
363 bowTie.lineTo(1, 1);
364 bowTie.lineTo(1, 1);
365 bowTie.lineTo(1, 0);
366 bowTie.lineTo(1, 0);
367 bowTie.lineTo(1, 0);
368 bowTie.lineTo(0, 1);
369 bowTie.lineTo(0, 1);
370 bowTie.lineTo(0, 1);
371 bowTie.close();
reed@google.com6f8f2922011-03-04 22:27:10 +0000372
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000373 testIter.reset(bowTie);
374 testPath.resetFromIter(&testIter);
375 GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
376
377 GrPath spiral;
378 spiral.moveTo(0, 0);
379 spiral.lineTo(1, 0);
380 spiral.lineTo(1, 1);
381 spiral.lineTo(0, 1);
382 spiral.lineTo(0,.5);
383 spiral.lineTo(.5,.5);
384 spiral.lineTo(.5,.75);
385 spiral.close();
386
387 testIter.reset(spiral);
388 testPath.resetFromIter(&testIter);
389 GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
390
391 GrPath dent;
392 dent.moveTo(0, 0);
393 dent.lineTo(1, 1);
394 dent.lineTo(0, 1);
395 dent.lineTo(-.5,2);
396 dent.lineTo(-2, 1);
397 dent.close();
398
399 testIter.reset(dent);
400 testPath.resetFromIter(&testIter);
401 GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
402}
reed@google.comac10a2d2010-12-22 21:39:39 +0000403///////////////////////////////////////////////////////////////////////////////
404
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000405GrPath::Iter::Iter() : fPath(NULL) {
406}
407
408GrPath::Iter::Iter(const GrPath& path) : fPath(&path) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000409 this->rewind();
410}
411
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000412GrPathCmd GrPath::Iter::next(GrPoint points[]) {
413 if (fCmdIndex == fPath->fCmds.count()) {
414 GrAssert(fPtIndex == fPath->fPts.count());
415 return kEnd_PathCmd;
reed@google.comac10a2d2010-12-22 21:39:39 +0000416 } else {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000417 GrAssert(fCmdIndex < fPath->fCmds.count());
reed@google.comac10a2d2010-12-22 21:39:39 +0000418 }
419
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000420 GrPathCmd cmd = fPath->fCmds[fCmdIndex++];
421 const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex;
reed@google.comac10a2d2010-12-22 21:39:39 +0000422
423 switch (cmd) {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000424 case kMove_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000425 if (points) {
426 points[0] = srcPts[0];
427 }
428 fLastPt = srcPts[0];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000429 GrAssert(fPtIndex <= fPath->fPts.count() + 1);
reed@google.comac10a2d2010-12-22 21:39:39 +0000430 fPtIndex += 1;
431 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000432 case kLine_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000433 if (points) {
434 points[0] = fLastPt;
435 points[1] = srcPts[0];
436 }
437 fLastPt = srcPts[0];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000438 GrAssert(fPtIndex <= fPath->fPts.count() + 1);
reed@google.comac10a2d2010-12-22 21:39:39 +0000439 fPtIndex += 1;
440 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000441 case kQuadratic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000442 if (points) {
443 points[0] = fLastPt;
444 points[1] = srcPts[0];
445 points[2] = srcPts[1];
446 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000447 fLastPt = srcPts[1];
448 GrAssert(fPtIndex <= fPath->fPts.count() + 2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000449 fPtIndex += 2;
450 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000451 case kCubic_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000452 if (points) {
453 points[0] = fLastPt;
454 points[1] = srcPts[0];
455 points[2] = srcPts[1];
456 points[3] = srcPts[2];
457 }
458 fLastPt = srcPts[2];
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000459 GrAssert(fPtIndex <= fPath->fPts.count() + 3);
reed@google.comac10a2d2010-12-22 21:39:39 +0000460 fPtIndex += 3;
461 break;
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000462 case kClose_PathCmd:
reed@google.comac10a2d2010-12-22 21:39:39 +0000463 break;
464 default:
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000465 GrAssert(!"unknown grpath cmd");
reed@google.comac10a2d2010-12-22 21:39:39 +0000466 break;
467 }
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000468 return cmd;
reed@google.comac10a2d2010-12-22 21:39:39 +0000469}
470
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000471GrConvexHint GrPath::Iter::convexHint() const {
472 return fPath->getConvexHint();
reed@google.comac10a2d2010-12-22 21:39:39 +0000473}
474
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000475GrPathCmd GrPath::Iter::next() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000476 return this->next(NULL);
477}
478
479void GrPath::Iter::rewind() {
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000480 this->reset(*fPath);
reed@google.comac10a2d2010-12-22 21:39:39 +0000481}
482
bsalomon@google.com5aaa69e2011-03-04 20:29:08 +0000483void GrPath::Iter::reset(const GrPath& path) {
484 fPath = &path;
485 fCmdIndex = fPtIndex = 0;
486}
reed@google.comac10a2d2010-12-22 21:39:39 +0000487
488