From 60691a5127852631b03250f15fa6cda9a504befc Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Tue, 5 Dec 2017 15:11:24 -0500 Subject: [PATCH] add serial procs to pictures Bug: skia:7380 Change-Id: Ic1b7e437316c7913711cf5cb119e3fe904cd2c05 Reviewed-on: https://skia-review.googlesource.com/76980 Commit-Queue: Mike Reed Reviewed-by: Florin Malita Reviewed-by: Mike Klein --- gn/tests.gni | 1 + include/core/SkPicture.h | 12 ++++- include/core/SkSerialProcs.h | 60 ++++++++++++++++++++++ include/core/SkWriteBuffer.h | 31 +++++++---- src/core/SkPicture.cpp | 99 ++++++++++++++++++++++++++++++++---- src/core/SkPictureCommon.h | 4 ++ src/core/SkPictureData.cpp | 23 ++++----- src/core/SkPictureData.h | 10 ++-- src/core/SkReadBuffer.cpp | 58 ++++++++++++++------- src/core/SkReadBuffer.h | 17 +++++-- src/core/SkWriteBuffer.cpp | 43 ++++++++++------ src/pipe/SkPipeCanvas.cpp | 1 + tests/SerialProcsTest.cpp | 93 +++++++++++++++++++++++++++++++++ tools/sk_tool_utils.h | 2 +- 14 files changed, 374 insertions(+), 80 deletions(-) create mode 100644 include/core/SkSerialProcs.h create mode 100644 tests/SerialProcsTest.cpp diff --git a/gn/tests.gni b/gn/tests.gni index 03e0a44fea..8c08d166f4 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -203,6 +203,7 @@ tests_sources = [ "$_tests/ScalarTest.cpp", "$_tests/ScaleToSidesTest.cpp", "$_tests/SerializationTest.cpp", + "$_tests/SerialProcsTest.cpp", "$_tests/ShaderOpacityTest.cpp", "$_tests/ShaderTest.cpp", "$_tests/ShadowTest.cpp", diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index b3bc96bb88..1e32636ca3 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -17,6 +17,7 @@ class SkBigPicture; class SkBitmap; class SkCanvas; class SkData; +struct SkDeserialProcs; class SkImage; class SkImageDeserializer; class SkPath; @@ -24,6 +25,7 @@ class SkPictureData; class SkPixelSerializer; class SkReadBuffer; class SkRefCntSet; +struct SkSerialProcs; class SkStream; class SkTypefacePlayback; class SkWStream; @@ -62,6 +64,10 @@ public: SkImageDeserializer* = nullptr); static sk_sp MakeFromData(const SkData* data, SkImageDeserializer* = nullptr); + static sk_sp MakeFromStream(SkStream*, const SkDeserialProcs& procs); + static sk_sp MakeFromData(const SkData* data, const SkDeserialProcs& procs); + static sk_sp MakeFromData(sk_sp data, const SkDeserialProcs& procs); + /** * Recreate a picture that was serialized into a buffer. If the creation requires bitmap * decoding, the decoder must be set on the SkReadBuffer parameter by calling @@ -112,6 +118,8 @@ public: */ sk_sp serialize(SkPixelSerializer* = nullptr) const; + sk_sp serialize(const SkSerialProcs&) const; + /** * Serialize to a stream. If non nullptr, pixel-serializer will be used to * customize how images reference by the picture are serialized/compressed. @@ -169,8 +177,8 @@ private: friend class SkEmptyPicture; template friend class SkMiniPicture; - void serialize(SkWStream*, SkPixelSerializer*, SkRefCntSet* typefaces) const; - static sk_sp MakeFromStream(SkStream*, SkImageDeserializer*, SkTypefacePlayback*); + void serialize(SkWStream*, const SkSerialProcs&, SkRefCntSet* typefaces) const; + static sk_sp MakeFromStream(SkStream*, const SkDeserialProcs&, SkTypefacePlayback*); friend class SkPictureData; virtual int numSlowPaths() const = 0; diff --git a/include/core/SkSerialProcs.h b/include/core/SkSerialProcs.h new file mode 100644 index 0000000000..d55d1a8405 --- /dev/null +++ b/include/core/SkSerialProcs.h @@ -0,0 +1,60 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSerialProcs_DEFINED +#define SkSerialProcs_DEFINED + +#include "SkImage.h" +#include "SkPicture.h" +#include "SkTypeface.h" + +/** + * A serial-proc is asked to serialize the specified object (e.g. picture or image), by writing + * its serialized form into the specified stream. If the proc does this, it returns true. + * + * If the proc chooses to have Skia perform its default action, it ignores the stream parameter + * and just returns false. + */ + +typedef bool (*SkSerialPictureProc)(SkPicture*, SkWStream*, void* ctx); +typedef bool (*SkSerialImageProc)(SkImage*, SkWStream*, void* ctx); +typedef bool (*SkSerialTypefaceProc)(SkTypeface*, SkWStream*, void* ctx); + +/** + * A deserial-proc is given the serialized form previously returned by the corresponding + * serial-proc, and should return the re-constituted object. In case of an error, the proc + * can return nullptr. + */ + +typedef sk_sp (*SkDeserialPictureProc)(const void* data, size_t length, void* ctx); +typedef sk_sp (*SkDeserialImageProc)(const void* data, size_t length, void* ctx); +typedef sk_sp (*SkDeserialTypefaceProc)(const void* data, size_t length, void* ctx); + +struct SkSerialProcs { + SkSerialPictureProc fPictureProc = nullptr; + void* fPictureCtx = nullptr; + + SkSerialImageProc fImageProc = nullptr; + void* fImageCtx = nullptr; + + SkSerialTypefaceProc fTypefaceProc = nullptr; + void* fTypefaceCtx = nullptr; +}; + +struct SkDeserialProcs { + SkDeserialPictureProc fPictureProc = nullptr; + void* fPictureCtx = nullptr; + + SkDeserialImageProc fImageProc = nullptr; + void* fImageCtx = nullptr; + + SkDeserialTypefaceProc fTypefaceProc = nullptr; + void* fTypefaceCtx = nullptr; +}; + +#endif + diff --git a/include/core/SkWriteBuffer.h b/include/core/SkWriteBuffer.h index b2db00873a..42d8f96eb9 100644 --- a/include/core/SkWriteBuffer.h +++ b/include/core/SkWriteBuffer.h @@ -9,15 +9,21 @@ #ifndef SkWriteBuffer_DEFINED #define SkWriteBuffer_DEFINED +#define SK_SUPPORT_LEGACY_SERIAL_BUFFER_OBJECTS + #include "SkData.h" #include "SkImage.h" #include "SkPath.h" #include "SkPicture.h" -#include "SkPixelSerializer.h" #include "SkRefCnt.h" +#include "SkSerialProcs.h" #include "SkWriter32.h" #include "../private/SkTHash.h" +#ifdef SK_SUPPORT_LEGACY_SERIAL_BUFFER_OBJECTS +#include "SkPixelSerializer.h" +#endif + class SkBitmap; class SkDeduper; class SkFactorySet; @@ -102,6 +108,9 @@ public: void write(const void* buffer, size_t bytes) { fWriter.write(buffer, bytes); } + void writePad32(const void* buffer, size_t bytes) { + fWriter.writePad(buffer, bytes); + } void reset(void* storage = nullptr, size_t storageSize = 0) { fWriter.reset(storage, storageSize); @@ -141,26 +150,26 @@ public: SkFactorySet* setFactoryRecorder(SkFactorySet*); SkRefCntSet* setTypefaceRecorder(SkRefCntSet*); - /** - * Set an SkPixelSerializer to store an encoded representation of pixels, - * e.g. SkBitmaps. - * - * TODO: Encode SkImage pixels as well. - */ + void setSerialProcs(const SkSerialProcs& procs) { fProcs = procs; } + +#ifdef SK_SUPPORT_LEGACY_SERIAL_BUFFER_OBJECTS void setPixelSerializer(sk_sp); - SkPixelSerializer* getPixelSerializer() const { return fPixelSerializer.get(); } +#endif private: const uint32_t fFlags; SkFactorySet* fFactorySet; SkWriter32 fWriter; - SkRefCntSet* fTFSet; - - sk_sp fPixelSerializer; + SkRefCntSet* fTFSet; + SkSerialProcs fProcs; // Only used if we do not have an fFactorySet SkTHashMap fFlattenableDict; + +#ifdef SK_SUPPORT_LEGACY_SERIAL_BUFFER_OBJECTS + sk_sp fPS; +#endif }; #endif // SkWriteBuffer_DEFINED diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp index 972b81f997..e87439aad9 100644 --- a/src/core/SkPicture.cpp +++ b/src/core/SkPicture.cpp @@ -9,10 +9,12 @@ #include "SkImageDeserializer.h" #include "SkImageGenerator.h" #include "SkPicture.h" +#include "SkPictureCommon.h" #include "SkPictureData.h" #include "SkPicturePlayback.h" #include "SkPictureRecord.h" #include "SkPictureRecorder.h" +#include "SkSerialProcs.h" #if defined(SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS) || \ defined(SK_ENABLE_PICTURE_IO_SECURITY_PRECAUTIONS) @@ -129,7 +131,10 @@ sk_sp SkPicture::Forwardport(const SkPictInfo& info, } sk_sp SkPicture::MakeFromStream(SkStream* stream, SkImageDeserializer* factory) { - return MakeFromStream(stream, factory, nullptr); + SkDeserialProcs procs; + procs.fImageProc = ImageDeserializer_SkDeserialImageProc; + procs.fImageCtx = factory; + return MakeFromStream(stream, procs, nullptr); } sk_sp SkPicture::MakeFromStream(SkStream* stream) { @@ -140,7 +145,7 @@ sk_sp SkPicture::MakeFromStream(SkStream* stream) { sk_sp SkPicture::MakeFromData(const void* data, size_t size, SkImageDeserializer* factory) { SkMemoryStream stream(data, size); - return MakeFromStream(&stream, factory, nullptr); + return MakeFromStream(&stream, factory); } sk_sp SkPicture::MakeFromData(const SkData* data, SkImageDeserializer* factory) { @@ -148,17 +153,33 @@ sk_sp SkPicture::MakeFromData(const SkData* data, SkImageDeserializer return nullptr; } SkMemoryStream stream(data->data(), data->size()); - return MakeFromStream(&stream, factory, nullptr); + return MakeFromStream(&stream, factory); } -sk_sp SkPicture::MakeFromStream(SkStream* stream, SkImageDeserializer* factory, +sk_sp SkPicture::MakeFromData(const SkData* data, const SkDeserialProcs& procs) { + if (!data) { + return nullptr; + } + SkMemoryStream stream(data->data(), data->size()); + return MakeFromStream(&stream, procs, nullptr); +} + +sk_sp SkPicture::MakeFromData(sk_sp data, const SkDeserialProcs& procs) { + return MakeFromData(data.get(), procs); +} + +sk_sp SkPicture::MakeFromStream(SkStream* stream, const SkDeserialProcs& procs) { + return MakeFromStream(stream, procs, nullptr); +} + +sk_sp SkPicture::MakeFromStream(SkStream* stream, const SkDeserialProcs& procs, SkTypefacePlayback* typefaces) { SkPictInfo info; if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) { return nullptr; } std::unique_ptr data( - SkPictureData::CreateFromStream(stream, info, factory, typefaces)); + SkPictureData::CreateFromStream(stream, info, procs, typefaces)); return Forwardport(info, data.get(), nullptr); } @@ -181,17 +202,27 @@ SkPictureData* SkPicture::backport() const { } void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const { - this->serialize(stream, pixelSerializer, nullptr); + SkSerialProcs procs; + if (pixelSerializer) { + procs.fImageProc = PixelSerializer_SkSerialImageProc; + procs.fImageCtx = pixelSerializer; + } + this->serialize(stream, procs, nullptr); } sk_sp SkPicture::serialize(SkPixelSerializer* pixelSerializer) const { SkDynamicMemoryWStream stream; - this->serialize(&stream, pixelSerializer, nullptr); + this->serialize(&stream, pixelSerializer); return stream.detachAsData(); } -void SkPicture::serialize(SkWStream* stream, - SkPixelSerializer* pixelSerializer, +sk_sp SkPicture::serialize(const SkSerialProcs& procs) const { + SkDynamicMemoryWStream stream; + this->serialize(&stream, procs, nullptr); + return stream.detachAsData(); +} + +void SkPicture::serialize(SkWStream* stream, const SkSerialProcs& procs, SkRefCntSet* typefaceSet) const { SkPictInfo info = this->createHeader(); std::unique_ptr data(this->backport()); @@ -199,7 +230,7 @@ void SkPicture::serialize(SkWStream* stream, stream->write(&info, sizeof(info)); if (data) { stream->writeBool(true); - data->serialize(stream, pixelSerializer, typefaceSet); + data->serialize(stream, procs, typefaceSet); } else { stream->writeBool(false); } @@ -239,3 +270,51 @@ void SkPicture::SetPictureIOSecurityPrecautionsEnabled_Dangerous(bool set) { bool SkPicture::PictureIOSecurityPrecautionsEnabled() { return g_AllPictureIOSecurityPrecautionsEnabled; } + +////////////////////////////////////////////////////////////////////////////////////////////////// + +bool PixelSerializer_SkSerialImageProc(SkImage* img, SkWStream* stream, void* ctx) { + SkASSERT(ctx); + sk_sp enc = img->encodeToData(static_cast(ctx)); + if (enc) { + stream->write(enc->data(), enc->size()); + return true; + } + return false; +} + +sk_sp ImageDeserializer_SkDeserialImageProc(const void* data, size_t length, void* ctx) { + SkASSERT(ctx); + SkImageDeserializer* imd = static_cast(ctx); + const SkIRect* subset = nullptr; + return imd->makeFromMemory(data, length, subset); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef SK_SUPPORT_LEGACY_SERIAL_BUFFER_OBJECTS +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +void SkBinaryWriteBuffer::setPixelSerializer(sk_sp ps) { + fPS = ps; + if (ps) { + fProcs.fImageProc = PixelSerializer_SkSerialImageProc; + fProcs.fImageCtx = ps.get(); + } else { + fProcs.fImageProc = nullptr; + fProcs.fImageCtx = nullptr; + } +} + +void SkReadBuffer::setImageDeserializer(SkImageDeserializer* factory) { + if (factory) { + fProcs.fImageProc = ImageDeserializer_SkDeserialImageProc; + fProcs.fImageCtx = factory; + } else { + fProcs.fImageProc = nullptr; + fProcs.fImageCtx = nullptr; + } +} +#endif + diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h index 43805fd49f..74d693f56b 100644 --- a/src/core/SkPictureCommon.h +++ b/src/core/SkPictureCommon.h @@ -141,4 +141,8 @@ struct SkPathCounter { int fNumSlowPathsAndDashEffects; }; + +bool PixelSerializer_SkSerialImageProc(SkImage*, SkWStream*, void* sk_pixelserializer); +sk_sp ImageDeserializer_SkDeserialImageProc(const void*, size_t, void* imagedeserializer); + #endif // SkPictureCommon_DEFINED diff --git a/src/core/SkPictureData.cpp b/src/core/SkPictureData.cpp index a5a644fe69..24e161f191 100644 --- a/src/core/SkPictureData.cpp +++ b/src/core/SkPictureData.cpp @@ -278,8 +278,7 @@ void SkPictureData::flattenToBuffer(SkWriteBuffer& buffer) const { } } -void SkPictureData::serialize(SkWStream* stream, - SkPixelSerializer* pixelSerializer, +void SkPictureData::serialize(SkWStream* stream, const SkSerialProcs& procs, SkRefCntSet* topLevelTypeFaceSet) const { // This can happen at pretty much any time, so might as well do it first. write_tag_size(stream, SK_PICT_READER_TAG, fOpData->size()); @@ -294,7 +293,7 @@ void SkPictureData::serialize(SkWStream* stream, SkFactorySet factSet; // buffer refs factSet, so factSet must come first. SkBinaryWriteBuffer buffer(SkBinaryWriteBuffer::kCrossProcess_Flag); buffer.setFactoryRecorder(&factSet); - buffer.setPixelSerializer(sk_ref_sp(pixelSerializer)); + buffer.setSerialProcs(procs); buffer.setTypefaceRecorder(typefaceSet); this->flattenToBuffer(buffer); @@ -307,7 +306,7 @@ void SkPictureData::serialize(SkWStream* stream, size_t bytesWritten() const override { return fBytesWritten; } } devnull; for (int i = 0; i < fPictureCount; i++) { - fPictureRefs[i]->serialize(&devnull, pixelSerializer, typefaceSet); + fPictureRefs[i]->serialize(&devnull, procs, typefaceSet); } // We need to write factories before we write the buffer. @@ -325,7 +324,7 @@ void SkPictureData::serialize(SkWStream* stream, if (fPictureCount > 0) { write_tag_size(stream, SK_PICT_PICTURE_TAG, fPictureCount); for (int i = 0; i < fPictureCount; i++) { - fPictureRefs[i]->serialize(stream, pixelSerializer, typefaceSet); + fPictureRefs[i]->serialize(stream, procs, typefaceSet); } } @@ -383,7 +382,7 @@ static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) { bool SkPictureData::parseStreamTag(SkStream* stream, uint32_t tag, uint32_t size, - SkImageDeserializer* factory, + const SkDeserialProcs& procs, SkTypefacePlayback* topLevelTFPlayback) { /* * By the time we encounter BUFFER_SIZE_TAG, we need to have already seen @@ -436,7 +435,7 @@ bool SkPictureData::parseStreamTag(SkStream* stream, fPictureCount = 0; fPictureRefs = new const SkPicture* [size]; for (uint32_t i = 0; i < size; i++) { - fPictureRefs[i] = SkPicture::MakeFromStream(stream, factory, topLevelTFPlayback).release(); + fPictureRefs[i] = SkPicture::MakeFromStream(stream, procs, topLevelTFPlayback).release(); if (!fPictureRefs[i]) { return false; } @@ -458,7 +457,7 @@ bool SkPictureData::parseStreamTag(SkStream* stream, return false; } fFactoryPlayback->setupBuffer(buffer); - buffer.setImageDeserializer(factory); + buffer.setDeserialProcs(procs); if (fTFPlayback.count() > 0) { // .skp files <= v43 have typefaces serialized with each sub picture. @@ -602,14 +601,14 @@ bool SkPictureData::parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t SkPictureData* SkPictureData::CreateFromStream(SkStream* stream, const SkPictInfo& info, - SkImageDeserializer* factory, + const SkDeserialProcs& procs, SkTypefacePlayback* topLevelTFPlayback) { std::unique_ptr data(new SkPictureData(info)); if (!topLevelTFPlayback) { topLevelTFPlayback = &data->fTFPlayback; } - if (!data->parseStream(stream, factory, topLevelTFPlayback)) { + if (!data->parseStream(stream, procs, topLevelTFPlayback)) { return nullptr; } return data.release(); @@ -627,7 +626,7 @@ SkPictureData* SkPictureData::CreateFromBuffer(SkReadBuffer& buffer, } bool SkPictureData::parseStream(SkStream* stream, - SkImageDeserializer* factory, + const SkDeserialProcs& procs, SkTypefacePlayback* topLevelTFPlayback) { for (;;) { uint32_t tag = stream->readU32(); @@ -636,7 +635,7 @@ bool SkPictureData::parseStream(SkStream* stream, } uint32_t size = stream->readU32(); - if (!this->parseStreamTag(stream, tag, size, factory, topLevelTFPlayback)) { + if (!this->parseStreamTag(stream, tag, size, procs, topLevelTFPlayback)) { return false; // we're invalid } } diff --git a/src/core/SkPictureData.h b/src/core/SkPictureData.h index b95f428807..551af70312 100644 --- a/src/core/SkPictureData.h +++ b/src/core/SkPictureData.h @@ -16,8 +16,8 @@ class SkData; class SkPictureRecord; -class SkPixelSerializer; class SkReader32; +struct SkSerialProcs; class SkStream; class SkWStream; class SkBBoxHierarchy; @@ -79,13 +79,13 @@ public: // Does not affect ownership of SkStream. static SkPictureData* CreateFromStream(SkStream*, const SkPictInfo&, - SkImageDeserializer*, + const SkDeserialProcs&, SkTypefacePlayback*); static SkPictureData* CreateFromBuffer(SkReadBuffer&, const SkPictInfo&); virtual ~SkPictureData(); - void serialize(SkWStream*, SkPixelSerializer*, SkRefCntSet*) const; + void serialize(SkWStream*, const SkSerialProcs&, SkRefCntSet*) const; void flatten(SkWriteBuffer&) const; bool containsBitmaps() const; @@ -100,7 +100,7 @@ protected: explicit SkPictureData(const SkPictInfo& info); // Does not affect ownership of SkStream. - bool parseStream(SkStream*, SkImageDeserializer*, SkTypefacePlayback*); + bool parseStream(SkStream*, const SkDeserialProcs&, SkTypefacePlayback*); bool parseBuffer(SkReadBuffer& buffer); public: @@ -172,7 +172,7 @@ private: // these help us with reading/writing // Does not affect ownership of SkStream. bool parseStreamTag(SkStream*, uint32_t tag, uint32_t size, - SkImageDeserializer*, SkTypefacePlayback*); + const SkDeserialProcs&, SkTypefacePlayback*); bool parseBufferTag(SkReadBuffer&, uint32_t tag, uint32_t size); void flattenToBuffer(SkWriteBuffer&) const; diff --git a/src/core/SkReadBuffer.cpp b/src/core/SkReadBuffer.cpp index 628e3af062..b1d7808dcb 100644 --- a/src/core/SkReadBuffer.cpp +++ b/src/core/SkReadBuffer.cpp @@ -44,9 +44,6 @@ static uint32_t default_flags() { return flags; } -// This has an empty constructor and destructor, and is thread-safe, so we can use a singleton. -static SkImageDeserializer gDefaultImageDeserializer; - SkReadBuffer::SkReadBuffer() { fFlags = default_flags(); fVersion = 0; @@ -57,7 +54,6 @@ SkReadBuffer::SkReadBuffer() { fFactoryArray = nullptr; fFactoryCount = 0; - fImageDeserializer = &gDefaultImageDeserializer; #ifdef DEBUG_NON_DETERMINISTIC_ASSERT fDecodedBitmapIndex = -1; #endif // DEBUG_NON_DETERMINISTIC_ASSERT @@ -74,7 +70,6 @@ SkReadBuffer::SkReadBuffer(const void* data, size_t size) { fFactoryArray = nullptr; fFactoryCount = 0; - fImageDeserializer = &gDefaultImageDeserializer; #ifdef DEBUG_NON_DETERMINISTIC_ASSERT fDecodedBitmapIndex = -1; #endif // DEBUG_NON_DETERMINISTIC_ASSERT @@ -93,7 +88,6 @@ SkReadBuffer::SkReadBuffer(SkStream* stream) { fFactoryArray = nullptr; fFactoryCount = 0; - fImageDeserializer = &gDefaultImageDeserializer; #ifdef DEBUG_NON_DETERMINISTIC_ASSERT fDecodedBitmapIndex = -1; #endif // DEBUG_NON_DETERMINISTIC_ASSERT @@ -103,8 +97,8 @@ SkReadBuffer::~SkReadBuffer() { sk_free(fMemoryPtr); } -void SkReadBuffer::setImageDeserializer(SkImageDeserializer* deserializer) { - fImageDeserializer = deserializer ? deserializer : &gDefaultImageDeserializer; +void SkReadBuffer::setDeserialProcs(const SkDeserialProcs& procs) { + fProcs = procs; } bool SkReadBuffer::readBool() { @@ -131,6 +125,14 @@ int32_t SkReadBuffer::read32() { return fReader.readInt(); } +bool SkReadBuffer::readPad32(void* buffer, size_t bytes) { + if (!fReader.isAvailable(bytes)) { + return false; + } + fReader.read(buffer, bytes); + return true; +} + uint8_t SkReadBuffer::peekByte() { SkASSERT(fReader.available() > 0); return *((uint8_t*) fReader.peek()); @@ -235,10 +237,16 @@ sk_sp SkReadBuffer::readImage() { return nullptr; } - uint32_t encoded_size = this->getArrayCount(); + /* + * What follows is a 32bit encoded size. + * 0 : failure, nothing else to do + * <0 : negative (int32_t) of a custom encoded blob using SerialProcs + * >0 : standard encoded blob size (use MakeFromEncoded) + */ + + int32_t encoded_size = this->read32(); if (encoded_size == 0) { // The image could not be encoded at serialization time - return an empty placeholder. - (void)this->readUInt(); // Swallow that encoded_size == 0 sentinel. return MakeEmptyImage(width, height); } if (encoded_size == 1) { @@ -247,19 +255,33 @@ sk_sp SkReadBuffer::readImage() { return nullptr; } - // The SkImage encoded itself. - sk_sp encoded(this->readByteArrayAsData()); - - int originX = this->read32(); - int originY = this->read32(); + size_t size = SkAbs32(encoded_size); + sk_sp data = SkData::MakeUninitialized(size); + if (!this->readPad32(data->writable_data(), size)) { + this->validate(false); + return nullptr; + } + int32_t originX = this->read32(); + int32_t originY = this->read32(); if (originX < 0 || originY < 0) { this->validate(false); return nullptr; } - const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height); - - sk_sp image = fImageDeserializer->makeFromData(encoded.get(), &subset); + sk_sp image; + if (encoded_size < 0) { // custom encoded, need serial proc + if (fProcs.fImageProc) { + image = fProcs.fImageProc(data->data(), data->size(), fProcs.fImageCtx); + } else { + // Nothing to do (no client proc), but since we've already "read" the custom data, + // wee just leave image as nullptr. + } + } else { + SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height); + image = SkImage::MakeFromEncoded(std::move(data), &subset); + } + // Question: are we correct to return an "empty" image instead of nullptr, if the decoder + // failed for some reason? return image ? image : MakeEmptyImage(width, height); } diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h index b7c54c4c72..ae3fde6818 100644 --- a/src/core/SkReadBuffer.h +++ b/src/core/SkReadBuffer.h @@ -10,6 +10,7 @@ #include "SkColorFilter.h" #include "SkData.h" +#include "SkSerialProcs.h" #include "SkDrawLooper.h" #include "SkImageFilter.h" #include "SkMaskFilter.h" @@ -24,7 +25,6 @@ #include "SkTHash.h" #include "SkWriteBuffer.h" -class SkBitmap; class SkImage; class SkInflator; @@ -157,6 +157,9 @@ public: sk_sp readRasterizer() { return this->readFlattenable(); } sk_sp readShader() { return this->readFlattenable(); } + // Reads SkAlign4(bytes), but will only copy bytes into the buffer. + virtual bool readPad32(void* buffer, size_t bytes); + // binary data and arrays virtual bool readByteArray(void* value, size_t size); virtual bool readColorArray(SkColor* colors, size_t size); @@ -178,6 +181,9 @@ public: // helpers to get info about arrays and binary data virtual uint32_t getArrayCount(); + // If there is a real error (e.g. data is corrupted) this returns null. If the image cannot + // be created (e.g. it was not originally encoded) then this returns an image that doesn't + // draw. sk_sp readImage(); virtual sk_sp readTypeface(); @@ -209,9 +215,11 @@ public: fCustomFactory.set(name, factory); } - // If nullptr is passed, then the default deserializer will be used - // which calls SkImage::MakeFromEncoded() + void setDeserialProcs(const SkDeserialProcs& procs); + +#ifdef SK_SUPPORT_LEGACY_SERIAL_BUFFER_OBJECTS void setImageDeserializer(SkImageDeserializer* factory); +#endif // Default impelementations don't check anything. virtual bool validate(bool isValid) { return isValid; } @@ -274,8 +282,7 @@ private: // Only used if we do not have an fFactoryArray. SkTHashMap fCustomFactory; - // We do not own this ptr, we just use it (guaranteed to never be null) - SkImageDeserializer* fImageDeserializer; + SkDeserialProcs fProcs; #ifdef DEBUG_NON_DETERMINISTIC_ASSERT // Debugging counter to keep track of how many bitmaps we diff --git a/src/core/SkWriteBuffer.cpp b/src/core/SkWriteBuffer.cpp index e6620f9e83..49cccd6783 100644 --- a/src/core/SkWriteBuffer.cpp +++ b/src/core/SkWriteBuffer.cpp @@ -130,13 +130,6 @@ bool SkBinaryWriteBuffer::writeToStream(SkWStream* stream) { return fWriter.writeToStream(stream); } -static void write_encoded_bitmap(SkBinaryWriteBuffer* buffer, SkData* data, - const SkIPoint& origin) { - buffer->writeDataAsByteArray(data); - buffer->write32(origin.fX); - buffer->write32(origin.fY); -} - void SkBinaryWriteBuffer::writeImage(const SkImage* image) { if (fDeduper) { this->write32(fDeduper->findOrDefineImage(const_cast(image))); @@ -146,12 +139,34 @@ void SkBinaryWriteBuffer::writeImage(const SkImage* image) { this->writeInt(image->width()); this->writeInt(image->height()); - sk_sp encoded = image->encodeToData(this->getPixelSerializer()); - if (encoded && encoded->size() > 0) { - write_encoded_bitmap(this, encoded.get(), SkIPoint::Make(0, 0)); - return; + auto write_data = [this](sk_sp data, int sign) { + if (data) { + size_t size = data->size(); + if (size && sk_64_isS32(size)) { + this->write32(SkToS32(size) * sign); + this->writePad32(data->data(), size); // does nothing if size == 0 + this->write32(0); // origin-x + this->write32(0); // origin-y + return; + } + } + this->write32(0); // no data or size too big + }; + + /* + * What follows is a 32bit encoded size. + * 0 : failure, nothing else to do + * <0 : negative (int32_t) of a custom encoded blob using SerialProcs + * >0 : standard encoded blob size (use MakeFromEncoded) + */ + if (fProcs.fImageProc) { + SkDynamicMemoryWStream stream; + if (fProcs.fImageProc(const_cast(image), &stream, fProcs.fImageCtx)) { + write_data(stream.detachAsData(), -1); // -1 signals custom encoder + return; + } } - this->writeUInt(0); // signal no pixels (in place of the size of the encoded data) + write_data(image->encodeToData(), 1); // +1 signals standard encoder } void SkBinaryWriteBuffer::writeTypeface(SkTypeface* obj) { @@ -181,10 +196,6 @@ SkRefCntSet* SkBinaryWriteBuffer::setTypefaceRecorder(SkRefCntSet* rec) { return rec; } -void SkBinaryWriteBuffer::setPixelSerializer(sk_sp serializer) { - fPixelSerializer = std::move(serializer); -} - void SkBinaryWriteBuffer::writeFlattenable(const SkFlattenable* flattenable) { if (nullptr == flattenable) { this->write32(0); diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp index 226a95725c..b9fcd2d4b5 100644 --- a/src/pipe/SkPipeCanvas.cpp +++ b/src/pipe/SkPipeCanvas.cpp @@ -13,6 +13,7 @@ #include "SkPathEffect.h" #include "SkPipeCanvas.h" #include "SkPipeFormat.h" +#include "SkPixelSerializer.h" #include "SkRSXform.h" #include "SkRasterizer.h" #include "SkShader.h" diff --git a/tests/SerialProcsTest.cpp b/tests/SerialProcsTest.cpp new file mode 100644 index 0000000000..7368a20c98 --- /dev/null +++ b/tests/SerialProcsTest.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" +#include "Resources.h" +#include "sk_tool_utils.h" +#include "SkCanvas.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkSerialProcs.h" +#include "SkSurface.h" + +static sk_sp picture_to_image(sk_sp pic) { + SkIRect r = pic->cullRect().round(); + auto surf = SkSurface::MakeRasterN32Premul(r.width(), r.height()); + surf->getCanvas()->drawPicture(pic); + return surf->makeImageSnapshot(); +} + +struct State { + const char* fStr; + SkImage* fImg; +}; + +DEF_TEST(serial_procs_image, reporter) { + auto src_img = GetResourceAsImage("mandrill_128.png"); + const char magic_str[] = "magic signature"; + + const SkSerialImageProc sprocs[] = { + [](SkImage* img, SkWStream* stream, void* ctx) { + return false; + }, + [](SkImage* img, SkWStream* stream, void* ctx) { + auto d = img->encodeToData(); + stream->write(d->data(), d->size()); + return true; + }, + [](SkImage* img, SkWStream* stream, void* ctx) { + State* state = (State*)ctx; + stream->write(state->fStr, strlen(state->fStr)); + return true; + }, + }; + const SkDeserialImageProc dprocs[] = { + [](const void* data, size_t length, void*) -> sk_sp { + SK_ABORT("should not get called"); + return nullptr; + }, + [](const void* data, size_t length, void*) -> sk_sp { + return SkImage::MakeFromEncoded(SkData::MakeWithCopy(data, length)); + }, + [](const void* data, size_t length, void* ctx) -> sk_sp { + State* state = (State*)ctx; + if (length != strlen(state->fStr) || memcmp(data, state->fStr, length)) { + return nullptr; + } + return sk_ref_sp(state->fImg); + }, + }; + + sk_sp pic; + { + SkPictureRecorder rec; + SkCanvas* canvas = rec.beginRecording(128, 128); + canvas->drawImage(src_img, 0, 0, nullptr); + pic = rec.finishRecordingAsPicture(); + } + + State state = { magic_str, src_img.get() }; + + SkSerialProcs sproc; + sproc.fImageCtx = &state; + SkDeserialProcs dproc; + dproc.fImageCtx = &state; + + for (size_t i = 0; i < SK_ARRAY_COUNT(sprocs); ++i) { + sproc.fImageProc = sprocs[i]; + auto data = pic->serialize(sproc); + REPORTER_ASSERT(reporter, data); + + dproc.fImageProc = dprocs[i]; + auto new_pic = SkPicture::MakeFromData(data, dproc); + REPORTER_ASSERT(reporter, data); + + auto dst_img = picture_to_image(new_pic); + REPORTER_ASSERT(reporter, sk_tool_utils::equal_pixels(src_img.get(), dst_img.get())); + } +} + diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h index db7aa3ee43..b81610bc4f 100644 --- a/tools/sk_tool_utils.h +++ b/tools/sk_tool_utils.h @@ -84,7 +84,7 @@ namespace sk_tool_utils { bool respectColorSpaces = false); bool equal_pixels(const SkBitmap&, const SkBitmap&, unsigned maxDiff = 0, bool respectColorSpaces = false); - bool equal_pixels(const SkImage* a, const SkImage* b, unsigned maxDiff, + bool equal_pixels(const SkImage* a, const SkImage* b, unsigned maxDiff = 0, bool respectColorSpaces = false); // private to sk_tool_utils