diff options
author | scroggo <scroggo@google.com> | 2015-11-23 07:20:57 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-23 07:20:57 -0800 |
commit | 3389e00136188800b98ca69488c0418c374fd78b (patch) | |
tree | 6cbb5b6bc005e618daca29be3873f7211780ebbc /tests | |
parent | a98419be092cce2a7ef27a09b6fd8065ff709cff (diff) |
Add SkPngChunkReader.
This class allows a client of SkCodec to read chunks in the data
stream that are not recognized by libpng. This is used by Android
to specify ninepatch data.
Taken from SkImageDecoder::Peeker. Modify the name of the class
and its method to be more specific to their use. Make
SkImageDecoder::Peeker a subclass of the new class, to help stage
the change in Android.
Add a test to verify that it works.
BUG=skia:4574
BUG=skia:3257
Review URL: https://codereview.chromium.org/1040453002
Diffstat (limited to 'tests')
-rw-r--r-- | tests/CodexTest.cpp | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp index febaf7d43b..b53cbe1a4d 100644 --- a/tests/CodexTest.cpp +++ b/tests/CodexTest.cpp @@ -12,8 +12,12 @@ #include "SkData.h" #include "SkMD5.h" #include "SkRandom.h" +#include "SkStream.h" +#include "SkPngChunkReader.h" #include "Test.h" +#include "png.h" + static SkStreamAsset* resource(const char path[]) { SkString fullPath = GetResourcePath(path); return SkStream::NewFromFile(fullPath.c_str()); @@ -685,3 +689,159 @@ DEF_TEST(Codec_Params, r) { test_invalid_parameters(r, "index8.png"); test_invalid_parameters(r, "mandrill.wbmp"); } + +static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { + SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); + if (!sk_stream->write(data, len)) { + png_error(png_ptr, "sk_write_fn Error!"); + } +} + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +DEF_TEST(Codec_pngChunkReader, r) { + // Create a dummy bitmap. Use unpremul RGBA for libpng. + SkBitmap bm; + const int w = 1; + const int h = 1; + const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, + kUnpremul_SkAlphaType); + bm.setInfo(bmInfo); + bm.allocPixels(); + bm.eraseColor(SK_ColorBLUE); + SkMD5::Digest goodDigest; + md5(bm, &goodDigest); + + // Write to a png file. + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + REPORTER_ASSERT(r, png); + if (!png) { + return; + } + + png_infop info = png_create_info_struct(png); + REPORTER_ASSERT(r, info); + if (!info) { + png_destroy_write_struct(&png, nullptr); + return; + } + + if (setjmp(png_jmpbuf(png))) { + ERRORF(r, "failed writing png"); + png_destroy_write_struct(&png, &info); + return; + } + + SkDynamicMemoryWStream wStream; + png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); + + png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + // Create some chunks that match the Android framework's use. + static png_unknown_chunk gUnknowns[] = { + { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_PLTE }, + { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_PLTE }, + { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_PLTE }, + }; + + png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3); + png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); +#if PNG_LIBPNG_VER < 10600 + /* Deal with unknown chunk location bug in 1.5.x and earlier */ + png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_PLTE); + png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_PLTE); +#endif + + png_write_info(png, info); + + for (int j = 0; j < h; j++) { + png_bytep row = (png_bytep)(bm.getAddr(0, j)); + png_write_rows(png, &row, 1); + } + png_write_end(png, info); + png_destroy_write_struct(&png, &info); + + class ChunkReader : public SkPngChunkReader { + public: + ChunkReader(skiatest::Reporter* r) + : fReporter(r) + { + this->reset(); + } + + bool readChunk(const char tag[], const void* data, size_t length) override { + for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { + if (!strcmp(tag, (const char*) gUnknowns[i].name)) { + // Tag matches. This should have been the first time we see it. + REPORTER_ASSERT(fReporter, !fSeen[i]); + fSeen[i] = true; + + // Data and length should match + REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); + REPORTER_ASSERT(fReporter, !strcmp((const char*) data, + (const char*) gUnknowns[i].data)); + return true; + } + } + ERRORF(fReporter, "Saw an unexpected unknown chunk."); + return true; + } + + bool allHaveBeenSeen() { + bool ret = true; + for (auto seen : fSeen) { + ret &= seen; + } + return ret; + } + + void reset() { + sk_bzero(fSeen, sizeof(fSeen)); + } + + private: + skiatest::Reporter* fReporter; // Unowned + bool fSeen[3]; + }; + + ChunkReader chunkReader(r); + + // Now read the file with SkCodec. + SkAutoTUnref<SkData> data(wStream.copyToData()); + SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data, &chunkReader)); + REPORTER_ASSERT(r, codec); + if (!codec) { + return; + } + + // Now compare to the original. + SkBitmap decodedBm; + decodedBm.setInfo(codec->getInfo()); + decodedBm.allocPixels(); + SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), + decodedBm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + + if (decodedBm.colorType() != bm.colorType()) { + SkBitmap tmp; + bool success = decodedBm.copyTo(&tmp, bm.colorType()); + REPORTER_ASSERT(r, success); + if (!success) { + return; + } + + tmp.swap(decodedBm); + } + + compare_to_good_digest(r, goodDigest, decodedBm); + REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); + + // Decoding again will read the chunks again. + chunkReader.reset(); + REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); + result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); + REPORTER_ASSERT(r, SkCodec::kSuccess == result); + REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); +} +#endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED |