Handle zero-length encoded images gracefully during deserialization
Image encoding may fail during serialization, resulting in zero-length encoded data in the SKP. Instead of invalidating the stream (and preventing deserialization of the whole picture) we can instantiate placeholder images. BUG=skia:4285 R=reed@google.com,robertphillips@google.com Review URL: https://codereview.chromium.org/1308273011
This commit is contained in:
parent
19e9b35750
commit
c3470340b6
@ -103,9 +103,7 @@ public:
|
||||
|
||||
/**
|
||||
* Serialize to a stream. If non NULL, serializer will be used to serialize
|
||||
* any bitmaps in the picture.
|
||||
*
|
||||
* TODO: Use serializer to serialize SkImages as well.
|
||||
* bitmaps and images in the picture.
|
||||
*/
|
||||
void serialize(SkWStream*, SkPixelSerializer* = NULL) const;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include <new>
|
||||
#include "SkImageGenerator.h"
|
||||
#include "SkPictureData.h"
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkReadBuffer.h"
|
||||
@ -433,6 +434,20 @@ bool SkPictureData::parseStreamTag(SkStream* stream,
|
||||
return true; // success
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// This generator intentionally should always fail on all attempts to get its pixels,
|
||||
// simulating a bad or empty codec stream.
|
||||
class EmptyImageGenerator final : public SkImageGenerator {
|
||||
public:
|
||||
EmptyImageGenerator(const SkImageInfo& info) : INHERITED(info) { }
|
||||
|
||||
private:
|
||||
typedef SkImageGenerator INHERITED;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static const SkImage* create_image_from_buffer(SkReadBuffer& buffer) {
|
||||
int width = buffer.read32();
|
||||
int height = buffer.read32();
|
||||
@ -442,9 +457,15 @@ static const SkImage* create_image_from_buffer(SkReadBuffer& buffer) {
|
||||
}
|
||||
|
||||
SkAutoTUnref<SkData> encoded(buffer.readByteArrayAsData());
|
||||
if (encoded->size() == 0) {
|
||||
// The image could not be encoded at serialization time - return an empty placeholder.
|
||||
return SkImage::NewFromGenerator(
|
||||
new EmptyImageGenerator(SkImageInfo::MakeN32Premul(width, height)));
|
||||
}
|
||||
|
||||
int originX = buffer.read32();
|
||||
int originY = buffer.read32();
|
||||
if (0 == encoded->size() || originX < 0 || originY < 0) {
|
||||
if (originX < 0 || originY < 0) {
|
||||
buffer.validate(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ void SkWriteBuffer::writeImage(const SkImage* image) {
|
||||
this->writeInt(image->height());
|
||||
|
||||
SkAutoTUnref<SkData> encoded(image->encode(this->getPixelSerializer()));
|
||||
if (encoded) {
|
||||
if (encoded && encoded->size() > 0) {
|
||||
write_encoded_bitmap(this, encoded, SkIPoint::Make(0, 0));
|
||||
return;
|
||||
}
|
||||
|
@ -10,8 +10,11 @@
|
||||
#include "SkDevice.h"
|
||||
#include "SkImageEncoder.h"
|
||||
#include "SkImage_Base.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
#include "SkPixelSerializer.h"
|
||||
#include "SkRRect.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkSurface.h"
|
||||
#include "SkUtils.h"
|
||||
#include "Test.h"
|
||||
@ -110,31 +113,75 @@ namespace {
|
||||
const char* kSerializedData = "serialized";
|
||||
|
||||
class MockSerializer : public SkPixelSerializer {
|
||||
public:
|
||||
MockSerializer(SkData* (*func)()) : fFunc(func), fDidEncode(false) { }
|
||||
|
||||
bool didEncode() const { return fDidEncode; }
|
||||
|
||||
protected:
|
||||
bool onUseEncodedData(const void*, size_t) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkData* onEncodePixels(const SkImageInfo&, const void*, size_t) override {
|
||||
return SkData::NewWithCString(kSerializedData);
|
||||
fDidEncode = true;
|
||||
return fFunc();
|
||||
}
|
||||
|
||||
private:
|
||||
SkData* (*fFunc)();
|
||||
bool fDidEncode;
|
||||
|
||||
typedef SkPixelSerializer INHERITED;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// Test that SkImage encoding observes custom pixel serializers.
|
||||
DEF_TEST(Image_Encode_Serializer, reporter) {
|
||||
MockSerializer serializer;
|
||||
MockSerializer serializer([]() -> SkData* { return SkData::NewWithCString(kSerializedData); });
|
||||
const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10);
|
||||
SkAutoTUnref<SkImage> image(make_image(nullptr, 20, 20, ir));
|
||||
SkAutoTUnref<SkData> encoded(image->encode(&serializer));
|
||||
SkAutoTUnref<SkData> reference(SkData::NewWithCString(kSerializedData));
|
||||
|
||||
REPORTER_ASSERT(reporter, serializer.didEncode());
|
||||
REPORTER_ASSERT(reporter, encoded);
|
||||
REPORTER_ASSERT(reporter, encoded->size() > 0);
|
||||
REPORTER_ASSERT(reporter, encoded->equals(reference));
|
||||
}
|
||||
|
||||
// Test that image encoding failures do not break picture serialization/deserialization.
|
||||
DEF_TEST(Image_Serialize_Encoding_Failure, reporter) {
|
||||
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
|
||||
surface->getCanvas()->clear(SK_ColorGREEN);
|
||||
SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
|
||||
REPORTER_ASSERT(reporter, image);
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas* canvas = recorder.beginRecording(100, 100);
|
||||
canvas->drawImage(image, 0, 0);
|
||||
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
||||
REPORTER_ASSERT(reporter, picture);
|
||||
REPORTER_ASSERT(reporter, picture->approximateOpCount() > 0);
|
||||
|
||||
MockSerializer emptySerializer([]() -> SkData* { return SkData::NewEmpty(); });
|
||||
MockSerializer nullSerializer([]() -> SkData* { return nullptr; });
|
||||
MockSerializer* serializers[] = { &emptySerializer, &nullSerializer };
|
||||
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(serializers); ++i) {
|
||||
SkDynamicMemoryWStream wstream;
|
||||
REPORTER_ASSERT(reporter, !serializers[i]->didEncode());
|
||||
picture->serialize(&wstream, serializers[i]);
|
||||
REPORTER_ASSERT(reporter, serializers[i]->didEncode());
|
||||
|
||||
SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
|
||||
SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rstream));
|
||||
REPORTER_ASSERT(reporter, deserialized);
|
||||
REPORTER_ASSERT(reporter, deserialized->approximateOpCount() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(Image_NewRasterCopy, reporter) {
|
||||
const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0);
|
||||
const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
|
||||
|
Loading…
Reference in New Issue
Block a user