OLD | NEW |
| (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 | |
OLD | NEW |