Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(468)

Side by Side Diff: skia/sgl/SkPath.cpp

Issue 113827: Remove the remainder of the skia source code from the Chromium repo.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « skia/sgl/SkPaint.cpp ('k') | skia/sgl/SkPathEffect.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /* libs/graphics/sgl/SkPath.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include "SkPath.h"
19 #include "SkFlattenable.h"
20 #include "SkMath.h"
21
22 ////////////////////////////////////////////////////////////////////////////
23
24 /* This guy's constructor/destructor bracket a path editing operation. It is
25 used when we know the bounds of the amount we are going to add to the path
26 (usually a new contour, but not required).
27
28 It captures some state about the path up front (i.e. if it already has a
29 cached bounds), and the if it can, it updates the cache bounds explicitly,
30 avoiding the need to revisit all of the points in computeBounds().
31 */
32 class SkAutoPathBoundsUpdate {
33 public:
34 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
35 this->init(path);
36 }
37
38 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
39 SkScalar right, SkScalar bottom) {
40 fRect.set(left, top, right, bottom);
41 this->init(path);
42 }
43
44 ~SkAutoPathBoundsUpdate() {
45 if (fEmpty) {
46 fPath->fFastBounds = fRect;
47 fPath->fFastBoundsIsDirty = false;
48 } else if (!fDirty) {
49 fPath->fFastBounds.join(fRect);
50 fPath->fFastBoundsIsDirty = false;
51 }
52 }
53
54 private:
55 const SkPath* fPath;
56 SkRect fRect;
57 bool fDirty;
58 bool fEmpty;
59
60 // returns true if we should proceed
61 void init(const SkPath* path) {
62 fPath = path;
63 fDirty = path->fFastBoundsIsDirty;
64 fEmpty = path->isEmpty();
65 }
66 };
67
68 static void compute_fast_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
69 if (pts.count() <= 1) { // we ignore just 1 point (moveto)
70 bounds->set(0, 0, 0, 0);
71 } else {
72 bounds->set(pts.begin(), pts.count());
73 // SkDebugf("------- compute bounds %p %d", &pts, pts.count());
74 }
75 }
76
77 ////////////////////////////////////////////////////////////////////////////
78
79 /*
80 Stores the verbs and points as they are given to us, with exceptions:
81 - we only record "Close" if it was immediately preceeded by Line | Quad | Cu bic
82 - we insert a Move(0,0) if Line | Quad | Cubic is our first command
83
84 The iterator does more cleanup, especially if forceClose == true
85 1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
86 2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1
87 3. if we encounter Line | Quad | Cubic after Close, cons up a Move
88 */
89
90 ////////////////////////////////////////////////////////////////////////////
91
92 SkPath::SkPath() : fFastBoundsIsDirty(true), fFillType(kWinding_FillType) {}
93
94 SkPath::SkPath(const SkPath& src) {
95 SkDEBUGCODE(src.validate();)
96 *this = src;
97 }
98
99 SkPath::~SkPath() {
100 SkDEBUGCODE(this->validate();)
101 }
102
103 SkPath& SkPath::operator=(const SkPath& src) {
104 SkDEBUGCODE(src.validate();)
105
106 if (this != &src) {
107 fFastBounds = src.fFastBounds;
108 fPts = src.fPts;
109 fVerbs = src.fVerbs;
110 fFillType = src.fFillType;
111 fFastBoundsIsDirty = src.fFastBoundsIsDirty;
112 }
113 SkDEBUGCODE(this->validate();)
114 return *this;
115 }
116
117 void SkPath::swap(SkPath& other) {
118 SkASSERT(&other != NULL);
119
120 if (this != &other) {
121 SkTSwap<SkRect>(fFastBounds, other.fFastBounds);
122 fPts.swap(other.fPts);
123 fVerbs.swap(other.fVerbs);
124 SkTSwap<uint8_t>(fFillType, other.fFillType);
125 SkTSwap<uint8_t>(fFastBoundsIsDirty, other.fFastBoundsIsDirty);
126 }
127 }
128
129 void SkPath::reset() {
130 SkDEBUGCODE(this->validate();)
131
132 fPts.reset();
133 fVerbs.reset();
134 fFastBoundsIsDirty = true;
135 }
136
137 void SkPath::rewind() {
138 SkDEBUGCODE(this->validate();)
139
140 fPts.rewind();
141 fVerbs.rewind();
142 fFastBoundsIsDirty = true;
143 }
144
145 bool SkPath::isEmpty() const {
146 SkDEBUGCODE(this->validate();)
147
148 int count = fVerbs.count();
149 return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
150 }
151
152 bool SkPath::isRect(SkRect*) const {
153 SkDEBUGCODE(this->validate();)
154
155 SkASSERT(!"unimplemented");
156 return false;
157 }
158
159 int SkPath::getPoints(SkPoint copy[], int max) const {
160 SkDEBUGCODE(this->validate();)
161
162 SkASSERT(max >= 0);
163 int count = fPts.count();
164 if (copy && max > 0 && count > 0) {
165 memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
166 }
167 return count;
168 }
169
170 void SkPath::getLastPt(SkPoint* lastPt) const {
171 SkDEBUGCODE(this->validate();)
172
173 if (lastPt) {
174 int count = fPts.count();
175 if (count == 0) {
176 lastPt->set(0, 0);
177 } else {
178 *lastPt = fPts[count - 1];
179 }
180 }
181 }
182
183 void SkPath::setLastPt(SkScalar x, SkScalar y) {
184 SkDEBUGCODE(this->validate();)
185
186 int count = fPts.count();
187 if (count == 0) {
188 this->moveTo(x, y);
189 } else {
190 fPts[count - 1].set(x, y);
191 }
192 }
193
194 #define ALWAYS_FAST_BOUNDS_FOR_NOW true
195
196 void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const {
197 SkDEBUGCODE(this->validate();)
198
199 SkASSERT(bounds);
200
201 // we BoundsType for now
202
203 if (fFastBoundsIsDirty) {
204 fFastBoundsIsDirty = false;
205 compute_fast_bounds(&fFastBounds, fPts);
206 }
207 *bounds = fFastBounds;
208 }
209
210 //////////////////////////////////////////////////////////////////////////////
211 // Construction methods
212
213 void SkPath::incReserve(U16CPU inc) {
214 SkDEBUGCODE(this->validate();)
215
216 fVerbs.setReserve(fVerbs.count() + inc);
217 fPts.setReserve(fPts.count() + inc);
218
219 SkDEBUGCODE(this->validate();)
220 }
221
222 void SkPath::moveTo(SkScalar x, SkScalar y) {
223 SkDEBUGCODE(this->validate();)
224
225 int vc = fVerbs.count();
226 SkPoint* pt;
227
228 if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
229 pt = &fPts[fPts.count() - 1];
230 } else {
231 pt = fPts.append();
232 *fVerbs.append() = kMove_Verb;
233 }
234 pt->set(x, y);
235
236 fFastBoundsIsDirty = true;
237 }
238
239 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
240 SkPoint pt;
241 this->getLastPt(&pt);
242 this->moveTo(pt.fX + x, pt.fY + y);
243 }
244
245 void SkPath::lineTo(SkScalar x, SkScalar y) {
246 SkDEBUGCODE(this->validate();)
247
248 if (fVerbs.count() == 0) {
249 fPts.append()->set(0, 0);
250 *fVerbs.append() = kMove_Verb;
251 }
252 fPts.append()->set(x, y);
253 *fVerbs.append() = kLine_Verb;
254
255 fFastBoundsIsDirty = true;
256 }
257
258 void SkPath::rLineTo(SkScalar x, SkScalar y) {
259 SkPoint pt;
260 this->getLastPt(&pt);
261 this->lineTo(pt.fX + x, pt.fY + y);
262 }
263
264 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
265 SkDEBUGCODE(this->validate();)
266
267 if (fVerbs.count() == 0) {
268 fPts.append()->set(0, 0);
269 *fVerbs.append() = kMove_Verb;
270 }
271
272 SkPoint* pts = fPts.append(2);
273 pts[0].set(x1, y1);
274 pts[1].set(x2, y2);
275 *fVerbs.append() = kQuad_Verb;
276
277 fFastBoundsIsDirty = true;
278 }
279
280 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
281 SkPoint pt;
282 this->getLastPt(&pt);
283 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
284 }
285
286 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
287 SkScalar x3, SkScalar y3) {
288 SkDEBUGCODE(this->validate();)
289
290 if (fVerbs.count() == 0) {
291 fPts.append()->set(0, 0);
292 *fVerbs.append() = kMove_Verb;
293 }
294 SkPoint* pts = fPts.append(3);
295 pts[0].set(x1, y1);
296 pts[1].set(x2, y2);
297 pts[2].set(x3, y3);
298 *fVerbs.append() = kCubic_Verb;
299
300 fFastBoundsIsDirty = true;
301 }
302
303 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
304 SkScalar x3, SkScalar y3) {
305 SkPoint pt;
306 this->getLastPt(&pt);
307 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
308 pt.fX + x3, pt.fY + y3);
309 }
310
311 void SkPath::close() {
312 SkDEBUGCODE(this->validate();)
313
314 int count = fVerbs.count();
315 if (count > 0) {
316 switch (fVerbs[count - 1]) {
317 case kLine_Verb:
318 case kQuad_Verb:
319 case kCubic_Verb:
320 *fVerbs.append() = kClose_Verb;
321 break;
322 default:
323 // don't add a close if the prev wasn't a primitive
324 break;
325 }
326 }
327 }
328
329 ///////////////////////////////////////////////////////////////////////////////
330
331 void SkPath::addRect(const SkRect& rect, Direction dir) {
332 this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
333 }
334
335 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
336 SkScalar bottom, Direction dir) {
337 SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
338
339 this->incReserve(5);
340
341 this->moveTo(left, top);
342 if (dir == kCCW_Direction) {
343 this->lineTo(left, bottom);
344 this->lineTo(right, bottom);
345 this->lineTo(right, top);
346 } else {
347 this->lineTo(right, top);
348 this->lineTo(right, bottom);
349 this->lineTo(left, bottom);
350 }
351 this->close();
352 }
353
354 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
355
356 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
357 Direction dir) {
358 SkAutoPathBoundsUpdate apbu(this, rect);
359
360 SkScalar w = rect.width();
361 SkScalar halfW = SkScalarHalf(w);
362 SkScalar h = rect.height();
363 SkScalar halfH = SkScalarHalf(h);
364
365 if (halfW <= 0 || halfH <= 0) {
366 return;
367 }
368
369 bool skip_hori = rx >= halfW;
370 bool skip_vert = ry >= halfH;
371
372 if (skip_hori && skip_vert) {
373 this->addOval(rect, dir);
374 return;
375 }
376 if (skip_hori) {
377 rx = halfW;
378 } else if (skip_vert) {
379 ry = halfH;
380 }
381
382 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
383 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
384
385 this->incReserve(17);
386 this->moveTo(rect.fRight - rx, rect.fTop);
387 if (dir == kCCW_Direction) {
388 if (!skip_hori) {
389 this->lineTo(rect.fLeft + rx, rect.fTop); // top
390 }
391 this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
392 rect.fLeft, rect.fTop + ry - sy,
393 rect.fLeft, rect.fTop + ry); // top-left
394 if (!skip_vert) {
395 this->lineTo(rect.fLeft, rect.fBottom - ry); // left
396 }
397 this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
398 rect.fLeft + rx - sx, rect.fBottom,
399 rect.fLeft + rx, rect.fBottom); // bot-left
400 if (!skip_hori) {
401 this->lineTo(rect.fRight - rx, rect.fBottom); // bottom
402 }
403 this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
404 rect.fRight, rect.fBottom - ry + sy,
405 rect.fRight, rect.fBottom - ry); // bot-right
406 if (!skip_vert) {
407 this->lineTo(rect.fRight, rect.fTop + ry);
408 }
409 this->cubicTo(rect.fRight, rect.fTop + ry - sy,
410 rect.fRight - rx + sx, rect.fTop,
411 rect.fRight - rx, rect.fTop); // top-right
412 } else {
413 this->cubicTo(rect.fRight - rx + sx, rect.fTop,
414 rect.fRight, rect.fTop + ry - sy,
415 rect.fRight, rect.fTop + ry); // top-right
416 if (!skip_vert) {
417 this->lineTo(rect.fRight, rect.fBottom - ry);
418 }
419 this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
420 rect.fRight - rx + sx, rect.fBottom,
421 rect.fRight - rx, rect.fBottom); // bot-right
422 if (!skip_hori) {
423 this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom
424 }
425 this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
426 rect.fLeft, rect.fBottom - ry + sy,
427 rect.fLeft, rect.fBottom - ry); // bot-left
428 if (!skip_vert) {
429 this->lineTo(rect.fLeft, rect.fTop + ry); // left
430 }
431 this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
432 rect.fLeft + rx - sx, rect.fTop,
433 rect.fLeft + rx, rect.fTop); // top-left
434 if (!skip_hori) {
435 this->lineTo(rect.fRight - rx, rect.fTop); // top
436 }
437 }
438 this->close();
439 }
440
441 static void add_corner_arc(SkPath* path, const SkRect& rect,
442 SkScalar rx, SkScalar ry, int startAngle,
443 SkPath::Direction dir, bool forceMoveTo) {
444 rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
445 ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
446
447 SkRect r;
448 r.set(-rx, -ry, rx, ry);
449
450 switch (startAngle) {
451 case 0:
452 r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
453 break;
454 case 90:
455 r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom);
456 break;
457 case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break;
458 case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
459 default: SkASSERT(!"unexpected startAngle in add_corner_arc");
460 }
461
462 SkScalar start = SkIntToScalar(startAngle);
463 SkScalar sweep = SkIntToScalar(90);
464 if (SkPath::kCCW_Direction == dir) {
465 start += sweep;
466 sweep = -sweep;
467 }
468
469 path->arcTo(r, start, sweep, forceMoveTo);
470 }
471
472 void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
473 Direction dir) {
474 SkAutoPathBoundsUpdate apbu(this, rect);
475
476 if (kCW_Direction == dir) {
477 add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
478 add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
479 add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
480 add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
481 } else {
482 add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
483 add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
484 add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
485 add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
486 }
487 this->close();
488 }
489
490 void SkPath::addOval(const SkRect& oval, Direction dir) {
491 SkAutoPathBoundsUpdate apbu(this, oval);
492
493 SkScalar cx = oval.centerX();
494 SkScalar cy = oval.centerY();
495 SkScalar rx = SkScalarHalf(oval.width());
496 SkScalar ry = SkScalarHalf(oval.height());
497 #if 0 // these seem faster than using quads (1/2 the number of edges)
498 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
499 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
500
501 this->incReserve(13);
502 this->moveTo(cx + rx, cy);
503 if (dir == kCCW_Direction) {
504 this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
505 this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
506 this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
507 this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
508 } else {
509 this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
510 this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
511 this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
512 this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
513 }
514 #else
515 SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
516 SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
517 SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
518 SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2);
519
520 this->incReserve(17); // 8 quads + close
521 this->moveTo(cx + rx, cy);
522 if (dir == kCCW_Direction) {
523 this->quadTo(cx + rx, cy - sy, cx + mx, cy - my);
524 this->quadTo(cx + sx, cy - ry, cx + 0, cy - ry);
525 this->quadTo(cx - sx, cy - ry, cx - mx, cy - my);
526 this->quadTo(cx - rx, cy - sy, cx - rx, cy - 0);
527 this->quadTo(cx - rx, cy + sy, cx - mx, cy + my);
528 this->quadTo(cx - sx, cy + ry, cx - 0, cy + ry);
529 this->quadTo(cx + sx, cy + ry, cx + mx, cy + my);
530 this->quadTo(cx + rx, cy + sy, cx + rx, cy + 0);
531 } else {
532 this->quadTo(cx + rx, cy + sy, cx + mx, cy + my);
533 this->quadTo(cx + sx, cy + ry, cx - 0, cy + ry);
534 this->quadTo(cx - sx, cy + ry, cx - mx, cy + my);
535 this->quadTo(cx - rx, cy + sy, cx - rx, cy - 0);
536 this->quadTo(cx - rx, cy - sy, cx - mx, cy - my);
537 this->quadTo(cx - sx, cy - ry, cx + 0, cy - ry);
538 this->quadTo(cx + sx, cy - ry, cx + mx, cy - my);
539 this->quadTo(cx + rx, cy - sy, cx + rx, cy + 0);
540 }
541 #endif
542 this->close();
543 }
544
545 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
546 if (r > 0) {
547 SkRect rect;
548 rect.set(x - r, y - r, x + r, y + r);
549 this->addOval(rect, dir);
550 }
551 }
552
553 #include "SkGeometry.h"
554
555 static int build_arc_points(const SkRect& oval, SkScalar startAngle,
556 SkScalar sweepAngle,
557 SkPoint pts[kSkBuildQuadArcStorage]) {
558 SkVector start, stop;
559
560 start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
561 stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
562 &stop.fX);
563
564 SkMatrix matrix;
565
566 matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
567 matrix.postTranslate(oval.centerX(), oval.centerY());
568
569 return SkBuildQuadArc(start, stop,
570 sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
571 &matrix, pts);
572 }
573
574 void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
575 bool forceMoveTo) {
576 if (oval.width() < 0 || oval.height() < 0) {
577 return;
578 }
579
580 SkPoint pts[kSkBuildQuadArcStorage];
581 int count = build_arc_points(oval, startAngle, sweepAngle, pts);
582 SkASSERT((count & 1) == 1);
583
584 if (fVerbs.count() == 0) {
585 forceMoveTo = true;
586 }
587 this->incReserve(count);
588 forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
589 for (int i = 1; i < count; i += 2) {
590 this->quadTo(pts[i], pts[i+1]);
591 }
592 }
593
594 void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
595 SkScalar sweepAngle) {
596 if (oval.isEmpty() || 0 == sweepAngle) {
597 return;
598 }
599
600 const SkScalar kFullCircleAngle = SkIntToScalar(360);
601
602 if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
603 this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
604 return;
605 }
606
607 SkPoint pts[kSkBuildQuadArcStorage];
608 int count = build_arc_points(oval, startAngle, sweepAngle, pts);
609
610 this->incReserve(count);
611 this->moveTo(pts[0]);
612 for (int i = 1; i < count; i += 2) {
613 this->quadTo(pts[i], pts[i+1]);
614 }
615 }
616
617 /*
618 Need to handle the case when the angle is sharp, and our computed end-points
619 for the arc go behind pt1 and/or p2...
620 */
621 void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
622 SkScalar radius) {
623 SkVector before, after;
624
625 // need to know our prev pt so we can construct tangent vectors
626 {
627 SkPoint start;
628 this->getLastPt(&start);
629 before.setNormalize(x1 - start.fX, y1 - start.fY);
630 after.setNormalize(x2 - x1, y2 - y1);
631 }
632
633 SkScalar cosh = SkPoint::DotProduct(before, after);
634 SkScalar sinh = SkPoint::CrossProduct(before, after);
635
636 if (SkScalarNearlyZero(sinh)) { // angle is too tight
637 return;
638 }
639
640 SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
641 if (dist < 0) {
642 dist = -dist;
643 }
644
645 SkScalar xx = x1 - SkScalarMul(dist, before.fX);
646 SkScalar yy = y1 - SkScalarMul(dist, before.fY);
647 SkRotationDirection arcDir;
648
649 // now turn before/after into normals
650 if (sinh > 0) {
651 before.rotateCCW();
652 after.rotateCCW();
653 arcDir = kCW_SkRotationDirection;
654 } else {
655 before.rotateCW();
656 after.rotateCW();
657 arcDir = kCCW_SkRotationDirection;
658 }
659
660 SkMatrix matrix;
661 SkPoint pts[kSkBuildQuadArcStorage];
662
663 matrix.setScale(radius, radius);
664 matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
665 yy - SkScalarMul(radius, before.fY));
666
667 int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
668
669 this->incReserve(count);
670 // [xx,yy] == pts[0]
671 this->lineTo(xx, yy);
672 for (int i = 1; i < count; i += 2) {
673 this->quadTo(pts[i], pts[i+1]);
674 }
675 }
676
677 ///////////////////////////////////////////////////////////////////////////////
678
679 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
680 SkMatrix matrix;
681
682 matrix.setTranslate(dx, dy);
683 this->addPath(path, matrix);
684 }
685
686 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
687 this->incReserve(path.fPts.count());
688
689 Iter iter(path, false);
690 SkPoint pts[4];
691 Verb verb;
692
693 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
694
695 while ((verb = iter.next(pts)) != kDone_Verb) {
696 switch (verb) {
697 case kMove_Verb:
698 proc(matrix, &pts[0], &pts[0], 1);
699 this->moveTo(pts[0]);
700 break;
701 case kLine_Verb:
702 proc(matrix, &pts[1], &pts[1], 1);
703 this->lineTo(pts[1]);
704 break;
705 case kQuad_Verb:
706 proc(matrix, &pts[1], &pts[1], 2);
707 this->quadTo(pts[1], pts[2]);
708 break;
709 case kCubic_Verb:
710 proc(matrix, &pts[1], &pts[1], 3);
711 this->cubicTo(pts[1], pts[2], pts[3]);
712 break;
713 case kClose_Verb:
714 this->close();
715 break;
716 default:
717 SkASSERT(!"unknown verb");
718 }
719 }
720 }
721
722 ///////////////////////////////////////////////////////////////////////////////
723
724 static const uint8_t gPtsInVerb[] = {
725 1, // kMove
726 1, // kLine
727 2, // kQuad
728 3, // kCubic
729 0, // kClose
730 0 // kDone
731 };
732
733 // ignore the initial moveto, and stop when the 1st contour ends
734 void SkPath::pathTo(const SkPath& path) {
735 int i, vcount = path.fVerbs.count();
736 if (vcount == 0) {
737 return;
738 }
739
740 this->incReserve(vcount);
741
742 const uint8_t* verbs = path.fVerbs.begin();
743 const SkPoint* pts = path.fPts.begin() + 1; // 1 for the initial moveTo
744
745 SkASSERT(verbs[0] == kMove_Verb);
746 for (i = 1; i < vcount; i++) {
747 switch (verbs[i]) {
748 case kLine_Verb:
749 this->lineTo(pts[0].fX, pts[0].fY);
750 break;
751 case kQuad_Verb:
752 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
753 break;
754 case kCubic_Verb:
755 this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
756 pts[2].fX, pts[2].fY);
757 break;
758 case kClose_Verb:
759 return;
760 }
761 pts += gPtsInVerb[verbs[i]];
762 }
763 }
764
765 // ignore the last point of the 1st contour
766 void SkPath::reversePathTo(const SkPath& path) {
767 int i, vcount = path.fVerbs.count();
768 if (vcount == 0) {
769 return;
770 }
771
772 this->incReserve(vcount);
773
774 const uint8_t* verbs = path.fVerbs.begin();
775 const SkPoint* pts = path.fPts.begin();
776
777 SkASSERT(verbs[0] == kMove_Verb);
778 for (i = 1; i < vcount; i++) {
779 int n = gPtsInVerb[verbs[i]];
780 if (n == 0) {
781 break;
782 }
783 pts += n;
784 }
785
786 while (--i > 0) {
787 switch (verbs[i]) {
788 case kLine_Verb:
789 this->lineTo(pts[-1].fX, pts[-1].fY);
790 break;
791 case kQuad_Verb:
792 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
793 break;
794 case kCubic_Verb:
795 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
796 pts[-3].fX, pts[-3].fY);
797 break;
798 default:
799 SkASSERT(!"bad verb");
800 break;
801 }
802 pts -= gPtsInVerb[verbs[i]];
803 }
804 }
805
806 ///////////////////////////////////////////////////////////////////////////////
807
808 void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
809 SkMatrix matrix;
810
811 matrix.setTranslate(dx, dy);
812 this->transform(matrix, dst);
813 }
814
815 #include "SkGeometry.h"
816
817 static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
818 int level = 2) {
819 if (--level >= 0) {
820 SkPoint tmp[5];
821
822 SkChopQuadAtHalf(pts, tmp);
823 subdivide_quad_to(path, &tmp[0], level);
824 subdivide_quad_to(path, &tmp[2], level);
825 } else {
826 path->quadTo(pts[1], pts[2]);
827 }
828 }
829
830 static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
831 int level = 2) {
832 if (--level >= 0) {
833 SkPoint tmp[7];
834
835 SkChopCubicAtHalf(pts, tmp);
836 subdivide_cubic_to(path, &tmp[0], level);
837 subdivide_cubic_to(path, &tmp[3], level);
838 } else {
839 path->cubicTo(pts[1], pts[2], pts[3]);
840 }
841 }
842
843 void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
844 SkDEBUGCODE(this->validate();)
845 if (dst == NULL) {
846 dst = (SkPath*)this;
847 }
848
849 if (matrix.getType() & SkMatrix::kPerspective_Mask) {
850 SkPath tmp;
851 tmp.fFillType = fFillType;
852
853 SkPath::Iter iter(*this, false);
854 SkPoint pts[4];
855 SkPath::Verb verb;
856
857 while ((verb = iter.next(pts)) != kDone_Verb) {
858 switch (verb) {
859 case kMove_Verb:
860 tmp.moveTo(pts[0]);
861 break;
862 case kLine_Verb:
863 tmp.lineTo(pts[1]);
864 break;
865 case kQuad_Verb:
866 subdivide_quad_to(&tmp, pts);
867 break;
868 case kCubic_Verb:
869 subdivide_cubic_to(&tmp, pts);
870 break;
871 case kClose_Verb:
872 tmp.close();
873 break;
874 default:
875 SkASSERT(!"unknown verb");
876 break;
877 }
878 }
879
880 dst->swap(tmp);
881 matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
882 } else {
883 // remember that dst might == this, so be sure to check
884 // fFastBoundsIsDirty before we set it
885 if (!fFastBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
886 // if we're empty, fastbounds should not be mapped
887 matrix.mapRect(&dst->fFastBounds, fFastBounds);
888 dst->fFastBoundsIsDirty = false;
889 } else {
890 dst->fFastBoundsIsDirty = true;
891 }
892
893 if (this != dst) {
894 dst->fVerbs = fVerbs;
895 dst->fPts.setCount(fPts.count());
896 dst->fFillType = fFillType;
897 }
898 matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
899 SkDEBUGCODE(dst->validate();)
900 }
901 }
902
903 void SkPath::updateBoundsCache() const {
904 if (fFastBoundsIsDirty) {
905 SkRect r;
906 this->computeBounds(&r, kFast_BoundsType);
907 SkASSERT(!fFastBoundsIsDirty);
908 }
909 }
910
911 ///////////////////////////////////////////////////////////////////////////////
912 ///////////////////////////////////////////////////////////////////////////////
913
914 enum NeedMoveToState {
915 kAfterClose_NeedMoveToState,
916 kAfterCons_NeedMoveToState,
917 kAfterPrefix_NeedMoveToState
918 };
919
920 SkPath::Iter::Iter() {
921 #ifdef SK_DEBUG
922 fPts = NULL;
923 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
924 fForceClose = fNeedMoveTo = fCloseLine = false;
925 #endif
926 // need to init enough to make next() harmlessly return kDone_Verb
927 fVerbs = NULL;
928 fVerbStop = NULL;
929 fNeedClose = false;
930 }
931
932 SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
933 this->setPath(path, forceClose);
934 }
935
936 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
937 fPts = path.fPts.begin();
938 fVerbs = path.fVerbs.begin();
939 fVerbStop = path.fVerbs.end();
940 fForceClose = SkToU8(forceClose);
941 fNeedClose = false;
942 fNeedMoveTo = kAfterPrefix_NeedMoveToState;
943 }
944
945 bool SkPath::Iter::isClosedContour() const {
946 if (fVerbs == NULL || fVerbs == fVerbStop) {
947 return false;
948 }
949 if (fForceClose) {
950 return true;
951 }
952
953 const uint8_t* verbs = fVerbs;
954 const uint8_t* stop = fVerbStop;
955
956 if (kMove_Verb == *verbs) {
957 verbs += 1; // skip the initial moveto
958 }
959
960 while (verbs < stop) {
961 unsigned v = *verbs++;
962 if (kMove_Verb == v) {
963 break;
964 }
965 if (kClose_Verb == v) {
966 return true;
967 }
968 }
969 return false;
970 }
971
972 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
973 if (fLastPt != fMoveTo) {
974 if (pts) {
975 pts[0] = fLastPt;
976 pts[1] = fMoveTo;
977 }
978 fLastPt = fMoveTo;
979 fCloseLine = true;
980 return kLine_Verb;
981 }
982 return kClose_Verb;
983 }
984
985 bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
986 if (fNeedMoveTo == kAfterClose_NeedMoveToState) {
987 if (pts) {
988 *pts = fMoveTo;
989 }
990 fNeedClose = fForceClose;
991 fNeedMoveTo = kAfterCons_NeedMoveToState;
992 fVerbs -= 1;
993 return true;
994 }
995
996 if (fNeedMoveTo == kAfterCons_NeedMoveToState) {
997 if (pts) {
998 *pts = fMoveTo;
999 }
1000 fNeedMoveTo = kAfterPrefix_NeedMoveToState;
1001 } else {
1002 SkASSERT(fNeedMoveTo == kAfterPrefix_NeedMoveToState);
1003 if (pts) {
1004 *pts = fPts[-1];
1005 }
1006 }
1007 return false;
1008 }
1009
1010 SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
1011 if (fVerbs == fVerbStop) {
1012 if (fNeedClose) {
1013 if (kLine_Verb == this->autoClose(pts)) {
1014 return kLine_Verb;
1015 }
1016 fNeedClose = false;
1017 return kClose_Verb;
1018 }
1019 return kDone_Verb;
1020 }
1021
1022 unsigned verb = *fVerbs++;
1023 const SkPoint* srcPts = fPts;
1024
1025 switch (verb) {
1026 case kMove_Verb:
1027 if (fNeedClose) {
1028 fVerbs -= 1;
1029 verb = this->autoClose(pts);
1030 if (verb == kClose_Verb) {
1031 fNeedClose = false;
1032 }
1033 return (Verb)verb;
1034 }
1035 if (fVerbs == fVerbStop) { // might be a trailing moveto
1036 return kDone_Verb;
1037 }
1038 fMoveTo = *srcPts;
1039 if (pts) {
1040 pts[0] = *srcPts;
1041 }
1042 srcPts += 1;
1043 fNeedMoveTo = kAfterCons_NeedMoveToState;
1044 fNeedClose = fForceClose;
1045 break;
1046 case kLine_Verb:
1047 if (this->cons_moveTo(pts)) {
1048 return kMove_Verb;
1049 }
1050 if (pts) {
1051 pts[1] = srcPts[0];
1052 }
1053 fLastPt = srcPts[0];
1054 fCloseLine = false;
1055 srcPts += 1;
1056 break;
1057 case kQuad_Verb:
1058 if (this->cons_moveTo(pts)) {
1059 return kMove_Verb;
1060 }
1061 if (pts) {
1062 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
1063 }
1064 fLastPt = srcPts[1];
1065 srcPts += 2;
1066 break;
1067 case kCubic_Verb:
1068 if (this->cons_moveTo(pts)) {
1069 return kMove_Verb;
1070 }
1071 if (pts) {
1072 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
1073 }
1074 fLastPt = srcPts[2];
1075 srcPts += 3;
1076 break;
1077 case kClose_Verb:
1078 verb = this->autoClose(pts);
1079 if (verb == kLine_Verb) {
1080 fVerbs -= 1;
1081 } else {
1082 fNeedClose = false;
1083 }
1084 fNeedMoveTo = kAfterClose_NeedMoveToState;
1085 break;
1086 }
1087 fPts = srcPts;
1088 return (Verb)verb;
1089 }
1090
1091 ///////////////////////////////////////////////////////////////////////////////
1092
1093 static bool exceeds_dist(const SkScalar p[], const SkScalar q[], SkScalar dist,
1094 int count) {
1095 SkASSERT(dist > 0);
1096
1097 count *= 2;
1098 for (int i = 0; i < count; i++) {
1099 if (SkScalarAbs(p[i] - q[i]) > dist) {
1100 return true;
1101 }
1102 }
1103 return false;
1104 }
1105
1106 static void subdivide_quad(SkPath* dst, const SkPoint pts[3], SkScalar dist,
1107 int subLevel = 4) {
1108 if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 4)) {
1109 SkPoint tmp[5];
1110 SkChopQuadAtHalf(pts, tmp);
1111
1112 subdivide_quad(dst, &tmp[0], dist, subLevel);
1113 subdivide_quad(dst, &tmp[2], dist, subLevel);
1114 } else {
1115 dst->quadTo(pts[1], pts[2]);
1116 }
1117 }
1118
1119 static void subdivide_cubic(SkPath* dst, const SkPoint pts[4], SkScalar dist,
1120 int subLevel = 4) {
1121 if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 6)) {
1122 SkPoint tmp[7];
1123 SkChopCubicAtHalf(pts, tmp);
1124
1125 subdivide_cubic(dst, &tmp[0], dist, subLevel);
1126 subdivide_cubic(dst, &tmp[3], dist, subLevel);
1127 } else {
1128 dst->cubicTo(pts[1], pts[2], pts[3]);
1129 }
1130 }
1131
1132 void SkPath::subdivide(SkScalar dist, bool bendLines, SkPath* dst) const {
1133 SkPath tmpPath;
1134 if (NULL == dst || this == dst) {
1135 dst = &tmpPath;
1136 }
1137
1138 SkPath::Iter iter(*this, false);
1139 SkPoint pts[4];
1140
1141 for (;;) {
1142 switch (iter.next(pts)) {
1143 case SkPath::kMove_Verb:
1144 dst->moveTo(pts[0]);
1145 break;
1146 case SkPath::kLine_Verb:
1147 if (!bendLines) {
1148 dst->lineTo(pts[1]);
1149 break;
1150 }
1151 // construct a quad from the line
1152 pts[2] = pts[1];
1153 pts[1].set(SkScalarAve(pts[0].fX, pts[2].fX),
1154 SkScalarAve(pts[0].fY, pts[2].fY));
1155 // fall through to the quad case
1156 case SkPath::kQuad_Verb:
1157 subdivide_quad(dst, pts, dist);
1158 break;
1159 case SkPath::kCubic_Verb:
1160 subdivide_cubic(dst, pts, dist);
1161 break;
1162 case SkPath::kClose_Verb:
1163 dst->close();
1164 break;
1165 case SkPath::kDone_Verb:
1166 goto DONE;
1167 }
1168 }
1169 DONE:
1170 if (&tmpPath == dst) { // i.e. the dst should be us
1171 dst->swap(*(SkPath*)this);
1172 }
1173 }
1174
1175 ///////////////////////////////////////////////////////////////////////
1176 /*
1177 Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
1178 */
1179
1180 void SkPath::flatten(SkFlattenableWriteBuffer& buffer) const {
1181 SkDEBUGCODE(this->validate();)
1182
1183 buffer.write32(fPts.count());
1184 buffer.write32(fVerbs.count());
1185 buffer.write32(fFillType);
1186 buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
1187 buffer.writePad(fVerbs.begin(), fVerbs.count());
1188 }
1189
1190 void SkPath::unflatten(SkFlattenableReadBuffer& buffer) {
1191 fPts.setCount(buffer.readS32());
1192 fVerbs.setCount(buffer.readS32());
1193 fFillType = buffer.readS32();
1194 buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
1195 buffer.read(fVerbs.begin(), fVerbs.count());
1196
1197 fFastBoundsIsDirty = true;
1198
1199 SkDEBUGCODE(this->validate();)
1200 }
1201
1202 ///////////////////////////////////////////////////////////////////////////////
1203
1204 #include "SkString.h"
1205 #include "SkStream.h"
1206
1207 static void write_scalar(SkWStream* stream, SkScalar value) {
1208 char buffer[SkStrAppendScalar_MaxSize];
1209 char* stop = SkStrAppendScalar(buffer, value);
1210 stream->write(buffer, stop - buffer);
1211 }
1212
1213 static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
1214 int count) {
1215 stream->write(&verb, 1);
1216 write_scalar(stream, data[0]);
1217 for (int i = 1; i < count; i++) {
1218 if (data[i] >= 0) {
1219 // can skip the separater if data[i] is negative
1220 stream->write(" ", 1);
1221 }
1222 write_scalar(stream, data[i]);
1223 }
1224 }
1225
1226 void SkPath::toString(SkString* str) const {
1227 SkDynamicMemoryWStream stream;
1228
1229 SkPath::Iter iter(*this, false);
1230 SkPoint pts[4];
1231
1232 for (;;) {
1233 switch (iter.next(pts)) {
1234 case SkPath::kMove_Verb:
1235 append_scalars(&stream, 'M', &pts[0].fX, 2);
1236 break;
1237 case SkPath::kLine_Verb:
1238 append_scalars(&stream, 'L', &pts[1].fX, 2);
1239 break;
1240 case SkPath::kQuad_Verb:
1241 append_scalars(&stream, 'Q', &pts[1].fX, 4);
1242 break;
1243 case SkPath::kCubic_Verb:
1244 append_scalars(&stream, 'C', &pts[1].fX, 6);
1245 break;
1246 case SkPath::kClose_Verb:
1247 stream.write("Z", 1);
1248 break;
1249 case SkPath::kDone_Verb:
1250 str->resize(stream.getOffset());
1251 stream.copyTo(str->writable_str());
1252 return;
1253 }
1254 }
1255 }
1256
1257 ///////////////////////////////////////////////////////////////////////////////
1258 ///////////////////////////////////////////////////////////////////////////////
1259
1260 #ifdef SK_DEBUG
1261
1262 void SkPath::validate() const {
1263 SkASSERT(this != NULL);
1264 SkASSERT((fFillType & ~3) == 0);
1265 fPts.validate();
1266 fVerbs.validate();
1267
1268 if (!fFastBoundsIsDirty) {
1269 SkRect bounds;
1270 compute_fast_bounds(&bounds, fPts);
1271 // can't call contains(), since it returns false if the rect is empty
1272 SkASSERT(fFastBounds.fLeft <= bounds.fLeft);
1273 SkASSERT(fFastBounds.fTop <= bounds.fTop);
1274 SkASSERT(fFastBounds.fRight >= bounds.fRight);
1275 SkASSERT(fFastBounds.fBottom >= bounds.fBottom);
1276 }
1277 }
1278
1279 #if 0 // test to ensure that the iterator returns the same data as the path
1280 void SkPath::test() const
1281 {
1282 Iter iter(*this, false);
1283 SkPoint pts[4];
1284 Verb verb;
1285
1286 const uint8_t* verbs = fVerbs.begin();
1287 const SkPoint* points = fPts.begin();
1288
1289 while ((verb = iter.next(pts)) != kDone_Verb)
1290 {
1291 SkASSERT(*verbs == verb);
1292 verbs += 1;
1293
1294 int count;
1295 switch (verb) {
1296 case kMove_Verb:
1297 count = 1;
1298 break;
1299 case kLine_Verb:
1300 count = 2;
1301 break;
1302 case kQuad_Verb:
1303 count = 3;
1304 break;
1305 case kCubic_Verb:
1306 count = 4;
1307 break;
1308 case kClose_Verb:
1309 default:
1310 count = 0;
1311 break;
1312 }
1313 if (count > 1)
1314 points -= 1;
1315 SkASSERT(memcmp(pts, points, count * sizeof(SkPoint)) == 0);
1316 points += count;
1317 }
1318
1319 int vc = fVerbs.count(), pc = fPts.count();
1320 if (vc && fVerbs.begin()[vc-1] == kMove_Verb)
1321 {
1322 vc -= 1;
1323 pc -= 1;
1324 }
1325 SkASSERT(verbs - fVerbs.begin() == vc);
1326 SkASSERT(points - fPts.begin() == pc);
1327 }
1328 #endif
1329
1330 void SkPath::dump(bool forceClose, const char title[]) const {
1331 Iter iter(*this, forceClose);
1332 SkPoint pts[4];
1333 Verb verb;
1334
1335 SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
1336 title ? title : "");
1337
1338 while ((verb = iter.next(pts)) != kDone_Verb) {
1339 switch (verb) {
1340 case kMove_Verb:
1341 #ifdef SK_CAN_USE_FLOAT
1342 SkDebugf(" path: moveTo [%g %g]\n",
1343 SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
1344 #else
1345 SkDebugf(" path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
1346 #endif
1347 break;
1348 case kLine_Verb:
1349 #ifdef SK_CAN_USE_FLOAT
1350 SkDebugf(" path: lineTo [%g %g]\n",
1351 SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
1352 #else
1353 SkDebugf(" path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
1354 #endif
1355 break;
1356 case kQuad_Verb:
1357 #ifdef SK_CAN_USE_FLOAT
1358 SkDebugf(" path: quadTo [%g %g] [%g %g]\n",
1359 SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
1360 SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
1361 #else
1362 SkDebugf(" path: quadTo [%x %x] [%x %x]\n",
1363 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
1364 #endif
1365 break;
1366 case kCubic_Verb:
1367 #ifdef SK_CAN_USE_FLOAT
1368 SkDebugf(" path: cubeTo [%g %g] [%g %g] [%g %g]\n",
1369 SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
1370 SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
1371 SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
1372 #else
1373 SkDebugf(" path: cubeTo [%x %x] [%x %x] [%x %x]\n",
1374 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
1375 pts[3].fX, pts[3].fY);
1376 #endif
1377 break;
1378 case kClose_Verb:
1379 SkDebugf(" path: close\n");
1380 break;
1381 default:
1382 SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
1383 verb = kDone_Verb; // stop the loop
1384 break;
1385 }
1386 }
1387 SkDebugf("path: done %s\n", title ? title : "");
1388 }
1389
1390 #include "SkTSort.h"
1391
1392 void SkPath::UnitTest() {
1393 #ifdef SK_SUPPORT_UNITTEST
1394 SkPath p;
1395 SkRect r;
1396
1397 r.set(0, 0, 10, 20);
1398 p.addRect(r);
1399 p.dump(false);
1400 p.dump(true);
1401
1402 {
1403 int array[] = { 5, 3, 7, 2, 6, 1, 2, 9, 5, 0 };
1404 int i;
1405
1406 for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) {
1407 SkDebugf(" %d", array[i]);
1408 }
1409 SkDebugf("\n");
1410 SkTHeapSort<int>(array, SK_ARRAY_COUNT(array));
1411 for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++)
1412 SkDebugf(" %d", array[i]);
1413 SkDebugf("\n");
1414 }
1415
1416 {
1417 SkPath p;
1418 SkPoint pt;
1419
1420 p.moveTo(SK_Scalar1, 0);
1421 p.getLastPt(&pt);
1422 SkASSERT(pt.fX == SK_Scalar1);
1423 }
1424 #endif
1425 }
1426
1427 #endif
OLDNEW
« no previous file with comments | « skia/sgl/SkPaint.cpp ('k') | skia/sgl/SkPathEffect.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698