/*
|
* Copyright 2018 Google Inc.
|
*
|
* Use of this source code is governed by a BSD-style license that can be
|
* found in the LICENSE file.
|
*/
|
|
#include "SkBuffer.h"
|
#include "SkData.h"
|
#include "SkMath.h"
|
#include "SkPathPriv.h"
|
#include "SkPathRef.h"
|
#include "SkRRectPriv.h"
|
#include "SkSafeMath.h"
|
#include "SkTo.h"
|
|
#include <cmath>
|
|
enum SerializationOffsets {
|
kType_SerializationShift = 28, // requires 4 bits
|
kDirection_SerializationShift = 26, // requires 2 bits
|
kFillType_SerializationShift = 8, // requires 8 bits
|
// low-8-bits are version
|
kVersion_SerializationMask = 0xFF,
|
};
|
|
enum SerializationVersions {
|
// kPathPrivFirstDirection_Version = 1,
|
kPathPrivLastMoveToIndex_Version = 2,
|
kPathPrivTypeEnumVersion = 3,
|
kJustPublicData_Version = 4, // introduced Feb/2018
|
|
kCurrent_Version = kJustPublicData_Version
|
};
|
|
enum SerializationType {
|
kGeneral = 0,
|
kRRect = 1
|
};
|
|
static unsigned extract_version(uint32_t packed) {
|
return packed & kVersion_SerializationMask;
|
}
|
|
static SkPath::FillType extract_filltype(uint32_t packed) {
|
return static_cast<SkPath::FillType>((packed >> kFillType_SerializationShift) & 0x3);
|
}
|
|
static SerializationType extract_serializationtype(uint32_t packed) {
|
return static_cast<SerializationType>((packed >> kType_SerializationShift) & 0xF);
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
size_t SkPath::writeToMemoryAsRRect(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 0;
|
}
|
|
// packed header, rrect, start index.
|
const size_t sizeNeeded = sizeof(int32_t) + SkRRect::kSizeInMemory + sizeof(int32_t);
|
if (!storage) {
|
return sizeNeeded;
|
}
|
|
int firstDir = isCCW ? SkPathPriv::kCCW_FirstDirection : SkPathPriv::kCW_FirstDirection;
|
int32_t packed = (fFillType << kFillType_SerializationShift) |
|
(firstDir << kDirection_SerializationShift) |
|
(SerializationType::kRRect << kType_SerializationShift) |
|
kCurrent_Version;
|
|
SkWBuffer buffer(storage);
|
buffer.write32(packed);
|
SkRRectPriv::WriteToBuffer(rrect, &buffer);
|
buffer.write32(SkToS32(start));
|
buffer.padToAlign4();
|
SkASSERT(sizeNeeded == buffer.pos());
|
return buffer.pos();
|
}
|
|
size_t SkPath::writeToMemory(void* storage) const {
|
SkDEBUGCODE(this->validate();)
|
|
if (size_t bytes = this->writeToMemoryAsRRect(storage)) {
|
return bytes;
|
}
|
|
int32_t packed = (fFillType << kFillType_SerializationShift) |
|
(SerializationType::kGeneral << kType_SerializationShift) |
|
kCurrent_Version;
|
|
int32_t pts = fPathRef->countPoints();
|
int32_t cnx = fPathRef->countWeights();
|
int32_t vbs = fPathRef->countVerbs();
|
|
SkSafeMath safe;
|
size_t size = 4 * sizeof(int32_t);
|
size = safe.add(size, safe.mul(pts, sizeof(SkPoint)));
|
size = safe.add(size, safe.mul(cnx, sizeof(SkScalar)));
|
size = safe.add(size, safe.mul(vbs, sizeof(uint8_t)));
|
size = safe.alignUp(size, 4);
|
if (!safe) {
|
return 0;
|
}
|
if (!storage) {
|
return size;
|
}
|
|
SkWBuffer buffer(storage);
|
buffer.write32(packed);
|
buffer.write32(pts);
|
buffer.write32(cnx);
|
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.padToAlign4();
|
|
SkASSERT(buffer.pos() == size);
|
return size;
|
}
|
|
sk_sp<SkData> SkPath::serialize() const {
|
size_t size = this->writeToMemory(nullptr);
|
sk_sp<SkData> data = SkData::MakeUninitialized(size);
|
this->writeToMemory(data->writable_data());
|
return data;
|
}
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////
|
// reading
|
|
size_t SkPath::readFromMemory(const void* storage, size_t length) {
|
SkRBuffer buffer(storage, length);
|
uint32_t packed;
|
if (!buffer.readU32(&packed)) {
|
return 0;
|
}
|
unsigned version = extract_version(packed);
|
if (version <= kPathPrivTypeEnumVersion) {
|
return this->readFromMemory_LE3(storage, length);
|
}
|
if (version == kJustPublicData_Version) {
|
return this->readFromMemory_EQ4(storage, length);
|
}
|
return 0;
|
}
|
|
size_t SkPath::readAsRRect(const void* storage, size_t length) {
|
SkRBuffer buffer(storage, length);
|
uint32_t packed;
|
if (!buffer.readU32(&packed)) {
|
return 0;
|
}
|
|
SkASSERT(extract_serializationtype(packed) == SerializationType::kRRect);
|
|
uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
|
FillType fillType = extract_filltype(packed);
|
|
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 (!SkRRectPriv::ReadFromBuffer(&buffer, &rrect)) {
|
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();
|
}
|
|
size_t SkPath::readFromMemory_EQ4(const void* storage, size_t length) {
|
SkRBuffer buffer(storage, length);
|
uint32_t packed;
|
if (!buffer.readU32(&packed)) {
|
return 0;
|
}
|
|
SkASSERT(extract_version(packed) == 4);
|
|
switch (extract_serializationtype(packed)) {
|
case SerializationType::kRRect:
|
return this->readAsRRect(storage, length);
|
case SerializationType::kGeneral:
|
break; // fall through
|
default:
|
return 0;
|
}
|
|
int32_t pts, cnx, vbs;
|
if (!buffer.readS32(&pts) || !buffer.readS32(&cnx) || !buffer.readS32(&vbs)) {
|
return 0;
|
}
|
|
const SkPoint* points = buffer.skipCount<SkPoint>(pts);
|
const SkScalar* conics = buffer.skipCount<SkScalar>(cnx);
|
const uint8_t* verbs = buffer.skipCount<uint8_t>(vbs);
|
buffer.skipToAlign4();
|
if (!buffer.isValid()) {
|
return 0;
|
}
|
SkASSERT(buffer.pos() <= length);
|
|
#define CHECK_POINTS_CONICS(p, c) \
|
do { \
|
if (p && ((pts -= p) < 0)) { \
|
return 0; \
|
} \
|
if (c && ((cnx -= c) < 0)) { \
|
return 0; \
|
} \
|
} while (0)
|
|
SkPath tmp;
|
tmp.setFillType(extract_filltype(packed));
|
tmp.incReserve(pts);
|
for (int i = vbs - 1; i >= 0; --i) {
|
switch (verbs[i]) {
|
case kMove_Verb:
|
CHECK_POINTS_CONICS(1, 0);
|
tmp.moveTo(*points++);
|
break;
|
case kLine_Verb:
|
CHECK_POINTS_CONICS(1, 0);
|
tmp.lineTo(*points++);
|
break;
|
case kQuad_Verb:
|
CHECK_POINTS_CONICS(2, 0);
|
tmp.quadTo(points[0], points[1]);
|
points += 2;
|
break;
|
case kConic_Verb:
|
CHECK_POINTS_CONICS(2, 1);
|
tmp.conicTo(points[0], points[1], *conics++);
|
points += 2;
|
break;
|
case kCubic_Verb:
|
CHECK_POINTS_CONICS(3, 0);
|
tmp.cubicTo(points[0], points[1], points[2]);
|
points += 3;
|
break;
|
case kClose_Verb:
|
tmp.close();
|
break;
|
default:
|
return 0; // bad verb
|
}
|
}
|
#undef CHECK_POINTS_CONICS
|
if (pts || cnx) {
|
return 0; // leftover points and/or conics
|
}
|
|
*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();
|
}
|