reverse/restore order of verbs in path to be forward (normal)

As part of this (clean up), also removed support for serialized-paths
older than version-4, which was introduced Feb 2018.

Change-Id: I2dc74a52bb8bdd7ea0cb2d8a78b644ca783eb31f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/239102
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
Mike Reed 2019-09-05 14:14:38 -04:00 committed by Skia Commit-Bot
parent 59ed3b9880
commit 8fda88ed6d
8 changed files with 146 additions and 570 deletions

View File

@ -1685,8 +1685,7 @@ private:
size_t writeToMemoryAsRRect(void* buffer) const; size_t writeToMemoryAsRRect(void* buffer) const;
size_t readAsRRect(const void*, size_t); size_t readAsRRect(const void*, size_t);
size_t readFromMemory_LE3(const void*, size_t); size_t readFromMemory_EQ4Or5(const void*, size_t);
size_t readFromMemory_EQ4(const void*, size_t);
friend class Iter; friend class Iter;
friend class SkPathPriv; friend class SkPathPriv;

View File

@ -51,20 +51,14 @@ public:
/** /**
* Returns the array of points. * Returns the array of points.
*/ */
SkPoint* points() { return fPathRef->getPoints(); } SkPoint* writablePoints() { return fPathRef->getWritablePoints(); }
const SkPoint* points() const { return fPathRef->points(); } const SkPoint* points() const { return fPathRef->points(); }
/** /**
* Gets the ith point. Shortcut for this->points() + i * Gets the ith point. Shortcut for this->points() + i
*/ */
SkPoint* atPoint(int i) { SkPoint* atPoint(int i) { return fPathRef->getWritablePoints() + i; }
SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt); const SkPoint* atPoint(int i) const { return &fPathRef->fPoints[i]; }
return this->points() + i;
}
const SkPoint* atPoint(int i) const {
SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
return this->points() + i;
}
/** /**
* Adds the verb and allocates space for the number of points indicated by the verb. The * Adds the verb and allocates space for the number of points indicated by the verb. The
@ -238,7 +232,7 @@ public:
const SkPathRef& src, const SkPathRef& src,
const SkMatrix& matrix); const SkMatrix& matrix);
static SkPathRef* CreateFromBuffer(SkRBuffer* buffer); // static SkPathRef* CreateFromBuffer(SkRBuffer* buffer);
/** /**
* Rollsback a path ref to zero verbs and points with the assumption that the path ref will be * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
@ -248,24 +242,24 @@ public:
static void Rewind(sk_sp<SkPathRef>* pathRef); static void Rewind(sk_sp<SkPathRef>* pathRef);
~SkPathRef(); ~SkPathRef();
int countPoints() const { return fPointCnt; } int countPoints() const { return fPoints.count(); }
int countVerbs() const { return fVerbCnt; } int countVerbs() const { return fVerbs.count(); }
int countWeights() const { return fConicWeights.count(); } int countWeights() const { return fConicWeights.count(); }
/** /**
* Returns a pointer one beyond the first logical verb (last verb in memory order). * Returns a pointer one beyond the first logical verb (last verb in memory order).
*/ */
const uint8_t* verbs() const { return fVerbs; } const uint8_t* verbsBegin() const { return fVerbs.begin(); }
/** /**
* Returns a const pointer to the first verb in memory (which is the last logical verb). * Returns a const pointer to the first verb in memory (which is the last logical verb).
*/ */
const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; } const uint8_t* verbsEnd() const { return fVerbs.end(); }
/** /**
* Returns a const pointer to the first point. * Returns a const pointer to the first point.
*/ */
const SkPoint* points() const { return fPoints; } const SkPoint* points() const { return fPoints.begin(); }
/** /**
* Shortcut for this->points() + this->countPoints() * Shortcut for this->points() + this->countPoints()
@ -278,14 +272,8 @@ public:
/** /**
* Convenience methods for getting to a verb or point by index. * Convenience methods for getting to a verb or point by index.
*/ */
uint8_t atVerb(int index) const { uint8_t atVerb(int index) const { return fVerbs[index]; }
SkASSERT((unsigned) index < (unsigned) fVerbCnt); const SkPoint& atPoint(int index) const { return fPoints[index]; }
return this->verbs()[~index];
}
const SkPoint& atPoint(int index) const {
SkASSERT((unsigned) index < (unsigned) fPointCnt);
return this->points()[index];
}
bool operator== (const SkPathRef& ref) const; bool operator== (const SkPathRef& ref) const;
@ -345,11 +333,6 @@ private:
SkPathRef() { SkPathRef() {
fBoundsIsDirty = true; // this also invalidates fIsFinite fBoundsIsDirty = true; // this also invalidates fIsFinite
fPointCnt = 0;
fVerbCnt = 0;
fVerbs = nullptr;
fPoints = nullptr;
fFreeSpace = 0;
fGenerationID = kEmptyGenID; fGenerationID = kEmptyGenID;
fSegmentMask = 0; fSegmentMask = 0;
fIsOval = false; fIsOval = false;
@ -392,8 +375,8 @@ private:
/** Makes additional room but does not change the counts or change the genID */ /** Makes additional room but does not change the counts or change the genID */
void incReserve(int additionalVerbs, int additionalPoints) { void incReserve(int additionalVerbs, int additionalPoints) {
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint); fPoints.setReserve(fPoints.count() + additionalPoints);
this->makeSpace(space); fVerbs.setReserve(fVerbs.count() + additionalVerbs);
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
} }
@ -410,28 +393,10 @@ private:
fIsOval = false; fIsOval = false;
fIsRRect = false; fIsRRect = false;
size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; fPoints.setReserve(pointCount + reservePoints);
size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints; fPoints.setCount(pointCount);
size_t minSize = newSize + newReserve; fVerbs.setReserve(verbCount + reserveVerbs);
fVerbs.setCount(verbCount);
ptrdiff_t sizeDelta = this->currSize() - minSize;
if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) {
sk_free(fPoints);
fPoints = nullptr;
fVerbs = nullptr;
fFreeSpace = 0;
fVerbCnt = 0;
fPointCnt = 0;
this->makeSpace(minSize, true);
fVerbCnt = verbCount;
fPointCnt = pointCount;
fFreeSpace -= newSize;
} else {
fPointCnt = pointCount;
fVerbCnt = verbCount;
fFreeSpace = this->currSize() - minSize;
}
fConicWeights.setCount(conicCount); fConicWeights.setCount(conicCount);
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
} }
@ -451,63 +416,10 @@ private:
*/ */
SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
/**
* Ensures that the free space available in the path ref is >= size. The verb and point counts
* are not changed. May allocate extra capacity, unless |exact| is true.
*/
void makeSpace(size_t size, bool exact = false) {
SkDEBUGCODE(this->validate();)
if (size <= fFreeSpace) {
return;
}
size_t growSize = size - fFreeSpace;
size_t oldSize = this->currSize();
if (!exact) {
// round to next multiple of 8 bytes
growSize = (growSize + 7) & ~static_cast<size_t>(7);
// we always at least double the allocation
if (growSize < oldSize) {
growSize = oldSize;
}
if (growSize < kMinSize) {
growSize = kMinSize;
}
}
constexpr size_t maxSize = std::numeric_limits<size_t>::max();
size_t newSize;
if (growSize <= maxSize - oldSize) {
newSize = oldSize + growSize;
} else {
SK_ABORT("Path too big.");
}
// Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
// encapsulate this.
fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
void* newVerbsDst = SkTAddOffset<void>(fPoints, newSize - oldVerbSize);
void* oldVerbsSrc = SkTAddOffset<void>(fPoints, oldSize - oldVerbSize);
memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
fVerbs = SkTAddOffset<uint8_t>(fPoints, newSize);
fFreeSpace += growSize;
SkDEBUGCODE(this->validate();)
}
/** /**
* Private, non-const-ptr version of the public function verbsMemBegin(). * Private, non-const-ptr version of the public function verbsMemBegin().
*/ */
uint8_t* verbsMemWritable() { uint8_t* verbsBeginWritable() { return fVerbs.begin(); }
SkDEBUGCODE(this->validate();)
return fVerbs - fVerbCnt;
}
/**
* Gets the total amount of space allocated for verbs, points, and reserve.
*/
size_t currSize() const {
return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints);
}
/** /**
* Called the first time someone calls CreateEmpty to actually create the singleton. * Called the first time someone calls CreateEmpty to actually create the singleton.
@ -527,16 +439,16 @@ private:
} }
// called only by the editor. Note that this is not a const function. // called only by the editor. Note that this is not a const function.
SkPoint* getPoints() { SkPoint* getWritablePoints() {
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
fIsOval = false; fIsOval = false;
fIsRRect = false; fIsRRect = false;
return fPoints; return fPoints.begin();
} }
const SkPoint* getPoints() const { const SkPoint* getPoints() const {
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
return fPoints; return fPoints.begin();
} }
void callGenIDChangeListeners(); void callGenIDChangeListeners();
@ -547,11 +459,8 @@ private:
mutable SkRect fBounds; mutable SkRect fBounds;
SkPoint* fPoints; // points to begining of the allocation SkTDArray<SkPoint> fPoints;
uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards) SkTDArray<uint8_t> fVerbs;
int fVerbCnt;
int fPointCnt;
size_t fFreeSpace; // redundant but saves computation
SkTDArray<SkScalar> fConicWeights; SkTDArray<SkScalar> fConicWeights;
enum { enum {

View File

@ -325,8 +325,8 @@ public:
private: private:
T* fArray; T* fArray;
int fReserve; int fReserve; // size of the allocation in fArray (#elements)
int fCount; int fCount; // logical number of elements (fCount <= fReserve)
/** /**
* Adjusts the number of elements in the array. * Adjusts the number of elements in the array.

View File

@ -235,20 +235,10 @@ void SkPath::swap(SkPath& that) {
} }
bool SkPath::isInterpolatable(const SkPath& compare) const { bool SkPath::isInterpolatable(const SkPath& compare) const {
int count = fPathRef->countVerbs(); // need the same structure (verbs, conicweights) and same point-count
if (count != compare.fPathRef->countVerbs()) { return fPathRef->fPoints.count() == compare.fPathRef->fPoints.count() &&
return false; fPathRef->fVerbs == compare.fPathRef->fVerbs &&
} fPathRef->fConicWeights == compare.fPathRef->fConicWeights;
if (!count) {
return true;
}
if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
count)) {
return false;
}
return !fPathRef->countWeights() ||
!SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
} }
bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const { bool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
@ -690,21 +680,15 @@ int SkPath::countVerbs() const {
return fPathRef->countVerbs(); return fPathRef->countVerbs();
} }
static inline void copy_verbs_reverse(uint8_t* inorderDst,
const uint8_t* reversedSrc,
int count) {
for (int i = 0; i < count; ++i) {
inorderDst[i] = reversedSrc[~i];
}
}
int SkPath::getVerbs(uint8_t dst[], int max) const { int SkPath::getVerbs(uint8_t dst[], int max) const {
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
SkASSERT(max >= 0); SkASSERT(max >= 0);
SkASSERT(!max || dst); SkASSERT(!max || dst);
int count = SkMin32(max, fPathRef->countVerbs()); int count = SkMin32(max, fPathRef->countVerbs());
copy_verbs_reverse(dst, fPathRef->verbs(), count); if (count) {
memcpy(dst, fPathRef->verbsBegin(), count);
}
return fPathRef->countVerbs(); return fPathRef->countVerbs();
} }
@ -1256,7 +1240,7 @@ SkPath& SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startInde
bool SkPath::hasOnlyMoveTos() const { bool SkPath::hasOnlyMoveTos() const {
int count = fPathRef->countVerbs(); int count = fPathRef->countVerbs();
const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin(); const uint8_t* verbs = fPathRef->verbsBegin();
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
if (*verbs == kLine_Verb || if (*verbs == kLine_Verb ||
*verbs == kQuad_Verb || *verbs == kQuad_Verb ||
@ -1702,17 +1686,18 @@ static int pts_in_verb(unsigned verb) {
// ignore the last point of the 1st contour // ignore the last point of the 1st contour
SkPath& SkPath::reversePathTo(const SkPath& path) { SkPath& SkPath::reversePathTo(const SkPath& path) {
const uint8_t* verbs = path.fPathRef->verbsMemBegin(); // points at the last verb if (path.fPathRef->fVerbs.count() == 0) {
if (!verbs) { // empty path returns nullptr
return *this; return *this;
} }
const uint8_t* verbsEnd = path.fPathRef->verbs() - 1; // points just past the first verb
SkASSERT(verbsEnd[0] == kMove_Verb); const uint8_t* verbs = path.fPathRef->verbsEnd();
const uint8_t* verbsBegin = path.fPathRef->verbsBegin();
SkASSERT(verbsBegin[0] == kMove_Verb);
const SkPoint* pts = path.fPathRef->pointsEnd() - 1; const SkPoint* pts = path.fPathRef->pointsEnd() - 1;
const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd(); const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
while (verbs < verbsEnd) { while (verbs > verbsBegin) {
uint8_t v = *verbs++; uint8_t v = *--verbs;
pts -= pts_in_verb(v); pts -= pts_in_verb(v);
switch (v) { switch (v) {
case kMove_Verb: case kMove_Verb:
@ -1731,7 +1716,6 @@ SkPath& SkPath::reversePathTo(const SkPath& path) {
this->cubicTo(pts[2], pts[1], pts[0]); this->cubicTo(pts[2], pts[1], pts[0]);
break; break;
case kClose_Verb: case kClose_Verb:
SkASSERT(verbs - path.fPathRef->verbsMemBegin() == 1);
break; break;
default: default:
SkDEBUGFAIL("bad verb"); SkDEBUGFAIL("bad verb");
@ -1751,16 +1735,15 @@ SkPath& SkPath::reverseAddPath(const SkPath& srcPath) {
SkPathRef::Editor ed(&fPathRef, src->countVerbs(), src->countPoints()); SkPathRef::Editor ed(&fPathRef, src->countVerbs(), src->countPoints());
const uint8_t* verbsBegin = src->fPathRef->verbsBegin();
const uint8_t* verbs = src->fPathRef->verbsEnd();
const SkPoint* pts = src->fPathRef->pointsEnd(); const SkPoint* pts = src->fPathRef->pointsEnd();
// we will iterate through src's verbs backwards
const uint8_t* verbs = src->fPathRef->verbsMemBegin(); // points at the last verb
const uint8_t* verbsEnd = src->fPathRef->verbs(); // points just past the first verb
const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd(); const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd();
bool needMove = true; bool needMove = true;
bool needClose = false; bool needClose = false;
while (verbs < verbsEnd) { while (verbs > verbsBegin) {
uint8_t v = *(verbs++); uint8_t v = *--verbs;
int n = pts_in_verb(v); int n = pts_in_verb(v);
if (needMove) { if (needMove) {
@ -1874,7 +1857,7 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
dst->swap(tmp); dst->swap(tmp);
SkPathRef::Editor ed(&dst->fPathRef); SkPathRef::Editor ed(&dst->fPathRef);
matrix.mapPoints(ed.points(), ed.pathRef()->countPoints()); matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints());
dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection); dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
} else { } else {
Convexity convexity = this->getConvexityOrUnknown(); Convexity convexity = this->getConvexityOrUnknown();
@ -1946,8 +1929,8 @@ SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
void SkPath::Iter::setPath(const SkPath& path, bool forceClose) { void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
fPts = path.fPathRef->points(); fPts = path.fPathRef->points();
fVerbs = path.fPathRef->verbs(); fVerbs = path.fPathRef->verbsBegin();
fVerbStop = path.fPathRef->verbsMemBegin(); fVerbStop = path.fPathRef->verbsEnd();
fConicWeights = path.fPathRef->conicWeights(); fConicWeights = path.fPathRef->conicWeights();
if (fConicWeights) { if (fConicWeights) {
fConicWeights -= 1; // begin one behind fConicWeights -= 1; // begin one behind
@ -1970,13 +1953,13 @@ bool SkPath::Iter::isClosedContour() const {
const uint8_t* verbs = fVerbs; const uint8_t* verbs = fVerbs;
const uint8_t* stop = fVerbStop; const uint8_t* stop = fVerbStop;
if (kMove_Verb == *(verbs - 1)) { if (kMove_Verb == *verbs) {
verbs -= 1; // skip the initial moveto verbs += 1; // skip the initial moveto
} }
while (verbs > stop) { while (verbs < stop) {
// verbs points one beyond the current verb, decrement first. // verbs points one beyond the current verb, decrement first.
unsigned v = *(--verbs); unsigned v = *verbs++;
if (kMove_Verb == v) { if (kMove_Verb == v) {
break; break;
} }
@ -2036,15 +2019,14 @@ SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) {
return kDone_Verb; return kDone_Verb;
} }
// fVerbs is one beyond the current verb, decrement first unsigned verb = *fVerbs++;
unsigned verb = *(--fVerbs);
const SkPoint* SK_RESTRICT srcPts = fPts; const SkPoint* SK_RESTRICT srcPts = fPts;
SkPoint* SK_RESTRICT pts = ptsParam; SkPoint* SK_RESTRICT pts = ptsParam;
switch (verb) { switch (verb) {
case kMove_Verb: case kMove_Verb:
if (fNeedClose) { if (fNeedClose) {
fVerbs++; // move back one verb fVerbs--; // move back one verb
verb = this->autoClose(pts); verb = this->autoClose(pts);
if (verb == kClose_Verb) { if (verb == kClose_Verb) {
fNeedClose = false; fNeedClose = false;
@ -2086,7 +2068,7 @@ SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) {
case kClose_Verb: case kClose_Verb:
verb = this->autoClose(pts); verb = this->autoClose(pts);
if (verb == kLine_Verb) { if (verb == kLine_Verb) {
fVerbs++; // move back one verb fVerbs--; // move back one verb
} else { } else {
fNeedClose = false; fNeedClose = false;
fSegmentState = kEmptyContour_SegmentState; fSegmentState = kEmptyContour_SegmentState;
@ -2828,10 +2810,10 @@ private:
}; };
ContourIter::ContourIter(const SkPathRef& pathRef) { ContourIter::ContourIter(const SkPathRef& pathRef) {
fStopVerbs = pathRef.verbsMemBegin(); fStopVerbs = pathRef.verbsEnd();
fDone = false; fDone = false;
fCurrPt = pathRef.points(); fCurrPt = pathRef.points();
fCurrVerb = pathRef.verbs(); fCurrVerb = pathRef.verbsBegin();
fCurrConicWeight = pathRef.conicWeights(); fCurrConicWeight = pathRef.conicWeights();
fCurrPtCount = 0; fCurrPtCount = 0;
SkDEBUGCODE(fContourCounter = 0;) SkDEBUGCODE(fContourCounter = 0;)
@ -2839,7 +2821,7 @@ ContourIter::ContourIter(const SkPathRef& pathRef) {
} }
void ContourIter::next() { void ContourIter::next() {
if (fCurrVerb <= fStopVerbs) { if (fCurrVerb >= fStopVerbs) {
fDone = true; fDone = true;
} }
if (fDone) { if (fDone) {
@ -2849,12 +2831,12 @@ void ContourIter::next() {
// skip pts of prev contour // skip pts of prev contour
fCurrPt += fCurrPtCount; fCurrPt += fCurrPtCount;
SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]); SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]);
int ptCount = 1; // moveTo int ptCount = 1; // moveTo
const uint8_t* verbs = fCurrVerb; const uint8_t* verbs = fCurrVerb;
for (--verbs; verbs > fStopVerbs; --verbs) { for (verbs++; verbs < fStopVerbs; verbs++) {
switch (verbs[~0]) { switch (*verbs) {
case SkPath::kMove_Verb: case SkPath::kMove_Verb:
goto CONTOUR_END; goto CONTOUR_END;
case SkPath::kLine_Verb: case SkPath::kLine_Verb:

View File

@ -66,9 +66,9 @@ public:
if (verbCount == 0) if (verbCount == 0)
return false; return false;
int moveCount = 0; int moveCount = 0;
auto verbs = path.fPathRef->verbs(); auto verbs = path.fPathRef->verbsBegin();
for (int i = 0; i < verbCount; i++) { for (int i = 0; i < verbCount; i++) {
switch (verbs[~i]) { // verbs are stored backwards; we use [~i] to get the i'th verb switch (verbs[i]) {
case SkPath::Verb::kMove_Verb: case SkPath::Verb::kMove_Verb:
moveCount += 1; moveCount += 1;
if (moveCount > 1) { if (moveCount > 1) {
@ -123,13 +123,13 @@ public:
public: public:
Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {} Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {}
struct Iter { struct Iter {
void operator++() { --fVerb; } // verbs are laid out backwards in memory. void operator++() { fVerb++; }
bool operator!=(const Iter& b) { return fVerb != b.fVerb; } bool operator!=(const Iter& b) { return fVerb != b.fVerb; }
SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); } SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); }
const uint8_t* fVerb; const uint8_t* fVerb;
}; };
Iter begin() { return Iter{fPathRef->verbs() - 1}; } Iter begin() { return Iter{fPathRef->verbsBegin()}; }
Iter end() { return Iter{fPathRef->verbs() - fPathRef->countVerbs() - 1}; } Iter end() { return Iter{fPathRef->verbsEnd()}; }
private: private:
Verbs(const Verbs&) = delete; Verbs(const Verbs&) = delete;
Verbs& operator=(const Verbs&) = delete; Verbs& operator=(const Verbs&) = delete;
@ -137,11 +137,10 @@ public:
}; };
/** /**
* Returns a pointer to the verb data. Note that the verbs are stored backwards in memory and * Returns a pointer to the verb data.
* thus the returned pointer is the last verb.
*/ */
static const uint8_t* VerbData(const SkPath& path) { static const uint8_t* VerbData(const SkPath& path) {
return path.fPathRef->verbsMemBegin(); return path.fPathRef->verbsBegin();
} }
/** Returns a raw pointer to the path points */ /** Returns a raw pointer to the path points */
@ -289,8 +288,8 @@ public:
// Roughly the same as SkPath::Iter(path, true), but does not return moves or closes // Roughly the same as SkPath::Iter(path, true), but does not return moves or closes
// //
class SkPathEdgeIter { class SkPathEdgeIter {
const uint8_t* fVerbsStart; const uint8_t* fVerbs;
const uint8_t* fVerbs; // reverse const uint8_t* fVerbsStop;
const SkPoint* fPts; const SkPoint* fPts;
const SkPoint* fMoveToPtr; const SkPoint* fMoveToPtr;
const SkScalar* fConicWeights; const SkScalar* fConicWeights;
@ -338,8 +337,8 @@ public:
}; };
for (;;) { for (;;) {
SkASSERT(fVerbs >= fVerbsStart); SkASSERT(fVerbs <= fVerbsStop);
if (fVerbs == fVerbsStart) { if (fVerbs == fVerbsStop) {
return fNeedsCloseLine return fNeedsCloseLine
? closeline() ? closeline()
: Result{ nullptr, Edge(kIllegalEdgeValue) }; : Result{ nullptr, Edge(kIllegalEdgeValue) };
@ -347,7 +346,7 @@ public:
SkDEBUGCODE(fIsConic = false;) SkDEBUGCODE(fIsConic = false;)
const auto v = *--fVerbs; const auto v = *fVerbs++;
switch (v) { switch (v) {
case SkPath::kMove_Verb: { case SkPath::kMove_Verb: {
if (fNeedsCloseLine) { if (fNeedsCloseLine) {

View File

@ -15,16 +15,6 @@
#include "src/core/SkPathPriv.h" #include "src/core/SkPathPriv.h"
#include "src/core/SkSafeMath.h" #include "src/core/SkSafeMath.h"
// Conic weights must be 0 < weight <= finite
static bool validate_conic_weights(const SkScalar weights[], int count) {
for (int i = 0; i < count; ++i) {
if (weights[i] <= 0 || !SkScalarIsFinite(weights[i])) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
SkPathRef::Editor::Editor(sk_sp<SkPathRef>* pathRef, SkPathRef::Editor::Editor(sk_sp<SkPathRef>* pathRef,
int incReserveVerbs, int incReserveVerbs,
@ -51,39 +41,9 @@ SkPathRef::Editor::Editor(sk_sp<SkPathRef>* pathRef,
// allocations to just fit the current needs. makeSpace() will only grow, but never shrinks. // allocations to just fit the current needs. makeSpace() will only grow, but never shrinks.
// //
void SkPath::shrinkToFit() { void SkPath::shrinkToFit() {
const size_t kMinFreeSpaceForShrink = 8; // just made up a small number fPathRef->fPoints.shrinkToFit();
fPathRef->fVerbs.shrinkToFit();
if (fPathRef->fFreeSpace <= kMinFreeSpaceForShrink) { fPathRef->fConicWeights.shrinkToFit();
return;
}
if (fPathRef->unique()) {
int pointCount = fPathRef->fPointCnt;
int verbCount = fPathRef->fVerbCnt;
size_t ptsSize = sizeof(SkPoint) * pointCount;
size_t vrbSize = sizeof(uint8_t) * verbCount;
size_t minSize = ptsSize + vrbSize;
void* newAlloc = sk_malloc_canfail(minSize);
if (!newAlloc) {
return; // couldn't allocate the smaller buffer, but that's ok
}
sk_careful_memcpy(newAlloc, fPathRef->fPoints, ptsSize);
sk_careful_memcpy((char*)newAlloc + minSize - vrbSize, fPathRef->verbsMemBegin(), vrbSize);
sk_free(fPathRef->fPoints);
fPathRef->fPoints = static_cast<SkPoint*>(newAlloc);
fPathRef->fVerbs = (uint8_t*)newAlloc + minSize;
fPathRef->fFreeSpace = 0;
fPathRef->fConicWeights.shrinkToFit();
} else {
sk_sp<SkPathRef> pr(new SkPathRef);
pr->copy(*fPathRef, 0, 0);
fPathRef = std::move(pr);
}
SkDEBUGCODE(fPathRef->validate();) SkDEBUGCODE(fPathRef->validate();)
} }
@ -94,13 +54,6 @@ SkPathRef::~SkPathRef() {
// to read one that's not valid and then free its memory without asserting. // to read one that's not valid and then free its memory without asserting.
this->callGenIDChangeListeners(); this->callGenIDChangeListeners();
SkASSERT(fGenIDChangeListeners.empty()); // These are raw ptrs. SkASSERT(fGenIDChangeListeners.empty()); // These are raw ptrs.
sk_free(fPoints);
SkDEBUGCODE(fPoints = nullptr;)
SkDEBUGCODE(fVerbs = nullptr;)
SkDEBUGCODE(fVerbCnt = 0x9999999;)
SkDEBUGCODE(fPointCnt = 0xAAAAAAA;)
SkDEBUGCODE(fPointCnt = 0xBBBBBBB;)
SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;) SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;)
SkDEBUGCODE(fEditorsAttached.store(0x7777777);) SkDEBUGCODE(fEditorsAttached.store(0x7777777);)
} }
@ -189,20 +142,17 @@ void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst,
} }
if (dst->get() != &src) { if (dst->get() != &src) {
(*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count()); (*dst)->fPoints = src.fPoints;
sk_careful_memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), (*dst)->fVerbs = src.fVerbs;
src.fVerbCnt * sizeof(uint8_t));
(*dst)->fConicWeights = src.fConicWeights; (*dst)->fConicWeights = src.fConicWeights;
(*dst)->callGenIDChangeListeners();
(*dst)->fGenerationID = 0; // mark as dirty
} }
SkASSERT((*dst)->countPoints() == src.countPoints());
SkASSERT((*dst)->countVerbs() == src.countVerbs());
SkASSERT((*dst)->fConicWeights.count() == src.fConicWeights.count());
// Need to check this here in case (&src == dst) // Need to check this here in case (&src == dst)
bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1; bool canXformBounds = !src.fBoundsIsDirty && matrix.rectStaysRect() && src.countPoints() > 1;
matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt); matrix.mapPoints((*dst)->fPoints.begin(), src.fPoints.begin(), src.fPoints.count());
/* /*
* Here we optimize the bounds computation, by noting if the bounds are * Here we optimize the bounds computation, by noting if the bounds are
@ -251,163 +201,14 @@ void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst,
SkDEBUGCODE((*dst)->validate();) SkDEBUGCODE((*dst)->validate();)
} }
static bool validate_verb_sequence(const uint8_t verbs[], int vCount) {
// verbs are stored backwards, but we need to visit them in logical order to determine if
// they form a valid sequence.
bool needsMoveTo = true;
bool invalidSequence = false;
for (int i = vCount - 1; i >= 0; --i) {
switch (verbs[i]) {
case SkPath::kMove_Verb:
needsMoveTo = false;
break;
case SkPath::kLine_Verb:
case SkPath::kQuad_Verb:
case SkPath::kConic_Verb:
case SkPath::kCubic_Verb:
invalidSequence |= needsMoveTo;
break;
case SkPath::kClose_Verb:
needsMoveTo = true;
break;
default:
return false; // unknown verb
}
}
return !invalidSequence;
}
// Given the verb array, deduce the required number of pts and conics,
// or if an invalid verb is encountered, return false.
static bool deduce_pts_conics(const uint8_t verbs[], int vCount, int* ptCountPtr,
int* conicCountPtr) {
// When there is at least one verb, the first is required to be kMove_Verb.
if (0 < vCount && verbs[vCount-1] != SkPath::kMove_Verb) {
return false;
}
SkSafeMath safe;
int ptCount = 0;
int conicCount = 0;
for (int i = 0; i < vCount; ++i) {
switch (verbs[i]) {
case SkPath::kMove_Verb:
case SkPath::kLine_Verb:
ptCount = safe.addInt(ptCount, 1);
break;
case SkPath::kConic_Verb:
conicCount += 1;
// fall-through
case SkPath::kQuad_Verb:
ptCount = safe.addInt(ptCount, 2);
break;
case SkPath::kCubic_Verb:
ptCount = safe.addInt(ptCount, 3);
break;
case SkPath::kClose_Verb:
break;
default:
return false;
}
}
if (!safe) {
return false;
}
*ptCountPtr = ptCount;
*conicCountPtr = conicCount;
return true;
}
SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) {
std::unique_ptr<SkPathRef> ref(new SkPathRef);
int32_t packed;
if (!buffer->readS32(&packed)) {
return nullptr;
}
ref->fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
int32_t verbCount, pointCount, conicCount;
if (!buffer->readU32(&(ref->fGenerationID)) ||
!buffer->readS32(&verbCount) || (verbCount < 0) ||
!buffer->readS32(&pointCount) || (pointCount < 0) ||
!buffer->readS32(&conicCount) || (conicCount < 0))
{
return nullptr;
}
uint64_t pointSize64 = sk_64_mul(pointCount, sizeof(SkPoint));
uint64_t conicSize64 = sk_64_mul(conicCount, sizeof(SkScalar));
if (!SkTFitsIn<size_t>(pointSize64) || !SkTFitsIn<size_t>(conicSize64)) {
return nullptr;
}
size_t verbSize = verbCount * sizeof(uint8_t);
size_t pointSize = SkToSizeT(pointSize64);
size_t conicSize = SkToSizeT(conicSize64);
{
uint64_t requiredBufferSize = sizeof(SkRect);
requiredBufferSize += verbSize;
requiredBufferSize += pointSize;
requiredBufferSize += conicSize;
if (buffer->available() < requiredBufferSize) {
return nullptr;
}
}
ref->resetToSize(verbCount, pointCount, conicCount);
SkASSERT(verbCount == ref->countVerbs());
SkASSERT(pointCount == ref->countPoints());
SkASSERT(conicCount == ref->fConicWeights.count());
if (!buffer->read(ref->verbsMemWritable(), verbSize) ||
!buffer->read(ref->fPoints, pointSize) ||
!buffer->read(ref->fConicWeights.begin(), conicSize) ||
!buffer->read(&ref->fBounds, sizeof(SkRect))) {
return nullptr;
}
// Check that the verbs are valid, and imply the correct number of pts and conics
{
int pCount, cCount;
if (!validate_verb_sequence(ref->verbsMemBegin(), ref->countVerbs())) {
return nullptr;
}
if (!deduce_pts_conics(ref->verbsMemBegin(), ref->countVerbs(), &pCount, &cCount) ||
pCount != ref->countPoints() || cCount != ref->fConicWeights.count()) {
return nullptr;
}
if (!validate_conic_weights(ref->fConicWeights.begin(), ref->fConicWeights.count())) {
return nullptr;
}
// Check that the bounds match the serialized bounds.
SkRect bounds;
if (ComputePtBounds(&bounds, *ref) != SkToBool(ref->fIsFinite) || bounds != ref->fBounds) {
return nullptr;
}
// call this after validate_verb_sequence, since it relies on valid verbs
ref->fSegmentMask = ref->computeSegmentMask();
}
ref->fBoundsIsDirty = false;
return ref.release();
}
void SkPathRef::Rewind(sk_sp<SkPathRef>* pathRef) { void SkPathRef::Rewind(sk_sp<SkPathRef>* pathRef) {
if ((*pathRef)->unique()) { if ((*pathRef)->unique()) {
SkDEBUGCODE((*pathRef)->validate();) SkDEBUGCODE((*pathRef)->validate();)
(*pathRef)->callGenIDChangeListeners(); (*pathRef)->callGenIDChangeListeners();
(*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite (*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite
(*pathRef)->fVerbCnt = 0;
(*pathRef)->fPointCnt = 0;
(*pathRef)->fFreeSpace = (*pathRef)->currSize();
(*pathRef)->fGenerationID = 0; (*pathRef)->fGenerationID = 0;
(*pathRef)->fPoints.rewind();
(*pathRef)->fVerbs.rewind();
(*pathRef)->fConicWeights.rewind(); (*pathRef)->fConicWeights.rewind();
(*pathRef)->fSegmentMask = 0; (*pathRef)->fSegmentMask = 0;
(*pathRef)->fIsOval = false; (*pathRef)->fIsOval = false;
@ -438,32 +239,12 @@ bool SkPathRef::operator== (const SkPathRef& ref) const {
return true; return true;
} }
#endif #endif
if (fPointCnt != ref.fPointCnt || if (fPoints != ref.fPoints || fConicWeights != ref.fConicWeights || fVerbs != ref.fVerbs) {
fVerbCnt != ref.fVerbCnt) {
SkASSERT(!genIDMatch); SkASSERT(!genIDMatch);
return false; return false;
} }
if (0 == ref.fVerbCnt) { if (ref.fVerbs.count() == 0) {
SkASSERT(0 == ref.fPointCnt); SkASSERT(ref.fPoints.count() == 0);
return true;
}
SkASSERT(this->verbsMemBegin() && ref.verbsMemBegin());
if (0 != memcmp(this->verbsMemBegin(),
ref.verbsMemBegin(),
ref.fVerbCnt * sizeof(uint8_t))) {
SkASSERT(!genIDMatch);
return false;
}
SkASSERT(this->points() && ref.points());
if (0 != memcmp(this->points(),
ref.points(),
ref.fPointCnt * sizeof(SkPoint))) {
SkASSERT(!genIDMatch);
return false;
}
if (fConicWeights != ref.fConicWeights) {
SkASSERT(!genIDMatch);
return false;
} }
return true; return true;
} }
@ -485,11 +266,11 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
// TODO: write gen ID here. Problem: We don't know if we're cross process or not from // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
// SkWBuffer. Until this is fixed we write 0. // SkWBuffer. Until this is fixed we write 0.
buffer->write32(0); buffer->write32(0);
buffer->write32(fVerbCnt); buffer->write32(fVerbs.count());
buffer->write32(fPointCnt); buffer->write32(fPoints.count());
buffer->write32(fConicWeights.count()); buffer->write32(fConicWeights.count());
buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t)); buffer->write(fVerbs.begin(), fVerbs.bytes());
buffer->write(fPoints, fPointCnt * sizeof(SkPoint)); buffer->write(fPoints.begin(), fVerbs.bytes());
buffer->write(fConicWeights.begin(), fConicWeights.bytes()); buffer->write(fConicWeights.begin(), fConicWeights.bytes());
buffer->write(&bounds, sizeof(bounds)); buffer->write(&bounds, sizeof(bounds));
@ -498,9 +279,7 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
uint32_t SkPathRef::writeSize() const { uint32_t SkPathRef::writeSize() const {
return uint32_t(5 * sizeof(uint32_t) + return uint32_t(5 * sizeof(uint32_t) +
fVerbCnt * sizeof(uint8_t) + fVerbs.bytes() + fPoints.bytes() + fConicWeights.bytes() +
fPointCnt * sizeof(SkPoint) +
fConicWeights.bytes() +
sizeof(SkRect)); sizeof(SkRect));
} }
@ -508,10 +287,10 @@ void SkPathRef::copy(const SkPathRef& ref,
int additionalReserveVerbs, int additionalReserveVerbs,
int additionalReservePoints) { int additionalReservePoints) {
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(), this->resetToSize(ref.fVerbs.count(), ref.fPoints.count(), ref.fConicWeights.count(),
additionalReserveVerbs, additionalReservePoints); additionalReserveVerbs, additionalReservePoints);
sk_careful_memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt*sizeof(uint8_t)); fVerbs = ref.fVerbs;
sk_careful_memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint)); fPoints = ref.fPoints;
fConicWeights = ref.fConicWeights; fConicWeights = ref.fConicWeights;
fBoundsIsDirty = ref.fBoundsIsDirty; fBoundsIsDirty = ref.fBoundsIsDirty;
if (!fBoundsIsDirty) { if (!fBoundsIsDirty) {
@ -527,9 +306,9 @@ void SkPathRef::copy(const SkPathRef& ref,
} }
unsigned SkPathRef::computeSegmentMask() const { unsigned SkPathRef::computeSegmentMask() const {
const uint8_t* verbs = this->verbsMemBegin(); const uint8_t* verbs = fVerbs.begin();
unsigned mask = 0; unsigned mask = 0;
for (int i = this->countVerbs() - 1; i >= 0; --i) { for (int i = 0; i < fVerbs.count(); ++i) {
switch (verbs[i]) { switch (verbs[i]) {
case SkPath::kLine_Verb: mask |= SkPath::kLine_SegmentMask; break; case SkPath::kLine_Verb: mask |= SkPath::kLine_SegmentMask; break;
case SkPath::kQuad_Verb: mask |= SkPath::kQuad_SegmentMask; break; case SkPath::kQuad_Verb: mask |= SkPath::kQuad_SegmentMask; break;
@ -543,7 +322,7 @@ unsigned SkPathRef::computeSegmentMask() const {
void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const { void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const {
const SkScalar* inValues = &ending.getPoints()->fX; const SkScalar* inValues = &ending.getPoints()->fX;
SkScalar* outValues = &out->getPoints()->fX; SkScalar* outValues = &out->getWritablePoints()->fX;
int count = out->countPoints() * 2; int count = out->countPoints() * 2;
for (int index = 0; index < count; ++index) { for (int index = 0; index < count; ++index) {
outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight); outValues[index] = outValues[index] * weight + inValues[index] * (1 - weight);
@ -556,11 +335,6 @@ void SkPathRef::interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef*
SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb, SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
int numVbs, int numVbs,
SkScalar** weights) { SkScalar** weights) {
// 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 will appear to have been a fluke...
static const unsigned int kMIN_COUNT_FOR_MEMSET_TO_BE_FAST = 16;
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
int pCnt; int pCnt;
switch (verb) { switch (verb) {
@ -595,40 +369,19 @@ SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
pCnt = 0; pCnt = 0;
} }
size_t space = numVbs * sizeof(uint8_t) + pCnt * sizeof (SkPoint);
this->makeSpace(space);
SkPoint* ret = fPoints + fPointCnt;
uint8_t* vb = fVerbs - fVerbCnt;
// cast to unsigned, so if kMIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
// be 0, the compiler will remove the test/branch entirely.
if ((unsigned)numVbs >= kMIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
memset(vb - numVbs, verb, numVbs);
} else {
for (int i = 0; i < numVbs; ++i) {
vb[~i] = verb;
}
}
SkSafeMath safe;
fVerbCnt = safe.addInt(fVerbCnt, numVbs);
fPointCnt = safe.addInt(fPointCnt, pCnt);
if (!safe) {
SK_ABORT("cannot grow path");
}
fFreeSpace -= space;
fBoundsIsDirty = true; // this also invalidates fIsFinite fBoundsIsDirty = true; // this also invalidates fIsFinite
fIsOval = false; fIsOval = false;
fIsRRect = false; fIsRRect = false;
memset(fVerbs.append(numVbs), verb, numVbs);
if (SkPath::kConic_Verb == verb) { if (SkPath::kConic_Verb == verb) {
SkASSERT(weights); SkASSERT(weights);
*weights = fConicWeights.append(numVbs); *weights = fConicWeights.append(numVbs);
} }
SkPoint* pts = fPoints.append(pCnt);
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
return ret; return pts;
} }
SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) { SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
@ -665,30 +418,20 @@ SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
SkDEBUGFAIL("default is not reached"); SkDEBUGFAIL("default is not reached");
pCnt = 0; pCnt = 0;
} }
SkSafeMath safe;
int newPointCnt = safe.addInt(fPointCnt, pCnt);
int newVerbCnt = safe.addInt(fVerbCnt, 1);
if (!safe) {
SK_ABORT("cannot grow path");
}
size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
this->makeSpace(space);
this->fVerbs[~fVerbCnt] = verb;
SkPoint* ret = fPoints + fPointCnt;
fVerbCnt = newVerbCnt;
fPointCnt = newPointCnt;
fSegmentMask |= mask; fSegmentMask |= mask;
fFreeSpace -= space;
fBoundsIsDirty = true; // this also invalidates fIsFinite fBoundsIsDirty = true; // this also invalidates fIsFinite
fIsOval = false; fIsOval = false;
fIsRRect = false; fIsRRect = false;
*fVerbs.append() = verb;
if (SkPath::kConic_Verb == verb) { if (SkPath::kConic_Verb == verb) {
*fConicWeights.append() = weight; *fConicWeights.append() = weight;
} }
SkPoint* pts = fPoints.append(pCnt);
SkDEBUGCODE(this->validate();) SkDEBUGCODE(this->validate();)
return ret; return pts;
} }
uint32_t SkPathRef::genID() const { uint32_t SkPathRef::genID() const {
@ -696,7 +439,7 @@ uint32_t SkPathRef::genID() const {
static const uint32_t kMask = (static_cast<int64_t>(1) << SkPathPriv::kPathRefGenIDBitCnt) - 1; static const uint32_t kMask = (static_cast<int64_t>(1) << SkPathPriv::kPathRefGenIDBitCnt) - 1;
if (fGenerationID == 0) { if (fGenerationID == 0) {
if (fPointCnt == 0 && fVerbCnt == 0) { if (fPoints.count() == 0 && fVerbs.count() == 0) {
fGenerationID = kEmptyGenID; fGenerationID = kEmptyGenID;
} else { } else {
static std::atomic<uint32_t> nextID{kEmptyGenID + 1}; static std::atomic<uint32_t> nextID{kEmptyGenID + 1};
@ -800,11 +543,11 @@ SkPathRef::Iter::Iter(const SkPathRef& path) {
void SkPathRef::Iter::setPathRef(const SkPathRef& path) { void SkPathRef::Iter::setPathRef(const SkPathRef& path) {
fPts = path.points(); fPts = path.points();
fVerbs = path.verbs(); fVerbs = path.verbsBegin();
fVerbStop = path.verbsMemBegin(); fVerbStop = path.verbsEnd();
fConicWeights = path.conicWeights(); fConicWeights = path.conicWeights();
if (fConicWeights) { if (fConicWeights) {
fConicWeights -= 1; // begin one behind fConicWeights -= 1; // begin one behind
} }
// Don't allow iteration through non-finite points. // Don't allow iteration through non-finite points.
@ -824,7 +567,7 @@ uint8_t SkPathRef::Iter::next(SkPoint pts[4]) {
} }
// fVerbs points one beyond next verb so decrement first. // fVerbs points one beyond next verb so decrement first.
unsigned verb = *(--fVerbs); unsigned verb = *fVerbs++;
const SkPoint* srcPts = fPts; const SkPoint* srcPts = fPts;
switch (verb) { switch (verb) {
@ -865,35 +608,11 @@ uint8_t SkPathRef::Iter::next(SkPoint pts[4]) {
} }
uint8_t SkPathRef::Iter::peek() const { uint8_t SkPathRef::Iter::peek() const {
const uint8_t* next = fVerbs; return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb;
return next <= fVerbStop ? (uint8_t) SkPath::kDone_Verb : next[-1];
} }
bool SkPathRef::isValid() const { bool SkPathRef::isValid() const {
if (static_cast<ptrdiff_t>(fFreeSpace) < 0) {
return false;
}
if (reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) < 0) {
return false;
}
if ((nullptr == fPoints) != (nullptr == fVerbs)) {
return false;
}
if (nullptr == fPoints && 0 != fFreeSpace) {
return false;
}
if (nullptr == fPoints && fPointCnt) {
return false;
}
if (nullptr == fVerbs && fVerbCnt) {
return false;
}
if (this->currSize() !=
fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt) {
return false;
}
if (fIsOval || fIsRRect) { if (fIsOval || fIsRRect) {
// Currently we don't allow both of these to be set, even though ovals are ro // Currently we don't allow both of these to be set, even though ovals are ro
if (fIsOval == fIsRRect) { if (fIsOval == fIsRRect) {
@ -914,14 +633,14 @@ bool SkPathRef::isValid() const {
bool isFinite = true; bool isFinite = true;
Sk2s leftTop = Sk2s(fBounds.fLeft, fBounds.fTop); Sk2s leftTop = Sk2s(fBounds.fLeft, fBounds.fTop);
Sk2s rightBot = Sk2s(fBounds.fRight, fBounds.fBottom); Sk2s rightBot = Sk2s(fBounds.fRight, fBounds.fBottom);
for (int i = 0; i < fPointCnt; ++i) { for (int i = 0; i < fPoints.count(); ++i) {
Sk2s point = Sk2s(fPoints[i].fX, fPoints[i].fY); Sk2s point = Sk2s(fPoints[i].fX, fPoints[i].fY);
#ifdef SK_DEBUG #ifdef SK_DEBUG
if (fPoints[i].isFinite() && if (fPoints[i].isFinite() &&
((point < leftTop).anyTrue() || (point > rightBot).anyTrue())) { ((point < leftTop).anyTrue() || (point > rightBot).anyTrue())) {
SkDebugf("bad SkPathRef bounds: %g %g %g %g\n", SkDebugf("bad SkPathRef bounds: %g %g %g %g\n",
fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom); fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
for (int j = 0; j < fPointCnt; ++j) { for (int j = 0; j < fPoints.count(); ++j) {
if (i == j) { if (i == j) {
SkDebugf("*** bounds do not contain: "); SkDebugf("*** bounds do not contain: ");
} }
@ -948,8 +667,8 @@ bool SkPathRef::isValid() const {
SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) { SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) {
fMoveToPtr = fPts = path.fPathRef->points(); fMoveToPtr = fPts = path.fPathRef->points();
fVerbs = path.fPathRef->verbs(); fVerbs = path.fPathRef->verbsBegin();
fVerbsStart = path.fPathRef->verbsMemBegin(); fVerbsStop = path.fPathRef->verbsEnd();
fConicWeights = path.fPathRef->conicWeights(); fConicWeights = path.fPathRef->conicWeights();
if (fConicWeights) { if (fConicWeights) {
fConicWeights -= 1; // begin one behind fConicWeights -= 1; // begin one behind

View File

@ -26,11 +26,13 @@ enum SerializationOffsets {
enum SerializationVersions { enum SerializationVersions {
// kPathPrivFirstDirection_Version = 1, // kPathPrivFirstDirection_Version = 1,
kPathPrivLastMoveToIndex_Version = 2, // kPathPrivLastMoveToIndex_Version = 2,
kPathPrivTypeEnumVersion = 3, // kPathPrivTypeEnumVersion = 3,
kJustPublicData_Version = 4, // introduced Feb/2018 kJustPublicData_Version = 4, // introduced Feb/2018
kVerbsAreStoredForward_Version = 5, // introduced Sept/2019
kCurrent_Version = kJustPublicData_Version kMin_Version = kJustPublicData_Version,
kCurrent_Version = kVerbsAreStoredForward_Version
}; };
enum SerializationType { enum SerializationType {
@ -121,7 +123,7 @@ size_t SkPath::writeToMemory(void* storage) const {
buffer.write32(vbs); buffer.write32(vbs);
buffer.write(fPathRef->points(), pts * sizeof(SkPoint)); buffer.write(fPathRef->points(), pts * sizeof(SkPoint));
buffer.write(fPathRef->conicWeights(), cnx * sizeof(SkScalar)); buffer.write(fPathRef->conicWeights(), cnx * sizeof(SkScalar));
buffer.write(fPathRef->verbsMemBegin(), vbs * sizeof(uint8_t)); buffer.write(fPathRef->verbsBegin(), vbs * sizeof(uint8_t));
buffer.padToAlign4(); buffer.padToAlign4();
SkASSERT(buffer.pos() == size); SkASSERT(buffer.pos() == size);
@ -145,11 +147,12 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) {
return 0; return 0;
} }
unsigned version = extract_version(packed); unsigned version = extract_version(packed);
if (version <= kPathPrivTypeEnumVersion) { if (version < kMin_Version || version > kCurrent_Version) {
return this->readFromMemory_LE3(storage, length); return 0;
} }
if (version == kJustPublicData_Version) {
return this->readFromMemory_EQ4(storage, length); if (version == kJustPublicData_Version || version == kVerbsAreStoredForward_Version) {
return this->readFromMemory_EQ4Or5(storage, length);
} }
return 0; return 0;
} }
@ -192,14 +195,17 @@ size_t SkPath::readAsRRect(const void* storage, size_t length) {
return buffer.pos(); return buffer.pos();
} }
size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) { size_t SkPath::readFromMemory_EQ4Or5(const void* storage, size_t length) {
SkRBuffer buffer(storage, length); SkRBuffer buffer(storage, length);
uint32_t packed; uint32_t packed;
if (!buffer.readU32(&packed)) { if (!buffer.readU32(&packed)) {
return 0; return 0;
} }
SkASSERT(extract_version(packed) == 4); bool verbsAreReversed = true;
if (extract_version(packed) == kVerbsAreStoredForward_Version) {
verbsAreReversed = false;
}
switch (extract_serializationtype(packed)) { switch (extract_serializationtype(packed)) {
case SerializationType::kRRect: case SerializationType::kRRect:
@ -234,11 +240,17 @@ size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
} \ } \
} while (0) } while (0)
int verbsStep = 1;
if (verbsAreReversed) {
verbs += vbs - 1;
verbsStep = -1;
}
SkPath tmp; SkPath tmp;
tmp.setFillType(extract_filltype(packed)); tmp.setFillType(extract_filltype(packed));
tmp.incReserve(pts); tmp.incReserve(pts);
for (int i = vbs - 1; i >= 0; --i) { for (int i = 0; i < vbs; ++i) {
switch (verbs[i]) { switch (*verbs) {
case kMove_Verb: case kMove_Verb:
CHECK_POINTS_CONICS(1, 0); CHECK_POINTS_CONICS(1, 0);
tmp.moveTo(*points++); tmp.moveTo(*points++);
@ -268,6 +280,7 @@ size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
default: default:
return 0; // bad verb return 0; // bad verb
} }
verbs += verbsStep;
} }
#undef CHECK_POINTS_CONICS #undef CHECK_POINTS_CONICS
if (pts || cnx) { if (pts || cnx) {
@ -277,51 +290,3 @@ size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
*this = std::move(tmp); *this = std::move(tmp);
return buffer.pos(); return buffer.pos();
} }
size_t SkPath::readFromMemory_LE3(const void* storage, size_t length) {
SkRBuffer buffer(storage, length);
int32_t packed;
if (!buffer.readS32(&packed)) {
return 0;
}
unsigned version = extract_version(packed);
SkASSERT(version <= 3);
FillType fillType = extract_filltype(packed);
if (version >= kPathPrivTypeEnumVersion) {
switch (extract_serializationtype(packed)) {
case SerializationType::kRRect:
return this->readAsRRect(storage, length);
case SerializationType::kGeneral:
// Fall through to general path deserialization
break;
default:
return 0;
}
}
if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) {
return 0;
}
// These are written into the serialized data but we no longer use them in the deserialized
// path. If convexity is corrupted it may cause the GPU backend to make incorrect
// rendering choices, possibly crashing. We set them to unknown so that they'll be recomputed if
// requested.
fConvexity = kUnknown_Convexity;
fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
fFillType = fillType;
fIsVolatile = 0;
SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
if (!pathRef) {
return 0;
}
fPathRef.reset(pathRef);
SkDEBUGCODE(this->validate();)
buffer.skipToAlign4();
return buffer.pos();
}

View File

@ -2797,12 +2797,14 @@ static void test_transform(skiatest::Reporter* reporter) {
p2.addRect({ 10, 20, 30, 40 }); p2.addRect({ 10, 20, 30, 40 });
uint32_t id1 = p1.getGenerationID(); uint32_t id1 = p1.getGenerationID();
uint32_t id2 = p2.getGenerationID(); uint32_t id2 = p2.getGenerationID();
REPORTER_ASSERT(reporter, id1 != id2);
SkMatrix matrix; SkMatrix matrix;
matrix.setScale(2, 2); matrix.setScale(2, 2);
p1.transform(matrix, &p2); p1.transform(matrix, &p2);
REPORTER_ASSERT(reporter, id1 == p1.getGenerationID());
REPORTER_ASSERT(reporter, id2 != p2.getGenerationID());
p1.transform(matrix); p1.transform(matrix);
REPORTER_ASSERT(reporter, id1 != p1.getGenerationID()); REPORTER_ASSERT(reporter, id1 != p1.getGenerationID());
REPORTER_ASSERT(reporter, id2 != p2.getGenerationID());
} }
} }
@ -4174,7 +4176,8 @@ static void test_contains(skiatest::Reporter* reporter) {
class PathRefTest_Private { class PathRefTest_Private {
public: public:
static size_t GetFreeSpace(const SkPathRef& ref) { static size_t GetFreeSpace(const SkPathRef& ref) {
return ref.fFreeSpace; return (ref.fPoints.reserved() - ref.fPoints.count()) * sizeof(SkPoint)
+ (ref.fVerbs.reserved() - ref.fVerbs.count()) * sizeof(uint8_t);
} }
static void TestPathRef(skiatest::Reporter* reporter) { static void TestPathRef(skiatest::Reporter* reporter) {