| Index: src/core/SkPath.cpp
|
| ===================================================================
|
| --- src/core/SkPath.cpp (revision 11888)
|
| +++ src/core/SkPath.cpp (working copy)
|
| @@ -17,11 +17,6 @@
|
|
|
| SK_DEFINE_INST_COUNT(SkPath);
|
|
|
| -// This value is just made-up for now. When count is 4, calling memset was much
|
| -// slower than just writing the loop. This seems odd, and hopefully in the
|
| -// future this we appear to have been a fluke...
|
| -#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16
|
| -
|
| ////////////////////////////////////////////////////////////////////////////
|
|
|
| /**
|
| @@ -42,36 +37,6 @@
|
| return SkPath::kDone_Verb == iter.next(pts);
|
| }
|
|
|
| -class SkAutoDisableOvalCheck {
|
| -public:
|
| - SkAutoDisableOvalCheck(SkPath* path) : fPath(path) {
|
| - fSaved = fPath->fIsOval;
|
| - }
|
| -
|
| - ~SkAutoDisableOvalCheck() {
|
| - fPath->fIsOval = fSaved;
|
| - }
|
| -
|
| -private:
|
| - SkPath* fPath;
|
| - bool fSaved;
|
| -};
|
| -
|
| -class SkAutoDisableDirectionCheck {
|
| -public:
|
| - SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
|
| - fSaved = static_cast<SkPath::Direction>(fPath->fDirection);
|
| - }
|
| -
|
| - ~SkAutoDisableDirectionCheck() {
|
| - fPath->fDirection = fSaved;
|
| - }
|
| -
|
| -private:
|
| - SkPath* fPath;
|
| - SkPath::Direction fSaved;
|
| -};
|
| -
|
| /* This guy's constructor/destructor bracket a path editing operation. It is
|
| used when we know the bounds of the amount we are going to add to the path
|
| (usually a new contour, but not required).
|
| @@ -82,7 +47,7 @@
|
|
|
| It also notes if the path was originally degenerate, and if so, sets
|
| isConvex to true. Thus it can only be used if the contour being added is
|
| - convex (which is always true since we only allow the addition of rects).
|
| + convex.
|
| */
|
| class SkAutoPathBoundsUpdate {
|
| public:
|
| @@ -97,9 +62,12 @@
|
| }
|
|
|
| ~SkAutoPathBoundsUpdate() {
|
| - fPath->setIsConvex(fDegenerate);
|
| + SkPathRef::Editor ed(&fPath->fPathRef);
|
| +
|
| + ed.setConvexity(fDegenerate ? SkPath::kConvex_Convexity
|
| + : SkPath::kConcave_Convexity);
|
| if (fEmpty || fHasValidBounds) {
|
| - fPath->setBounds(fRect);
|
| + ed.setBounds(fRect);
|
| }
|
| }
|
|
|
| @@ -140,10 +108,6 @@
|
| */
|
|
|
| ////////////////////////////////////////////////////////////////////////////
|
| -
|
| -// flag to require a moveTo if we begin with something else, like lineTo etc.
|
| -#define INITIAL_LASTMOVETOINDEX_VALUE ~0
|
| -
|
| SkPath::SkPath()
|
| : fPathRef(SkPathRef::CreateEmpty())
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| @@ -155,12 +119,7 @@
|
|
|
| void SkPath::resetFields() {
|
| //fPathRef is assumed to have been emptied by the caller.
|
| - fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
|
| fFillType = kWinding_FillType;
|
| - fSegmentMask = 0;
|
| - fConvexity = kUnknown_Convexity;
|
| - fDirection = kUnknown_Direction;
|
| - fIsOval = false;
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| GEN_ID_INC;
|
| // We don't touch fSourcePath. It's used to track texture garbage collection, so we don't
|
| @@ -199,25 +158,12 @@
|
|
|
| void SkPath::copyFields(const SkPath& that) {
|
| //fPathRef is assumed to have been set by the caller.
|
| - fLastMoveToIndex = that.fLastMoveToIndex;
|
| fFillType = that.fFillType;
|
| - fSegmentMask = that.fSegmentMask;
|
| - fConvexity = that.fConvexity;
|
| - fDirection = that.fDirection;
|
| - fIsOval = that.fIsOval;
|
| }
|
|
|
| bool operator==(const SkPath& a, const SkPath& b) {
|
| - // note: don't need to look at isConvex or bounds, since just comparing the
|
| - // raw data is sufficient.
|
| -
|
| - // We explicitly check fSegmentMask as a quick-reject. We could skip it,
|
| - // since it is only a cache of info in the fVerbs, but its a fast way to
|
| - // notice a difference
|
| -
|
| return &a == &b ||
|
| - (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
|
| - *a.fPathRef.get() == *b.fPathRef.get());
|
| + (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
|
| }
|
|
|
| void SkPath::swap(SkPath& that) {
|
| @@ -225,12 +171,7 @@
|
|
|
| if (this != &that) {
|
| fPathRef.swap(&that.fPathRef);
|
| - SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
|
| SkTSwap<uint8_t>(fFillType, that.fFillType);
|
| - SkTSwap<uint8_t>(fSegmentMask, that.fSegmentMask);
|
| - SkTSwap<uint8_t>(fConvexity, that.fConvexity);
|
| - SkTSwap<uint8_t>(fDirection, that.fDirection);
|
| - SkTSwap<SkBool8>(fIsOval, that.fIsOval);
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| // It doesn't really make sense to swap the generation IDs here, because they might go
|
| // backwards. To be safe we increment both to mark them both as changed.
|
| @@ -410,7 +351,7 @@
|
| is a rectangle, though the caller failed to close the path.
|
| */
|
| bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
|
| - bool* isClosed, Direction* direction) const {
|
| + bool* isClosed, Direction* direction) const {
|
| int corners = 0;
|
| SkPoint first, last;
|
| const SkPoint* pts = *ptsPtr;
|
| @@ -630,7 +571,6 @@
|
| if (count == 0) {
|
| this->moveTo(x, y);
|
| } else {
|
| - fIsOval = false;
|
| SkPathRef::Editor ed(&fPathRef);
|
| ed.atPoint(count-1)->set(x, y);
|
| GEN_ID_INC;
|
| @@ -638,8 +578,9 @@
|
| }
|
|
|
| void SkPath::setConvexity(Convexity c) {
|
| - if (fConvexity != c) {
|
| - fConvexity = c;
|
| + if (static_cast<SkPath::Convexity>(fPathRef->getConvexity()) != c) {
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + ed.setConvexity(c);
|
| GEN_ID_INC;
|
| }
|
| }
|
| @@ -665,9 +606,6 @@
|
|
|
| SkPathRef::Editor ed(&fPathRef);
|
|
|
| - // remember our index
|
| - fLastMoveToIndex = ed.pathRef()->countPoints();
|
| -
|
| ed.growForVerb(kMove_Verb)->set(x, y);
|
|
|
| GEN_ID_INC;
|
| @@ -679,60 +617,54 @@
|
| this->moveTo(pt.fX + x, pt.fY + y);
|
| }
|
|
|
| -void SkPath::injectMoveToIfNeeded() {
|
| - if (fLastMoveToIndex < 0) {
|
| - SkScalar x, y;
|
| - if (fPathRef->countVerbs() == 0) {
|
| - x = y = 0;
|
| - } else {
|
| - const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
|
| - x = pt.fX;
|
| - y = pt.fY;
|
| - }
|
| - this->moveTo(x, y);
|
| - }
|
| -}
|
| -
|
| void SkPath::lineTo(SkScalar x, SkScalar y) {
|
| SkDEBUGCODE(this->validate();)
|
|
|
| - this->injectMoveToIfNeeded();
|
| -
|
| SkPathRef::Editor ed(&fPathRef);
|
| ed.growForVerb(kLine_Verb)->set(x, y);
|
| - fSegmentMask |= kLine_SegmentMask;
|
|
|
| GEN_ID_INC;
|
| - DIRTY_AFTER_EDIT;
|
| }
|
|
|
| void SkPath::rLineTo(SkScalar x, SkScalar y) {
|
| - this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
|
| - SkPoint pt;
|
| - this->getLastPt(&pt);
|
| - this->lineTo(pt.fX + x, pt.fY + y);
|
| + SkDEBUGCODE(this->validate();)
|
| +
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + SkPoint* dst = ed.growForVerb(kLine_Verb);
|
| +
|
| + // growForVerb can change the result of this->getPoint().
|
| + SkASSERT(this->countPoints() >= 2);
|
| + SkPoint pt = this->getPoint(this->countPoints() - 2);
|
| +
|
| + dst->set(pt.fX + x, pt.fY + y);
|
| + GEN_ID_INC;
|
| }
|
|
|
| void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
|
| SkDEBUGCODE(this->validate();)
|
|
|
| - this->injectMoveToIfNeeded();
|
| -
|
| SkPathRef::Editor ed(&fPathRef);
|
| SkPoint* pts = ed.growForVerb(kQuad_Verb);
|
| pts[0].set(x1, y1);
|
| pts[1].set(x2, y2);
|
| - fSegmentMask |= kQuad_SegmentMask;
|
|
|
| GEN_ID_INC;
|
| - DIRTY_AFTER_EDIT;
|
| }
|
|
|
| void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
|
| - this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
|
| - SkPoint pt;
|
| - this->getLastPt(&pt);
|
| - this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
|
| + SkDEBUGCODE(this->validate();)
|
| +
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + SkPoint* pts = ed.growForVerb(kQuad_Verb);
|
| +
|
| + // growForVerb can change the result of this->getPoint().
|
| + SkASSERT(this->countPoints() >= 3);
|
| + SkPoint pt = this->getPoint(this->countPoints() - 3);
|
| +
|
| + pts[0].set(pt.fX + x1, pt.fY + y1);
|
| + pts[1].set(pt.fX + x2, pt.fY + y2);
|
| +
|
| + GEN_ID_INC;
|
| }
|
|
|
| void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
| @@ -748,51 +680,71 @@
|
| } else {
|
| SkDEBUGCODE(this->validate();)
|
|
|
| - this->injectMoveToIfNeeded();
|
| -
|
| SkPathRef::Editor ed(&fPathRef);
|
| SkPoint* pts = ed.growForConic(w);
|
| pts[0].set(x1, y1);
|
| pts[1].set(x2, y2);
|
| - fSegmentMask |= kConic_SegmentMask;
|
|
|
| GEN_ID_INC;
|
| - DIRTY_AFTER_EDIT;
|
| }
|
| }
|
|
|
| void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
|
| SkScalar w) {
|
| - this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
|
| - SkPoint pt;
|
| - this->getLastPt(&pt);
|
| - this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
|
| + // check for <= 0 or NaN with this test
|
| + if (!(w > 0)) {
|
| + this->rLineTo(dx2, dy2);
|
| + } else if (!SkScalarIsFinite(w)) {
|
| + this->rLineTo(dx1, dy1);
|
| + this->rLineTo(dx2, dy2);
|
| + } else if (SK_Scalar1 == w) {
|
| + this->rQuadTo(dx1, dy1, dx2, dy2);
|
| + } else {
|
| + SkDEBUGCODE(this->validate();)
|
| +
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + SkPoint* pts = ed.growForConic(w);
|
| +
|
| + // growForConic can change the result of this->getPoint().
|
| + SkASSERT(this->countPoints() >= 3);
|
| + SkPoint pt = this->getPoint(this->countPoints() - 3);
|
| +
|
| + pts[0].set(pt.fX + dx1, pt.fY + dy1);
|
| + pts[1].set(pt.fX + dx2, pt.fY + dy2);
|
| +
|
| + GEN_ID_INC;
|
| + }
|
| }
|
|
|
| void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
| SkScalar x3, SkScalar y3) {
|
| SkDEBUGCODE(this->validate();)
|
|
|
| - this->injectMoveToIfNeeded();
|
| -
|
| SkPathRef::Editor ed(&fPathRef);
|
| SkPoint* pts = ed.growForVerb(kCubic_Verb);
|
| pts[0].set(x1, y1);
|
| pts[1].set(x2, y2);
|
| pts[2].set(x3, y3);
|
| - fSegmentMask |= kCubic_SegmentMask;
|
|
|
| GEN_ID_INC;
|
| - DIRTY_AFTER_EDIT;
|
| }
|
|
|
| void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
|
| SkScalar x3, SkScalar y3) {
|
| - this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt().
|
| - SkPoint pt;
|
| - this->getLastPt(&pt);
|
| - this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
|
| - pt.fX + x3, pt.fY + y3);
|
| + SkDEBUGCODE(this->validate();)
|
| +
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + SkPoint* pts = ed.growForVerb(kCubic_Verb);
|
| +
|
| + // growForVerb can change the result of this->getPoint().
|
| + SkASSERT(this->countPoints() >= 4);
|
| + SkPoint pt = this->getPoint(this->countPoints() - 4);
|
| +
|
| + pts[0].set(pt.fX + x1, pt.fY + y1);
|
| + pts[1].set(pt.fX + x2, pt.fY + y2);
|
| + pts[2].set(pt.fX + x3, pt.fY + y3);
|
| +
|
| + GEN_ID_INC;
|
| }
|
|
|
| void SkPath::close() {
|
| @@ -819,15 +771,6 @@
|
| break;
|
| }
|
| }
|
| -
|
| - // signal that we need a moveTo to follow us (unless we're done)
|
| -#if 0
|
| - if (fLastMoveToIndex >= 0) {
|
| - fLastMoveToIndex = ~fLastMoveToIndex;
|
| - }
|
| -#else
|
| - fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
|
| -#endif
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
| @@ -843,8 +786,7 @@
|
| void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
|
| SkScalar bottom, Direction dir) {
|
| assert_known_direction(dir);
|
| - fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
|
| - SkAutoDisableDirectionCheck addc(this);
|
| + Direction newDir = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
|
|
|
| SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
|
|
|
| @@ -861,6 +803,9 @@
|
| this->lineTo(left, bottom);
|
| }
|
| this->close();
|
| +
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + ed.setDirection(newDir);
|
| }
|
|
|
| void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
|
| @@ -869,33 +814,22 @@
|
| return;
|
| }
|
|
|
| - SkPathRef::Editor ed(&fPathRef);
|
| - fLastMoveToIndex = ed.pathRef()->countPoints();
|
| - uint8_t* vb;
|
| - SkPoint* p;
|
| // +close makes room for the extra kClose_Verb
|
| - ed.grow(count + close, count, &vb, &p);
|
| + SkPathRef::Editor ed(&fPathRef, count+close, count);
|
|
|
| - memcpy(p, pts, count * sizeof(SkPoint));
|
| - vb[~0] = kMove_Verb;
|
| + ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
|
| +
|
| if (count > 1) {
|
| - // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
|
| - // be 0, the compiler will remove the test/branch entirely.
|
| - if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
|
| - memset(vb - count, kLine_Verb, count - 1);
|
| - } else {
|
| - for (int i = 1; i < count; ++i) {
|
| - vb[~i] = kLine_Verb;
|
| - }
|
| - }
|
| - fSegmentMask |= kLine_SegmentMask;
|
| + SkPoint* p = ed.growForLines(count - 1);
|
| +
|
| + memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
|
| }
|
| +
|
| if (close) {
|
| - vb[~count] = kClose_Verb;
|
| + ed.growForVerb(kClose_Verb);
|
| }
|
|
|
| GEN_ID_INC;
|
| - DIRTY_AFTER_EDIT;
|
| SkDEBUGCODE(this->validate();)
|
| }
|
|
|
| @@ -1056,8 +990,7 @@
|
| #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
|
| #endif
|
|
|
| -void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
|
| - Direction dir) {
|
| +void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir) {
|
| assert_known_direction(dir);
|
|
|
| if (rx < 0 || ry < 0) {
|
| @@ -1085,10 +1018,9 @@
|
| return;
|
| }
|
|
|
| - fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
|
| + Direction newDir = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
|
|
|
| SkAutoPathBoundsUpdate apbu(this, rect);
|
| - SkAutoDisableDirectionCheck(this);
|
|
|
| if (skip_hori) {
|
| rx = halfW;
|
| @@ -1224,6 +1156,9 @@
|
| }
|
| }
|
| this->close();
|
| +
|
| + SkPathRef::Editor ed(&fPathRef);
|
| + ed.setDirection(newDir);
|
| }
|
|
|
| void SkPath::addOval(const SkRect& oval, Direction dir) {
|
| @@ -1235,16 +1170,9 @@
|
| We can't simply check isEmpty() in this case, as additional
|
| moveTo() would mark the path non empty.
|
| */
|
| - fIsOval = hasOnlyMoveTos();
|
| - if (fIsOval) {
|
| - fDirection = dir;
|
| - } else {
|
| - fDirection = kUnknown_Direction;
|
| - }
|
| + bool isOval = this->hasOnlyMoveTos();
|
| + Direction newDir = isOval ? dir : kUnknown_Direction;
|
|
|
| - SkAutoDisableOvalCheck adoc(this);
|
| - SkAutoDisableDirectionCheck addc(this);
|
| -
|
| SkAutoPathBoundsUpdate apbu(this, oval);
|
|
|
| SkScalar cx = oval.centerX();
|
| @@ -1290,14 +1218,11 @@
|
| this->quadTo( R, cy - sy, R, cy );
|
| }
|
| this->close();
|
| -}
|
|
|
| -bool SkPath::isOval(SkRect* rect) const {
|
| - if (fIsOval && rect) {
|
| - *rect = getBounds();
|
| - }
|
| + SkPathRef::Editor ed(&fPathRef);
|
|
|
| - return fIsOval;
|
| + ed.setIsOval(isOval);
|
| + ed.setDirection(newDir);
|
| }
|
|
|
| void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
|
| @@ -1431,8 +1356,6 @@
|
| void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
|
| SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
|
|
|
| - fIsOval = false;
|
| -
|
| RawIter iter(path);
|
| SkPoint pts[4];
|
| Verb verb;
|
| @@ -1497,8 +1420,6 @@
|
|
|
| SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
|
|
|
| - fIsOval = false;
|
| -
|
| const uint8_t* verbs = path.fPathRef->verbs();
|
| // skip the initial moveTo
|
| const SkPoint* pts = path.fPathRef->points() + 1;
|
| @@ -1536,8 +1457,6 @@
|
|
|
| SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
|
|
|
| - fIsOval = false;
|
| -
|
| const uint8_t* verbs = path.fPathRef->verbs();
|
| const SkPoint* pts = path.fPathRef->points();
|
| const SkScalar* conicWeights = path.fPathRef->conicWeights();
|
| @@ -1577,7 +1496,7 @@
|
| }
|
|
|
| void SkPath::reverseAddPath(const SkPath& src) {
|
| - SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
|
| + SkPathRef::Editor(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
|
|
|
| const SkPoint* pts = src.fPathRef->pointsEnd();
|
| // we will iterator through src's verbs backwards
|
| @@ -1585,8 +1504,6 @@
|
| const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
|
| const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
|
|
|
| - fIsOval = false;
|
| -
|
| bool needMove = true;
|
| bool needClose = false;
|
| while (verbs < verbsEnd) {
|
| @@ -1710,14 +1627,12 @@
|
| dst->swap(tmp);
|
| SkPathRef::Editor ed(&dst->fPathRef);
|
| matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
|
| - dst->fDirection = kUnknown_Direction;
|
| + ed.setDirection(kUnknown_Direction);
|
| } else {
|
| SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
|
|
|
| if (this != dst) {
|
| dst->fFillType = fFillType;
|
| - dst->fSegmentMask = fSegmentMask;
|
| - dst->fConvexity = fConvexity;
|
| }
|
|
|
| #ifdef SK_BUILD_FOR_ANDROID
|
| @@ -1726,24 +1641,6 @@
|
| }
|
| #endif
|
|
|
| - if (kUnknown_Direction == fDirection) {
|
| - dst->fDirection = kUnknown_Direction;
|
| - } else {
|
| - SkScalar det2x2 =
|
| - SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) -
|
| - SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY));
|
| - if (det2x2 < 0) {
|
| - dst->fDirection = SkPath::OppositeDirection(static_cast<Direction>(fDirection));
|
| - } else if (det2x2 > 0) {
|
| - dst->fDirection = fDirection;
|
| - } else {
|
| - dst->fDirection = kUnknown_Direction;
|
| - }
|
| - }
|
| -
|
| - // It's an oval only if it stays a rect.
|
| - dst->fIsOval = fIsOval && matrix.rectStaysRect();
|
| -
|
| SkDEBUGCODE(dst->validate();)
|
| }
|
| }
|
| @@ -1775,14 +1672,22 @@
|
| }
|
|
|
| SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
|
| - this->setPath(path, forceClose);
|
| + this->setPathRef(path.fPathRef.get(), forceClose);
|
| }
|
|
|
| +SkPath::Iter::Iter(const SkPathRef* pathRef, bool forceClose) {
|
| + this->setPathRef(pathRef, forceClose);
|
| +}
|
| +
|
| void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
|
| - fPts = path.fPathRef->points();
|
| - fVerbs = path.fPathRef->verbs();
|
| - fVerbStop = path.fPathRef->verbsMemBegin();
|
| - fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
|
| + this->setPathRef(path.fPathRef.get(), forceClose);
|
| +}
|
| +
|
| +void SkPath::Iter::setPathRef(const SkPathRef* pathRef, bool forceClose) {
|
| + fPts = pathRef->points();
|
| + fVerbs = pathRef->verbs();
|
| + fVerbStop = pathRef->verbsMemBegin();
|
| + fConicWeights = pathRef->conicWeights() - 1; // begin one behind
|
| fLastPt.fX = fLastPt.fY = 0;
|
| fMoveTo.fX = fMoveTo.fY = 0;
|
| fForceClose = SkToU8(forceClose);
|
| @@ -2096,11 +2001,7 @@
|
|
|
| SkWBuffer buffer(storage);
|
|
|
| - int32_t packed = ((fIsOval & 1) << kIsOval_SerializationShift) |
|
| - (fConvexity << kConvexity_SerializationShift) |
|
| - (fFillType << kFillType_SerializationShift) |
|
| - (fSegmentMask << kSegmentMask_SerializationShift) |
|
| - (fDirection << kDirection_SerializationShift)
|
| + int32_t packed = (fFillType << kFillType_SerializationShift)
|
| #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
|
| | (0x1 << kNewFormat_SerializationShift);
|
| #endif
|
| @@ -2117,11 +2018,7 @@
|
| SkRBuffer buffer(storage);
|
|
|
| uint32_t packed = buffer.readS32();
|
| - fIsOval = (packed >> kIsOval_SerializationShift) & 1;
|
| - fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
|
| fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
|
| - fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
|
| - fDirection = (packed >> kDirection_SerializationShift) & 0x3;
|
| #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V14_AND_ALL_OTHER_INSTANCES_TOO
|
| bool newFormat = (packed >> kNewFormat_SerializationShift) & 1;
|
| #endif
|
| @@ -2221,543 +2118,14 @@
|
| void SkPath::validate() const {
|
| SkASSERT(this != NULL);
|
| SkASSERT((fFillType & ~3) == 0);
|
| -
|
| -#ifdef SK_DEBUG_PATH
|
| - if (!fBoundsIsDirty) {
|
| - SkRect bounds;
|
| -
|
| - bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
|
| - SkASSERT(SkToBool(fIsFinite) == isFinite);
|
| -
|
| - if (fPathRef->countPoints() <= 1) {
|
| - // if we're empty, fBounds may be empty but translated, so we can't
|
| - // necessarily compare to bounds directly
|
| - // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
|
| - // be [2, 2, 2, 2]
|
| - SkASSERT(bounds.isEmpty());
|
| - SkASSERT(fBounds.isEmpty());
|
| - } else {
|
| - if (bounds.isEmpty()) {
|
| - SkASSERT(fBounds.isEmpty());
|
| - } else {
|
| - if (!fBounds.isEmpty()) {
|
| - SkASSERT(fBounds.contains(bounds));
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - uint32_t mask = 0;
|
| - const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbs();
|
| - for (int i = 0; i < fPathRef->countVerbs(); i++) {
|
| - switch (verbs[~i]) {
|
| - case kLine_Verb:
|
| - mask |= kLine_SegmentMask;
|
| - break;
|
| - case kQuad_Verb:
|
| - mask |= kQuad_SegmentMask;
|
| - break;
|
| - case kConic_Verb:
|
| - mask |= kConic_SegmentMask;
|
| - break;
|
| - case kCubic_Verb:
|
| - mask |= kCubic_SegmentMask;
|
| - case kMove_Verb: // these verbs aren't included in the segment mask.
|
| - case kClose_Verb:
|
| - break;
|
| - case kDone_Verb:
|
| - SkDEBUGFAIL("Done verb shouldn't be recorded.");
|
| - break;
|
| - default:
|
| - SkDEBUGFAIL("Unknown Verb");
|
| - break;
|
| - }
|
| - }
|
| - SkASSERT(mask == fSegmentMask);
|
| -#endif // SK_DEBUG_PATH
|
| }
|
| #endif // SK_DEBUG
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| -static int sign(SkScalar x) { return x < 0; }
|
| -#define kValueNeverReturnedBySign 2
|
|
|
| -static int CrossProductSign(const SkVector& a, const SkVector& b) {
|
| - return SkScalarSignAsInt(SkPoint::CrossProduct(a, b));
|
| -}
|
| -
|
| -// only valid for a single contour
|
| -struct Convexicator {
|
| - Convexicator()
|
| - : fPtCount(0)
|
| - , fConvexity(SkPath::kConvex_Convexity)
|
| - , fDirection(SkPath::kUnknown_Direction) {
|
| - fSign = 0;
|
| - // warnings
|
| - fCurrPt.set(0, 0);
|
| - fVec0.set(0, 0);
|
| - fVec1.set(0, 0);
|
| - fFirstVec.set(0, 0);
|
| -
|
| - fDx = fDy = 0;
|
| - fSx = fSy = kValueNeverReturnedBySign;
|
| - }
|
| -
|
| - SkPath::Convexity getConvexity() const { return fConvexity; }
|
| -
|
| - /** The direction returned is only valid if the path is determined convex */
|
| - SkPath::Direction getDirection() const { return fDirection; }
|
| -
|
| - void addPt(const SkPoint& pt) {
|
| - if (SkPath::kConcave_Convexity == fConvexity) {
|
| - return;
|
| - }
|
| -
|
| - if (0 == fPtCount) {
|
| - fCurrPt = pt;
|
| - ++fPtCount;
|
| - } else {
|
| - SkVector vec = pt - fCurrPt;
|
| - if (vec.fX || vec.fY) {
|
| - fCurrPt = pt;
|
| - if (++fPtCount == 2) {
|
| - fFirstVec = fVec1 = vec;
|
| - } else {
|
| - SkASSERT(fPtCount > 2);
|
| - this->addVec(vec);
|
| - }
|
| -
|
| - int sx = sign(vec.fX);
|
| - int sy = sign(vec.fY);
|
| - fDx += (sx != fSx);
|
| - fDy += (sy != fSy);
|
| - fSx = sx;
|
| - fSy = sy;
|
| -
|
| - if (fDx > 3 || fDy > 3) {
|
| - fConvexity = SkPath::kConcave_Convexity;
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - void close() {
|
| - if (fPtCount > 2) {
|
| - this->addVec(fFirstVec);
|
| - }
|
| - }
|
| -
|
| -private:
|
| - void addVec(const SkVector& vec) {
|
| - SkASSERT(vec.fX || vec.fY);
|
| - fVec0 = fVec1;
|
| - fVec1 = vec;
|
| - int sign = CrossProductSign(fVec0, fVec1);
|
| - if (0 == fSign) {
|
| - fSign = sign;
|
| - if (1 == sign) {
|
| - fDirection = SkPath::kCW_Direction;
|
| - } else if (-1 == sign) {
|
| - fDirection = SkPath::kCCW_Direction;
|
| - }
|
| - } else if (sign) {
|
| - if (fSign != sign) {
|
| - fConvexity = SkPath::kConcave_Convexity;
|
| - fDirection = SkPath::kUnknown_Direction;
|
| - }
|
| - }
|
| - }
|
| -
|
| - SkPoint fCurrPt;
|
| - SkVector fVec0, fVec1, fFirstVec;
|
| - int fPtCount; // non-degenerate points
|
| - int fSign;
|
| - SkPath::Convexity fConvexity;
|
| - SkPath::Direction fDirection;
|
| - int fDx, fDy, fSx, fSy;
|
| -};
|
| -
|
| -SkPath::Convexity SkPath::internalGetConvexity() const {
|
| - SkASSERT(kUnknown_Convexity == fConvexity);
|
| - SkPoint pts[4];
|
| - SkPath::Verb verb;
|
| - SkPath::Iter iter(*this, true);
|
| -
|
| - int contourCount = 0;
|
| - int count;
|
| - Convexicator state;
|
| -
|
| - while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
| - switch (verb) {
|
| - case kMove_Verb:
|
| - if (++contourCount > 1) {
|
| - fConvexity = kConcave_Convexity;
|
| - return kConcave_Convexity;
|
| - }
|
| - pts[1] = pts[0];
|
| - count = 1;
|
| - break;
|
| - case kLine_Verb: count = 1; break;
|
| - case kQuad_Verb: count = 2; break;
|
| - case kConic_Verb: count = 2; break;
|
| - case kCubic_Verb: count = 3; break;
|
| - case kClose_Verb:
|
| - state.close();
|
| - count = 0;
|
| - break;
|
| - default:
|
| - SkDEBUGFAIL("bad verb");
|
| - fConvexity = kConcave_Convexity;
|
| - return kConcave_Convexity;
|
| - }
|
| -
|
| - for (int i = 1; i <= count; i++) {
|
| - state.addPt(pts[i]);
|
| - }
|
| - // early exit
|
| - if (kConcave_Convexity == state.getConvexity()) {
|
| - fConvexity = kConcave_Convexity;
|
| - return kConcave_Convexity;
|
| - }
|
| - }
|
| - fConvexity = state.getConvexity();
|
| - if (kConvex_Convexity == fConvexity && kUnknown_Direction == fDirection) {
|
| - fDirection = state.getDirection();
|
| - }
|
| - return static_cast<Convexity>(fConvexity);
|
| -}
|
| -
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| -class ContourIter {
|
| -public:
|
| - ContourIter(const SkPathRef& pathRef);
|
| -
|
| - bool done() const { return fDone; }
|
| - // if !done() then these may be called
|
| - int count() const { return fCurrPtCount; }
|
| - const SkPoint* pts() const { return fCurrPt; }
|
| - void next();
|
| -
|
| -private:
|
| - int fCurrPtCount;
|
| - const SkPoint* fCurrPt;
|
| - const uint8_t* fCurrVerb;
|
| - const uint8_t* fStopVerbs;
|
| - const SkScalar* fCurrConicWeight;
|
| - bool fDone;
|
| - SkDEBUGCODE(int fContourCounter;)
|
| -};
|
| -
|
| -ContourIter::ContourIter(const SkPathRef& pathRef) {
|
| - fStopVerbs = pathRef.verbsMemBegin();
|
| - fDone = false;
|
| - fCurrPt = pathRef.points();
|
| - fCurrVerb = pathRef.verbs();
|
| - fCurrConicWeight = pathRef.conicWeights();
|
| - fCurrPtCount = 0;
|
| - SkDEBUGCODE(fContourCounter = 0;)
|
| - this->next();
|
| -}
|
| -
|
| -void ContourIter::next() {
|
| - if (fCurrVerb <= fStopVerbs) {
|
| - fDone = true;
|
| - }
|
| - if (fDone) {
|
| - return;
|
| - }
|
| -
|
| - // skip pts of prev contour
|
| - fCurrPt += fCurrPtCount;
|
| -
|
| - SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
|
| - int ptCount = 1; // moveTo
|
| - const uint8_t* verbs = fCurrVerb;
|
| -
|
| - for (--verbs; verbs > fStopVerbs; --verbs) {
|
| - switch (verbs[~0]) {
|
| - case SkPath::kMove_Verb:
|
| - goto CONTOUR_END;
|
| - case SkPath::kLine_Verb:
|
| - ptCount += 1;
|
| - break;
|
| - case SkPath::kConic_Verb:
|
| - fCurrConicWeight += 1;
|
| - // fall-through
|
| - case SkPath::kQuad_Verb:
|
| - ptCount += 2;
|
| - break;
|
| - case SkPath::kCubic_Verb:
|
| - ptCount += 3;
|
| - break;
|
| - case SkPath::kClose_Verb:
|
| - break;
|
| - default:
|
| - SkDEBUGFAIL("unexpected verb");
|
| - break;
|
| - }
|
| - }
|
| -CONTOUR_END:
|
| - fCurrPtCount = ptCount;
|
| - fCurrVerb = verbs;
|
| - SkDEBUGCODE(++fContourCounter;)
|
| -}
|
| -
|
| -// returns cross product of (p1 - p0) and (p2 - p0)
|
| -static SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
|
| - SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
|
| - // We may get 0 when the above subtracts underflow. We expect this to be
|
| - // very rare and lazily promote to double.
|
| - if (0 == cross) {
|
| - double p0x = SkScalarToDouble(p0.fX);
|
| - double p0y = SkScalarToDouble(p0.fY);
|
| -
|
| - double p1x = SkScalarToDouble(p1.fX);
|
| - double p1y = SkScalarToDouble(p1.fY);
|
| -
|
| - double p2x = SkScalarToDouble(p2.fX);
|
| - double p2y = SkScalarToDouble(p2.fY);
|
| -
|
| - cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
|
| - (p1y - p0y) * (p2x - p0x));
|
| -
|
| - }
|
| - return cross;
|
| -}
|
| -
|
| -// Returns the first pt with the maximum Y coordinate
|
| -static int find_max_y(const SkPoint pts[], int count) {
|
| - SkASSERT(count > 0);
|
| - SkScalar max = pts[0].fY;
|
| - int firstIndex = 0;
|
| - for (int i = 1; i < count; ++i) {
|
| - SkScalar y = pts[i].fY;
|
| - if (y > max) {
|
| - max = y;
|
| - firstIndex = i;
|
| - }
|
| - }
|
| - return firstIndex;
|
| -}
|
| -
|
| -static int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
|
| - int i = index;
|
| - for (;;) {
|
| - i = (i + inc) % n;
|
| - if (i == index) { // we wrapped around, so abort
|
| - break;
|
| - }
|
| - if (pts[index] != pts[i]) { // found a different point, success!
|
| - break;
|
| - }
|
| - }
|
| - return i;
|
| -}
|
| -
|
| -/**
|
| - * Starting at index, and moving forward (incrementing), find the xmin and
|
| - * xmax of the contiguous points that have the same Y.
|
| - */
|
| -static int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
|
| - int* maxIndexPtr) {
|
| - const SkScalar y = pts[index].fY;
|
| - SkScalar min = pts[index].fX;
|
| - SkScalar max = min;
|
| - int minIndex = index;
|
| - int maxIndex = index;
|
| - for (int i = index + 1; i < n; ++i) {
|
| - if (pts[i].fY != y) {
|
| - break;
|
| - }
|
| - SkScalar x = pts[i].fX;
|
| - if (x < min) {
|
| - min = x;
|
| - minIndex = i;
|
| - } else if (x > max) {
|
| - max = x;
|
| - maxIndex = i;
|
| - }
|
| - }
|
| - *maxIndexPtr = maxIndex;
|
| - return minIndex;
|
| -}
|
| -
|
| -static void crossToDir(SkScalar cross, SkPath::Direction* dir) {
|
| - if (dir) {
|
| - *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
|
| - }
|
| -}
|
| -
|
| -#if 0
|
| -#include "SkString.h"
|
| -#include "../utils/SkParsePath.h"
|
| -static void dumpPath(const SkPath& path) {
|
| - SkString str;
|
| - SkParsePath::ToSVGString(path, &str);
|
| - SkDebugf("%s\n", str.c_str());
|
| -}
|
| -#endif
|
| -
|
| -namespace {
|
| -// for use with convex_dir_test
|
| -double mul(double a, double b) { return a * b; }
|
| -SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); }
|
| -double toDouble(SkScalar a) { return SkScalarToDouble(a); }
|
| -SkScalar toScalar(SkScalar a) { return a; }
|
| -
|
| -// determines the winding direction of a convex polygon with the precision
|
| -// of T. CAST_SCALAR casts an SkScalar to T.
|
| -template <typename T, T (CAST_SCALAR)(SkScalar)>
|
| -bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) {
|
| - // we find the first three points that form a non-degenerate
|
| - // triangle. If there are no such points then the path is
|
| - // degenerate. The first is always point 0. Now we find the second
|
| - // point.
|
| - int i = 0;
|
| - enum { kX = 0, kY = 1 };
|
| - T v0[2];
|
| - while (1) {
|
| - v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
|
| - v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
|
| - if (v0[kX] || v0[kY]) {
|
| - break;
|
| - }
|
| - if (++i == n - 1) {
|
| - return false;
|
| - }
|
| - }
|
| - // now find a third point that is not colinear with the first two
|
| - // points and check the orientation of the triangle (which will be
|
| - // the same as the orientation of the path).
|
| - for (++i; i < n; ++i) {
|
| - T v1[2];
|
| - v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
|
| - v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
|
| - T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]);
|
| - if (0 != cross) {
|
| - *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -}
|
| -
|
| -/*
|
| - * We loop through all contours, and keep the computed cross-product of the
|
| - * contour that contained the global y-max. If we just look at the first
|
| - * contour, we may find one that is wound the opposite way (correctly) since
|
| - * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
|
| - * that is outer most (or at least has the global y-max) before we can consider
|
| - * its cross product.
|
| - */
|
| -bool SkPath::cheapComputeDirection(Direction* dir) const {
|
| -// dumpPath(*this);
|
| - // don't want to pay the cost for computing this if it
|
| - // is unknown, so we don't call isConvex()
|
| -
|
| - if (kUnknown_Direction != fDirection) {
|
| - *dir = static_cast<Direction>(fDirection);
|
| - return true;
|
| - }
|
| - const Convexity conv = this->getConvexityOrUnknown();
|
| -
|
| - ContourIter iter(*fPathRef.get());
|
| -
|
| - // initialize with our logical y-min
|
| - SkScalar ymax = this->getBounds().fTop;
|
| - SkScalar ymaxCross = 0;
|
| -
|
| - for (; !iter.done(); iter.next()) {
|
| - int n = iter.count();
|
| - if (n < 3) {
|
| - continue;
|
| - }
|
| -
|
| - const SkPoint* pts = iter.pts();
|
| - SkScalar cross = 0;
|
| - if (kConvex_Convexity == conv) {
|
| - // We try first at scalar precision, and then again at double
|
| - // precision. This is because the vectors computed between distant
|
| - // points may lose too much precision.
|
| - if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) {
|
| - fDirection = *dir;
|
| - return true;
|
| - }
|
| - if (convex_dir_test<double, toDouble>(n, pts, dir)) {
|
| - fDirection = *dir;
|
| - return true;
|
| - } else {
|
| - return false;
|
| - }
|
| - } else {
|
| - int index = find_max_y(pts, n);
|
| - if (pts[index].fY < ymax) {
|
| - continue;
|
| - }
|
| -
|
| - // If there is more than 1 distinct point at the y-max, we take the
|
| - // x-min and x-max of them and just subtract to compute the dir.
|
| - if (pts[(index + 1) % n].fY == pts[index].fY) {
|
| - int maxIndex;
|
| - int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
|
| - if (minIndex == maxIndex) {
|
| - goto TRY_CROSSPROD;
|
| - }
|
| - SkASSERT(pts[minIndex].fY == pts[index].fY);
|
| - SkASSERT(pts[maxIndex].fY == pts[index].fY);
|
| - SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
|
| - // we just subtract the indices, and let that auto-convert to
|
| - // SkScalar, since we just want - or + to signal the direction.
|
| - cross = minIndex - maxIndex;
|
| - } else {
|
| - TRY_CROSSPROD:
|
| - // Find a next and prev index to use for the cross-product test,
|
| - // but we try to find pts that form non-zero vectors from pts[index]
|
| - //
|
| - // Its possible that we can't find two non-degenerate vectors, so
|
| - // we have to guard our search (e.g. all the pts could be in the
|
| - // same place).
|
| -
|
| - // we pass n - 1 instead of -1 so we don't foul up % operator by
|
| - // passing it a negative LH argument.
|
| - int prev = find_diff_pt(pts, index, n, n - 1);
|
| - if (prev == index) {
|
| - // completely degenerate, skip to next contour
|
| - continue;
|
| - }
|
| - int next = find_diff_pt(pts, index, n, 1);
|
| - SkASSERT(next != index);
|
| - cross = cross_prod(pts[prev], pts[index], pts[next]);
|
| - // if we get a zero and the points are horizontal, then we look at the spread in
|
| - // x-direction. We really should continue to walk away from the degeneracy until
|
| - // there is a divergence.
|
| - if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
|
| - // construct the subtract so we get the correct Direction below
|
| - cross = pts[index].fX - pts[next].fX;
|
| - }
|
| - }
|
| -
|
| - if (cross) {
|
| - // record our best guess so far
|
| - ymax = pts[index].fY;
|
| - ymaxCross = cross;
|
| - }
|
| - }
|
| - }
|
| - if (ymaxCross) {
|
| - crossToDir(ymaxCross, dir);
|
| - fDirection = *dir;
|
| - return true;
|
| - } else {
|
| - return false;
|
| - }
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
|
| SkScalar D, SkScalar t) {
|
| return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
|
|
|