diff --git a/include/core/SkImage.h b/include/core/SkImage.h index 2bcb90b5d3..b302792c58 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -21,6 +21,7 @@ class SkColorTable; class SkImageGenerator; class SkPaint; class SkPicture; +class SkPixelSerializer; class SkString; class SkSurface; class SkSurfaceProps; @@ -234,12 +235,27 @@ public: * * If the image type cannot be encoded, or the requested encoder type is * not supported, this will return NULL. + * + * Note: this will attempt to encode the image's pixels in the specified format, + * even if the image returns a data from refEncoded(). That data will be ignored. */ SkData* encode(SkImageEncoder::Type, int quality) const; - SkData* encode() const { - return this->encode(SkImageEncoder::kPNG_Type, 100); - } + /** + * Encode the image and return the result as a caller-managed SkData. This will + * attempt to reuse existing encoded data (as returned by refEncoded). + * + * We defer to the SkPixelSerializer both for vetting existing encoded data + * (useEncodedData) and for encoding the image (encodePixels) when no such data is + * present or is rejected by the serializer. + * + * If not specified, we use a default serializer which 1) always accepts existing data + * (in any format) and 2) encodes to PNG. + * + * If no compatible encoded data exists and encoding fails, this method will also + * fail (return NULL). + */ + SkData* encode(SkPixelSerializer* = nullptr) const; /** * If the image already has its contents in encoded form (e.g. PNG or JPEG), return a ref diff --git a/src/core/SkWriteBuffer.cpp b/src/core/SkWriteBuffer.cpp index 7ddc77f93b..ac3d678dbc 100644 --- a/src/core/SkWriteBuffer.cpp +++ b/src/core/SkWriteBuffer.cpp @@ -218,31 +218,16 @@ void SkWriteBuffer::writeBitmap(const SkBitmap& bitmap) { SkBitmap::WriteRawPixels(this, bitmap); } -static bool try_write_encoded(SkWriteBuffer* buffer, SkData* encoded) { - SkPixelSerializer* ps = buffer->getPixelSerializer(); - // Assumes that if the client did not set a serializer, they are - // happy to get the encoded data. - if (!ps || ps->useEncodedData(encoded->data(), encoded->size())) { - write_encoded_bitmap(buffer, encoded, SkIPoint::Make(0, 0)); - return true; - } - return false; -} - void SkWriteBuffer::writeImage(const SkImage* image) { this->writeInt(image->width()); this->writeInt(image->height()); - SkAutoTUnref encoded(image->refEncoded()); - if (encoded && try_write_encoded(this, encoded)) { + SkAutoTUnref encoded(image->encode(this->getPixelSerializer())); + if (encoded) { + write_encoded_bitmap(this, encoded, SkIPoint::Make(0, 0)); return; } - encoded.reset(image->encode(SkImageEncoder::kPNG_Type, 100)); - if (encoded && try_write_encoded(this, encoded)) { - return; - } - this->writeUInt(0); // signal no pixels (in place of the size of the encoded data) } diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index c55d28a177..5a67c84caf 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -13,6 +13,7 @@ #include "SkImage_Base.h" #include "SkNextID.h" #include "SkPixelRef.h" +#include "SkPixelSerializer.h" #include "SkReadPixelsRec.h" #include "SkString.h" #include "SkSurface.h" @@ -71,6 +72,40 @@ SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const { return nullptr; } +namespace { + +class DefaultSerializer : public SkPixelSerializer { +protected: + bool onUseEncodedData(const void *data, size_t len) override { + return true; + } + + SkData* onEncodePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes) override { + return SkImageEncoder::EncodeData(info, pixels, rowBytes, SkImageEncoder::kPNG_Type, 100); + } +}; + +} // anonymous namespace + +SkData* SkImage::encode(SkPixelSerializer* serializer) const { + DefaultSerializer defaultSerializer; + SkPixelSerializer* effectiveSerializer = serializer ? serializer : &defaultSerializer; + + SkAutoTUnref encoded(this->refEncoded()); + if (encoded && effectiveSerializer->useEncodedData(encoded->data(), encoded->size())) { + return encoded.detach(); + } + + SkBitmap bm; + SkAutoPixmapUnlock apu; + if (as_IB(this)->getROPixels(&bm) && bm.requestLock(&apu)) { + const SkPixmap& pmap = apu.pixmap(); + return effectiveSerializer->encodePixels(pmap.info(), pmap.addr(), pmap.rowBytes()); + } + + return nullptr; +} + SkData* SkImage::refEncoded() const { return as_IB(this)->onRefEncoded(); } diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index bbf6682084..d271966979 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -10,6 +10,7 @@ #include "SkDevice.h" #include "SkImageEncoder.h" #include "SkImage_Base.h" +#include "SkPixelSerializer.h" #include "SkRRect.h" #include "SkSurface.h" #include "SkUtils.h" @@ -104,6 +105,36 @@ DEF_GPUTEST(Image_Encode_Gpu, reporter, factory) { } #endif +namespace { + +const char* kSerializedData = "serialized"; + +class MockSerializer : public SkPixelSerializer { +protected: + bool onUseEncodedData(const void*, size_t) override { + return false; + } + + SkData* onEncodePixels(const SkImageInfo&, const void*, size_t) override { + return SkData::NewWithCString(kSerializedData); + } +}; + +} // anonymous namespace + +// Test that SkImage encoding observes custom pixel serializers. +DEF_TEST(Image_Encode_Serializer, reporter) { + MockSerializer serializer; + const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10); + SkAutoTUnref image(make_image(nullptr, 20, 20, ir)); + SkAutoTUnref encoded(image->encode(&serializer)); + SkAutoTUnref reference(SkData::NewWithCString(kSerializedData)); + + REPORTER_ASSERT(reporter, encoded); + REPORTER_ASSERT(reporter, encoded->size() > 0); + REPORTER_ASSERT(reporter, encoded->equals(reference)); +} + DEF_TEST(Image_NewRasterCopy, reporter) { const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0); const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);