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>
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 7f52f07..0eb626b 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -1682,20 +1682,25 @@
private:
enum SerializationOffsets {
- // 1 free bit at 29
- kUnused1_SerializationShift = 28, // 1 free bit
- kDirection_SerializationShift = 26, // requires 2 bits
+ kType_SerializationShift = 28, // requires 4 bits
+ kDirection_SerializationShift = 26, // requires 2 bits
kIsVolatile_SerializationShift = 25, // requires 1 bit
// 1 free bit at 24
- kConvexity_SerializationShift = 16, // requires 8 bits
- kFillType_SerializationShift = 8, // requires 8 bits
+ kConvexity_SerializationShift = 16, // requires 8 bits
+ kFillType_SerializationShift = 8, // requires 8 bits
// low-8-bits are version
};
enum SerializationVersions {
kPathPrivFirstDirection_Version = 1,
kPathPrivLastMoveToIndex_Version = 2,
- kCurrent_Version = 2
+ kPathPrivTypeEnumVersion = 3,
+ kCurrent_Version = 3
+ };
+
+ enum SerializationType {
+ kGeneral = 0,
+ kRRect = 1
};
sk_sp<SkPathRef> fPathRef;
@@ -1717,6 +1722,9 @@
*/
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 SkPathPriv;
friend class SkPathStroker;
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
index eb983b6..0a231b6 100644
--- a/include/core/SkRRect.h
+++ b/include/core/SkRRect.h
@@ -13,6 +13,8 @@
class SkPath;
class SkMatrix;
+class SkRBuffer;
+class SkWBuffer;
// Path forward:
// core work
@@ -302,6 +304,7 @@
* a multiple of 4. Return kSizeInMemory.
*/
size_t writeToMemory(void* buffer) const;
+ void writeToBuffer(SkWBuffer*) const;
/**
* Reads the rrect from the specified buffer
@@ -315,6 +318,7 @@
* 0 if there was not enough memory available
*/
size_t readFromMemory(const void* buffer, size_t length);
+ bool readFromBuffer(SkRBuffer*);
/**
* Transform by the specified matrix, and put the result in dst.
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 1959221..bcf26c0 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -2040,22 +2040,56 @@
Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
*/
-size_t SkPath::writeToMemory(void* storage) const {
- SkDEBUGCODE(this->validate();)
-
- if (nullptr == storage) {
- const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize();
- return SkAlign4(byteCount);
+size_t SkPath::writeToMemoryAsRRect(int32_t packedHeader, void* storage) const {
+ SkRect oval;
+ SkRRect rrect;
+ bool isCCW;
+ unsigned start;
+ 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) |
(fFillType << kFillType_SerializationShift) |
(fFirstDirection << kDirection_SerializationShift) |
(fIsVolatile << kIsVolatile_SerializationShift) |
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(fLastMoveToIndex);
@@ -2081,13 +2115,51 @@
}
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)) {
return 0;
}
fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
- fFillType = (packed >> kFillType_SerializationShift) & 0x3;
- uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
+ fFillType = fillType;
fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
if (!pathRef) {
diff --git a/src/core/SkRRect.cpp b/src/core/SkRRect.cpp
index f1b85fb..81184b1 100644
--- a/src/core/SkRRect.cpp
+++ b/src/core/SkRRect.cpp
@@ -7,6 +7,7 @@
#include <cmath>
#include "SkRRect.h"
+#include "SkBuffer.h"
#include "SkMatrix.h"
#include "SkScaleToSides.h"
@@ -461,6 +462,11 @@
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) {
if (length < kSizeInMemory) {
return 0;
@@ -478,6 +484,20 @@
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 "SkStringUtils.h"