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); |