OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "Resources.h" | 8 #include "Resources.h" |
9 #include "SkAndroidCodec.h" | 9 #include "SkAndroidCodec.h" |
10 #include "SkBitmap.h" | 10 #include "SkBitmap.h" |
11 #include "SkCodec.h" | 11 #include "SkCodec.h" |
12 #include "SkData.h" | 12 #include "SkData.h" |
13 #include "SkMD5.h" | 13 #include "SkMD5.h" |
14 #include "SkRandom.h" | 14 #include "SkRandom.h" |
| 15 #include "SkStream.h" |
| 16 #include "SkPngChunkReader.h" |
15 #include "Test.h" | 17 #include "Test.h" |
16 | 18 |
| 19 #include "png.h" |
| 20 |
17 static SkStreamAsset* resource(const char path[]) { | 21 static SkStreamAsset* resource(const char path[]) { |
18 SkString fullPath = GetResourcePath(path); | 22 SkString fullPath = GetResourcePath(path); |
19 return SkStream::NewFromFile(fullPath.c_str()); | 23 return SkStream::NewFromFile(fullPath.c_str()); |
20 } | 24 } |
21 | 25 |
22 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { | 26 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { |
23 SkAutoLockPixels autoLockPixels(bm); | 27 SkAutoLockPixels autoLockPixels(bm); |
24 SkASSERT(bm.getPixels()); | 28 SkASSERT(bm.getPixels()); |
25 SkMD5 md5; | 29 SkMD5 md5; |
26 size_t rowLen = bm.info().bytesPerPixel() * bm.width(); | 30 size_t rowLen = bm.info().bytesPerPixel() * bm.width(); |
(...skipping 651 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
678 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); | 682 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); |
679 result = decoder->startScanlineDecode( | 683 result = decoder->startScanlineDecode( |
680 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); | 684 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); |
681 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); | 685 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); |
682 } | 686 } |
683 | 687 |
684 DEF_TEST(Codec_Params, r) { | 688 DEF_TEST(Codec_Params, r) { |
685 test_invalid_parameters(r, "index8.png"); | 689 test_invalid_parameters(r, "index8.png"); |
686 test_invalid_parameters(r, "mandrill.wbmp"); | 690 test_invalid_parameters(r, "mandrill.wbmp"); |
687 } | 691 } |
| 692 |
| 693 static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t
len) { |
| 694 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); |
| 695 if (!sk_stream->write(data, len)) { |
| 696 png_error(png_ptr, "sk_write_fn Error!"); |
| 697 } |
| 698 } |
| 699 |
| 700 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED |
| 701 DEF_TEST(Codec_pngChunkReader, r) { |
| 702 // Create a dummy bitmap. Use unpremul RGBA for libpng. |
| 703 SkBitmap bm; |
| 704 const int w = 1; |
| 705 const int h = 1; |
| 706 const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, |
| 707 kUnpremul_SkAlphaType); |
| 708 bm.setInfo(bmInfo); |
| 709 bm.allocPixels(); |
| 710 bm.eraseColor(SK_ColorBLUE); |
| 711 SkMD5::Digest goodDigest; |
| 712 md5(bm, &goodDigest); |
| 713 |
| 714 // Write to a png file. |
| 715 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nu
llptr, nullptr); |
| 716 REPORTER_ASSERT(r, png); |
| 717 if (!png) { |
| 718 return; |
| 719 } |
| 720 |
| 721 png_infop info = png_create_info_struct(png); |
| 722 REPORTER_ASSERT(r, info); |
| 723 if (!info) { |
| 724 png_destroy_write_struct(&png, nullptr); |
| 725 return; |
| 726 } |
| 727 |
| 728 if (setjmp(png_jmpbuf(png))) { |
| 729 ERRORF(r, "failed writing png"); |
| 730 png_destroy_write_struct(&png, &info); |
| 731 return; |
| 732 } |
| 733 |
| 734 SkDynamicMemoryWStream wStream; |
| 735 png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); |
| 736 |
| 737 png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, |
| 738 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, |
| 739 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); |
| 740 |
| 741 // Create some chunks that match the Android framework's use. |
| 742 static png_unknown_chunk gUnknowns[] = { |
| 743 { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_PLTE }, |
| 744 { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_PL
TE }, |
| 745 { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_
PLTE }, |
| 746 }; |
| 747 |
| 748 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0
npLb\0npTc\0", 3); |
| 749 png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); |
| 750 #if PNG_LIBPNG_VER < 10600 |
| 751 /* Deal with unknown chunk location bug in 1.5.x and earlier */ |
| 752 png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_PLTE); |
| 753 png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_PLTE); |
| 754 #endif |
| 755 |
| 756 png_write_info(png, info); |
| 757 |
| 758 for (int j = 0; j < h; j++) { |
| 759 png_bytep row = (png_bytep)(bm.getAddr(0, j)); |
| 760 png_write_rows(png, &row, 1); |
| 761 } |
| 762 png_write_end(png, info); |
| 763 png_destroy_write_struct(&png, &info); |
| 764 |
| 765 class ChunkReader : public SkPngChunkReader { |
| 766 public: |
| 767 ChunkReader(skiatest::Reporter* r) |
| 768 : fReporter(r) |
| 769 { |
| 770 this->reset(); |
| 771 } |
| 772 |
| 773 bool readChunk(const char tag[], const void* data, size_t length) overri
de { |
| 774 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { |
| 775 if (!strcmp(tag, (const char*) gUnknowns[i].name)) { |
| 776 // Tag matches. This should have been the first time we see
it. |
| 777 REPORTER_ASSERT(fReporter, !fSeen[i]); |
| 778 fSeen[i] = true; |
| 779 |
| 780 // Data and length should match |
| 781 REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); |
| 782 REPORTER_ASSERT(fReporter, !strcmp((const char*) data, |
| 783 (const char*) gUnknowns[i
].data)); |
| 784 return true; |
| 785 } |
| 786 } |
| 787 ERRORF(fReporter, "Saw an unexpected unknown chunk."); |
| 788 return true; |
| 789 } |
| 790 |
| 791 bool allHaveBeenSeen() { |
| 792 bool ret = true; |
| 793 for (auto seen : fSeen) { |
| 794 ret &= seen; |
| 795 } |
| 796 return ret; |
| 797 } |
| 798 |
| 799 void reset() { |
| 800 sk_bzero(fSeen, sizeof(fSeen)); |
| 801 } |
| 802 |
| 803 private: |
| 804 skiatest::Reporter* fReporter; // Unowned |
| 805 bool fSeen[3]; |
| 806 }; |
| 807 |
| 808 ChunkReader chunkReader(r); |
| 809 |
| 810 // Now read the file with SkCodec. |
| 811 SkAutoTUnref<SkData> data(wStream.copyToData()); |
| 812 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data, &chunkReader)); |
| 813 REPORTER_ASSERT(r, codec); |
| 814 if (!codec) { |
| 815 return; |
| 816 } |
| 817 |
| 818 // Now compare to the original. |
| 819 SkBitmap decodedBm; |
| 820 decodedBm.setInfo(codec->getInfo()); |
| 821 decodedBm.allocPixels(); |
| 822 SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPix
els(), |
| 823 decodedBm.rowBytes()); |
| 824 REPORTER_ASSERT(r, SkCodec::kSuccess == result); |
| 825 |
| 826 if (decodedBm.colorType() != bm.colorType()) { |
| 827 SkBitmap tmp; |
| 828 bool success = decodedBm.copyTo(&tmp, bm.colorType()); |
| 829 REPORTER_ASSERT(r, success); |
| 830 if (!success) { |
| 831 return; |
| 832 } |
| 833 |
| 834 tmp.swap(decodedBm); |
| 835 } |
| 836 |
| 837 compare_to_good_digest(r, goodDigest, decodedBm); |
| 838 REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); |
| 839 |
| 840 // Decoding again will read the chunks again. |
| 841 chunkReader.reset(); |
| 842 REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); |
| 843 result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm
.rowBytes()); |
| 844 REPORTER_ASSERT(r, SkCodec::kSuccess == result); |
| 845 REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); |
| 846 } |
| 847 #endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED |
OLD | NEW |