/* * 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(); }