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