Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1140)

Unified Diff: src/core/SkPath.cpp

Issue 25787002: Move more of SkPath into SkPathRef (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: cleaned up Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « include/core/SkPathRef.h ('k') | src/core/SkPathRef.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « include/core/SkPathRef.h ('k') | src/core/SkPathRef.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698