diff options
-rw-r--r-- | include/core/SkPath.h | 7 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 8 | ||||
-rw-r--r-- | src/core/SkPathRef.cpp | 43 | ||||
-rw-r--r-- | tests/PathTest.cpp | 55 |
4 files changed, 98 insertions, 15 deletions
diff --git a/include/core/SkPath.h b/include/core/SkPath.h index b36fa65a6d..d0841fd250 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -12,6 +12,7 @@ #include "../private/SkPathRef.h" class SkAutoPathBoundsUpdate; +class SkData; class SkRRect; class SkWStream; @@ -1090,6 +1091,12 @@ public: * If buffer is NULL, it still returns the number of bytes. */ size_t writeToMemory(void* buffer) const; + + /** + * Write the path to a buffer. Can recreate the path by calling readFromMemory(). + */ + sk_sp<SkData> serialize() const; + /** * Initializes the path from the buffer * diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index 2be3251401..e304b4d8c5 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -8,6 +8,7 @@ #include <cmath> #include "SkBuffer.h" #include "SkCubicClipper.h" +#include "SkData.h" #include "SkGeometry.h" #include "SkMath.h" #include "SkPathPriv.h" @@ -2061,6 +2062,13 @@ size_t SkPath::writeToMemory(void* storage) const { return buffer.pos(); } +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; +} + size_t SkPath::readFromMemory(const void* storage, size_t length) { SkRBuffer buffer(storage, length); diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index 3338225391..abe1f906a5 100644 --- a/src/core/SkPathRef.cpp +++ b/src/core/SkPathRef.cpp @@ -243,29 +243,42 @@ SkPathRef* SkPathRef::CreateFromBuffer(SkRBuffer* buffer) { unsigned rrectOrOvalStartIdx = (packed >> kRRectOrOvalStartIdx_SerializationShift) & 0x7; int32_t verbCount, pointCount, conicCount; - ptrdiff_t maxPtrDiff = std::numeric_limits<ptrdiff_t>::max(); if (!buffer->readU32(&(ref->fGenerationID)) || - !buffer->readS32(&verbCount) || - verbCount < 0 || - static_cast<uint32_t>(verbCount) > maxPtrDiff/sizeof(uint8_t) || - !buffer->readS32(&pointCount) || - pointCount < 0 || - static_cast<uint32_t>(pointCount) > maxPtrDiff/sizeof(SkPoint) || - sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount > - static_cast<size_t>(maxPtrDiff) || - !buffer->readS32(&conicCount) || - conicCount < 0) { + !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(verbCount == ref->countVerbs()); SkASSERT(pointCount == ref->countPoints()); SkASSERT(conicCount == ref->fConicWeights.count()); - if (!buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t)) || - !buffer->read(ref->fPoints, pointCount * sizeof(SkPoint)) || - !buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar)) || + 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; } diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 117da5bdeb..cdaa1c5845 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -4775,3 +4775,58 @@ DEF_TEST(skbug_6450, r) { orr.setRectRadii(ro, rdo); SkMakeNullCanvas()->drawDRRect(orr, irr, SkPaint()); } + +DEF_TEST(PathRefSerialization, reporter) { + SkPath path; + const size_t numMoves = 5; + const size_t numConics = 7; + const size_t numPoints = numMoves + 2 * numConics; + const size_t numVerbs = numMoves + numConics; + for (size_t i = 0; i < numMoves; ++i) path.moveTo(1, 2); + for (size_t i = 0; i < numConics; ++i) path.conicTo(1, 2, 3, 4, 5); + REPORTER_ASSERT(reporter, path.countPoints() == numPoints); + REPORTER_ASSERT(reporter, path.countVerbs() == numVerbs); + + // Verify that path serializes/deserializes properly. + sk_sp<SkData> data = path.serialize(); + size_t bytesWritten = data->size(); + + { + SkPath readBack; + REPORTER_ASSERT(reporter, readBack != path); + size_t bytesRead = readBack.readFromMemory(data->data(), bytesWritten); + REPORTER_ASSERT(reporter, bytesRead == bytesWritten); + REPORTER_ASSERT(reporter, readBack == path); + } + + // uint32_t[] offset into serialized path. + const size_t verbCountOffset = 4; + const size_t pointCountOffset = 5; + const size_t conicCountOffset = 6; + + // Verify that this test is changing the right values. + const int* writtenValues = static_cast<const int*>(data->data()); + REPORTER_ASSERT(reporter, writtenValues[verbCountOffset] == numVerbs); + REPORTER_ASSERT(reporter, writtenValues[pointCountOffset] == numPoints); + REPORTER_ASSERT(reporter, writtenValues[conicCountOffset] == numConics); + + // Too many verbs, points, or conics fails to deserialize silently. + const int tooManyObjects = INT_MAX; + size_t offsets[] = {verbCountOffset, pointCountOffset, conicCountOffset}; + for (size_t i = 0; i < 3; ++i) { + SkAutoMalloc storage_copy(bytesWritten); + memcpy(storage_copy.get(), data->data(), bytesWritten); + static_cast<int*>(storage_copy.get())[offsets[i]] = tooManyObjects; + SkPath readBack; + size_t bytesRead = readBack.readFromMemory(storage_copy.get(), bytesWritten); + REPORTER_ASSERT(reporter, !bytesRead); + } + + // One less byte (rounded down to alignment) than was written will also + // fail to be deserialized. + { + SkPath readBack; + size_t bytesRead = readBack.readFromMemory(data->data(), bytesWritten - 4); + REPORTER_ASSERT(reporter, !bytesRead); + } +} |