Serialize rrect/oval paths as rrects rather than points and verbs.

This is a step towards not trusting deserialized isoval/isrrect for general paths without losing the performance advantages of knowing that a path is a rrect/oval.

Change-Id: I1a8c0608c0f29f4bf7a118dfa1d475e2ab5802ea
Reviewed-on: https://skia-review.googlesource.com/49761
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Brian Salomon 2017-09-21 12:29:24 -04:00 committed by Skia Commit-Bot
parent 2fbf1bc8c9
commit 1e3b79e0c6
4 changed files with 119 additions and 15 deletions

View File

@ -1682,20 +1682,25 @@ enum Direction {
private: private:
enum SerializationOffsets { enum SerializationOffsets {
// 1 free bit at 29 kType_SerializationShift = 28, // requires 4 bits
kUnused1_SerializationShift = 28, // 1 free bit kDirection_SerializationShift = 26, // requires 2 bits
kDirection_SerializationShift = 26, // requires 2 bits
kIsVolatile_SerializationShift = 25, // requires 1 bit kIsVolatile_SerializationShift = 25, // requires 1 bit
// 1 free bit at 24 // 1 free bit at 24
kConvexity_SerializationShift = 16, // requires 8 bits kConvexity_SerializationShift = 16, // requires 8 bits
kFillType_SerializationShift = 8, // requires 8 bits kFillType_SerializationShift = 8, // requires 8 bits
// low-8-bits are version // low-8-bits are version
}; };
enum SerializationVersions { enum SerializationVersions {
kPathPrivFirstDirection_Version = 1, kPathPrivFirstDirection_Version = 1,
kPathPrivLastMoveToIndex_Version = 2, kPathPrivLastMoveToIndex_Version = 2,
kCurrent_Version = 2 kPathPrivTypeEnumVersion = 3,
kCurrent_Version = 3
};
enum SerializationType {
kGeneral = 0,
kRRect = 1
}; };
sk_sp<SkPathRef> fPathRef; sk_sp<SkPathRef> fPathRef;
@ -1717,6 +1722,9 @@ private:
*/ */
void copyFields(const SkPath& that); void copyFields(const SkPath& that);
size_t writeToMemoryAsRRect(int32_t packedHeader, void* buffer) const;
size_t readFromMemoryAsRRect(const void* buffer) const;
friend class Iter; friend class Iter;
friend class SkPathPriv; friend class SkPathPriv;
friend class SkPathStroker; friend class SkPathStroker;

View File

@ -13,6 +13,8 @@
class SkPath; class SkPath;
class SkMatrix; class SkMatrix;
class SkRBuffer;
class SkWBuffer;
// Path forward: // Path forward:
// core work // core work
@ -302,6 +304,7 @@ public:
* a multiple of 4. Return kSizeInMemory. * a multiple of 4. Return kSizeInMemory.
*/ */
size_t writeToMemory(void* buffer) const; size_t writeToMemory(void* buffer) const;
void writeToBuffer(SkWBuffer*) const;
/** /**
* Reads the rrect from the specified buffer * Reads the rrect from the specified buffer
@ -315,6 +318,7 @@ public:
* 0 if there was not enough memory available * 0 if there was not enough memory available
*/ */
size_t readFromMemory(const void* buffer, size_t length); size_t readFromMemory(const void* buffer, size_t length);
bool readFromBuffer(SkRBuffer*);
/** /**
* Transform by the specified matrix, and put the result in dst. * Transform by the specified matrix, and put the result in dst.

View File

@ -2040,22 +2040,56 @@ SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]] Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
*/ */
size_t SkPath::writeToMemory(void* storage) const { size_t SkPath::writeToMemoryAsRRect(int32_t packedHeader, void* storage) const {
SkDEBUGCODE(this->validate();) SkRect oval;
SkRRect rrect;
if (nullptr == storage) { bool isCCW;
const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize(); unsigned start;
return SkAlign4(byteCount); if (fPathRef->isOval(&oval, &isCCW, &start)) {
rrect.setOval(oval);
// Convert to rrect start indices.
start *= 2;
} else if (!fPathRef->isRRect(&rrect, &isCCW, &start)) {
return false;
}
if (!storage) {
// packed header, rrect, start index.
return sizeof(int32_t) + SkRRect::kSizeInMemory + sizeof(int32_t);
} }
SkWBuffer buffer(storage); SkWBuffer buffer(storage);
// Rewrite header's first direction based on rrect direction.
uint8_t firstDir = isCCW ? SkPathPriv::kCCW_FirstDirection : SkPathPriv::kCW_FirstDirection;
packedHeader &= ~(0x3 << kDirection_SerializationShift);
packedHeader |= firstDir << kDirection_SerializationShift;
packedHeader |= SerializationType::kRRect << kType_SerializationShift;
buffer.write32(packedHeader);
rrect.writeToBuffer(&buffer);
buffer.write32(SkToS32(start));
buffer.padToAlign4();
return buffer.pos();
}
size_t SkPath::writeToMemory(void* storage) const {
SkDEBUGCODE(this->validate();)
int32_t packed = (fConvexity << kConvexity_SerializationShift) | int32_t packed = (fConvexity << kConvexity_SerializationShift) |
(fFillType << kFillType_SerializationShift) | (fFillType << kFillType_SerializationShift) |
(fFirstDirection << kDirection_SerializationShift) | (fFirstDirection << kDirection_SerializationShift) |
(fIsVolatile << kIsVolatile_SerializationShift) | (fIsVolatile << kIsVolatile_SerializationShift) |
kCurrent_Version; kCurrent_Version;
if (size_t bytes = this->writeToMemoryAsRRect(packed, storage)) {
return bytes;
}
SkWBuffer buffer(storage);
static_assert(0 == SerializationType::kGeneral, "packed has zero in type bits");
if (nullptr == storage) {
// packed header, pathref, start index
const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize();
return SkAlign4(byteCount);
}
buffer.write32(packed); buffer.write32(packed);
buffer.write32(fLastMoveToIndex); buffer.write32(fLastMoveToIndex);
@ -2081,13 +2115,51 @@ size_t SkPath::readFromMemory(const void* storage, size_t length) {
} }
unsigned version = packed & 0xFF; unsigned version = packed & 0xFF;
uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
FillType fillType = static_cast<FillType>((packed >> kFillType_SerializationShift) & 0x3);
if (version >= kPathPrivTypeEnumVersion) {
SerializationType type =
static_cast<SerializationType>((packed >> kType_SerializationShift) & 0xF);
switch (type) {
case SerializationType::kRRect: {
Direction rrectDir;
SkRRect rrect;
int32_t start;
switch (dir) {
case SkPathPriv::kCW_FirstDirection:
rrectDir = kCW_Direction;
break;
case SkPathPriv::kCCW_FirstDirection:
rrectDir = kCCW_Direction;
break;
default:
return 0;
}
if (!rrect.readFromBuffer(&buffer)) {
return 0;
}
if (!buffer.readS32(&start) || start != SkTPin(start, 0, 7)) {
return 0;
}
this->reset();
this->addRRect(rrect, rrectDir, SkToUInt(start));
this->setFillType(fillType);
buffer.skipToAlign4();
return buffer.pos();
}
case SerializationType::kGeneral:
// Fall through to general path deserialization
break;
default:
return 0;
}
}
if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) { if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) {
return 0; return 0;
} }
fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF; fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
fFillType = (packed >> kFillType_SerializationShift) & 0x3; fFillType = fillType;
uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1; fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer); SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
if (!pathRef) { if (!pathRef) {

View File

@ -7,6 +7,7 @@
#include <cmath> #include <cmath>
#include "SkRRect.h" #include "SkRRect.h"
#include "SkBuffer.h"
#include "SkMatrix.h" #include "SkMatrix.h"
#include "SkScaleToSides.h" #include "SkScaleToSides.h"
@ -461,6 +462,11 @@ size_t SkRRect::writeToMemory(void* buffer) const {
return kSizeInMemory; return kSizeInMemory;
} }
void SkRRect::writeToBuffer(SkWBuffer* buffer) const {
// Serialize only the rect and corners, but not the derived type tag.
buffer->write(this, kSizeInMemory);
}
size_t SkRRect::readFromMemory(const void* buffer, size_t length) { size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
if (length < kSizeInMemory) { if (length < kSizeInMemory) {
return 0; return 0;
@ -478,6 +484,20 @@ size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
return kSizeInMemory; return kSizeInMemory;
} }
bool SkRRect::readFromBuffer(SkRBuffer* buffer) {
if (buffer->available() < kSizeInMemory) {
return false;
}
SkRRect readData;
buffer->read(&readData, kSizeInMemory);
if (!AreRectAndRadiiValid(readData.fRect, readData.fRadii)) {
return false;
}
memcpy(this, &readData, kSizeInMemory);
this->computeType();
return true;
}
#include "SkString.h" #include "SkString.h"
#include "SkStringUtils.h" #include "SkStringUtils.h"