| OLD | NEW |
| 1 | 1 |
| 2 /* | 2 /* |
| 3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
| 4 * | 4 * |
| 5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 | 9 |
| 10 #include "SkBuffer.h" | 10 #include "SkBuffer.h" |
| 11 #include "SkErrorInternals.h" | 11 #include "SkErrorInternals.h" |
| 12 #include "SkMath.h" | 12 #include "SkMath.h" |
| 13 #include "SkPath.h" | 13 #include "SkPath.h" |
| 14 #include "SkPathRef.h" | 14 #include "SkPathRef.h" |
| 15 #include "SkRRect.h" | 15 #include "SkRRect.h" |
| 16 #include "SkThread.h" | 16 #include "SkThread.h" |
| 17 | 17 |
| 18 SK_DEFINE_INST_COUNT(SkPath); | 18 SK_DEFINE_INST_COUNT(SkPath); |
| 19 | 19 |
| 20 // This value is just made-up for now. When count is 4, calling memset was much | |
| 21 // slower than just writing the loop. This seems odd, and hopefully in the | |
| 22 // future this we appear to have been a fluke... | |
| 23 #define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16 | |
| 24 | |
| 25 //////////////////////////////////////////////////////////////////////////// | 20 //////////////////////////////////////////////////////////////////////////// |
| 26 | 21 |
| 27 /** | 22 /** |
| 28 * Path.bounds is defined to be the bounds of all the control points. | 23 * Path.bounds is defined to be the bounds of all the control points. |
| 29 * If we called bounds.join(r) we would skip r if r was empty, which breaks | 24 * If we called bounds.join(r) we would skip r if r was empty, which breaks |
| 30 * our promise. Hence we have a custom joiner that doesn't look at emptiness | 25 * our promise. Hence we have a custom joiner that doesn't look at emptiness |
| 31 */ | 26 */ |
| 32 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { | 27 static void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { |
| 33 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft); | 28 dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft); |
| 34 dst->fTop = SkMinScalar(dst->fTop, src.fTop); | 29 dst->fTop = SkMinScalar(dst->fTop, src.fTop); |
| 35 dst->fRight = SkMaxScalar(dst->fRight, src.fRight); | 30 dst->fRight = SkMaxScalar(dst->fRight, src.fRight); |
| 36 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom); | 31 dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom); |
| 37 } | 32 } |
| 38 | 33 |
| 39 static bool is_degenerate(const SkPath& path) { | 34 static bool is_degenerate(const SkPath& path) { |
| 40 SkPath::Iter iter(path, false); | 35 SkPath::Iter iter(path, false); |
| 41 SkPoint pts[4]; | 36 SkPoint pts[4]; |
| 42 return SkPath::kDone_Verb == iter.next(pts); | 37 return SkPath::kDone_Verb == iter.next(pts); |
| 43 } | 38 } |
| 44 | 39 |
| 45 class SkAutoDisableOvalCheck { | |
| 46 public: | |
| 47 SkAutoDisableOvalCheck(SkPath* path) : fPath(path) { | |
| 48 fSaved = fPath->fIsOval; | |
| 49 } | |
| 50 | |
| 51 ~SkAutoDisableOvalCheck() { | |
| 52 fPath->fIsOval = fSaved; | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 SkPath* fPath; | |
| 57 bool fSaved; | |
| 58 }; | |
| 59 | |
| 60 class SkAutoDisableDirectionCheck { | |
| 61 public: | |
| 62 SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) { | |
| 63 fSaved = static_cast<SkPath::Direction>(fPath->fDirection); | |
| 64 } | |
| 65 | |
| 66 ~SkAutoDisableDirectionCheck() { | |
| 67 fPath->fDirection = fSaved; | |
| 68 } | |
| 69 | |
| 70 private: | |
| 71 SkPath* fPath; | |
| 72 SkPath::Direction fSaved; | |
| 73 }; | |
| 74 | |
| 75 /* This guy's constructor/destructor bracket a path editing operation. It is | 40 /* This guy's constructor/destructor bracket a path editing operation. It is |
| 76 used when we know the bounds of the amount we are going to add to the path | 41 used when we know the bounds of the amount we are going to add to the path |
| 77 (usually a new contour, but not required). | 42 (usually a new contour, but not required). |
| 78 | 43 |
| 79 It captures some state about the path up front (i.e. if it already has a | 44 It captures some state about the path up front (i.e. if it already has a |
| 80 cached bounds), and then if it can, it updates the cache bounds explicitly, | 45 cached bounds), and then if it can, it updates the cache bounds explicitly, |
| 81 avoiding the need to revisit all of the points in getBounds(). | 46 avoiding the need to revisit all of the points in getBounds(). |
| 82 | 47 |
| 83 It also notes if the path was originally degenerate, and if so, sets | 48 It also notes if the path was originally degenerate, and if so, sets |
| 84 isConvex to true. Thus it can only be used if the contour being added is | 49 isConvex to true. Thus it can only be used if the contour being added is |
| 85 convex (which is always true since we only allow the addition of rects). | 50 convex. |
| 86 */ | 51 */ |
| 87 class SkAutoPathBoundsUpdate { | 52 class SkAutoPathBoundsUpdate { |
| 88 public: | 53 public: |
| 89 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) { | 54 SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) { |
| 90 this->init(path); | 55 this->init(path); |
| 91 } | 56 } |
| 92 | 57 |
| 93 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top, | 58 SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top, |
| 94 SkScalar right, SkScalar bottom) { | 59 SkScalar right, SkScalar bottom) { |
| 95 fRect.set(left, top, right, bottom); | 60 fRect.set(left, top, right, bottom); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 133 - we insert a Move(0,0) if Line | Quad | Cubic is our first command | 98 - we insert a Move(0,0) if Line | Quad | Cubic is our first command |
| 134 | 99 |
| 135 The iterator does more cleanup, especially if forceClose == true | 100 The iterator does more cleanup, especially if forceClose == true |
| 136 1. If we encounter degenerate segments, remove them | 101 1. If we encounter degenerate segments, remove them |
| 137 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt !=
start-pt) | 102 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt !=
start-pt) |
| 138 3. if we encounter Move without a preceeding Close, and forceClose is true,
goto #2 | 103 3. if we encounter Move without a preceeding Close, and forceClose is true,
goto #2 |
| 139 4. if we encounter Line | Quad | Cubic after Close, cons up a Move | 104 4. if we encounter Line | Quad | Cubic after Close, cons up a Move |
| 140 */ | 105 */ |
| 141 | 106 |
| 142 //////////////////////////////////////////////////////////////////////////// | 107 //////////////////////////////////////////////////////////////////////////// |
| 143 | |
| 144 // flag to require a moveTo if we begin with something else, like lineTo etc. | |
| 145 #define INITIAL_LASTMOVETOINDEX_VALUE ~0 | |
| 146 | |
| 147 SkPath::SkPath() | 108 SkPath::SkPath() |
| 148 : fPathRef(SkPathRef::CreateEmpty()) | 109 : fPathRef(SkPathRef::CreateEmpty()) |
| 149 #ifdef SK_BUILD_FOR_ANDROID | 110 #ifdef SK_BUILD_FOR_ANDROID |
| 150 , fGenerationID(0) | 111 , fGenerationID(0) |
| 151 #endif | 112 #endif |
| 152 { | 113 { |
| 153 this->resetFields(); | 114 this->resetFields(); |
| 154 } | 115 } |
| 155 | 116 |
| 156 void SkPath::resetFields() { | 117 void SkPath::resetFields() { |
| 157 //fPathRef is assumed to have been emptied by the caller. | 118 //fPathRef is assumed to have been emptied by the caller. |
| 158 fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; | |
| 159 fFillType = kWinding_FillType; | 119 fFillType = kWinding_FillType; |
| 160 fSegmentMask = 0; | |
| 161 fConvexity = kUnknown_Convexity; | |
| 162 fDirection = kUnknown_Direction; | |
| 163 fIsOval = false; | |
| 164 #ifdef SK_BUILD_FOR_ANDROID | 120 #ifdef SK_BUILD_FOR_ANDROID |
| 165 GEN_ID_INC; | 121 GEN_ID_INC; |
| 166 // We don't touch fSourcePath. It's used to track texture garbage collectio
n, so we don't | 122 // We don't touch fSourcePath. It's used to track texture garbage collectio
n, so we don't |
| 167 // want to muck with it if it's been set to something non-NULL. | 123 // want to muck with it if it's been set to something non-NULL. |
| 168 #endif | 124 #endif |
| 169 } | 125 } |
| 170 | 126 |
| 171 SkPath::SkPath(const SkPath& that) | 127 SkPath::SkPath(const SkPath& that) |
| 172 : fPathRef(SkRef(that.fPathRef.get())) { | 128 : fPathRef(SkRef(that.fPathRef.get())) { |
| 173 this->copyFields(that); | 129 this->copyFields(that); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 192 GEN_ID_INC; // Similar to swap, we can't just copy this or it could go
back in time. | 148 GEN_ID_INC; // Similar to swap, we can't just copy this or it could go
back in time. |
| 193 fSourcePath = that.fSourcePath; | 149 fSourcePath = that.fSourcePath; |
| 194 #endif | 150 #endif |
| 195 } | 151 } |
| 196 SkDEBUGCODE(this->validate();) | 152 SkDEBUGCODE(this->validate();) |
| 197 return *this; | 153 return *this; |
| 198 } | 154 } |
| 199 | 155 |
| 200 void SkPath::copyFields(const SkPath& that) { | 156 void SkPath::copyFields(const SkPath& that) { |
| 201 //fPathRef is assumed to have been set by the caller. | 157 //fPathRef is assumed to have been set by the caller. |
| 202 fLastMoveToIndex = that.fLastMoveToIndex; | |
| 203 fFillType = that.fFillType; | 158 fFillType = that.fFillType; |
| 204 fSegmentMask = that.fSegmentMask; | |
| 205 fConvexity = that.fConvexity; | |
| 206 fDirection = that.fDirection; | |
| 207 fIsOval = that.fIsOval; | |
| 208 } | 159 } |
| 209 | 160 |
| 210 bool operator==(const SkPath& a, const SkPath& b) { | 161 bool operator==(const SkPath& a, const SkPath& b) { |
| 211 // note: don't need to look at isConvex or bounds, since just comparing the | |
| 212 // raw data is sufficient. | |
| 213 | |
| 214 // We explicitly check fSegmentMask as a quick-reject. We could skip it, | |
| 215 // since it is only a cache of info in the fVerbs, but its a fast way to | |
| 216 // notice a difference | |
| 217 | |
| 218 return &a == &b || | 162 return &a == &b || |
| 219 (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask && | 163 (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get()); |
| 220 *a.fPathRef.get() == *b.fPathRef.get()); | |
| 221 } | 164 } |
| 222 | 165 |
| 223 void SkPath::swap(SkPath& that) { | 166 void SkPath::swap(SkPath& that) { |
| 224 SkASSERT(&that != NULL); | 167 SkASSERT(&that != NULL); |
| 225 | 168 |
| 226 if (this != &that) { | 169 if (this != &that) { |
| 227 fPathRef.swap(&that.fPathRef); | 170 fPathRef.swap(&that.fPathRef); |
| 228 SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex); | |
| 229 SkTSwap<uint8_t>(fFillType, that.fFillType); | 171 SkTSwap<uint8_t>(fFillType, that.fFillType); |
| 230 SkTSwap<uint8_t>(fSegmentMask, that.fSegmentMask); | |
| 231 SkTSwap<uint8_t>(fConvexity, that.fConvexity); | |
| 232 SkTSwap<uint8_t>(fDirection, that.fDirection); | |
| 233 SkTSwap<SkBool8>(fIsOval, that.fIsOval); | |
| 234 #ifdef SK_BUILD_FOR_ANDROID | 172 #ifdef SK_BUILD_FOR_ANDROID |
| 235 // It doesn't really make sense to swap the generation IDs here, because
they might go | 173 // It doesn't really make sense to swap the generation IDs here, because
they might go |
| 236 // backwards. To be safe we increment both to mark them both as changed
. | 174 // backwards. To be safe we increment both to mark them both as changed
. |
| 237 GEN_ID_INC; | 175 GEN_ID_INC; |
| 238 GEN_ID_PTR_INC(&that); | 176 GEN_ID_PTR_INC(&that); |
| 239 SkTSwap<const SkPath*>(fSourcePath, that.fSourcePath); | 177 SkTSwap<const SkPath*>(fSourcePath, that.fSourcePath); |
| 240 #endif | 178 #endif |
| 241 } | 179 } |
| 242 } | 180 } |
| 243 | 181 |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 Single points on the rectangle side. | 341 Single points on the rectangle side. |
| 404 | 342 |
| 405 The direction takes advantage of the corners found since opposite sides | 343 The direction takes advantage of the corners found since opposite sides |
| 406 must travel in opposite directions. | 344 must travel in opposite directions. |
| 407 | 345 |
| 408 FIXME: Allow colinear quads and cubics to be treated like lines. | 346 FIXME: Allow colinear quads and cubics to be treated like lines. |
| 409 FIXME: If the API passes fill-only, return true if the filled stroke | 347 FIXME: If the API passes fill-only, return true if the filled stroke |
| 410 is a rectangle, though the caller failed to close the path. | 348 is a rectangle, though the caller failed to close the path. |
| 411 */ | 349 */ |
| 412 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
Ptr, | 350 bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
Ptr, |
| 413 bool* isClosed, Direction* direction) const { | 351 bool* isClosed, Direction* direction) const { |
| 414 int corners = 0; | 352 int corners = 0; |
| 415 SkPoint first, last; | 353 SkPoint first, last; |
| 416 const SkPoint* pts = *ptsPtr; | 354 const SkPoint* pts = *ptsPtr; |
| 417 const SkPoint* savePts = NULL; | 355 const SkPoint* savePts = NULL; |
| 418 first.set(0, 0); | 356 first.set(0, 0); |
| 419 last.set(0, 0); | 357 last.set(0, 0); |
| 420 int firstDirection = 0; | 358 int firstDirection = 0; |
| 421 int lastDirection = 0; | 359 int lastDirection = 0; |
| 422 int nextDirection = 0; | 360 int nextDirection = 0; |
| 423 bool closedOrMoved = false; | 361 bool closedOrMoved = false; |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 623 return false; | 561 return false; |
| 624 } | 562 } |
| 625 | 563 |
| 626 void SkPath::setLastPt(SkScalar x, SkScalar y) { | 564 void SkPath::setLastPt(SkScalar x, SkScalar y) { |
| 627 SkDEBUGCODE(this->validate();) | 565 SkDEBUGCODE(this->validate();) |
| 628 | 566 |
| 629 int count = fPathRef->countPoints(); | 567 int count = fPathRef->countPoints(); |
| 630 if (count == 0) { | 568 if (count == 0) { |
| 631 this->moveTo(x, y); | 569 this->moveTo(x, y); |
| 632 } else { | 570 } else { |
| 633 fIsOval = false; | 571 fPathRef->setIsOval(false); |
| 634 SkPathRef::Editor ed(&fPathRef); | 572 SkPathRef::Editor ed(&fPathRef); |
| 635 ed.atPoint(count-1)->set(x, y); | 573 ed.atPoint(count-1)->set(x, y); |
| 636 GEN_ID_INC; | 574 GEN_ID_INC; |
| 637 } | 575 } |
| 638 } | 576 } |
| 639 | 577 |
| 640 void SkPath::setConvexity(Convexity c) { | 578 void SkPath::setConvexity(Convexity c) { |
| 641 if (fConvexity != c) { | 579 if (fPathRef->getConvexity() != static_cast<SkPathRef::Convexity>(c)) { |
| 642 fConvexity = c; | 580 fPathRef->setConvexity(static_cast<SkPathRef::Convexity>(c)); |
| 643 GEN_ID_INC; | 581 GEN_ID_INC; |
| 644 } | 582 } |
| 645 } | 583 } |
| 646 | 584 |
| 647 ////////////////////////////////////////////////////////////////////////////// | 585 ////////////////////////////////////////////////////////////////////////////// |
| 648 // Construction methods | 586 // Construction methods |
| 649 | 587 |
| 650 #define DIRTY_AFTER_EDIT \ | 588 #define DIRTY_AFTER_EDIT \ |
| 651 do { \ | 589 do { \ |
| 652 fConvexity = kUnknown_Convexity; \ | 590 fConvexity = kUnknown_Convexity; \ |
| 653 fDirection = kUnknown_Direction; \ | 591 fDirection = kUnknown_Direction; \ |
| 654 fIsOval = false; \ | 592 fIsOval = false; \ |
| 655 } while (0) | 593 } while (0) |
| 656 | 594 |
| 657 void SkPath::incReserve(U16CPU inc) { | 595 void SkPath::incReserve(U16CPU inc) { |
| 658 SkDEBUGCODE(this->validate();) | 596 SkDEBUGCODE(this->validate();) |
| 659 SkPathRef::Editor(&fPathRef, inc, inc); | 597 SkPathRef::Editor(&fPathRef, inc, inc); |
| 660 SkDEBUGCODE(this->validate();) | 598 SkDEBUGCODE(this->validate();) |
| 661 } | 599 } |
| 662 | 600 |
| 663 void SkPath::moveTo(SkScalar x, SkScalar y) { | 601 void SkPath::moveTo(SkScalar x, SkScalar y) { |
| 664 SkDEBUGCODE(this->validate();) | 602 SkDEBUGCODE(this->validate();) |
| 665 | 603 |
| 666 SkPathRef::Editor ed(&fPathRef); | 604 SkPathRef::Editor ed(&fPathRef); |
| 667 | 605 |
| 668 // remember our index | 606 ed.growForVerb(SkPathRef::kMove_Verb)->set(x, y); |
| 669 fLastMoveToIndex = ed.pathRef()->countPoints(); | |
| 670 | |
| 671 ed.growForVerb(kMove_Verb)->set(x, y); | |
| 672 | 607 |
| 673 GEN_ID_INC; | 608 GEN_ID_INC; |
| 674 } | 609 } |
| 675 | 610 |
| 676 void SkPath::rMoveTo(SkScalar x, SkScalar y) { | 611 void SkPath::rMoveTo(SkScalar x, SkScalar y) { |
| 677 SkPoint pt; | 612 SkPoint pt; |
| 678 this->getLastPt(&pt); | 613 this->getLastPt(&pt); |
| 679 this->moveTo(pt.fX + x, pt.fY + y); | 614 this->moveTo(pt.fX + x, pt.fY + y); |
| 680 } | 615 } |
| 681 | 616 |
| 682 void SkPath::injectMoveToIfNeeded() { | |
| 683 if (fLastMoveToIndex < 0) { | |
| 684 SkScalar x, y; | |
| 685 if (fPathRef->countVerbs() == 0) { | |
| 686 x = y = 0; | |
| 687 } else { | |
| 688 const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex); | |
| 689 x = pt.fX; | |
| 690 y = pt.fY; | |
| 691 } | |
| 692 this->moveTo(x, y); | |
| 693 } | |
| 694 } | |
| 695 | |
| 696 void SkPath::lineTo(SkScalar x, SkScalar y) { | 617 void SkPath::lineTo(SkScalar x, SkScalar y) { |
| 697 SkDEBUGCODE(this->validate();) | 618 SkDEBUGCODE(this->validate();) |
| 698 | 619 |
| 699 this->injectMoveToIfNeeded(); | |
| 700 | |
| 701 SkPathRef::Editor ed(&fPathRef); | 620 SkPathRef::Editor ed(&fPathRef); |
| 702 ed.growForVerb(kLine_Verb)->set(x, y); | 621 ed.growForVerb(SkPathRef::kLine_Verb)->set(x, y); |
| 703 fSegmentMask |= kLine_SegmentMask; | |
| 704 | 622 |
| 705 GEN_ID_INC; | 623 GEN_ID_INC; |
| 706 DIRTY_AFTER_EDIT; | |
| 707 } | 624 } |
| 708 | 625 |
| 709 void SkPath::rLineTo(SkScalar x, SkScalar y) { | 626 void SkPath::rLineTo(SkScalar x, SkScalar y) { |
| 710 this->injectMoveToIfNeeded(); // This can change the result of this->getLas
tPt(). | 627 SkDEBUGCODE(this->validate();) |
| 711 SkPoint pt; | 628 |
| 712 this->getLastPt(&pt); | 629 SkPathRef::Editor ed(&fPathRef); |
| 713 this->lineTo(pt.fX + x, pt.fY + y); | 630 SkPoint* dst = ed.growForVerb(SkPathRef::kLine_Verb); |
| 631 |
| 632 // growForVerb can change the result of this->getPoint(). |
| 633 SkASSERT(this->countPoints() >= 2); |
| 634 SkPoint pt = this->getPoint(this->countPoints() - 2); |
| 635 |
| 636 dst->set(pt.fX + x, pt.fY + y); |
| 637 GEN_ID_INC; |
| 714 } | 638 } |
| 715 | 639 |
| 716 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { | 640 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { |
| 717 SkDEBUGCODE(this->validate();) | 641 SkDEBUGCODE(this->validate();) |
| 718 | 642 |
| 719 this->injectMoveToIfNeeded(); | |
| 720 | |
| 721 SkPathRef::Editor ed(&fPathRef); | 643 SkPathRef::Editor ed(&fPathRef); |
| 722 SkPoint* pts = ed.growForVerb(kQuad_Verb); | 644 SkPoint* pts = ed.growForVerb(SkPathRef::kQuad_Verb); |
| 723 pts[0].set(x1, y1); | 645 pts[0].set(x1, y1); |
| 724 pts[1].set(x2, y2); | 646 pts[1].set(x2, y2); |
| 725 fSegmentMask |= kQuad_SegmentMask; | |
| 726 | 647 |
| 727 GEN_ID_INC; | 648 GEN_ID_INC; |
| 728 DIRTY_AFTER_EDIT; | |
| 729 } | 649 } |
| 730 | 650 |
| 731 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { | 651 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { |
| 732 this->injectMoveToIfNeeded(); // This can change the result of this->getLas
tPt(). | 652 SkDEBUGCODE(this->validate();) |
| 733 SkPoint pt; | 653 |
| 734 this->getLastPt(&pt); | 654 SkPathRef::Editor ed(&fPathRef); |
| 735 this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); | 655 SkPoint* pts = ed.growForVerb(SkPathRef::kQuad_Verb); |
| 656 |
| 657 // growForVerb can change the result of this->getPoint(). |
| 658 SkASSERT(this->countPoints() >= 3); |
| 659 SkPoint pt = this->getPoint(this->countPoints() - 3); |
| 660 |
| 661 pts[0].set(pt.fX + x1, pt.fY + y1); |
| 662 pts[1].set(pt.fX + x2, pt.fY + y2); |
| 663 |
| 664 GEN_ID_INC; |
| 736 } | 665 } |
| 737 | 666 |
| 738 void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, | 667 void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, |
| 739 SkScalar w) { | 668 SkScalar w) { |
| 740 // check for <= 0 or NaN with this test | 669 // check for <= 0 or NaN with this test |
| 741 if (!(w > 0)) { | 670 if (!(w > 0)) { |
| 742 this->lineTo(x2, y2); | 671 this->lineTo(x2, y2); |
| 743 } else if (!SkScalarIsFinite(w)) { | 672 } else if (!SkScalarIsFinite(w)) { |
| 744 this->lineTo(x1, y1); | 673 this->lineTo(x1, y1); |
| 745 this->lineTo(x2, y2); | 674 this->lineTo(x2, y2); |
| 746 } else if (SK_Scalar1 == w) { | 675 } else if (SK_Scalar1 == w) { |
| 747 this->quadTo(x1, y1, x2, y2); | 676 this->quadTo(x1, y1, x2, y2); |
| 748 } else { | 677 } else { |
| 749 SkDEBUGCODE(this->validate();) | 678 SkDEBUGCODE(this->validate();) |
| 750 | 679 |
| 751 this->injectMoveToIfNeeded(); | |
| 752 | |
| 753 SkPathRef::Editor ed(&fPathRef); | 680 SkPathRef::Editor ed(&fPathRef); |
| 754 SkPoint* pts = ed.growForConic(w); | 681 SkPoint* pts = ed.growForConic(w); |
| 755 pts[0].set(x1, y1); | 682 pts[0].set(x1, y1); |
| 756 pts[1].set(x2, y2); | 683 pts[1].set(x2, y2); |
| 757 fSegmentMask |= kConic_SegmentMask; | |
| 758 | 684 |
| 759 GEN_ID_INC; | 685 GEN_ID_INC; |
| 760 DIRTY_AFTER_EDIT; | |
| 761 } | 686 } |
| 762 } | 687 } |
| 763 | 688 |
| 764 void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, | 689 void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, |
| 765 SkScalar w) { | 690 SkScalar w) { |
| 766 this->injectMoveToIfNeeded(); // This can change the result of this->getLas
tPt(). | 691 // check for <= 0 or NaN with this test |
| 767 SkPoint pt; | 692 if (!(w > 0)) { |
| 768 this->getLastPt(&pt); | 693 this->rLineTo(dx2, dy2); |
| 769 this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w); | 694 } else if (!SkScalarIsFinite(w)) { |
| 695 this->rLineTo(dx1, dy1); |
| 696 this->rLineTo(dx2, dy2); |
| 697 } else if (SK_Scalar1 == w) { |
| 698 this->rQuadTo(dx1, dy1, dx2, dy2); |
| 699 } else { |
| 700 SkDEBUGCODE(this->validate();) |
| 701 |
| 702 SkPathRef::Editor ed(&fPathRef); |
| 703 SkPoint* pts = ed.growForConic(w); |
| 704 |
| 705 // growForConic can change the result of this->getPoint(). |
| 706 SkASSERT(this->countPoints() >= 3); |
| 707 SkPoint pt = this->getPoint(this->countPoints() - 3); |
| 708 |
| 709 pts[0].set(pt.fX + dx1, pt.fY + dy1); |
| 710 pts[1].set(pt.fX + dx2, pt.fY + dy2); |
| 711 |
| 712 GEN_ID_INC; |
| 713 } |
| 770 } | 714 } |
| 771 | 715 |
| 772 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, | 716 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, |
| 773 SkScalar x3, SkScalar y3) { | 717 SkScalar x3, SkScalar y3) { |
| 774 SkDEBUGCODE(this->validate();) | 718 SkDEBUGCODE(this->validate();) |
| 775 | 719 |
| 776 this->injectMoveToIfNeeded(); | |
| 777 | |
| 778 SkPathRef::Editor ed(&fPathRef); | 720 SkPathRef::Editor ed(&fPathRef); |
| 779 SkPoint* pts = ed.growForVerb(kCubic_Verb); | 721 SkPoint* pts = ed.growForVerb(SkPathRef::kCubic_Verb); |
| 780 pts[0].set(x1, y1); | 722 pts[0].set(x1, y1); |
| 781 pts[1].set(x2, y2); | 723 pts[1].set(x2, y2); |
| 782 pts[2].set(x3, y3); | 724 pts[2].set(x3, y3); |
| 783 fSegmentMask |= kCubic_SegmentMask; | |
| 784 | 725 |
| 785 GEN_ID_INC; | 726 GEN_ID_INC; |
| 786 DIRTY_AFTER_EDIT; | |
| 787 } | 727 } |
| 788 | 728 |
| 789 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, | 729 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, |
| 790 SkScalar x3, SkScalar y3) { | 730 SkScalar x3, SkScalar y3) { |
| 791 this->injectMoveToIfNeeded(); // This can change the result of this->getLas
tPt(). | 731 SkDEBUGCODE(this->validate();) |
| 792 SkPoint pt; | 732 |
| 793 this->getLastPt(&pt); | 733 SkPathRef::Editor ed(&fPathRef); |
| 794 this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, | 734 SkPoint* pts = ed.growForVerb(SkPathRef::kCubic_Verb); |
| 795 pt.fX + x3, pt.fY + y3); | 735 |
| 736 // growForVerb can change the result of this->getPoint(). |
| 737 SkASSERT(this->countPoints() >= 4); |
| 738 SkPoint pt = this->getPoint(this->countPoints() - 4); |
| 739 |
| 740 pts[0].set(pt.fX + x1, pt.fY + y1); |
| 741 pts[1].set(pt.fX + x2, pt.fY + y2); |
| 742 pts[2].set(pt.fX + x3, pt.fY + y3); |
| 743 |
| 744 GEN_ID_INC; |
| 796 } | 745 } |
| 797 | 746 |
| 798 void SkPath::close() { | 747 void SkPath::close() { |
| 799 SkDEBUGCODE(this->validate();) | 748 SkDEBUGCODE(this->validate();) |
| 800 | 749 |
| 801 int count = fPathRef->countVerbs(); | 750 int count = fPathRef->countVerbs(); |
| 802 if (count > 0) { | 751 if (count > 0) { |
| 803 switch (fPathRef->atVerb(count - 1)) { | 752 switch (fPathRef->atVerb(count - 1)) { |
| 804 case kLine_Verb: | 753 case kLine_Verb: |
| 805 case kQuad_Verb: | 754 case kQuad_Verb: |
| 806 case kConic_Verb: | 755 case kConic_Verb: |
| 807 case kCubic_Verb: | 756 case kCubic_Verb: |
| 808 case kMove_Verb: { | 757 case kMove_Verb: { |
| 809 SkPathRef::Editor ed(&fPathRef); | 758 SkPathRef::Editor ed(&fPathRef); |
| 810 ed.growForVerb(kClose_Verb); | 759 ed.growForVerb(SkPathRef::kClose_Verb); |
| 811 GEN_ID_INC; | 760 GEN_ID_INC; |
| 812 break; | 761 break; |
| 813 } | 762 } |
| 814 case kClose_Verb: | 763 case kClose_Verb: |
| 815 // don't add a close if it's the first verb or a repeat | 764 // don't add a close if it's the first verb or a repeat |
| 816 break; | 765 break; |
| 817 default: | 766 default: |
| 818 SkDEBUGFAIL("unexpected verb"); | 767 SkDEBUGFAIL("unexpected verb"); |
| 819 break; | 768 break; |
| 820 } | 769 } |
| 821 } | 770 } |
| 822 | |
| 823 // signal that we need a moveTo to follow us (unless we're done) | |
| 824 #if 0 | |
| 825 if (fLastMoveToIndex >= 0) { | |
| 826 fLastMoveToIndex = ~fLastMoveToIndex; | |
| 827 } | |
| 828 #else | |
| 829 fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); | |
| 830 #endif | |
| 831 } | 771 } |
| 832 | 772 |
| 833 /////////////////////////////////////////////////////////////////////////////// | 773 /////////////////////////////////////////////////////////////////////////////// |
| 834 | 774 |
| 835 static void assert_known_direction(int dir) { | 775 static void assert_known_direction(int dir) { |
| 836 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir); | 776 SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir); |
| 837 } | 777 } |
| 838 | 778 |
| 839 void SkPath::addRect(const SkRect& rect, Direction dir) { | 779 void SkPath::addRect(const SkRect& rect, Direction dir) { |
| 840 this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); | 780 this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir); |
| 841 } | 781 } |
| 842 | 782 |
| 843 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, | 783 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right, |
| 844 SkScalar bottom, Direction dir) { | 784 SkScalar bottom, Direction dir) { |
| 845 assert_known_direction(dir); | 785 assert_known_direction(dir); |
| 846 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; | 786 Direction newDir = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; |
| 847 SkAutoDisableDirectionCheck addc(this); | |
| 848 | 787 |
| 849 SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom); | 788 SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom); |
| 850 | 789 |
| 851 this->incReserve(5); | 790 this->incReserve(5); |
| 852 | 791 |
| 853 this->moveTo(left, top); | 792 this->moveTo(left, top); |
| 854 if (dir == kCCW_Direction) { | 793 if (dir == kCCW_Direction) { |
| 855 this->lineTo(left, bottom); | 794 this->lineTo(left, bottom); |
| 856 this->lineTo(right, bottom); | 795 this->lineTo(right, bottom); |
| 857 this->lineTo(right, top); | 796 this->lineTo(right, top); |
| 858 } else { | 797 } else { |
| 859 this->lineTo(right, top); | 798 this->lineTo(right, top); |
| 860 this->lineTo(right, bottom); | 799 this->lineTo(right, bottom); |
| 861 this->lineTo(left, bottom); | 800 this->lineTo(left, bottom); |
| 862 } | 801 } |
| 863 this->close(); | 802 this->close(); |
| 803 |
| 804 fPathRef->setDirection(static_cast<SkPathRef::Direction>(newDir)); |
| 864 } | 805 } |
| 865 | 806 |
| 866 void SkPath::addPoly(const SkPoint pts[], int count, bool close) { | 807 void SkPath::addPoly(const SkPoint pts[], int count, bool close) { |
| 867 SkDEBUGCODE(this->validate();) | 808 SkDEBUGCODE(this->validate();) |
| 868 if (count <= 0) { | 809 if (count <= 0) { |
| 869 return; | 810 return; |
| 870 } | 811 } |
| 871 | 812 |
| 872 SkPathRef::Editor ed(&fPathRef); | |
| 873 fLastMoveToIndex = ed.pathRef()->countPoints(); | |
| 874 uint8_t* vb; | |
| 875 SkPoint* p; | |
| 876 // +close makes room for the extra kClose_Verb | 813 // +close makes room for the extra kClose_Verb |
| 877 ed.grow(count + close, count, &vb, &p); | 814 SkPathRef::Editor ed(&fPathRef, count+close, count); |
| 878 | 815 |
| 879 memcpy(p, pts, count * sizeof(SkPoint)); | 816 ed.growForVerb(SkPathRef::kMove_Verb)->set(pts[0].fX, pts[0].fY); |
| 880 vb[~0] = kMove_Verb; | 817 |
| 881 if (count > 1) { | 818 if (count > 1) { |
| 882 // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to | 819 SkPoint* p = ed.growForLines(count - 1); |
| 883 // be 0, the compiler will remove the test/branch entirely. | 820 |
| 884 if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) { | 821 memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); |
| 885 memset(vb - count, kLine_Verb, count - 1); | |
| 886 } else { | |
| 887 for (int i = 1; i < count; ++i) { | |
| 888 vb[~i] = kLine_Verb; | |
| 889 } | |
| 890 } | |
| 891 fSegmentMask |= kLine_SegmentMask; | |
| 892 } | 822 } |
| 823 |
| 893 if (close) { | 824 if (close) { |
| 894 vb[~count] = kClose_Verb; | 825 ed.growForVerb(SkPathRef::kClose_Verb); |
| 895 } | 826 } |
| 896 | 827 |
| 897 GEN_ID_INC; | 828 GEN_ID_INC; |
| 898 DIRTY_AFTER_EDIT; | |
| 899 SkDEBUGCODE(this->validate();) | 829 SkDEBUGCODE(this->validate();) |
| 900 } | 830 } |
| 901 | 831 |
| 902 static void add_corner_arc(SkPath* path, const SkRect& rect, | 832 static void add_corner_arc(SkPath* path, const SkRect& rect, |
| 903 SkScalar rx, SkScalar ry, int startAngle, | 833 SkScalar rx, SkScalar ry, int startAngle, |
| 904 SkPath::Direction dir, bool forceMoveTo) { | 834 SkPath::Direction dir, bool forceMoveTo) { |
| 905 // These two asserts are not sufficient, since really we want to know | 835 // These two asserts are not sufficient, since really we want to know |
| 906 // that the pair of radii (e.g. left and right, or top and bottom) sum | 836 // that the pair of radii (e.g. left and right, or top and bottom) sum |
| 907 // to <= dimension, but we don't have that data here, so we just have | 837 // to <= dimension, but we don't have that data here, so we just have |
| 908 // these conservative asserts. | 838 // these conservative asserts. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 984 *verbs == kCubic_Verb) { | 914 *verbs == kCubic_Verb) { |
| 985 return false; | 915 return false; |
| 986 } | 916 } |
| 987 ++verbs; | 917 ++verbs; |
| 988 } | 918 } |
| 989 return true; | 919 return true; |
| 990 } | 920 } |
| 991 | 921 |
| 992 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) | 922 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) |
| 993 | 923 |
| 994 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, | 924 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Directio
n dir) { |
| 995 Direction dir) { | |
| 996 assert_known_direction(dir); | 925 assert_known_direction(dir); |
| 997 | 926 |
| 998 if (rx < 0 || ry < 0) { | 927 if (rx < 0 || ry < 0) { |
| 999 SkErrorInternals::SetError( kInvalidArgument_SkError, | 928 SkErrorInternals::SetError( kInvalidArgument_SkError, |
| 1000 "I got %f and %f as radii to SkPath::AddRoun
dRect, " | 929 "I got %f and %f as radii to SkPath::AddRoun
dRect, " |
| 1001 "but negative radii are not allowed.", | 930 "but negative radii are not allowed.", |
| 1002 SkScalarToDouble(rx), SkScalarToDouble(ry) )
; | 931 SkScalarToDouble(rx), SkScalarToDouble(ry) )
; |
| 1003 return; | 932 return; |
| 1004 } | 933 } |
| 1005 | 934 |
| 1006 SkScalar w = rect.width(); | 935 SkScalar w = rect.width(); |
| 1007 SkScalar halfW = SkScalarHalf(w); | 936 SkScalar halfW = SkScalarHalf(w); |
| 1008 SkScalar h = rect.height(); | 937 SkScalar h = rect.height(); |
| 1009 SkScalar halfH = SkScalarHalf(h); | 938 SkScalar halfH = SkScalarHalf(h); |
| 1010 | 939 |
| 1011 if (halfW <= 0 || halfH <= 0) { | 940 if (halfW <= 0 || halfH <= 0) { |
| 1012 return; | 941 return; |
| 1013 } | 942 } |
| 1014 | 943 |
| 1015 bool skip_hori = rx >= halfW; | 944 bool skip_hori = rx >= halfW; |
| 1016 bool skip_vert = ry >= halfH; | 945 bool skip_vert = ry >= halfH; |
| 1017 | 946 |
| 1018 if (skip_hori && skip_vert) { | 947 if (skip_hori && skip_vert) { |
| 1019 this->addOval(rect, dir); | 948 this->addOval(rect, dir); |
| 1020 return; | 949 return; |
| 1021 } | 950 } |
| 1022 | 951 |
| 1023 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; | 952 Direction newDir = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; |
| 1024 | 953 |
| 1025 SkAutoPathBoundsUpdate apbu(this, rect); | 954 SkAutoPathBoundsUpdate apbu(this, rect); |
| 1026 SkAutoDisableDirectionCheck(this); | |
| 1027 | 955 |
| 1028 if (skip_hori) { | 956 if (skip_hori) { |
| 1029 rx = halfW; | 957 rx = halfW; |
| 1030 } else if (skip_vert) { | 958 } else if (skip_vert) { |
| 1031 ry = halfH; | 959 ry = halfH; |
| 1032 } | 960 } |
| 1033 | 961 |
| 1034 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); | 962 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); |
| 1035 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); | 963 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); |
| 1036 | 964 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1081 this->lineTo(rect.fLeft, rect.fTop + ry); // left | 1009 this->lineTo(rect.fLeft, rect.fTop + ry); // left |
| 1082 } | 1010 } |
| 1083 this->cubicTo(rect.fLeft, rect.fTop + ry - sy, | 1011 this->cubicTo(rect.fLeft, rect.fTop + ry - sy, |
| 1084 rect.fLeft + rx - sx, rect.fTop, | 1012 rect.fLeft + rx - sx, rect.fTop, |
| 1085 rect.fLeft + rx, rect.fTop); // top-left | 1013 rect.fLeft + rx, rect.fTop); // top-left |
| 1086 if (!skip_hori) { | 1014 if (!skip_hori) { |
| 1087 this->lineTo(rect.fRight - rx, rect.fTop); // top | 1015 this->lineTo(rect.fRight - rx, rect.fTop); // top |
| 1088 } | 1016 } |
| 1089 } | 1017 } |
| 1090 this->close(); | 1018 this->close(); |
| 1019 |
| 1020 fPathRef->setDirection(static_cast<SkPathRef::Direction>(newDir)); |
| 1091 } | 1021 } |
| 1092 | 1022 |
| 1093 void SkPath::addOval(const SkRect& oval, Direction dir) { | 1023 void SkPath::addOval(const SkRect& oval, Direction dir) { |
| 1094 assert_known_direction(dir); | 1024 assert_known_direction(dir); |
| 1095 | 1025 |
| 1096 /* If addOval() is called after previous moveTo(), | 1026 /* If addOval() is called after previous moveTo(), |
| 1097 this path is still marked as an oval. This is used to | 1027 this path is still marked as an oval. This is used to |
| 1098 fit into WebKit's calling sequences. | 1028 fit into WebKit's calling sequences. |
| 1099 We can't simply check isEmpty() in this case, as additional | 1029 We can't simply check isEmpty() in this case, as additional |
| 1100 moveTo() would mark the path non empty. | 1030 moveTo() would mark the path non empty. |
| 1101 */ | 1031 */ |
| 1102 fIsOval = hasOnlyMoveTos(); | 1032 bool isOval = this->hasOnlyMoveTos(); |
| 1103 if (fIsOval) { | 1033 Direction newDir = isOval ? dir : kUnknown_Direction; |
| 1104 fDirection = dir; | |
| 1105 } else { | |
| 1106 fDirection = kUnknown_Direction; | |
| 1107 } | |
| 1108 | |
| 1109 SkAutoDisableOvalCheck adoc(this); | |
| 1110 SkAutoDisableDirectionCheck addc(this); | |
| 1111 | 1034 |
| 1112 SkAutoPathBoundsUpdate apbu(this, oval); | 1035 SkAutoPathBoundsUpdate apbu(this, oval); |
| 1113 | 1036 |
| 1114 SkScalar cx = oval.centerX(); | 1037 SkScalar cx = oval.centerX(); |
| 1115 SkScalar cy = oval.centerY(); | 1038 SkScalar cy = oval.centerY(); |
| 1116 SkScalar rx = SkScalarHalf(oval.width()); | 1039 SkScalar rx = SkScalarHalf(oval.width()); |
| 1117 SkScalar ry = SkScalarHalf(oval.height()); | 1040 SkScalar ry = SkScalarHalf(oval.height()); |
| 1118 | 1041 |
| 1119 SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8); | 1042 SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8); |
| 1120 SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8); | 1043 SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1147 this->quadTo( R, cy + sy, cx + mx, cy + my); | 1070 this->quadTo( R, cy + sy, cx + mx, cy + my); |
| 1148 this->quadTo(cx + sx, B, cx , B); | 1071 this->quadTo(cx + sx, B, cx , B); |
| 1149 this->quadTo(cx - sx, B, cx - mx, cy + my); | 1072 this->quadTo(cx - sx, B, cx - mx, cy + my); |
| 1150 this->quadTo( L, cy + sy, L, cy ); | 1073 this->quadTo( L, cy + sy, L, cy ); |
| 1151 this->quadTo( L, cy - sy, cx - mx, cy - my); | 1074 this->quadTo( L, cy - sy, cx - mx, cy - my); |
| 1152 this->quadTo(cx - sx, T, cx , T); | 1075 this->quadTo(cx - sx, T, cx , T); |
| 1153 this->quadTo(cx + sx, T, cx + mx, cy - my); | 1076 this->quadTo(cx + sx, T, cx + mx, cy - my); |
| 1154 this->quadTo( R, cy - sy, R, cy ); | 1077 this->quadTo( R, cy - sy, R, cy ); |
| 1155 } | 1078 } |
| 1156 this->close(); | 1079 this->close(); |
| 1157 } | |
| 1158 | 1080 |
| 1159 bool SkPath::isOval(SkRect* rect) const { | 1081 fPathRef->setIsOval(isOval); |
| 1160 if (fIsOval && rect) { | 1082 fPathRef->setDirection(static_cast<SkPathRef::Direction>(newDir)); |
| 1161 *rect = getBounds(); | |
| 1162 } | |
| 1163 | |
| 1164 return fIsOval; | |
| 1165 } | 1083 } |
| 1166 | 1084 |
| 1167 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { | 1085 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) { |
| 1168 if (r > 0) { | 1086 if (r > 0) { |
| 1169 SkRect rect; | 1087 SkRect rect; |
| 1170 rect.set(x - r, y - r, x + r, y + r); | 1088 rect.set(x - r, y - r, x + r, y + r); |
| 1171 this->addOval(rect, dir); | 1089 this->addOval(rect, dir); |
| 1172 } | 1090 } |
| 1173 } | 1091 } |
| 1174 | 1092 |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1352 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) { | 1270 void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) { |
| 1353 SkMatrix matrix; | 1271 SkMatrix matrix; |
| 1354 | 1272 |
| 1355 matrix.setTranslate(dx, dy); | 1273 matrix.setTranslate(dx, dy); |
| 1356 this->addPath(path, matrix); | 1274 this->addPath(path, matrix); |
| 1357 } | 1275 } |
| 1358 | 1276 |
| 1359 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) { | 1277 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) { |
| 1360 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints()); | 1278 SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints()); |
| 1361 | 1279 |
| 1362 fIsOval = false; | 1280 fPathRef->setIsOval(false); |
| 1363 | 1281 |
| 1364 RawIter iter(path); | 1282 RawIter iter(path); |
| 1365 SkPoint pts[4]; | 1283 SkPoint pts[4]; |
| 1366 Verb verb; | 1284 Verb verb; |
| 1367 | 1285 |
| 1368 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc(); | 1286 SkMatrix::MapPtsProc proc = matrix.getMapPtsProc(); |
| 1369 | 1287 |
| 1370 while ((verb = iter.next(pts)) != kDone_Verb) { | 1288 while ((verb = iter.next(pts)) != kDone_Verb) { |
| 1371 switch (verb) { | 1289 switch (verb) { |
| 1372 case kMove_Verb: | 1290 case kMove_Verb: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1418 // ignore the initial moveto, and stop when the 1st contour ends | 1336 // ignore the initial moveto, and stop when the 1st contour ends |
| 1419 void SkPath::pathTo(const SkPath& path) { | 1337 void SkPath::pathTo(const SkPath& path) { |
| 1420 int i, vcount = path.fPathRef->countVerbs(); | 1338 int i, vcount = path.fPathRef->countVerbs(); |
| 1421 // exit early if the path is empty, or just has a moveTo. | 1339 // exit early if the path is empty, or just has a moveTo. |
| 1422 if (vcount < 2) { | 1340 if (vcount < 2) { |
| 1423 return; | 1341 return; |
| 1424 } | 1342 } |
| 1425 | 1343 |
| 1426 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); | 1344 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); |
| 1427 | 1345 |
| 1428 fIsOval = false; | 1346 fPathRef->setIsOval(false); |
| 1429 | 1347 |
| 1430 const uint8_t* verbs = path.fPathRef->verbs(); | 1348 const uint8_t* verbs = path.fPathRef->verbs(); |
| 1431 // skip the initial moveTo | 1349 // skip the initial moveTo |
| 1432 const SkPoint* pts = path.fPathRef->points() + 1; | 1350 const SkPoint* pts = path.fPathRef->points() + 1; |
| 1433 const SkScalar* conicWeight = path.fPathRef->conicWeights(); | 1351 const SkScalar* conicWeight = path.fPathRef->conicWeights(); |
| 1434 | 1352 |
| 1435 SkASSERT(verbs[~0] == kMove_Verb); | 1353 SkASSERT(verbs[~0] == kMove_Verb); |
| 1436 for (i = 1; i < vcount; i++) { | 1354 for (i = 1; i < vcount; i++) { |
| 1437 switch (verbs[~i]) { | 1355 switch (verbs[~i]) { |
| 1438 case kLine_Verb: | 1356 case kLine_Verb: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1457 // ignore the last point of the 1st contour | 1375 // ignore the last point of the 1st contour |
| 1458 void SkPath::reversePathTo(const SkPath& path) { | 1376 void SkPath::reversePathTo(const SkPath& path) { |
| 1459 int i, vcount = path.fPathRef->countVerbs(); | 1377 int i, vcount = path.fPathRef->countVerbs(); |
| 1460 // exit early if the path is empty, or just has a moveTo. | 1378 // exit early if the path is empty, or just has a moveTo. |
| 1461 if (vcount < 2) { | 1379 if (vcount < 2) { |
| 1462 return; | 1380 return; |
| 1463 } | 1381 } |
| 1464 | 1382 |
| 1465 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); | 1383 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); |
| 1466 | 1384 |
| 1467 fIsOval = false; | 1385 fPathRef->setIsOval(false); |
| 1468 | 1386 |
| 1469 const uint8_t* verbs = path.fPathRef->verbs(); | 1387 const uint8_t* verbs = path.fPathRef->verbs(); |
| 1470 const SkPoint* pts = path.fPathRef->points(); | 1388 const SkPoint* pts = path.fPathRef->points(); |
| 1471 const SkScalar* conicWeights = path.fPathRef->conicWeights(); | 1389 const SkScalar* conicWeights = path.fPathRef->conicWeights(); |
| 1472 | 1390 |
| 1473 SkASSERT(verbs[~0] == kMove_Verb); | 1391 SkASSERT(verbs[~0] == kMove_Verb); |
| 1474 for (i = 1; i < vcount; ++i) { | 1392 for (i = 1; i < vcount; ++i) { |
| 1475 unsigned v = verbs[~i]; | 1393 unsigned v = verbs[~i]; |
| 1476 int n = pts_in_verb(v); | 1394 int n = pts_in_verb(v); |
| 1477 if (n == 0) { | 1395 if (n == 0) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1498 break; | 1416 break; |
| 1499 default: | 1417 default: |
| 1500 SkDEBUGFAIL("bad verb"); | 1418 SkDEBUGFAIL("bad verb"); |
| 1501 break; | 1419 break; |
| 1502 } | 1420 } |
| 1503 pts -= pts_in_verb(verbs[~i]); | 1421 pts -= pts_in_verb(verbs[~i]); |
| 1504 } | 1422 } |
| 1505 } | 1423 } |
| 1506 | 1424 |
| 1507 void SkPath::reverseAddPath(const SkPath& src) { | 1425 void SkPath::reverseAddPath(const SkPath& src) { |
| 1508 SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->c
ountVerbs()); | 1426 SkPathRef::Editor(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->coun
tVerbs()); |
| 1509 | 1427 |
| 1510 const SkPoint* pts = src.fPathRef->pointsEnd(); | 1428 const SkPoint* pts = src.fPathRef->pointsEnd(); |
| 1511 // we will iterator through src's verbs backwards | 1429 // we will iterator through src's verbs backwards |
| 1512 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last
verb | 1430 const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last
verb |
| 1513 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the fir
st verb | 1431 const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the fir
st verb |
| 1514 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd(); | 1432 const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd(); |
| 1515 | 1433 |
| 1516 fIsOval = false; | 1434 fPathRef->setIsOval(false); |
| 1517 | 1435 |
| 1518 bool needMove = true; | 1436 bool needMove = true; |
| 1519 bool needClose = false; | 1437 bool needClose = false; |
| 1520 while (verbs < verbsEnd) { | 1438 while (verbs < verbsEnd) { |
| 1521 uint8_t v = *(verbs++); | 1439 uint8_t v = *(verbs++); |
| 1522 int n = pts_in_verb(v); | 1440 int n = pts_in_verb(v); |
| 1523 | 1441 |
| 1524 if (needMove) { | 1442 if (needMove) { |
| 1525 --pts; | 1443 --pts; |
| 1526 this->moveTo(pts->fX, pts->fY); | 1444 this->moveTo(pts->fX, pts->fY); |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1631 break; | 1549 break; |
| 1632 default: | 1550 default: |
| 1633 SkDEBUGFAIL("unknown verb"); | 1551 SkDEBUGFAIL("unknown verb"); |
| 1634 break; | 1552 break; |
| 1635 } | 1553 } |
| 1636 } | 1554 } |
| 1637 | 1555 |
| 1638 dst->swap(tmp); | 1556 dst->swap(tmp); |
| 1639 SkPathRef::Editor ed(&dst->fPathRef); | 1557 SkPathRef::Editor ed(&dst->fPathRef); |
| 1640 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints()); | 1558 matrix.mapPoints(ed.points(), ed.pathRef()->countPoints()); |
| 1641 dst->fDirection = kUnknown_Direction; | 1559 dst->setDirection(kUnknown_Direction); |
| 1642 } else { | 1560 } else { |
| 1643 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix
); | 1561 SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix
); |
| 1644 | 1562 |
| 1645 if (this != dst) { | 1563 if (this != dst) { |
| 1646 dst->fFillType = fFillType; | 1564 dst->fFillType = fFillType; |
| 1647 dst->fSegmentMask = fSegmentMask; | |
| 1648 dst->fConvexity = fConvexity; | |
| 1649 } | 1565 } |
| 1650 | 1566 |
| 1651 #ifdef SK_BUILD_FOR_ANDROID | 1567 #ifdef SK_BUILD_FOR_ANDROID |
| 1652 if (!matrix.isIdentity() && !dst->hasComputedBounds()) { | 1568 if (!matrix.isIdentity() && !dst->hasComputedBounds()) { |
| 1653 GEN_ID_PTR_INC(dst); | 1569 GEN_ID_PTR_INC(dst); |
| 1654 } | 1570 } |
| 1655 #endif | 1571 #endif |
| 1656 | 1572 |
| 1657 if (kUnknown_Direction == fDirection) { | |
| 1658 dst->fDirection = kUnknown_Direction; | |
| 1659 } else { | |
| 1660 SkScalar det2x2 = | |
| 1661 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix:
:kMScaleY)) - | |
| 1662 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::
kMSkewY)); | |
| 1663 if (det2x2 < 0) { | |
| 1664 dst->fDirection = SkPath::OppositeDirection(static_cast<Directio
n>(fDirection)); | |
| 1665 } else if (det2x2 > 0) { | |
| 1666 dst->fDirection = fDirection; | |
| 1667 } else { | |
| 1668 dst->fDirection = kUnknown_Direction; | |
| 1669 } | |
| 1670 } | |
| 1671 | |
| 1672 // It's an oval only if it stays a rect. | |
| 1673 dst->fIsOval = fIsOval && matrix.rectStaysRect(); | |
| 1674 | |
| 1675 SkDEBUGCODE(dst->validate();) | 1573 SkDEBUGCODE(dst->validate();) |
| 1676 } | 1574 } |
| 1677 } | 1575 } |
| 1678 | 1576 |
| 1679 /////////////////////////////////////////////////////////////////////////////// | 1577 /////////////////////////////////////////////////////////////////////////////// |
| 1680 /////////////////////////////////////////////////////////////////////////////// | 1578 /////////////////////////////////////////////////////////////////////////////// |
| 1681 | 1579 |
| 1682 enum SegmentState { | 1580 enum SegmentState { |
| 1683 kEmptyContour_SegmentState, // The current contour is empty. We may be | 1581 kEmptyContour_SegmentState, // The current contour is empty. We may be |
| 1684 // starting processing or we may have just | 1582 // starting processing or we may have just |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1696 fForceClose = fCloseLine = false; | 1594 fForceClose = fCloseLine = false; |
| 1697 fSegmentState = kEmptyContour_SegmentState; | 1595 fSegmentState = kEmptyContour_SegmentState; |
| 1698 #endif | 1596 #endif |
| 1699 // need to init enough to make next() harmlessly return kDone_Verb | 1597 // need to init enough to make next() harmlessly return kDone_Verb |
| 1700 fVerbs = NULL; | 1598 fVerbs = NULL; |
| 1701 fVerbStop = NULL; | 1599 fVerbStop = NULL; |
| 1702 fNeedClose = false; | 1600 fNeedClose = false; |
| 1703 } | 1601 } |
| 1704 | 1602 |
| 1705 SkPath::Iter::Iter(const SkPath& path, bool forceClose) { | 1603 SkPath::Iter::Iter(const SkPath& path, bool forceClose) { |
| 1706 this->setPath(path, forceClose); | 1604 this->setPathRef(path.fPathRef.get(), forceClose); |
| 1605 } |
| 1606 |
| 1607 SkPath::Iter::Iter(const SkPathRef* pathRef, bool forceClose) { |
| 1608 this->setPathRef(pathRef, forceClose); |
| 1707 } | 1609 } |
| 1708 | 1610 |
| 1709 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { | 1611 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { |
| 1710 fPts = path.fPathRef->points(); | 1612 this->setPathRef(path.fPathRef.get(), forceClose); |
| 1711 fVerbs = path.fPathRef->verbs(); | 1613 } |
| 1712 fVerbStop = path.fPathRef->verbsMemBegin(); | 1614 |
| 1713 fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind | 1615 void SkPath::Iter::setPathRef(const SkPathRef* pathRef, bool forceClose) { |
| 1616 fPts = pathRef->points(); |
| 1617 fVerbs = pathRef->verbs(); |
| 1618 fVerbStop = pathRef->verbsMemBegin(); |
| 1619 fConicWeights = pathRef->conicWeights() - 1; // begin one behind |
| 1714 fLastPt.fX = fLastPt.fY = 0; | 1620 fLastPt.fX = fLastPt.fY = 0; |
| 1715 fMoveTo.fX = fMoveTo.fY = 0; | 1621 fMoveTo.fX = fMoveTo.fY = 0; |
| 1716 fForceClose = SkToU8(forceClose); | 1622 fForceClose = SkToU8(forceClose); |
| 1717 fNeedClose = false; | 1623 fNeedClose = false; |
| 1718 fSegmentState = kEmptyContour_SegmentState; | 1624 fSegmentState = kEmptyContour_SegmentState; |
| 1719 } | 1625 } |
| 1720 | 1626 |
| 1721 bool SkPath::Iter::isClosedContour() const { | 1627 bool SkPath::Iter::isClosedContour() const { |
| 1722 if (fVerbs == NULL || fVerbs == fVerbStop) { | 1628 if (fVerbs == NULL || fVerbs == fVerbStop) { |
| 1723 return false; | 1629 return false; |
| (...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2017 uint32_t SkPath::writeToMemory(void* storage) const { | 1923 uint32_t SkPath::writeToMemory(void* storage) const { |
| 2018 SkDEBUGCODE(this->validate();) | 1924 SkDEBUGCODE(this->validate();) |
| 2019 | 1925 |
| 2020 if (NULL == storage) { | 1926 if (NULL == storage) { |
| 2021 const int byteCount = sizeof(int32_t) + fPathRef->writeSize(); | 1927 const int byteCount = sizeof(int32_t) + fPathRef->writeSize(); |
| 2022 return SkAlign4(byteCount); | 1928 return SkAlign4(byteCount); |
| 2023 } | 1929 } |
| 2024 | 1930 |
| 2025 SkWBuffer buffer(storage); | 1931 SkWBuffer buffer(storage); |
| 2026 | 1932 |
| 2027 int32_t packed = ((fIsOval & 1) << kIsOval_SerializationShift) | | 1933 int32_t packed = (fFillType << kFillType_SerializationShift) |
| 2028 (fConvexity << kConvexity_SerializationShift) | | |
| 2029 (fFillType << kFillType_SerializationShift) | | |
| 2030 (fSegmentMask << kSegmentMask_SerializationShift) | | |
| 2031 (fDirection << kDirection_SerializationShift) | |
| 2032 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 1934 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
| 2033 | (0x1 << kNewFormat_SerializationShift); | 1935 | (0x1 << kNewFormat_SerializationShift); |
| 2034 #endif | 1936 #endif |
| 2035 | 1937 |
| 2036 buffer.write32(packed); | 1938 buffer.write32(packed); |
| 2037 | 1939 |
| 2038 fPathRef->writeToBuffer(&buffer); | 1940 fPathRef->writeToBuffer(&buffer); |
| 2039 | 1941 |
| 2040 buffer.padToAlign4(); | 1942 buffer.padToAlign4(); |
| 2041 return SkToU32(buffer.pos()); | 1943 return SkToU32(buffer.pos()); |
| 2042 } | 1944 } |
| 2043 | 1945 |
| 2044 uint32_t SkPath::readFromMemory(const void* storage) { | 1946 uint32_t SkPath::readFromMemory(const void* storage) { |
| 2045 SkRBuffer buffer(storage); | 1947 SkRBuffer buffer(storage); |
| 2046 | 1948 |
| 2047 uint32_t packed = buffer.readS32(); | 1949 uint32_t packed = buffer.readS32(); |
| 2048 fIsOval = (packed >> kIsOval_SerializationShift) & 1; | |
| 2049 fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; | |
| 2050 fFillType = (packed >> kFillType_SerializationShift) & 0xFF; | 1950 fFillType = (packed >> kFillType_SerializationShift) & 0xFF; |
| 2051 fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF; | |
| 2052 fDirection = (packed >> kDirection_SerializationShift) & 0x3; | |
| 2053 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 1951 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
| 2054 bool newFormat = (packed >> kNewFormat_SerializationShift) & 1; | 1952 bool newFormat = (packed >> kNewFormat_SerializationShift) & 1; |
| 2055 #endif | 1953 #endif |
| 2056 | 1954 |
| 2057 fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer | 1955 fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer |
| 2058 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O | 1956 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TO
O |
| 2059 , newFormat, packed) | 1957 , newFormat, packed) |
| 2060 #endif | 1958 #endif |
| 2061 ); | 1959 ); |
| 2062 | 1960 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2142 } | 2040 } |
| 2143 | 2041 |
| 2144 void SkPath::dump() const { | 2042 void SkPath::dump() const { |
| 2145 this->dump(false); | 2043 this->dump(false); |
| 2146 } | 2044 } |
| 2147 | 2045 |
| 2148 #ifdef SK_DEBUG | 2046 #ifdef SK_DEBUG |
| 2149 void SkPath::validate() const { | 2047 void SkPath::validate() const { |
| 2150 SkASSERT(this != NULL); | 2048 SkASSERT(this != NULL); |
| 2151 SkASSERT((fFillType & ~3) == 0); | 2049 SkASSERT((fFillType & ~3) == 0); |
| 2152 | |
| 2153 #ifdef SK_DEBUG_PATH | |
| 2154 if (!fBoundsIsDirty) { | |
| 2155 SkRect bounds; | |
| 2156 | |
| 2157 bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get()); | |
| 2158 SkASSERT(SkToBool(fIsFinite) == isFinite); | |
| 2159 | |
| 2160 if (fPathRef->countPoints() <= 1) { | |
| 2161 // if we're empty, fBounds may be empty but translated, so we can't | |
| 2162 // necessarily compare to bounds directly | |
| 2163 // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will | |
| 2164 // be [2, 2, 2, 2] | |
| 2165 SkASSERT(bounds.isEmpty()); | |
| 2166 SkASSERT(fBounds.isEmpty()); | |
| 2167 } else { | |
| 2168 if (bounds.isEmpty()) { | |
| 2169 SkASSERT(fBounds.isEmpty()); | |
| 2170 } else { | |
| 2171 if (!fBounds.isEmpty()) { | |
| 2172 SkASSERT(fBounds.contains(bounds)); | |
| 2173 } | |
| 2174 } | |
| 2175 } | |
| 2176 } | |
| 2177 | |
| 2178 uint32_t mask = 0; | |
| 2179 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbs()
; | |
| 2180 for (int i = 0; i < fPathRef->countVerbs(); i++) { | |
| 2181 switch (verbs[~i]) { | |
| 2182 case kLine_Verb: | |
| 2183 mask |= kLine_SegmentMask; | |
| 2184 break; | |
| 2185 case kQuad_Verb: | |
| 2186 mask |= kQuad_SegmentMask; | |
| 2187 break; | |
| 2188 case kConic_Verb: | |
| 2189 mask |= kConic_SegmentMask; | |
| 2190 break; | |
| 2191 case kCubic_Verb: | |
| 2192 mask |= kCubic_SegmentMask; | |
| 2193 case kMove_Verb: // these verbs aren't included in the segment mask
. | |
| 2194 case kClose_Verb: | |
| 2195 break; | |
| 2196 case kDone_Verb: | |
| 2197 SkDEBUGFAIL("Done verb shouldn't be recorded."); | |
| 2198 break; | |
| 2199 default: | |
| 2200 SkDEBUGFAIL("Unknown Verb"); | |
| 2201 break; | |
| 2202 } | |
| 2203 } | |
| 2204 SkASSERT(mask == fSegmentMask); | |
| 2205 #endif // SK_DEBUG_PATH | |
| 2206 } | 2050 } |
| 2207 #endif // SK_DEBUG | 2051 #endif // SK_DEBUG |
| 2208 | 2052 |
| 2209 /////////////////////////////////////////////////////////////////////////////// | 2053 /////////////////////////////////////////////////////////////////////////////// |
| 2210 | 2054 |
| 2211 static int sign(SkScalar x) { return x < 0; } | |
| 2212 #define kValueNeverReturnedBySign 2 | |
| 2213 | |
| 2214 static int CrossProductSign(const SkVector& a, const SkVector& b) { | |
| 2215 return SkScalarSignAsInt(SkPoint::CrossProduct(a, b)); | |
| 2216 } | |
| 2217 | |
| 2218 // only valid for a single contour | |
| 2219 struct Convexicator { | |
| 2220 Convexicator() | |
| 2221 : fPtCount(0) | |
| 2222 , fConvexity(SkPath::kConvex_Convexity) | |
| 2223 , fDirection(SkPath::kUnknown_Direction) { | |
| 2224 fSign = 0; | |
| 2225 // warnings | |
| 2226 fCurrPt.set(0, 0); | |
| 2227 fVec0.set(0, 0); | |
| 2228 fVec1.set(0, 0); | |
| 2229 fFirstVec.set(0, 0); | |
| 2230 | |
| 2231 fDx = fDy = 0; | |
| 2232 fSx = fSy = kValueNeverReturnedBySign; | |
| 2233 } | |
| 2234 | |
| 2235 SkPath::Convexity getConvexity() const { return fConvexity; } | |
| 2236 | |
| 2237 /** The direction returned is only valid if the path is determined convex */ | |
| 2238 SkPath::Direction getDirection() const { return fDirection; } | |
| 2239 | |
| 2240 void addPt(const SkPoint& pt) { | |
| 2241 if (SkPath::kConcave_Convexity == fConvexity) { | |
| 2242 return; | |
| 2243 } | |
| 2244 | |
| 2245 if (0 == fPtCount) { | |
| 2246 fCurrPt = pt; | |
| 2247 ++fPtCount; | |
| 2248 } else { | |
| 2249 SkVector vec = pt - fCurrPt; | |
| 2250 if (vec.fX || vec.fY) { | |
| 2251 fCurrPt = pt; | |
| 2252 if (++fPtCount == 2) { | |
| 2253 fFirstVec = fVec1 = vec; | |
| 2254 } else { | |
| 2255 SkASSERT(fPtCount > 2); | |
| 2256 this->addVec(vec); | |
| 2257 } | |
| 2258 | |
| 2259 int sx = sign(vec.fX); | |
| 2260 int sy = sign(vec.fY); | |
| 2261 fDx += (sx != fSx); | |
| 2262 fDy += (sy != fSy); | |
| 2263 fSx = sx; | |
| 2264 fSy = sy; | |
| 2265 | |
| 2266 if (fDx > 3 || fDy > 3) { | |
| 2267 fConvexity = SkPath::kConcave_Convexity; | |
| 2268 } | |
| 2269 } | |
| 2270 } | |
| 2271 } | |
| 2272 | |
| 2273 void close() { | |
| 2274 if (fPtCount > 2) { | |
| 2275 this->addVec(fFirstVec); | |
| 2276 } | |
| 2277 } | |
| 2278 | |
| 2279 private: | |
| 2280 void addVec(const SkVector& vec) { | |
| 2281 SkASSERT(vec.fX || vec.fY); | |
| 2282 fVec0 = fVec1; | |
| 2283 fVec1 = vec; | |
| 2284 int sign = CrossProductSign(fVec0, fVec1); | |
| 2285 if (0 == fSign) { | |
| 2286 fSign = sign; | |
| 2287 if (1 == sign) { | |
| 2288 fDirection = SkPath::kCW_Direction; | |
| 2289 } else if (-1 == sign) { | |
| 2290 fDirection = SkPath::kCCW_Direction; | |
| 2291 } | |
| 2292 } else if (sign) { | |
| 2293 if (fSign != sign) { | |
| 2294 fConvexity = SkPath::kConcave_Convexity; | |
| 2295 fDirection = SkPath::kUnknown_Direction; | |
| 2296 } | |
| 2297 } | |
| 2298 } | |
| 2299 | |
| 2300 SkPoint fCurrPt; | |
| 2301 SkVector fVec0, fVec1, fFirstVec; | |
| 2302 int fPtCount; // non-degenerate points | |
| 2303 int fSign; | |
| 2304 SkPath::Convexity fConvexity; | |
| 2305 SkPath::Direction fDirection; | |
| 2306 int fDx, fDy, fSx, fSy; | |
| 2307 }; | |
| 2308 | |
| 2309 SkPath::Convexity SkPath::internalGetConvexity() const { | |
| 2310 SkASSERT(kUnknown_Convexity == fConvexity); | |
| 2311 SkPoint pts[4]; | |
| 2312 SkPath::Verb verb; | |
| 2313 SkPath::Iter iter(*this, true); | |
| 2314 | |
| 2315 int contourCount = 0; | |
| 2316 int count; | |
| 2317 Convexicator state; | |
| 2318 | |
| 2319 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { | |
| 2320 switch (verb) { | |
| 2321 case kMove_Verb: | |
| 2322 if (++contourCount > 1) { | |
| 2323 fConvexity = kConcave_Convexity; | |
| 2324 return kConcave_Convexity; | |
| 2325 } | |
| 2326 pts[1] = pts[0]; | |
| 2327 count = 1; | |
| 2328 break; | |
| 2329 case kLine_Verb: count = 1; break; | |
| 2330 case kQuad_Verb: count = 2; break; | |
| 2331 case kConic_Verb: count = 2; break; | |
| 2332 case kCubic_Verb: count = 3; break; | |
| 2333 case kClose_Verb: | |
| 2334 state.close(); | |
| 2335 count = 0; | |
| 2336 break; | |
| 2337 default: | |
| 2338 SkDEBUGFAIL("bad verb"); | |
| 2339 fConvexity = kConcave_Convexity; | |
| 2340 return kConcave_Convexity; | |
| 2341 } | |
| 2342 | |
| 2343 for (int i = 1; i <= count; i++) { | |
| 2344 state.addPt(pts[i]); | |
| 2345 } | |
| 2346 // early exit | |
| 2347 if (kConcave_Convexity == state.getConvexity()) { | |
| 2348 fConvexity = kConcave_Convexity; | |
| 2349 return kConcave_Convexity; | |
| 2350 } | |
| 2351 } | |
| 2352 fConvexity = state.getConvexity(); | |
| 2353 if (kConvex_Convexity == fConvexity && kUnknown_Direction == fDirection) { | |
| 2354 fDirection = state.getDirection(); | |
| 2355 } | |
| 2356 return static_cast<Convexity>(fConvexity); | |
| 2357 } | |
| 2358 | |
| 2359 /////////////////////////////////////////////////////////////////////////////// | |
| 2360 | |
| 2361 class ContourIter { | |
| 2362 public: | |
| 2363 ContourIter(const SkPathRef& pathRef); | |
| 2364 | |
| 2365 bool done() const { return fDone; } | |
| 2366 // if !done() then these may be called | |
| 2367 int count() const { return fCurrPtCount; } | |
| 2368 const SkPoint* pts() const { return fCurrPt; } | |
| 2369 void next(); | |
| 2370 | |
| 2371 private: | |
| 2372 int fCurrPtCount; | |
| 2373 const SkPoint* fCurrPt; | |
| 2374 const uint8_t* fCurrVerb; | |
| 2375 const uint8_t* fStopVerbs; | |
| 2376 const SkScalar* fCurrConicWeight; | |
| 2377 bool fDone; | |
| 2378 SkDEBUGCODE(int fContourCounter;) | |
| 2379 }; | |
| 2380 | |
| 2381 ContourIter::ContourIter(const SkPathRef& pathRef) { | |
| 2382 fStopVerbs = pathRef.verbsMemBegin(); | |
| 2383 fDone = false; | |
| 2384 fCurrPt = pathRef.points(); | |
| 2385 fCurrVerb = pathRef.verbs(); | |
| 2386 fCurrConicWeight = pathRef.conicWeights(); | |
| 2387 fCurrPtCount = 0; | |
| 2388 SkDEBUGCODE(fContourCounter = 0;) | |
| 2389 this->next(); | |
| 2390 } | |
| 2391 | |
| 2392 void ContourIter::next() { | |
| 2393 if (fCurrVerb <= fStopVerbs) { | |
| 2394 fDone = true; | |
| 2395 } | |
| 2396 if (fDone) { | |
| 2397 return; | |
| 2398 } | |
| 2399 | |
| 2400 // skip pts of prev contour | |
| 2401 fCurrPt += fCurrPtCount; | |
| 2402 | |
| 2403 SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]); | |
| 2404 int ptCount = 1; // moveTo | |
| 2405 const uint8_t* verbs = fCurrVerb; | |
| 2406 | |
| 2407 for (--verbs; verbs > fStopVerbs; --verbs) { | |
| 2408 switch (verbs[~0]) { | |
| 2409 case SkPath::kMove_Verb: | |
| 2410 goto CONTOUR_END; | |
| 2411 case SkPath::kLine_Verb: | |
| 2412 ptCount += 1; | |
| 2413 break; | |
| 2414 case SkPath::kConic_Verb: | |
| 2415 fCurrConicWeight += 1; | |
| 2416 // fall-through | |
| 2417 case SkPath::kQuad_Verb: | |
| 2418 ptCount += 2; | |
| 2419 break; | |
| 2420 case SkPath::kCubic_Verb: | |
| 2421 ptCount += 3; | |
| 2422 break; | |
| 2423 case SkPath::kClose_Verb: | |
| 2424 break; | |
| 2425 default: | |
| 2426 SkDEBUGFAIL("unexpected verb"); | |
| 2427 break; | |
| 2428 } | |
| 2429 } | |
| 2430 CONTOUR_END: | |
| 2431 fCurrPtCount = ptCount; | |
| 2432 fCurrVerb = verbs; | |
| 2433 SkDEBUGCODE(++fContourCounter;) | |
| 2434 } | |
| 2435 | |
| 2436 // returns cross product of (p1 - p0) and (p2 - p0) | |
| 2437 static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint&
p2) { | |
| 2438 SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); | |
| 2439 // We may get 0 when the above subtracts underflow. We expect this to be | |
| 2440 // very rare and lazily promote to double. | |
| 2441 if (0 == cross) { | |
| 2442 double p0x = SkScalarToDouble(p0.fX); | |
| 2443 double p0y = SkScalarToDouble(p0.fY); | |
| 2444 | |
| 2445 double p1x = SkScalarToDouble(p1.fX); | |
| 2446 double p1y = SkScalarToDouble(p1.fY); | |
| 2447 | |
| 2448 double p2x = SkScalarToDouble(p2.fX); | |
| 2449 double p2y = SkScalarToDouble(p2.fY); | |
| 2450 | |
| 2451 cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - | |
| 2452 (p1y - p0y) * (p2x - p0x)); | |
| 2453 | |
| 2454 } | |
| 2455 return cross; | |
| 2456 } | |
| 2457 | |
| 2458 // Returns the first pt with the maximum Y coordinate | |
| 2459 static int find_max_y(const SkPoint pts[], int count) { | |
| 2460 SkASSERT(count > 0); | |
| 2461 SkScalar max = pts[0].fY; | |
| 2462 int firstIndex = 0; | |
| 2463 for (int i = 1; i < count; ++i) { | |
| 2464 SkScalar y = pts[i].fY; | |
| 2465 if (y > max) { | |
| 2466 max = y; | |
| 2467 firstIndex = i; | |
| 2468 } | |
| 2469 } | |
| 2470 return firstIndex; | |
| 2471 } | |
| 2472 | |
| 2473 static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) { | |
| 2474 int i = index; | |
| 2475 for (;;) { | |
| 2476 i = (i + inc) % n; | |
| 2477 if (i == index) { // we wrapped around, so abort | |
| 2478 break; | |
| 2479 } | |
| 2480 if (pts[index] != pts[i]) { // found a different point, success! | |
| 2481 break; | |
| 2482 } | |
| 2483 } | |
| 2484 return i; | |
| 2485 } | |
| 2486 | |
| 2487 /** | |
| 2488 * Starting at index, and moving forward (incrementing), find the xmin and | |
| 2489 * xmax of the contiguous points that have the same Y. | |
| 2490 */ | |
| 2491 static int find_min_max_x_at_y(const SkPoint pts[], int index, int n, | |
| 2492 int* maxIndexPtr) { | |
| 2493 const SkScalar y = pts[index].fY; | |
| 2494 SkScalar min = pts[index].fX; | |
| 2495 SkScalar max = min; | |
| 2496 int minIndex = index; | |
| 2497 int maxIndex = index; | |
| 2498 for (int i = index + 1; i < n; ++i) { | |
| 2499 if (pts[i].fY != y) { | |
| 2500 break; | |
| 2501 } | |
| 2502 SkScalar x = pts[i].fX; | |
| 2503 if (x < min) { | |
| 2504 min = x; | |
| 2505 minIndex = i; | |
| 2506 } else if (x > max) { | |
| 2507 max = x; | |
| 2508 maxIndex = i; | |
| 2509 } | |
| 2510 } | |
| 2511 *maxIndexPtr = maxIndex; | |
| 2512 return minIndex; | |
| 2513 } | |
| 2514 | |
| 2515 static void crossToDir(SkScalar cross, SkPath::Direction* dir) { | |
| 2516 if (dir) { | |
| 2517 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction; | |
| 2518 } | |
| 2519 } | |
| 2520 | |
| 2521 #if 0 | |
| 2522 #include "SkString.h" | |
| 2523 #include "../utils/SkParsePath.h" | |
| 2524 static void dumpPath(const SkPath& path) { | |
| 2525 SkString str; | |
| 2526 SkParsePath::ToSVGString(path, &str); | |
| 2527 SkDebugf("%s\n", str.c_str()); | |
| 2528 } | |
| 2529 #endif | |
| 2530 | |
| 2531 namespace { | |
| 2532 // for use with convex_dir_test | |
| 2533 double mul(double a, double b) { return a * b; } | |
| 2534 SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); } | |
| 2535 double toDouble(SkScalar a) { return SkScalarToDouble(a); } | |
| 2536 SkScalar toScalar(SkScalar a) { return a; } | |
| 2537 | |
| 2538 // determines the winding direction of a convex polygon with the precision | |
| 2539 // of T. CAST_SCALAR casts an SkScalar to T. | |
| 2540 template <typename T, T (CAST_SCALAR)(SkScalar)> | |
| 2541 bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) { | |
| 2542 // we find the first three points that form a non-degenerate | |
| 2543 // triangle. If there are no such points then the path is | |
| 2544 // degenerate. The first is always point 0. Now we find the second | |
| 2545 // point. | |
| 2546 int i = 0; | |
| 2547 enum { kX = 0, kY = 1 }; | |
| 2548 T v0[2]; | |
| 2549 while (1) { | |
| 2550 v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX); | |
| 2551 v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY); | |
| 2552 if (v0[kX] || v0[kY]) { | |
| 2553 break; | |
| 2554 } | |
| 2555 if (++i == n - 1) { | |
| 2556 return false; | |
| 2557 } | |
| 2558 } | |
| 2559 // now find a third point that is not colinear with the first two | |
| 2560 // points and check the orientation of the triangle (which will be | |
| 2561 // the same as the orientation of the path). | |
| 2562 for (++i; i < n; ++i) { | |
| 2563 T v1[2]; | |
| 2564 v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX); | |
| 2565 v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY); | |
| 2566 T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]); | |
| 2567 if (0 != cross) { | |
| 2568 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction; | |
| 2569 return true; | |
| 2570 } | |
| 2571 } | |
| 2572 return false; | |
| 2573 } | |
| 2574 } | |
| 2575 | |
| 2576 /* | |
| 2577 * We loop through all contours, and keep the computed cross-product of the | |
| 2578 * contour that contained the global y-max. If we just look at the first | |
| 2579 * contour, we may find one that is wound the opposite way (correctly) since | |
| 2580 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour | |
| 2581 * that is outer most (or at least has the global y-max) before we can consider | |
| 2582 * its cross product. | |
| 2583 */ | |
| 2584 bool SkPath::cheapComputeDirection(Direction* dir) const { | |
| 2585 // dumpPath(*this); | |
| 2586 // don't want to pay the cost for computing this if it | |
| 2587 // is unknown, so we don't call isConvex() | |
| 2588 | |
| 2589 if (kUnknown_Direction != fDirection) { | |
| 2590 *dir = static_cast<Direction>(fDirection); | |
| 2591 return true; | |
| 2592 } | |
| 2593 const Convexity conv = this->getConvexityOrUnknown(); | |
| 2594 | |
| 2595 ContourIter iter(*fPathRef.get()); | |
| 2596 | |
| 2597 // initialize with our logical y-min | |
| 2598 SkScalar ymax = this->getBounds().fTop; | |
| 2599 SkScalar ymaxCross = 0; | |
| 2600 | |
| 2601 for (; !iter.done(); iter.next()) { | |
| 2602 int n = iter.count(); | |
| 2603 if (n < 3) { | |
| 2604 continue; | |
| 2605 } | |
| 2606 | |
| 2607 const SkPoint* pts = iter.pts(); | |
| 2608 SkScalar cross = 0; | |
| 2609 if (kConvex_Convexity == conv) { | |
| 2610 // We try first at scalar precision, and then again at double | |
| 2611 // precision. This is because the vectors computed between distant | |
| 2612 // points may lose too much precision. | |
| 2613 if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) { | |
| 2614 fDirection = *dir; | |
| 2615 return true; | |
| 2616 } | |
| 2617 if (convex_dir_test<double, toDouble>(n, pts, dir)) { | |
| 2618 fDirection = *dir; | |
| 2619 return true; | |
| 2620 } else { | |
| 2621 return false; | |
| 2622 } | |
| 2623 } else { | |
| 2624 int index = find_max_y(pts, n); | |
| 2625 if (pts[index].fY < ymax) { | |
| 2626 continue; | |
| 2627 } | |
| 2628 | |
| 2629 // If there is more than 1 distinct point at the y-max, we take the | |
| 2630 // x-min and x-max of them and just subtract to compute the dir. | |
| 2631 if (pts[(index + 1) % n].fY == pts[index].fY) { | |
| 2632 int maxIndex; | |
| 2633 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); | |
| 2634 if (minIndex == maxIndex) { | |
| 2635 goto TRY_CROSSPROD; | |
| 2636 } | |
| 2637 SkASSERT(pts[minIndex].fY == pts[index].fY); | |
| 2638 SkASSERT(pts[maxIndex].fY == pts[index].fY); | |
| 2639 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); | |
| 2640 // we just subtract the indices, and let that auto-convert to | |
| 2641 // SkScalar, since we just want - or + to signal the direction. | |
| 2642 cross = minIndex - maxIndex; | |
| 2643 } else { | |
| 2644 TRY_CROSSPROD: | |
| 2645 // Find a next and prev index to use for the cross-product test, | |
| 2646 // but we try to find pts that form non-zero vectors from pts[in
dex] | |
| 2647 // | |
| 2648 // Its possible that we can't find two non-degenerate vectors, s
o | |
| 2649 // we have to guard our search (e.g. all the pts could be in the | |
| 2650 // same place). | |
| 2651 | |
| 2652 // we pass n - 1 instead of -1 so we don't foul up % operator by | |
| 2653 // passing it a negative LH argument. | |
| 2654 int prev = find_diff_pt(pts, index, n, n - 1); | |
| 2655 if (prev == index) { | |
| 2656 // completely degenerate, skip to next contour | |
| 2657 continue; | |
| 2658 } | |
| 2659 int next = find_diff_pt(pts, index, n, 1); | |
| 2660 SkASSERT(next != index); | |
| 2661 cross = cross_prod(pts[prev], pts[index], pts[next]); | |
| 2662 // if we get a zero and the points are horizontal, then we look
at the spread in | |
| 2663 // x-direction. We really should continue to walk away from the
degeneracy until | |
| 2664 // there is a divergence. | |
| 2665 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY
== pts[index].fY) { | |
| 2666 // construct the subtract so we get the correct Direction be
low | |
| 2667 cross = pts[index].fX - pts[next].fX; | |
| 2668 } | |
| 2669 } | |
| 2670 | |
| 2671 if (cross) { | |
| 2672 // record our best guess so far | |
| 2673 ymax = pts[index].fY; | |
| 2674 ymaxCross = cross; | |
| 2675 } | |
| 2676 } | |
| 2677 } | |
| 2678 if (ymaxCross) { | |
| 2679 crossToDir(ymaxCross, dir); | |
| 2680 fDirection = *dir; | |
| 2681 return true; | |
| 2682 } else { | |
| 2683 return false; | |
| 2684 } | |
| 2685 } | |
| 2686 | 2055 |
| 2687 /////////////////////////////////////////////////////////////////////////////// | 2056 /////////////////////////////////////////////////////////////////////////////// |
| 2688 | 2057 |
| 2689 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, | 2058 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, |
| 2690 SkScalar D, SkScalar t) { | 2059 SkScalar D, SkScalar t) { |
| 2691 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); | 2060 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); |
| 2692 } | 2061 } |
| 2693 | 2062 |
| 2694 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c
3, | 2063 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c
3, |
| 2695 SkScalar t) { | 2064 SkScalar t) { |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2930 switch (this->getFillType()) { | 2299 switch (this->getFillType()) { |
| 2931 case SkPath::kEvenOdd_FillType: | 2300 case SkPath::kEvenOdd_FillType: |
| 2932 case SkPath::kInverseEvenOdd_FillType: | 2301 case SkPath::kInverseEvenOdd_FillType: |
| 2933 w &= 1; | 2302 w &= 1; |
| 2934 break; | 2303 break; |
| 2935 default: | 2304 default: |
| 2936 break; | 2305 break; |
| 2937 } | 2306 } |
| 2938 return SkToBool(w); | 2307 return SkToBool(w); |
| 2939 } | 2308 } |
| OLD | NEW |