Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "platform/image-decoders/png/PNGImageDecoder.h" | |
| 6 | |
| 7 #include "platform/image-decoders/ImageDecoderTestHelpers.h" | |
| 8 #include "testing/gtest/include/gtest/gtest.h" | |
| 9 #include <memory> | |
| 10 | |
| 11 namespace blink { | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 std::unique_ptr<ImageDecoder> createDecoder(ImageDecoder::AlphaOption alphaOptio n) | |
| 16 { | |
| 17 return wrapUnique(new PNGImageDecoder(alphaOption, | |
| 18 ImageDecoder::GammaAndColorProfileAppl ied, | |
| 19 ImageDecoder::noDecodedImageByteLimit) ); | |
| 20 } | |
| 21 | |
| 22 std::unique_ptr<ImageDecoder> createDecoder() | |
| 23 { | |
| 24 return createDecoder(ImageDecoder::AlphaNotPremultiplied); | |
| 25 } | |
| 26 | |
| 27 std::unique_ptr<ImageDecoder> createDecoderWithPngData(const char* pngFile) | |
| 28 { | |
| 29 auto decoder = createDecoder(); | |
| 30 auto data = readFile(pngFile); | |
| 31 EXPECT_FALSE(data->isEmpty()); | |
| 32 decoder->setData(data.get(), true); | |
| 33 return decoder; | |
| 34 } | |
| 35 | |
| 36 void testSize(const char* pngFile, IntSize expectedSize) | |
| 37 { | |
| 38 auto decoder = createDecoderWithPngData(pngFile); | |
| 39 EXPECT_TRUE(decoder->isSizeAvailable()); | |
| 40 EXPECT_EQ(expectedSize, decoder->size()); | |
| 41 } | |
| 42 | |
| 43 void testRepetitionCount(const char* pngFile, int expectedRepetitionCount) | |
| 44 { | |
| 45 auto decoder = createDecoderWithPngData(pngFile); | |
| 46 // Decode frame count should see the number of repetitions as well. | |
| 47 decoder->frameCount(); | |
| 48 EXPECT_FALSE(decoder->failed()); | |
| 49 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); | |
| 50 } | |
| 51 | |
| 52 // Test whether querying for the size of the image works if we present the | |
| 53 // data byte by byte. | |
| 54 void testSizeByteByByte(const char *pngFile, size_t bytesNeededToDecodeSize, | |
| 55 IntSize expectedSize) | |
| 56 { | |
| 57 auto decoder = createDecoder(); | |
| 58 auto data = readFile(pngFile); | |
| 59 ASSERT_FALSE(data->isEmpty()); | |
| 60 ASSERT_LT(bytesNeededToDecodeSize, data->size()); | |
| 61 | |
| 62 for (size_t length = 1; length <= bytesNeededToDecodeSize; length++) { | |
| 63 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), | |
| 64 length); | |
| 65 decoder->setData(tempData.get(), false); | |
| 66 | |
| 67 if (length < bytesNeededToDecodeSize) { | |
| 68 EXPECT_FALSE(decoder->isSizeAvailable()); | |
| 69 EXPECT_TRUE(decoder->size().isEmpty()); | |
| 70 EXPECT_FALSE(decoder->failed()); | |
| 71 } else { | |
| 72 EXPECT_TRUE(decoder->isSizeAvailable()); | |
| 73 EXPECT_EQ(expectedSize, decoder->size()); | |
| 74 } | |
| 75 } | |
| 76 EXPECT_FALSE(decoder->failed()); | |
| 77 } | |
| 78 | |
| 79 struct PublicFrameInfo { | |
| 80 size_t duration; | |
| 81 IntRect frameRect; | |
| 82 ImageFrame::AlphaBlendSource alphaBlend; | |
| 83 ImageFrame::DisposalMethod disposalMethod; | |
| 84 }; | |
| 85 | |
| 86 // This is the frame data for the following PNG image: | |
| 87 // /LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png | |
| 88 static PublicFrameInfo pngAnimatedFrameInfo[] = { | |
| 89 {500, {IntPoint(0, 0), IntSize(5, 5)}, ImageFrame::BlendAtopBgcolor, | |
| 90 ImageFrame::DisposeKeep}, | |
| 91 {900, {IntPoint(1, 1), IntSize(3, 1)}, ImageFrame::BlendAtopBgcolor, | |
| 92 ImageFrame::DisposeOverwriteBgcolor}, | |
| 93 {2000, {IntPoint(1, 2), IntSize(3, 2)}, ImageFrame::BlendAtopPreviousFrame, | |
| 94 ImageFrame::DisposeKeep}, | |
| 95 {1500, {IntPoint(1, 2), IntSize(3, 1)}, ImageFrame::BlendAtopBgcolor, | |
| 96 ImageFrame::DisposeKeep}, | |
| 97 }; | |
| 98 | |
| 99 void compareFrameWithExpectation(const PublicFrameInfo& expected, | |
| 100 const ImageFrame* frame) | |
| 101 { | |
| 102 EXPECT_EQ(expected.duration, frame->duration()); | |
| 103 EXPECT_EQ(expected.frameRect, frame->originalFrameRect()); | |
| 104 EXPECT_EQ(expected.disposalMethod, frame->getDisposalMethod()); | |
| 105 EXPECT_EQ(expected.alphaBlend, frame->getAlphaBlendSource()); | |
| 106 } | |
| 107 | |
| 108 } // Anonymous namespace | |
| 109 | |
| 110 // Animated PNG Tests | |
| 111 | |
| 112 TEST(AnimatedPNGTests, sizeTest) | |
| 113 { | |
| 114 testSize("/LayoutTests/fast/images/resources/png-animated-idat-part-of-anima tion.png", IntSize(5, 5)); | |
| 115 testSize("/LayoutTests/fast/images/resources/png-animated-idat-not-part-of-a nimation.png", IntSize(227, 35)); | |
| 116 } | |
| 117 | |
| 118 TEST(AnimatedPNGTests, repetitionCountTest) | |
| 119 { | |
| 120 testRepetitionCount("/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png", 7u); | |
| 121 testRepetitionCount("/LayoutTests/fast/images/resources/png-animated-idat-no t-part-of-animation.png", cAnimationLoopInfinite); | |
| 122 } | |
| 123 | |
| 124 // Test if the decoded metdata corresponds to the defined expectations | |
| 125 TEST(AnimatedPNGTests, MetaDataTest) | |
| 126 { | |
| 127 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat- part-of-animation.png"; | |
| 128 constexpr size_t expectedFrameCount = 4; | |
| 129 | |
| 130 auto decoder = createDecoderWithPngData(pngFile); | |
| 131 ASSERT_EQ(expectedFrameCount, decoder->frameCount()); | |
| 132 for (size_t i = 0; i < expectedFrameCount; i++) | |
| 133 compareFrameWithExpectation(pngAnimatedFrameInfo[i], | |
| 134 decoder->frameBufferAtIndex(i)); | |
| 135 } | |
| 136 | |
| 137 TEST(AnimatedPNGTests, ByteByByteSizeAvailable) | |
| 138 { | |
| 139 testSizeByteByByte("/LayoutTests/fast/images/resources/png-animated-idat-par t-of-animation.png", | |
| 140 141u, IntSize(5, 5)); | |
| 141 testSizeByteByByte("/LayoutTests/fast/images/resources/png-animated-idat-not -part-of-animation.png", | |
| 142 79u, IntSize(227, 35)); | |
| 143 } | |
| 144 | |
| 145 // Test whether the frame metadata decoding also works when we provide the data | |
| 146 // byte by byte. This should cover the case when the client does not provide | |
| 147 // all data at once. At given offsets, we expect frames to become available. | |
| 148 // This test checks whether that is the case, and if so, if the frame data is | |
| 149 // equal to what we expected. | |
| 150 TEST(AnimatedPNGTests, ByteByByteMetaData) | |
| 151 { | |
| 152 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat- part-of-animation.png"; | |
| 153 constexpr size_t expectedFrameCount = 4; | |
| 154 | |
| 155 // These are the byte offsets where each frame should have been parsed. | |
| 156 // It boils down to the offset of the first fcTL / IEND after the last | |
| 157 // frame data chunk, plus 8 bytes for recognition. | |
| 158 size_t frameOffsets[expectedFrameCount] = {180, 249, 322, 430}; | |
| 159 | |
| 160 auto decoder = createDecoder(); | |
| 161 auto data = readFile(pngFile); | |
| 162 ASSERT_FALSE(data->isEmpty()); | |
| 163 size_t framesParsed = 0; | |
| 164 | |
| 165 for (size_t length = 1; length <= frameOffsets[expectedFrameCount - 1]; leng th++) { | |
| 166 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), | |
| 167 length); | |
| 168 decoder->setData(tempData.get(), false); | |
| 169 EXPECT_FALSE(decoder->failed()); | |
| 170 | |
| 171 if (length < frameOffsets[framesParsed]) { | |
| 172 EXPECT_EQ(framesParsed, decoder->frameCount()); | |
| 173 } else { | |
| 174 ASSERT_EQ(framesParsed + 1, decoder->frameCount()); | |
| 175 compareFrameWithExpectation(pngAnimatedFrameInfo[framesParsed], | |
| 176 decoder->frameBufferAtIndex(framesParsed )); | |
| 177 framesParsed++; | |
| 178 } | |
| 179 } | |
| 180 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); | |
| 181 EXPECT_FALSE(decoder->failed()); | |
| 182 } | |
| 183 | |
| 184 // This tests if the frame count gets set correctly when parsing frameCount | |
| 185 // fails in one of the parsing queries. | |
| 186 // | |
| 187 // First, enough data is provided such that two frames should be registered. | |
| 188 // The decoder should at this point not be in the failed status. | |
| 189 // | |
| 190 // Then, we provide the rest of the data except for the last IEND chunk, but | |
| 191 // tell the decoder that this is all the data we have. Now, the decoder should | |
| 192 // be in the failed state since all data is provided but no IEND chunk has been | |
| 193 // seen. The frame count should be three, since one extra frame should be | |
| 194 // discovered. The fourth frame should *not* be registered since the reader | |
|
scroggo_chromium
2016/10/17 15:27:06
This is the natural extension of what we discussed
| |
| 195 // should not be able to determine where the frame ends. | |
| 196 TEST(AnimatedPNGTests, FrameCountWithTruncatedData) | |
| 197 { | |
| 198 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat- part-of-animation.png"; | |
| 199 auto decoder = createDecoder(); | |
| 200 auto data = readFile(pngFile); | |
| 201 ASSERT_FALSE(data->isEmpty()); | |
| 202 | |
| 203 // Parse up to and including the first two frames | |
| 204 const size_t offsetTwoFrames = 249; | |
| 205 const size_t expectedFramesAfter249Bytes = 2; | |
| 206 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), | |
| 207 offsetTwoFrames); | |
| 208 decoder->setData(tempData.get(), false); | |
| 209 EXPECT_EQ(expectedFramesAfter249Bytes, decoder->frameCount()); | |
| 210 EXPECT_FALSE(decoder->failed()); | |
| 211 | |
| 212 // Provide the rest of the data except for the last IEND chunk. | |
| 213 const size_t expectedFramesAfterAllExpect12Bytes = 3; | |
| 214 tempData = SharedBuffer::create(data->data(), data->size() - 12); | |
| 215 decoder->setData(tempData.get(), true); | |
| 216 EXPECT_EQ(expectedFramesAfterAllExpect12Bytes, decoder->frameCount()); | |
| 217 EXPECT_TRUE(decoder->failed()); | |
| 218 } | |
| 219 | |
| 220 | |
| 221 | |
| 222 // Static PNG tests | |
| 223 | |
| 224 TEST(StaticPNGTests, repetitionCountTest) | |
| 225 { | |
| 226 testRepetitionCount("/LayoutTests/fast/images/resources/png-simple.png", | |
| 227 cAnimationNone); | |
| 228 } | |
| 229 | |
| 230 TEST(StaticPNGTests, sizeTest) | |
| 231 { | |
| 232 testSize("/LayoutTests/fast/images/resources/png-simple.png", | |
| 233 IntSize(111, 29)); | |
| 234 } | |
| 235 | |
| 236 TEST(StaticPNGTests, MetaDataTest) | |
| 237 { | |
| 238 const size_t expectedFrameCount = 1; | |
| 239 const size_t expectedDuration = 0; | |
| 240 auto decoder = createDecoderWithPngData("/LayoutTests/fast/images/resources/ png-simple.png"); | |
| 241 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); | |
| 242 EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0)); | |
| 243 } | |
| 244 | |
| 245 // This test removes two random byets from the IHDR chunk. The decoder should | |
| 246 // fail on this since all IHDR data is necessary to decode a PNG image. | |
| 247 TEST(StaticPNGTests, InvalidIHDRChunk) | |
| 248 { | |
| 249 const char* pngFile = "//LayoutTests/fast/images/resources/png-simple.png"; | |
| 250 auto decoder = createDecoder(); | |
| 251 auto data = readFile(pngFile); | |
| 252 ASSERT_FALSE(data->isEmpty()); | |
| 253 | |
| 254 const size_t offsetHalfwayIHDR = 20; | |
| 255 // Give the first part of the PNG stream: 8 signature, 8 IHDR indicator | |
| 256 // and the first four bytes of IHDR content. | |
| 257 RefPtr<SharedBuffer> invalidData = SharedBuffer::create(data->data(), | |
| 258 offsetHalfwayIHDR); | |
| 259 // Omit the 21st and 22nd byte, and append the rest. | |
| 260 invalidData->append(SharedBuffer::create(data->data() + 22, | |
| 261 data->size() - 22)); | |
| 262 ASSERT_EQ(data->size() - 2, invalidData->size()); | |
| 263 | |
| 264 decoder->setData(invalidData, true); | |
| 265 EXPECT_FALSE(decoder->isSizeAvailable()); | |
| 266 EXPECT_TRUE(decoder->failed()); | |
| 267 | |
| 268 } | |
| 269 }; // namespace blink | |
| OLD | NEW |