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" | |
17 #include "Test.h" | 15 #include "Test.h" |
18 | 16 |
19 #include "png.h" | |
20 | |
21 static SkStreamAsset* resource(const char path[]) { | 17 static SkStreamAsset* resource(const char path[]) { |
22 SkString fullPath = GetResourcePath(path); | 18 SkString fullPath = GetResourcePath(path); |
23 return SkStream::NewFromFile(fullPath.c_str()); | 19 return SkStream::NewFromFile(fullPath.c_str()); |
24 } | 20 } |
25 | 21 |
26 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { | 22 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { |
27 SkAutoLockPixels autoLockPixels(bm); | 23 SkAutoLockPixels autoLockPixels(bm); |
28 SkASSERT(bm.getPixels()); | 24 SkASSERT(bm.getPixels()); |
29 SkMD5 md5; | 25 SkMD5 md5; |
30 size_t rowLen = bm.info().bytesPerPixel() * bm.width(); | 26 size_t rowLen = bm.info().bytesPerPixel() * bm.width(); |
(...skipping 651 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
682 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); | 678 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); |
683 result = decoder->startScanlineDecode( | 679 result = decoder->startScanlineDecode( |
684 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); | 680 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); |
685 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); | 681 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); |
686 } | 682 } |
687 | 683 |
688 DEF_TEST(Codec_Params, r) { | 684 DEF_TEST(Codec_Params, r) { |
689 test_invalid_parameters(r, "index8.png"); | 685 test_invalid_parameters(r, "index8.png"); |
690 test_invalid_parameters(r, "mandrill.wbmp"); | 686 test_invalid_parameters(r, "mandrill.wbmp"); |
691 } | 687 } |
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 |