Index: tests/CodexTest.cpp |
diff --git a/tests/CodexTest.cpp b/tests/CodexTest.cpp |
index febaf7d43b84ee5e0176c6218f433b8d02832ae6..b53cbe1a4d7dd6c468af08f678f4b27087e91be6 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 |