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 readAsRRect(const void*, size_t);
size_t readFromMemory_LE3(const void*, size_t);
size_t readFromMemory_EQ4(const void*, size_t);
size_t readFromMemory_EQ4Or5(const void*, size_t);
friend class Iter;
friend class SkPathPriv;

View File

@ -51,20 +51,14 @@ public:
/**
* Returns the array of points.
*/
SkPoint* points() { return fPathRef->getPoints(); }
SkPoint* writablePoints() { return fPathRef->getWritablePoints(); }
const SkPoint* points() const { return fPathRef->points(); }
/**
* Gets the ith point. Shortcut for this->points() + i
*/
SkPoint* atPoint(int i) {
SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
return this->points() + i;
}
const SkPoint* atPoint(int i) const {
SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
return this->points() + i;
}
SkPoint* atPoint(int i) { return fPathRef->getWritablePoints() + i; }
const SkPoint* atPoint(int i) const { return &fPathRef->fPoints[i]; }
/**
* 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 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
@ -248,24 +242,24 @@ public:
static void Rewind(sk_sp<SkPathRef>* pathRef);
~SkPathRef();
int countPoints() const { return fPointCnt; }
int countVerbs() const { return fVerbCnt; }
int countPoints() const { return fPoints.count(); }
int countVerbs() const { return fVerbs.count(); }
int countWeights() const { return fConicWeights.count(); }
/**
* 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).
*/
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.
*/
const SkPoint* points() const { return fPoints; }
const SkPoint* points() const { return fPoints.begin(); }
/**
* Shortcut for this->points() + this->countPoints()
@ -278,14 +272,8 @@ public:
/**
* Convenience methods for getting to a verb or point by index.
*/
uint8_t atVerb(int index) const {
SkASSERT((unsigned) index < (unsigned) fVerbCnt);
return this->verbs()[~index];
}
const SkPoint& atPoint(int index) const {
SkASSERT((unsigned) index < (unsigned) fPointCnt);
return this->points()[index];
}
uint8_t atVerb(int index) const { return fVerbs[index]; }
const SkPoint& atPoint(int index) const { return fPoints[index]; }
bool operator== (const SkPathRef& ref) const;
@ -345,11 +333,6 @@ private:
SkPathRef() {
fBoundsIsDirty = true; // this also invalidates fIsFinite
fPointCnt = 0;
fVerbCnt = 0;
fVerbs = nullptr;
fPoints = nullptr;
fFreeSpace = 0;
fGenerationID = kEmptyGenID;
fSegmentMask = 0;
fIsOval = false;
@ -392,8 +375,8 @@ private:
/** Makes additional room but does not change the counts or change the genID */
void incReserve(int additionalVerbs, int additionalPoints) {
SkDEBUGCODE(this->validate();)
size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint);
this->makeSpace(space);
fPoints.setReserve(fPoints.count() + additionalPoints);
fVerbs.setReserve(fVerbs.count() + additionalVerbs);
SkDEBUGCODE(this->validate();)
}
@ -410,28 +393,10 @@ private:
fIsOval = false;
fIsRRect = false;
size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
size_t minSize = newSize + newReserve;
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;
}
fPoints.setReserve(pointCount + reservePoints);
fPoints.setCount(pointCount);
fVerbs.setReserve(verbCount + reserveVerbs);
fVerbs.setCount(verbCount);
fConicWeights.setCount(conicCount);
SkDEBUGCODE(this->validate();)
}
@ -451,63 +416,10 @@ private:
*/
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().
*/
uint8_t* verbsMemWritable() {
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);
}
uint8_t* verbsBeginWritable() { return fVerbs.begin(); }
/**
* 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.
SkPoint* getPoints() {
SkPoint* getWritablePoints() {
SkDEBUGCODE(this->validate();)
fIsOval = false;
fIsRRect = false;
return fPoints;
return fPoints.begin();
}
const SkPoint* getPoints() const {
SkDEBUGCODE(this->validate();)
return fPoints;
return fPoints.begin();
}
void callGenIDChangeListeners();
@ -547,11 +459,8 @@ private:
mutable SkRect fBounds;
SkPoint* fPoints; // points to begining of the allocation
uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards)
int fVerbCnt;
int fPointCnt;
size_t fFreeSpace; // redundant but saves computation
SkTDArray<SkPoint> fPoints;
SkTDArray<uint8_t> fVerbs;
SkTDArray<SkScalar> fConicWeights;
enum {

View File

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

View File

@ -66,9 +66,9 @@ public:
if (verbCount == 0)
return false;
int moveCount = 0;
auto verbs = path.fPathRef->verbs();
auto verbs = path.fPathRef->verbsBegin();
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:
moveCount += 1;
if (moveCount > 1) {
@ -123,13 +123,13 @@ public:
public:
Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {}
struct Iter {
void operator++() { --fVerb; } // verbs are laid out backwards in memory.
void operator++() { fVerb++; }
bool operator!=(const Iter& b) { return fVerb != b.fVerb; }
SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); }
const uint8_t* fVerb;
};
Iter begin() { return Iter{fPathRef->verbs() - 1}; }
Iter end() { return Iter{fPathRef->verbs() - fPathRef->countVerbs() - 1}; }
Iter begin() { return Iter{fPathRef->verbsBegin()}; }
Iter end() { return Iter{fPathRef->verbsEnd()}; }
private:
Verbs(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
* thus the returned pointer is the last verb.
* Returns a pointer to the verb data.
*/
static const uint8_t* VerbData(const SkPath& path) {
return path.fPathRef->verbsMemBegin();
return path.fPathRef->verbsBegin();
}
/** 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
//
class SkPathEdgeIter {
const uint8_t* fVerbsStart;
const uint8_t* fVerbs; // reverse
const uint8_t* fVerbs;
const uint8_t* fVerbsStop;
const SkPoint* fPts;
const SkPoint* fMoveToPtr;
const SkScalar* fConicWeights;
@ -338,8 +337,8 @@ public:
};
for (;;) {
SkASSERT(fVerbs >= fVerbsStart);
if (fVerbs == fVerbsStart) {
SkASSERT(fVerbs <= fVerbsStop);
if (fVerbs == fVerbsStop) {
return fNeedsCloseLine
? closeline()
: Result{ nullptr, Edge(kIllegalEdgeValue) };
@ -347,7 +346,7 @@ public:
SkDEBUGCODE(fIsConic = false;)
const auto v = *--fVerbs;
const auto v = *fVerbs++;
switch (v) {
case SkPath::kMove_Verb: {
if (fNeedsCloseLine) {

View File

@ -15,16 +15,6 @@
#include "src/core/SkPathPriv.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,
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.
//
void SkPath::shrinkToFit() {
const size_t kMinFreeSpaceForShrink = 8; // just made up a small number
if (fPathRef->fFreeSpace <= kMinFreeSpaceForShrink) {
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);
}
fPathRef->fPoints.shrinkToFit();
fPathRef->fVerbs.shrinkToFit();
fPathRef->fConicWeights.shrinkToFit();
SkDEBUGCODE(fPathRef->validate();)
}
@ -94,13 +54,6 @@ SkPathRef::~SkPathRef() {
// to read one that's not valid and then free its memory without asserting.
this->callGenIDChangeListeners();
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(fEditorsAttached.store(0x7777777);)
}
@ -189,20 +142,17 @@ void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst,
}
if (dst->get() != &src) {
(*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count());
sk_careful_memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(),
src.fVerbCnt * sizeof(uint8_t));
(*dst)->fPoints = src.fPoints;
(*dst)->fVerbs = src.fVerbs;
(*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)
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
@ -251,163 +201,14 @@ void SkPathRef::CreateTransformedCopy(sk_sp<SkPathRef>* dst,
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) {
if ((*pathRef)->unique()) {
SkDEBUGCODE((*pathRef)->validate();)
(*pathRef)->callGenIDChangeListeners();
(*pathRef)->fBoundsIsDirty = true; // this also invalidates fIsFinite
(*pathRef)->fVerbCnt = 0;
(*pathRef)->fPointCnt = 0;
(*pathRef)->fFreeSpace = (*pathRef)->currSize();
(*pathRef)->fGenerationID = 0;
(*pathRef)->fPoints.rewind();
(*pathRef)->fVerbs.rewind();
(*pathRef)->fConicWeights.rewind();
(*pathRef)->fSegmentMask = 0;
(*pathRef)->fIsOval = false;
@ -438,32 +239,12 @@ bool SkPathRef::operator== (const SkPathRef& ref) const {
return true;
}
#endif
if (fPointCnt != ref.fPointCnt ||
fVerbCnt != ref.fVerbCnt) {
if (fPoints != ref.fPoints || fConicWeights != ref.fConicWeights || fVerbs != ref.fVerbs) {
SkASSERT(!genIDMatch);
return false;
}
if (0 == ref.fVerbCnt) {
SkASSERT(0 == ref.fPointCnt);
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;
if (ref.fVerbs.count() == 0) {
SkASSERT(ref.fPoints.count() == 0);
}
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
// SkWBuffer. Until this is fixed we write 0.
buffer->write32(0);
buffer->write32(fVerbCnt);
buffer->write32(fPointCnt);
buffer->write32(fVerbs.count());
buffer->write32(fPoints.count());
buffer->write32(fConicWeights.count());
buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
buffer->write(fVerbs.begin(), fVerbs.bytes());
buffer->write(fPoints.begin(), fVerbs.bytes());
buffer->write(fConicWeights.begin(), fConicWeights.bytes());
buffer->write(&bounds, sizeof(bounds));
@ -498,9 +279,7 @@ void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
uint32_t SkPathRef::writeSize() const {
return uint32_t(5 * sizeof(uint32_t) +
fVerbCnt * sizeof(uint8_t) +
fPointCnt * sizeof(SkPoint) +
fConicWeights.bytes() +
fVerbs.bytes() + fPoints.bytes() + fConicWeights.bytes() +
sizeof(SkRect));
}
@ -508,10 +287,10 @@ void SkPathRef::copy(const SkPathRef& ref,
int additionalReserveVerbs,
int additionalReservePoints) {
SkDEBUGCODE(this->validate();)
this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(),
additionalReserveVerbs, additionalReservePoints);
sk_careful_memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt*sizeof(uint8_t));
sk_careful_memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
this->resetToSize(ref.fVerbs.count(), ref.fPoints.count(), ref.fConicWeights.count(),
additionalReserveVerbs, additionalReservePoints);
fVerbs = ref.fVerbs;
fPoints = ref.fPoints;
fConicWeights = ref.fConicWeights;
fBoundsIsDirty = ref.fBoundsIsDirty;
if (!fBoundsIsDirty) {
@ -527,9 +306,9 @@ void SkPathRef::copy(const SkPathRef& ref,
}
unsigned SkPathRef::computeSegmentMask() const {
const uint8_t* verbs = this->verbsMemBegin();
const uint8_t* verbs = fVerbs.begin();
unsigned mask = 0;
for (int i = this->countVerbs() - 1; i >= 0; --i) {
for (int i = 0; i < fVerbs.count(); ++i) {
switch (verbs[i]) {
case SkPath::kLine_Verb: mask |= SkPath::kLine_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 {
const SkScalar* inValues = &ending.getPoints()->fX;
SkScalar* outValues = &out->getPoints()->fX;
SkScalar* outValues = &out->getWritablePoints()->fX;
int count = out->countPoints() * 2;
for (int index = 0; index < count; ++index) {
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,
int numVbs,
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();)
int pCnt;
switch (verb) {
@ -595,40 +369,19 @@ SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
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
fIsOval = false;
fIsRRect = false;
memset(fVerbs.append(numVbs), verb, numVbs);
if (SkPath::kConic_Verb == verb) {
SkASSERT(weights);
*weights = fConicWeights.append(numVbs);
}
SkPoint* pts = fPoints.append(pCnt);
SkDEBUGCODE(this->validate();)
return ret;
return pts;
}
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");
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;
fFreeSpace -= space;
fBoundsIsDirty = true; // this also invalidates fIsFinite
fIsOval = false;
fIsRRect = false;
*fVerbs.append() = verb;
if (SkPath::kConic_Verb == verb) {
*fConicWeights.append() = weight;
}
SkPoint* pts = fPoints.append(pCnt);
SkDEBUGCODE(this->validate();)
return ret;
return pts;
}
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;
if (fGenerationID == 0) {
if (fPointCnt == 0 && fVerbCnt == 0) {
if (fPoints.count() == 0 && fVerbs.count() == 0) {
fGenerationID = kEmptyGenID;
} else {
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) {
fPts = path.points();
fVerbs = path.verbs();
fVerbStop = path.verbsMemBegin();
fVerbs = path.verbsBegin();
fVerbStop = path.verbsEnd();
fConicWeights = path.conicWeights();
if (fConicWeights) {
fConicWeights -= 1; // begin one behind
fConicWeights -= 1; // begin one behind
}
// 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.
unsigned verb = *(--fVerbs);
unsigned verb = *fVerbs++;
const SkPoint* srcPts = fPts;
switch (verb) {
@ -865,35 +608,11 @@ uint8_t SkPathRef::Iter::next(SkPoint pts[4]) {
}
uint8_t SkPathRef::Iter::peek() const {
const uint8_t* next = fVerbs;
return next <= fVerbStop ? (uint8_t) SkPath::kDone_Verb : next[-1];
return fVerbs < fVerbStop ? *fVerbs : (uint8_t) SkPath::kDone_Verb;
}
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) {
// Currently we don't allow both of these to be set, even though ovals are ro
if (fIsOval == fIsRRect) {
@ -914,14 +633,14 @@ bool SkPathRef::isValid() const {
bool isFinite = true;
Sk2s leftTop = Sk2s(fBounds.fLeft, fBounds.fTop);
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);
#ifdef SK_DEBUG
if (fPoints[i].isFinite() &&
((point < leftTop).anyTrue() || (point > rightBot).anyTrue())) {
SkDebugf("bad SkPathRef bounds: %g %g %g %g\n",
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) {
SkDebugf("*** bounds do not contain: ");
}
@ -948,8 +667,8 @@ bool SkPathRef::isValid() const {
SkPathEdgeIter::SkPathEdgeIter(const SkPath& path) {
fMoveToPtr = fPts = path.fPathRef->points();
fVerbs = path.fPathRef->verbs();
fVerbsStart = path.fPathRef->verbsMemBegin();
fVerbs = path.fPathRef->verbsBegin();
fVerbsStop = path.fPathRef->verbsEnd();
fConicWeights = path.fPathRef->conicWeights();
if (fConicWeights) {
fConicWeights -= 1; // begin one behind

View File

@ -26,11 +26,13 @@ enum SerializationOffsets {
enum SerializationVersions {
// kPathPrivFirstDirection_Version = 1,
kPathPrivLastMoveToIndex_Version = 2,
kPathPrivTypeEnumVersion = 3,
kJustPublicData_Version = 4, // introduced Feb/2018
// kPathPrivLastMoveToIndex_Version = 2,
// kPathPrivTypeEnumVersion = 3,
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 {
@ -121,7 +123,7 @@ size_t SkPath::writeToMemory(void* storage) const {
buffer.write32(vbs);
buffer.write(fPathRef->points(), pts * sizeof(SkPoint));
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();
SkASSERT(buffer.pos() == size);
@ -145,11 +147,12 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) {
return 0;
}
unsigned version = extract_version(packed);
if (version <= kPathPrivTypeEnumVersion) {
return this->readFromMemory_LE3(storage, length);
if (version < kMin_Version || version > kCurrent_Version) {
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;
}
@ -192,14 +195,17 @@ size_t SkPath::readAsRRect(const void* storage, size_t length) {
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);
uint32_t packed;
if (!buffer.readU32(&packed)) {
return 0;
}
SkASSERT(extract_version(packed) == 4);
bool verbsAreReversed = true;
if (extract_version(packed) == kVerbsAreStoredForward_Version) {
verbsAreReversed = false;
}
switch (extract_serializationtype(packed)) {
case SerializationType::kRRect:
@ -234,11 +240,17 @@ size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
} \
} while (0)
int verbsStep = 1;
if (verbsAreReversed) {
verbs += vbs - 1;
verbsStep = -1;
}
SkPath tmp;
tmp.setFillType(extract_filltype(packed));
tmp.incReserve(pts);
for (int i = vbs - 1; i >= 0; --i) {
switch (verbs[i]) {
for (int i = 0; i < vbs; ++i) {
switch (*verbs) {
case kMove_Verb:
CHECK_POINTS_CONICS(1, 0);
tmp.moveTo(*points++);
@ -268,6 +280,7 @@ size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
default:
return 0; // bad verb
}
verbs += verbsStep;
}
#undef CHECK_POINTS_CONICS
if (pts || cnx) {
@ -277,51 +290,3 @@ size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
*this = std::move(tmp);
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 });
uint32_t id1 = p1.getGenerationID();
uint32_t id2 = p2.getGenerationID();
REPORTER_ASSERT(reporter, id1 != id2);
SkMatrix matrix;
matrix.setScale(2, 2);
p1.transform(matrix, &p2);
REPORTER_ASSERT(reporter, id1 == p1.getGenerationID());
REPORTER_ASSERT(reporter, id2 != p2.getGenerationID());
p1.transform(matrix);
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 {
public:
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) {