OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "platform/image-decoders/png/PNGImageDecoder.h" | 5 #include "platform/image-decoders/png/PNGImageDecoder.h" |
6 | 6 |
| 7 #include <memory> |
7 #include "platform/image-decoders/ImageDecoderTestHelpers.h" | 8 #include "platform/image-decoders/ImageDecoderTestHelpers.h" |
| 9 #include "png.h" |
8 #include "testing/gtest/include/gtest/gtest.h" | 10 #include "testing/gtest/include/gtest/gtest.h" |
9 #include <memory> | 11 |
| 12 // /LayoutTests/images/resources/png-animated-idat-part-of-animation.png |
| 13 // is modified in multiple tests to simulate erroneous PNGs. As a reference, |
| 14 // the table below shows how the file is structured. |
| 15 // |
| 16 // Offset | 8 33 95 133 172 210 241 279 314 352 422 |
| 17 // ------------------------------------------------------------------------- |
| 18 // Chunk | IHDR acTL fcTL IDAT fcTL fdAT fcTL fdAT fcTL fdAT IEND |
| 19 // |
| 20 // In between the acTL and fcTL there are two other chunks, PLTE and tRNS, but |
| 21 // those are not specifically used in this test suite. The same holds for a |
| 22 // tEXT chunk in between the last fdAT and IEND. |
| 23 // |
| 24 // In the current behavior of PNG image decoders, the 4 frames are detected when |
| 25 // respectively 141, 249, 322 and 430 bytes are received. The first frame should |
| 26 // be detected when the IDAT has been received, and non-first frames when the |
| 27 // next fcTL or IEND chunk has been received. Note that all offsets are +8, |
| 28 // because a chunk is identified by byte 4-7. |
10 | 29 |
11 namespace blink { | 30 namespace blink { |
12 | 31 |
13 namespace { | 32 namespace { |
14 | 33 |
15 std::unique_ptr<ImageDecoder> createDecoder( | 34 std::unique_ptr<ImageDecoder> createDecoder( |
16 ImageDecoder::AlphaOption alphaOption) { | 35 ImageDecoder::AlphaOption alphaOption) { |
17 return WTF::wrapUnique(new PNGImageDecoder( | 36 return WTF::wrapUnique(new PNGImageDecoder( |
18 alphaOption, ColorBehavior::transformToTargetForTesting(), | 37 alphaOption, ColorBehavior::transformToTargetForTesting(), |
19 ImageDecoder::noDecodedImageByteLimit)); | 38 ImageDecoder::noDecodedImageByteLimit)); |
(...skipping 10 matching lines...) Expand all Loading... |
30 decoder->setData(data.get(), true); | 49 decoder->setData(data.get(), true); |
31 return decoder; | 50 return decoder; |
32 } | 51 } |
33 | 52 |
34 void testSize(const char* pngFile, IntSize expectedSize) { | 53 void testSize(const char* pngFile, IntSize expectedSize) { |
35 auto decoder = createDecoderWithPngData(pngFile); | 54 auto decoder = createDecoderWithPngData(pngFile); |
36 EXPECT_TRUE(decoder->isSizeAvailable()); | 55 EXPECT_TRUE(decoder->isSizeAvailable()); |
37 EXPECT_EQ(expectedSize, decoder->size()); | 56 EXPECT_EQ(expectedSize, decoder->size()); |
38 } | 57 } |
39 | 58 |
| 59 // Test whether querying for the size of the image works if we present the |
| 60 // data byte by byte. |
| 61 void testSizeByteByByte(const char* pngFile, |
| 62 size_t bytesNeededToDecodeSize, |
| 63 IntSize expectedSize) { |
| 64 auto decoder = createDecoder(); |
| 65 auto data = readFile(pngFile); |
| 66 ASSERT_FALSE(data->isEmpty()); |
| 67 ASSERT_LT(bytesNeededToDecodeSize, data->size()); |
| 68 |
| 69 const char* source = data->data(); |
| 70 RefPtr<SharedBuffer> partialData = SharedBuffer::create(); |
| 71 for (size_t length = 1; length <= bytesNeededToDecodeSize; length++) { |
| 72 partialData->append(source++, 1u); |
| 73 decoder->setData(partialData.get(), false); |
| 74 |
| 75 if (length < bytesNeededToDecodeSize) { |
| 76 EXPECT_FALSE(decoder->isSizeAvailable()); |
| 77 EXPECT_TRUE(decoder->size().isEmpty()); |
| 78 EXPECT_FALSE(decoder->failed()); |
| 79 } else { |
| 80 EXPECT_TRUE(decoder->isSizeAvailable()); |
| 81 EXPECT_EQ(expectedSize, decoder->size()); |
| 82 } |
| 83 } |
| 84 EXPECT_FALSE(decoder->failed()); |
| 85 } |
| 86 |
| 87 void writeUint32(uint32_t val, png_byte* data) { |
| 88 data[0] = val >> 24; |
| 89 data[1] = val >> 16; |
| 90 data[2] = val >> 8; |
| 91 data[3] = val; |
| 92 } |
| 93 |
40 void testRepetitionCount(const char* pngFile, int expectedRepetitionCount) { | 94 void testRepetitionCount(const char* pngFile, int expectedRepetitionCount) { |
41 auto decoder = createDecoderWithPngData(pngFile); | 95 auto decoder = createDecoderWithPngData(pngFile); |
42 // Decoding the frame count sets the repetition count as well. | 96 // Decoding the frame count sets the number of repetitions as well. |
43 decoder->frameCount(); | 97 decoder->frameCount(); |
44 EXPECT_FALSE(decoder->failed()); | 98 EXPECT_FALSE(decoder->failed()); |
45 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); | 99 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); |
46 } | 100 } |
47 | 101 |
| 102 struct PublicFrameInfo { |
| 103 size_t duration; |
| 104 IntRect frameRect; |
| 105 ImageFrame::AlphaBlendSource alphaBlend; |
| 106 ImageFrame::DisposalMethod disposalMethod; |
| 107 }; |
| 108 |
| 109 // This is the frame data for the following PNG image: |
| 110 // /LayoutTests/images/resources/png-animated-idat-part-of-animation.png |
| 111 static PublicFrameInfo pngAnimatedFrameInfo[] = { |
| 112 {500, |
| 113 {IntPoint(0, 0), IntSize(5, 5)}, |
| 114 ImageFrame::BlendAtopBgcolor, |
| 115 ImageFrame::DisposeKeep}, |
| 116 {900, |
| 117 {IntPoint(1, 1), IntSize(3, 1)}, |
| 118 ImageFrame::BlendAtopBgcolor, |
| 119 ImageFrame::DisposeOverwriteBgcolor}, |
| 120 {2000, |
| 121 {IntPoint(1, 2), IntSize(3, 2)}, |
| 122 ImageFrame::BlendAtopPreviousFrame, |
| 123 ImageFrame::DisposeKeep}, |
| 124 {1500, |
| 125 {IntPoint(1, 2), IntSize(3, 1)}, |
| 126 ImageFrame::BlendAtopBgcolor, |
| 127 ImageFrame::DisposeKeep}, |
| 128 }; |
| 129 |
| 130 void compareFrameWithExpectation(const PublicFrameInfo& expected, |
| 131 ImageDecoder* decoder, |
| 132 size_t index) { |
| 133 EXPECT_EQ(expected.duration, decoder->frameDurationAtIndex(index)); |
| 134 |
| 135 const auto* frame = decoder->frameBufferAtIndex(index); |
| 136 ASSERT_TRUE(frame); |
| 137 |
| 138 EXPECT_EQ(expected.duration, frame->duration()); |
| 139 EXPECT_EQ(expected.frameRect, frame->originalFrameRect()); |
| 140 EXPECT_EQ(expected.disposalMethod, frame->getDisposalMethod()); |
| 141 EXPECT_EQ(expected.alphaBlend, frame->getAlphaBlendSource()); |
| 142 } |
| 143 |
| 144 // This function removes |length| bytes at |offset|, and then calls frameCount. |
| 145 // It assumes the missing bytes should result in a failed decode because the |
| 146 // parser jumps |length| bytes too far in the next chunk. |
| 147 void testMissingDataBreaksDecoding(const char* pngFile, |
| 148 size_t offset, |
| 149 size_t length) { |
| 150 auto decoder = createDecoder(); |
| 151 auto data = readFile(pngFile); |
| 152 ASSERT_FALSE(data->isEmpty()); |
| 153 |
| 154 RefPtr<SharedBuffer> invalidData = SharedBuffer::create(data->data(), offset); |
| 155 invalidData->append(data->data() + offset + length, |
| 156 data->size() - offset - length); |
| 157 ASSERT_EQ(data->size() - length, invalidData->size()); |
| 158 |
| 159 decoder->setData(invalidData, true); |
| 160 decoder->frameCount(); |
| 161 EXPECT_TRUE(decoder->failed()); |
| 162 } |
| 163 |
| 164 // Verify that a decoder with a parse error converts to a static image. |
| 165 static void expectStatic(ImageDecoder* decoder) { |
| 166 EXPECT_EQ(1u, decoder->frameCount()); |
| 167 EXPECT_FALSE(decoder->failed()); |
| 168 |
| 169 ImageFrame* frame = decoder->frameBufferAtIndex(0); |
| 170 ASSERT_NE(nullptr, frame); |
| 171 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus()); |
| 172 EXPECT_FALSE(decoder->failed()); |
| 173 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); |
| 174 } |
| 175 |
| 176 // Decode up to the indicated fcTL offset and then provide an fcTL with the |
| 177 // wrong chunk size (20 instead of 26). |
| 178 void testInvalidFctlSize(const char* pngFile, |
| 179 size_t offsetFctl, |
| 180 size_t expectedFrameCount, |
| 181 bool shouldFail) { |
| 182 auto data = readFile(pngFile); |
| 183 ASSERT_FALSE(data->isEmpty()); |
| 184 |
| 185 auto decoder = createDecoder(); |
| 186 RefPtr<SharedBuffer> invalidData = |
| 187 SharedBuffer::create(data->data(), offsetFctl); |
| 188 |
| 189 // Test if this gives the correct frame count, before the fcTL is parsed. |
| 190 decoder->setData(invalidData, false); |
| 191 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
| 192 ASSERT_FALSE(decoder->failed()); |
| 193 |
| 194 // Append the wrong size to the data stream |
| 195 png_byte sizeChunk[4]; |
| 196 writeUint32(20, sizeChunk); |
| 197 invalidData->append(reinterpret_cast<char*>(sizeChunk), 4u); |
| 198 |
| 199 // Skip the size in the original data, but provide a truncated fcTL, |
| 200 // which is 4B of tag, 20B of data and 4B of CRC, totalling 28B. |
| 201 invalidData->append(data->data() + offsetFctl + 4, 28u); |
| 202 // Append the rest of the data |
| 203 const size_t offsetPostFctl = offsetFctl + 38; |
| 204 invalidData->append(data->data() + offsetPostFctl, |
| 205 data->size() - offsetPostFctl); |
| 206 |
| 207 decoder->setData(invalidData, false); |
| 208 if (shouldFail) { |
| 209 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
| 210 EXPECT_EQ(true, decoder->failed()); |
| 211 } else { |
| 212 expectStatic(decoder.get()); |
| 213 } |
| 214 } |
| 215 |
48 // Verify that the decoder can successfully decode the first frame when | 216 // Verify that the decoder can successfully decode the first frame when |
49 // initially only half of the frame data is received, resulting in a partially | 217 // initially only half of the frame data is received, resulting in a partially |
50 // decoded image, and then the rest of the image data is received. Verify that | 218 // decoded image, and then the rest of the image data is received. Verify that |
51 // the bitmap hashes of the two stages are different. Also verify that the final | 219 // the bitmap hashes of the two stages are different. Also verify that the final |
52 // bitmap hash is equivalent to the hash when all data is provided at once. | 220 // bitmap hash is equivalent to the hash when all data is provided at once. |
53 // | 221 // |
54 // This verifies that decoder correctly keeps track of where it stopped | 222 // This verifies that the decoder correctly keeps track of where it stopped |
55 // decoding when the image was not yet fully received. | 223 // decoding when the image was not yet fully received. |
56 void testProgressiveDecodingContinuesAfterFullData(const char* pngFile, | 224 void testProgressiveDecodingContinuesAfterFullData(const char* pngFile, |
57 size_t offsetMidFirstFrame) { | 225 size_t offsetMidFirstFrame) { |
58 auto fullData = readFile(pngFile); | 226 auto fullData = readFile(pngFile); |
59 ASSERT_FALSE(fullData->isEmpty()); | 227 ASSERT_FALSE(fullData->isEmpty()); |
60 | 228 |
61 auto decoderUpfront = createDecoder(); | 229 auto decoderUpfront = createDecoder(); |
62 decoderUpfront->setData(fullData.get(), true); | 230 decoderUpfront->setData(fullData.get(), true); |
63 EXPECT_GE(1u, decoderUpfront->frameCount()); | 231 EXPECT_GE(decoderUpfront->frameCount(), 1u); |
64 const ImageFrame* const frameUpfront = decoderUpfront->frameBufferAtIndex(0); | 232 const ImageFrame* const frameUpfront = decoderUpfront->frameBufferAtIndex(0); |
65 ASSERT_EQ(ImageFrame::FrameComplete, frameUpfront->getStatus()); | 233 ASSERT_EQ(ImageFrame::FrameComplete, frameUpfront->getStatus()); |
66 const unsigned hashUpfront = hashBitmap(frameUpfront->bitmap()); | 234 const unsigned hashUpfront = hashBitmap(frameUpfront->bitmap()); |
67 | 235 |
68 auto decoder = createDecoder(); | 236 auto decoder = createDecoder(); |
69 RefPtr<SharedBuffer> partialData = | 237 RefPtr<SharedBuffer> partialData = |
70 SharedBuffer::create(fullData->data(), offsetMidFirstFrame); | 238 SharedBuffer::create(fullData->data(), offsetMidFirstFrame); |
71 decoder->setData(partialData, false); | 239 decoder->setData(partialData, false); |
72 | 240 |
73 EXPECT_EQ(1u, decoder->frameCount()); | 241 EXPECT_EQ(1u, decoder->frameCount()); |
74 const ImageFrame* frame = decoder->frameBufferAtIndex(0); | 242 const ImageFrame* frame = decoder->frameBufferAtIndex(0); |
75 EXPECT_EQ(frame->getStatus(), ImageFrame::FramePartial); | 243 EXPECT_EQ(frame->getStatus(), ImageFrame::FramePartial); |
76 const unsigned hashPartial = hashBitmap(frame->bitmap()); | 244 const unsigned hashPartial = hashBitmap(frame->bitmap()); |
77 | 245 |
78 decoder->setData(fullData.get(), true); | 246 decoder->setData(fullData.get(), true); |
79 frame = decoder->frameBufferAtIndex(0); | 247 frame = decoder->frameBufferAtIndex(0); |
80 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete); | 248 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete); |
81 const unsigned hashFull = hashBitmap(frame->bitmap()); | 249 const unsigned hashFull = hashBitmap(frame->bitmap()); |
82 | 250 |
83 EXPECT_FALSE(decoder->failed()); | 251 EXPECT_FALSE(decoder->failed()); |
84 EXPECT_NE(hashFull, hashPartial); | 252 EXPECT_NE(hashFull, hashPartial); |
85 EXPECT_EQ(hashFull, hashUpfront); | 253 EXPECT_EQ(hashFull, hashUpfront); |
86 } | 254 } |
87 | 255 |
88 // Modify the frame data bytes for frame |frameIndex| so that decoding fails. | 256 // Modify the frame data bytes for frame |frameIndex| so that decoding fails. |
89 // Parsing should work fine, and is checked with |expectedFrameCountBefore|. If | 257 // Parsing should work fine, and is checked with |expectedFrameCount|. |
90 // the failure should invalidate the decoder, |expectFailure| should be set to | |
91 // true. If not, |expectedFrameCountAfter| should indicate the new frame count | |
92 // after the failure. | |
93 void testFailureDuringDecode(const char* file, | 258 void testFailureDuringDecode(const char* file, |
94 size_t idatOffset, | 259 size_t idatOffset, |
95 size_t frameIndex, | 260 size_t frameIndex, |
96 bool expectFailure, | 261 size_t expectedFrameCount) { |
97 size_t expectedFrameCountBefore, | |
98 size_t expectedFrameCountAfter = 0u) { | |
99 RefPtr<SharedBuffer> fullData = readFile(file); | 262 RefPtr<SharedBuffer> fullData = readFile(file); |
100 ASSERT_FALSE(fullData->isEmpty()); | 263 ASSERT_FALSE(fullData->isEmpty()); |
101 | 264 |
102 // This is the offset where the frame data chunk frame |frameIndex| starts. | 265 // This is the offset where the frame data chunk frame |frameIndex| starts. |
103 RefPtr<SharedBuffer> data = | 266 RefPtr<SharedBuffer> data = |
104 SharedBuffer::create(fullData->data(), idatOffset + 8u); | 267 SharedBuffer::create(fullData->data(), idatOffset + 8u); |
105 // Repeat the first 8 bytes of the frame data. This should result in a | 268 // Repeat the first 8 bytes of the frame data. This should result in a |
106 // successful parse, since frame data is not analyzed in that step, but | 269 // successful parse, since frame data is not analyzed in that step, but |
107 // should give an error during decoding. | 270 // should give an error during decoding. |
108 data->append(fullData->data() + idatOffset, 8u); | 271 data->append(fullData->data() + idatOffset, 8u); |
109 data->append(fullData->data() + idatOffset + 16u, | 272 data->append(fullData->data() + idatOffset + 16u, |
110 fullData->size() - idatOffset - 16u); | 273 fullData->size() - idatOffset - 16u); |
111 | 274 |
112 auto decoder = createDecoder(); | 275 auto decoder = createDecoder(); |
113 decoder->setData(data.get(), true); | 276 decoder->setData(data.get(), true); |
114 | 277 |
115 EXPECT_EQ(expectedFrameCountBefore, decoder->frameCount()); | 278 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
116 | 279 |
117 const ImageFrame* const frame = decoder->frameBufferAtIndex(frameIndex); | 280 decoder->frameBufferAtIndex(frameIndex); |
118 EXPECT_EQ(expectFailure, decoder->failed()); | 281 ASSERT_EQ(true, decoder->failed()); |
119 if (!expectFailure) { | 282 |
120 EXPECT_EQ(expectedFrameCountAfter, decoder->frameCount()); | 283 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
121 EXPECT_EQ(ImageFrame::FrameEmpty, frame->getStatus()); | |
122 } | |
123 } | 284 } |
124 | 285 |
125 } // Anonymous namespace | 286 } // Anonymous namespace |
126 | 287 |
| 288 // Animated PNG Tests |
| 289 |
| 290 TEST(AnimatedPNGTests, sizeTest) { |
| 291 testSize( |
| 292 "/LayoutTests/images/resources/" |
| 293 "png-animated-idat-part-of-animation.png", |
| 294 IntSize(5, 5)); |
| 295 testSize( |
| 296 "/LayoutTests/images/resources/" |
| 297 "png-animated-idat-not-part-of-animation.png", |
| 298 IntSize(227, 35)); |
| 299 } |
| 300 |
| 301 TEST(AnimatedPNGTests, repetitionCountTest) { |
| 302 testRepetitionCount( |
| 303 "/LayoutTests/images/resources/" |
| 304 "png-animated-idat-part-of-animation.png", |
| 305 6u); |
| 306 // This is an "animated" image with only one frame, that is, the IDAT is |
| 307 // ignored and there is one fdAT frame. so it should be considered |
| 308 // non-animated. |
| 309 testRepetitionCount( |
| 310 "/LayoutTests/images/resources/" |
| 311 "png-animated-idat-not-part-of-animation.png", |
| 312 cAnimationNone); |
| 313 } |
| 314 |
| 315 // Test if the decoded metdata corresponds to the defined expectations |
| 316 TEST(AnimatedPNGTests, MetaDataTest) { |
| 317 const char* pngFile = |
| 318 "/LayoutTests/images/resources/" |
| 319 "png-animated-idat-part-of-animation.png"; |
| 320 constexpr size_t expectedFrameCount = 4; |
| 321 |
| 322 auto decoder = createDecoderWithPngData(pngFile); |
| 323 ASSERT_EQ(expectedFrameCount, decoder->frameCount()); |
| 324 for (size_t i = 0; i < expectedFrameCount; i++) { |
| 325 compareFrameWithExpectation(pngAnimatedFrameInfo[i], decoder.get(), i); |
| 326 } |
| 327 } |
| 328 |
| 329 TEST(AnimatedPNGTests, EmptyFrame) { |
| 330 const char* pngFile = "/LayoutTests/images/resources/empty-frame.png"; |
| 331 auto decoder = createDecoderWithPngData(pngFile); |
| 332 // Frame 0 is empty. Ensure that decoding frame 1 (which depends on frame 0) |
| 333 // fails (rather than crashing). |
| 334 EXPECT_EQ(2u, decoder->frameCount()); |
| 335 EXPECT_FALSE(decoder->failed()); |
| 336 |
| 337 ImageFrame* frame = decoder->frameBufferAtIndex(1); |
| 338 EXPECT_TRUE(decoder->failed()); |
| 339 ASSERT_NE(nullptr, frame); |
| 340 EXPECT_EQ(ImageFrame::FrameEmpty, frame->getStatus()); |
| 341 } |
| 342 |
| 343 TEST(AnimatedPNGTests, ByteByByteSizeAvailable) { |
| 344 testSizeByteByByte( |
| 345 "/LayoutTests/images/resources/" |
| 346 "png-animated-idat-part-of-animation.png", |
| 347 141u, IntSize(5, 5)); |
| 348 testSizeByteByByte( |
| 349 "/LayoutTests/images/resources/" |
| 350 "png-animated-idat-not-part-of-animation.png", |
| 351 79u, IntSize(227, 35)); |
| 352 } |
| 353 |
| 354 TEST(AnimatedPNGTests, ByteByByteMetaData) { |
| 355 const char* pngFile = |
| 356 "/LayoutTests/images/resources/" |
| 357 "png-animated-idat-part-of-animation.png"; |
| 358 constexpr size_t expectedFrameCount = 4; |
| 359 |
| 360 // These are the byte offsets where each frame should have been parsed. |
| 361 // It boils down to the offset of the first fcTL / IEND after the last |
| 362 // frame data chunk, plus 8 bytes for recognition. The exception on this is |
| 363 // the first frame, which is reported when its first framedata is seen. |
| 364 size_t frameOffsets[expectedFrameCount] = {141, 249, 322, 430}; |
| 365 |
| 366 auto decoder = createDecoder(); |
| 367 auto data = readFile(pngFile); |
| 368 ASSERT_FALSE(data->isEmpty()); |
| 369 size_t framesParsed = 0; |
| 370 |
| 371 const char* source = data->data(); |
| 372 RefPtr<SharedBuffer> partialData = SharedBuffer::create(); |
| 373 for (size_t length = 1; length <= frameOffsets[expectedFrameCount - 1]; |
| 374 length++) { |
| 375 partialData->append(source++, 1u); |
| 376 decoder->setData(partialData.get(), false); |
| 377 EXPECT_FALSE(decoder->failed()); |
| 378 if (length < frameOffsets[framesParsed]) { |
| 379 EXPECT_EQ(framesParsed, decoder->frameCount()); |
| 380 } else { |
| 381 ASSERT_EQ(framesParsed + 1, decoder->frameCount()); |
| 382 compareFrameWithExpectation(pngAnimatedFrameInfo[framesParsed], |
| 383 decoder.get(), framesParsed); |
| 384 framesParsed++; |
| 385 } |
| 386 } |
| 387 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
| 388 EXPECT_FALSE(decoder->failed()); |
| 389 } |
| 390 |
| 391 TEST(AnimatedPNGTests, TestRandomFrameDecode) { |
| 392 testRandomFrameDecode(&createDecoder, |
| 393 "/LayoutTests/images/resources/" |
| 394 "png-animated-idat-part-of-animation.png", |
| 395 2u); |
| 396 } |
| 397 |
| 398 TEST(AnimatedPNGTests, TestDecodeAfterReallocation) { |
| 399 testDecodeAfterReallocatingData(&createDecoder, |
| 400 "/LayoutTests/images/resources/" |
| 401 "png-animated-idat-part-of-animation.png"); |
| 402 } |
| 403 |
| 404 TEST(AnimatedPNGTests, ProgressiveDecode) { |
| 405 testProgressiveDecoding(&createDecoder, |
| 406 "/LayoutTests/images/resources/" |
| 407 "png-animated-idat-part-of-animation.png", |
| 408 13u); |
| 409 } |
| 410 |
| 411 TEST(AnimatedPNGTests, ParseAndDecodeByteByByte) { |
| 412 testByteByByteDecode(&createDecoder, |
| 413 "/LayoutTests/images/resources/" |
| 414 "png-animated-idat-part-of-animation.png", |
| 415 4u, 6u); |
| 416 } |
| 417 |
| 418 TEST(AnimatedPNGTests, FailureDuringParsing) { |
| 419 // Test the first fcTL in the stream. Because no frame data has been set at |
| 420 // this point, the expected frame count is zero. 95 bytes is just before the |
| 421 // first fcTL chunk, at which the first frame is detected. This is before the |
| 422 // IDAT, so it should be treated as a static image. |
| 423 testInvalidFctlSize( |
| 424 "/LayoutTests/images/resources/" |
| 425 "png-animated-idat-part-of-animation.png", |
| 426 95u, 0u, false); |
| 427 |
| 428 // Test for the third fcTL in the stream. This should see 1 frame before the |
| 429 // fcTL, and then fail when parsing it. |
| 430 testInvalidFctlSize( |
| 431 "/LayoutTests/images/resources/" |
| 432 "png-animated-idat-part-of-animation.png", |
| 433 241u, 1u, true); |
| 434 } |
| 435 |
| 436 TEST(AnimatedPNGTests, ActlErrors) { |
| 437 const char* pngFile = |
| 438 "/LayoutTests/images/resources/" |
| 439 "png-animated-idat-part-of-animation.png"; |
| 440 auto data = readFile(pngFile); |
| 441 ASSERT_FALSE(data->isEmpty()); |
| 442 |
| 443 const size_t offsetActl = 33u; |
| 444 const size_t acTLSize = 20u; |
| 445 { |
| 446 // Remove the acTL chunk from the stream. This results in a static image. |
| 447 RefPtr<SharedBuffer> noActlData = |
| 448 SharedBuffer::create(data->data(), offsetActl); |
| 449 noActlData->append(data->data() + offsetActl + acTLSize, |
| 450 data->size() - offsetActl - acTLSize); |
| 451 |
| 452 auto decoder = createDecoder(); |
| 453 decoder->setData(noActlData, true); |
| 454 EXPECT_EQ(1u, decoder->frameCount()); |
| 455 EXPECT_FALSE(decoder->failed()); |
| 456 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); |
| 457 } |
| 458 |
| 459 // Store the acTL for more tests. |
| 460 char acTL[acTLSize]; |
| 461 memcpy(acTL, data->data() + offsetActl, acTLSize); |
| 462 |
| 463 // Insert an extra acTL at a couple of different offsets. |
| 464 // Prior to the IDAT, this should result in a static image. After, this |
| 465 // should fail. |
| 466 const struct { |
| 467 size_t offset; |
| 468 bool shouldFail; |
| 469 } gRecs[] = {{8u, false}, |
| 470 {offsetActl, false}, |
| 471 {133u, false}, |
| 472 {172u, true}, |
| 473 {422u, true}}; |
| 474 for (const auto& rec : gRecs) { |
| 475 const size_t offset = rec.offset; |
| 476 RefPtr<SharedBuffer> extraActlData = |
| 477 SharedBuffer::create(data->data(), offset); |
| 478 extraActlData->append(acTL, acTLSize); |
| 479 extraActlData->append(data->data() + offset, data->size() - offset); |
| 480 auto decoder = createDecoder(); |
| 481 decoder->setData(extraActlData, true); |
| 482 EXPECT_EQ(rec.shouldFail ? 0u : 1u, decoder->frameCount()); |
| 483 EXPECT_EQ(rec.shouldFail, decoder->failed()); |
| 484 } |
| 485 |
| 486 // An acTL after IDAT is ignored. |
| 487 pngFile = |
| 488 "/LayoutTests/images/resources/" |
| 489 "cHRM_color_spin.png"; |
| 490 { |
| 491 auto data2 = readFile(pngFile); |
| 492 ASSERT_FALSE(data2->isEmpty()); |
| 493 const size_t postIDATOffset = 30971u; |
| 494 for (size_t times = 0; times < 2; times++) { |
| 495 RefPtr<SharedBuffer> extraActlData = |
| 496 SharedBuffer::create(data2->data(), postIDATOffset); |
| 497 for (size_t i = 0; i < times; i++) |
| 498 extraActlData->append(acTL, acTLSize); |
| 499 extraActlData->append(data2->data() + postIDATOffset, |
| 500 data2->size() - postIDATOffset); |
| 501 |
| 502 auto decoder = createDecoder(); |
| 503 decoder->setData(extraActlData, true); |
| 504 EXPECT_EQ(1u, decoder->frameCount()); |
| 505 EXPECT_FALSE(decoder->failed()); |
| 506 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); |
| 507 EXPECT_NE(nullptr, decoder->frameBufferAtIndex(0)); |
| 508 EXPECT_FALSE(decoder->failed()); |
| 509 } |
| 510 } |
| 511 } |
| 512 |
| 513 TEST(AnimatedPNGTests, fdatBeforeIdat) { |
| 514 const char* pngFile = |
| 515 "/LayoutTests/images/resources/" |
| 516 "png-animated-idat-not-part-of-animation.png"; |
| 517 auto data = readFile(pngFile); |
| 518 ASSERT_FALSE(data->isEmpty()); |
| 519 |
| 520 // Insert fcTL and fdAT prior to the IDAT |
| 521 const size_t idatOffset = 71u; |
| 522 RefPtr<SharedBuffer> modifiedData = |
| 523 SharedBuffer::create(data->data(), idatOffset); |
| 524 // Copy fcTL and fdAT |
| 525 const size_t fctlPlusFdatSize = 38u + 1566u; |
| 526 modifiedData->append(data->data() + 2519u, fctlPlusFdatSize); |
| 527 // Copy IDAT |
| 528 modifiedData->append(data->data() + idatOffset, 2448u); |
| 529 // Copy the remaining |
| 530 modifiedData->append(data->data() + 4123u, 39u + 12u); |
| 531 // Data has just been rearranged. |
| 532 ASSERT_EQ(data->size(), modifiedData->size()); |
| 533 |
| 534 { |
| 535 // This broken APNG will be treated as a static png. |
| 536 auto decoder = createDecoder(); |
| 537 decoder->setData(modifiedData.get(), true); |
| 538 expectStatic(decoder.get()); |
| 539 } |
| 540 |
| 541 { |
| 542 // Remove the acTL from the modified image. It now has fdAT before |
| 543 // IDAT, but no acTL, so fdAT should be ignored. |
| 544 const size_t offsetActl = 33u; |
| 545 const size_t acTLSize = 20u; |
| 546 RefPtr<SharedBuffer> modifiedData2 = |
| 547 SharedBuffer::create(modifiedData->data(), offsetActl); |
| 548 modifiedData2->append(modifiedData->data() + offsetActl + acTLSize, |
| 549 modifiedData->size() - offsetActl - acTLSize); |
| 550 auto decoder = createDecoder(); |
| 551 decoder->setData(modifiedData2.get(), true); |
| 552 expectStatic(decoder.get()); |
| 553 |
| 554 // Likewise, if an acTL follows the fdAT, it is ignored. |
| 555 const size_t insertionOffset = idatOffset + fctlPlusFdatSize - acTLSize; |
| 556 RefPtr<SharedBuffer> modifiedData3 = |
| 557 SharedBuffer::create(modifiedData2->data(), insertionOffset); |
| 558 modifiedData3->append(data->data() + offsetActl, acTLSize); |
| 559 modifiedData3->append(modifiedData2->data() + insertionOffset, |
| 560 modifiedData2->size() - insertionOffset); |
| 561 decoder = createDecoder(); |
| 562 decoder->setData(modifiedData3.get(), true); |
| 563 expectStatic(decoder.get()); |
| 564 } |
| 565 } |
| 566 |
| 567 TEST(AnimatedPNGTests, IdatSizeMismatch) { |
| 568 // The default image must fill the image |
| 569 const char* pngFile = |
| 570 "/LayoutTests/images/resources/" |
| 571 "png-animated-idat-part-of-animation.png"; |
| 572 auto data = readFile(pngFile); |
| 573 ASSERT_FALSE(data->isEmpty()); |
| 574 |
| 575 const size_t fctlOffset = 95u; |
| 576 RefPtr<SharedBuffer> modifiedData = |
| 577 SharedBuffer::create(data->data(), fctlOffset); |
| 578 const size_t fctlSize = 38u; |
| 579 png_byte fctl[fctlSize]; |
| 580 memcpy(fctl, data->data() + fctlOffset, fctlSize); |
| 581 // Set the height to a smaller value, so it does not fill the image. |
| 582 writeUint32(3, fctl + 16); |
| 583 // Correct the crc |
| 584 writeUint32(3210324191, fctl + 34); |
| 585 modifiedData->append((const char*)fctl, fctlSize); |
| 586 const size_t afterFctl = fctlOffset + fctlSize; |
| 587 modifiedData->append(data->data() + afterFctl, data->size() - afterFctl); |
| 588 |
| 589 auto decoder = createDecoder(); |
| 590 decoder->setData(modifiedData.get(), true); |
| 591 expectStatic(decoder.get()); |
| 592 } |
| 593 |
| 594 // Originally, the third frame has an offset of (1,2) and a size of (3,2). By |
| 595 // changing the offset to (4,4), the frame rect is no longer within the image |
| 596 // size of 5x5. This results in a failure. |
| 597 TEST(AnimatedPNGTests, VerifyFrameOutsideImageSizeFails) { |
| 598 const char* pngFile = |
| 599 "/LayoutTests/images/resources/" |
| 600 "png-animated-idat-part-of-animation.png"; |
| 601 auto data = readFile(pngFile); |
| 602 auto decoder = createDecoder(); |
| 603 ASSERT_FALSE(data->isEmpty()); |
| 604 |
| 605 const size_t offsetThirdFctl = 241; |
| 606 RefPtr<SharedBuffer> modifiedData = |
| 607 SharedBuffer::create(data->data(), offsetThirdFctl); |
| 608 const size_t fctlSize = 38u; |
| 609 png_byte fctl[fctlSize]; |
| 610 memcpy(fctl, data->data() + offsetThirdFctl, fctlSize); |
| 611 // Modify offset and crc. |
| 612 writeUint32(4, fctl + 20u); |
| 613 writeUint32(4, fctl + 24u); |
| 614 writeUint32(3700322018, fctl + 34u); |
| 615 |
| 616 modifiedData->append(const_cast<const char*>(reinterpret_cast<char*>(fctl)), |
| 617 fctlSize); |
| 618 modifiedData->append(data->data() + offsetThirdFctl + fctlSize, |
| 619 data->size() - offsetThirdFctl - fctlSize); |
| 620 |
| 621 decoder->setData(modifiedData, true); |
| 622 |
| 623 IntSize expectedSize(5, 5); |
| 624 EXPECT_TRUE(decoder->isSizeAvailable()); |
| 625 EXPECT_EQ(expectedSize, decoder->size()); |
| 626 |
| 627 const size_t expectedFrameCount = 0; |
| 628 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
| 629 EXPECT_TRUE(decoder->failed()); |
| 630 } |
| 631 |
| 632 TEST(AnimatedPNGTests, ProgressiveDecodingContinuesAfterFullData) { |
| 633 // 160u is a randomly chosen offset in the IDAT chunk of the first frame. |
| 634 testProgressiveDecodingContinuesAfterFullData( |
| 635 "/LayoutTests/images/resources/" |
| 636 "png-animated-idat-part-of-animation.png", |
| 637 160u); |
| 638 } |
| 639 |
| 640 TEST(AnimatedPNGTests, RandomDecodeAfterClearFrameBufferCache) { |
| 641 testRandomDecodeAfterClearFrameBufferCache( |
| 642 &createDecoder, |
| 643 "/LayoutTests/images/resources/" |
| 644 "png-animated-idat-part-of-animation.png", |
| 645 2u); |
| 646 } |
| 647 |
| 648 TEST(AnimatedPNGTests, VerifyAlphaBlending) { |
| 649 testAlphaBlending(&createDecoder, |
| 650 "/LayoutTests/images/resources/" |
| 651 "png-animated-idat-part-of-animation.png"); |
| 652 } |
| 653 |
| 654 // This tests if the frame count gets set correctly when parsing frameCount |
| 655 // fails in one of the parsing queries. |
| 656 // |
| 657 // First, enough data is provided such that two frames should be registered. |
| 658 // The decoder should at this point not be in the failed status. |
| 659 // |
| 660 // Then, we provide the rest of the data except for the last IEND chunk, but |
| 661 // tell the decoder that this is all the data we have. The frame count should |
| 662 // be three, since one extra frame should be discovered. The fourth frame |
| 663 // should *not* be registered since the reader should not be able to determine |
| 664 // where the frame ends. The decoder should *not* be in the failed state since |
| 665 // there are three frames which can be shown. |
| 666 // Attempting to decode the third frame should fail, since the file is |
| 667 // truncated. |
| 668 TEST(AnimatedPNGTests, FailureMissingIendChunk) { |
| 669 RefPtr<SharedBuffer> fullData = readFile( |
| 670 "/LayoutTests/images/resources/" |
| 671 "png-animated-idat-part-of-animation.png"); |
| 672 ASSERT_FALSE(fullData->isEmpty()); |
| 673 auto decoder = createDecoder(); |
| 674 |
| 675 const size_t offsetTwoFrames = 249; |
| 676 const size_t expectedFramesAfter249Bytes = 2; |
| 677 RefPtr<SharedBuffer> tempData = |
| 678 SharedBuffer::create(fullData->data(), offsetTwoFrames); |
| 679 decoder->setData(tempData.get(), false); |
| 680 EXPECT_EQ(expectedFramesAfter249Bytes, decoder->frameCount()); |
| 681 EXPECT_FALSE(decoder->failed()); |
| 682 |
| 683 // Provide the rest of the data except for the last IEND chunk. |
| 684 const size_t expectedFramesAfterAllExcept12Bytes = 3; |
| 685 tempData = SharedBuffer::create(fullData->data(), fullData->size() - 12); |
| 686 decoder->setData(tempData.get(), true); |
| 687 ASSERT_EQ(expectedFramesAfterAllExcept12Bytes, decoder->frameCount()); |
| 688 |
| 689 for (size_t i = 0; i < expectedFramesAfterAllExcept12Bytes; i++) { |
| 690 EXPECT_FALSE(decoder->failed()); |
| 691 decoder->frameBufferAtIndex(i); |
| 692 } |
| 693 |
| 694 EXPECT_TRUE(decoder->failed()); |
| 695 } |
| 696 |
| 697 TEST(AnimatedPNGTests, FailureDuringDecodingInvalidatesDecoder) { |
| 698 testFailureDuringDecode( |
| 699 "/LayoutTests/images/resources/" |
| 700 "png-animated-idat-part-of-animation.png", |
| 701 291u, // fdat offset for frame index 2, plus 12 to move past sequence |
| 702 // number. |
| 703 2u, // try to decode frame index 2 |
| 704 4u); // expected frame count before failure |
| 705 |
| 706 testFailureDuringDecode( |
| 707 "/LayoutTests/images/resources/" |
| 708 "png-animated-idat-part-of-animation.png", |
| 709 133u, // idat offset for frame index 0 |
| 710 0u, // try to decode frame index 0 |
| 711 4u); // expected frame count before failure |
| 712 } |
| 713 |
| 714 // Verify that a malformatted PNG, where the IEND appears before any frame data |
| 715 // (IDAT), invalidates the decoder. |
| 716 TEST(AnimatedPNGTests, VerifyIENDBeforeIDATInvalidatesDecoder) { |
| 717 RefPtr<SharedBuffer> fullData = readFile( |
| 718 "/LayoutTests/images/resources/" |
| 719 "png-animated-idat-part-of-animation.png"); |
| 720 ASSERT_FALSE(fullData->isEmpty()); |
| 721 auto decoder = createDecoder(); |
| 722 |
| 723 const size_t offsetIDAT = 133; |
| 724 RefPtr<SharedBuffer> data = |
| 725 SharedBuffer::create(fullData->data(), offsetIDAT); |
| 726 data->append(fullData->data() + fullData->size() - 12u, 12u); |
| 727 data->append(fullData->data() + offsetIDAT, fullData->size() - offsetIDAT); |
| 728 decoder->setData(data.get(), true); |
| 729 |
| 730 const size_t expectedFrameCount = 0u; |
| 731 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
| 732 EXPECT_TRUE(decoder->failed()); |
| 733 } |
| 734 |
| 735 // All IDAT chunks must be before all fdAT chunks |
| 736 TEST(AnimatedPNGTests, MixedDataChunks) { |
| 737 const char* pngFile = |
| 738 "/LayoutTests/images/resources/" |
| 739 "png-animated-idat-part-of-animation.png"; |
| 740 RefPtr<SharedBuffer> fullData = readFile(pngFile); |
| 741 ASSERT_FALSE(fullData->isEmpty()); |
| 742 |
| 743 // Add an extra fdAT after the first IDAT, skipping fcTL. |
| 744 const size_t postIDAT = 172u; |
| 745 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), postIDAT); |
| 746 const size_t fcTLSize = 38u; |
| 747 const size_t fdATSize = 31u; |
| 748 png_byte fdAT[fdATSize]; |
| 749 memcpy(fdAT, fullData->data() + postIDAT + fcTLSize, fdATSize); |
| 750 // Modify the sequence number |
| 751 writeUint32(1u, fdAT + 8); |
| 752 data->append((const char*)fdAT, fdATSize); |
| 753 const size_t IENDOffset = 422u; |
| 754 data->append(fullData->data() + IENDOffset, fullData->size() - IENDOffset); |
| 755 auto decoder = createDecoder(); |
| 756 decoder->setData(data.get(), true); |
| 757 decoder->frameCount(); |
| 758 EXPECT_TRUE(decoder->failed()); |
| 759 |
| 760 // Insert an IDAT after an fdAT. |
| 761 const size_t postfdAT = postIDAT + fcTLSize + fdATSize; |
| 762 data = SharedBuffer::create(fullData->data(), postfdAT); |
| 763 const size_t IDATOffset = 133u; |
| 764 data->append(fullData->data() + IDATOffset, postIDAT - IDATOffset); |
| 765 // Append the rest. |
| 766 data->append(fullData->data() + postIDAT, fullData->size() - postIDAT); |
| 767 decoder = createDecoder(); |
| 768 decoder->setData(data.get(), true); |
| 769 decoder->frameCount(); |
| 770 EXPECT_TRUE(decoder->failed()); |
| 771 } |
| 772 |
| 773 // Verify that erroneous values for the disposal method and alpha blending |
| 774 // cause the decoder to fail. |
| 775 TEST(AnimatedPNGTests, VerifyInvalidDisposalAndBlending) { |
| 776 const char* pngFile = |
| 777 "/LayoutTests/images/resources/" |
| 778 "png-animated-idat-part-of-animation.png"; |
| 779 RefPtr<SharedBuffer> fullData = readFile(pngFile); |
| 780 ASSERT_FALSE(fullData->isEmpty()); |
| 781 auto decoder = createDecoder(); |
| 782 |
| 783 // The disposal byte in the frame control chunk is the 24th byte, alpha |
| 784 // blending the 25th. |offsetDisposalOp| is 241 bytes to get to the third |
| 785 // fctl chunk, 8 bytes to skip the length and tag bytes, and 24 bytes to get |
| 786 // to the disposal op. |
| 787 // |
| 788 // Write invalid values to the disposal and alpha blending byte, correct the |
| 789 // crc and append the rest of the buffer. |
| 790 const size_t offsetDisposalOp = 241 + 8 + 24; |
| 791 RefPtr<SharedBuffer> data = |
| 792 SharedBuffer::create(fullData->data(), offsetDisposalOp); |
| 793 png_byte disposalAndBlending[6u]; |
| 794 disposalAndBlending[0] = 7; |
| 795 disposalAndBlending[1] = 9; |
| 796 writeUint32(2408835439u, disposalAndBlending + 2u); |
| 797 data->append(reinterpret_cast<char*>(disposalAndBlending), 6u); |
| 798 data->append(fullData->data() + offsetDisposalOp + 6u, |
| 799 fullData->size() - offsetDisposalOp - 6u); |
| 800 |
| 801 decoder->setData(data.get(), true); |
| 802 decoder->frameCount(); |
| 803 ASSERT_TRUE(decoder->failed()); |
| 804 } |
| 805 |
| 806 // This test verifies that the following situation does not invalidate the |
| 807 // decoder: |
| 808 // - Frame 0 is decoded progressively, but there's not enough data to fully |
| 809 // decode it. |
| 810 // - The rest of the image data is received. |
| 811 // - Frame X, with X > 0, and X does not depend on frame 0, is decoded. |
| 812 // - Frame 0 is decoded. |
| 813 // This is a tricky case since the decoder resets the png struct for each frame, |
| 814 // and this test verifies that it does not break the decoding of frame 0, even |
| 815 // though it already started in the first call. |
| 816 TEST(AnimatedPNGTests, VerifySuccessfulFirstFrameDecodeAfterLaterFrame) { |
| 817 const char* pngFile = |
| 818 "/LayoutTests/images/resources/" |
| 819 "png-animated-three-independent-frames.png"; |
| 820 auto decoder = createDecoder(); |
| 821 auto fullData = readFile(pngFile); |
| 822 ASSERT_FALSE(fullData->isEmpty()); |
| 823 |
| 824 // 160u is a randomly chosen offset in the IDAT chunk of the first frame. |
| 825 const size_t middleFirstFrame = 160u; |
| 826 RefPtr<SharedBuffer> data = |
| 827 SharedBuffer::create(fullData->data(), middleFirstFrame); |
| 828 decoder->setData(data.get(), false); |
| 829 |
| 830 ASSERT_EQ(1u, decoder->frameCount()); |
| 831 ASSERT_EQ(ImageFrame::FramePartial, |
| 832 decoder->frameBufferAtIndex(0)->getStatus()); |
| 833 |
| 834 decoder->setData(fullData.get(), true); |
| 835 ASSERT_EQ(3u, decoder->frameCount()); |
| 836 ASSERT_EQ(ImageFrame::FrameComplete, |
| 837 decoder->frameBufferAtIndex(1)->getStatus()); |
| 838 // The point is that this call does not decode frame 0, which it won't do if |
| 839 // it does not have it as its required previous frame. |
| 840 ASSERT_EQ(kNotFound, |
| 841 decoder->frameBufferAtIndex(1)->requiredPreviousFrameIndex()); |
| 842 |
| 843 EXPECT_EQ(ImageFrame::FrameComplete, |
| 844 decoder->frameBufferAtIndex(0)->getStatus()); |
| 845 EXPECT_FALSE(decoder->failed()); |
| 846 } |
| 847 |
| 848 // If the decoder attempts to decode a non-first frame which is subset and |
| 849 // independent, it needs to discard its png_struct so it can use a modified |
| 850 // IHDR. Test this by comparing a decode of frame 1 after frame 0 to a decode |
| 851 // of frame 1 without decoding frame 0. |
| 852 TEST(AnimatedPNGTests, DecodeFromIndependentFrame) { |
| 853 const char* pngFile = |
| 854 "/LayoutTests/images/resources/" |
| 855 "png-animated-idat-part-of-animation.png"; |
| 856 auto originalData = readFile(pngFile); |
| 857 ASSERT_FALSE(originalData->isEmpty()); |
| 858 |
| 859 // This file almost fits the bill. Modify it to dispose frame 0, making |
| 860 // frame 1 independent. |
| 861 const size_t kDisposeOffset = 127u; |
| 862 auto data = SharedBuffer::create(originalData->data(), kDisposeOffset); |
| 863 // 1 Corresponds to APNG_DISPOSE_OP_BACKGROUND |
| 864 const char one = '\001'; |
| 865 data->append(&one, 1u); |
| 866 // No need to modify the blend op |
| 867 data->append(originalData->data() + kDisposeOffset + 1, 1u); |
| 868 // Modify the CRC |
| 869 png_byte crc[4]; |
| 870 writeUint32(2226670956, crc); |
| 871 data->append(reinterpret_cast<const char*>(crc), 4u); |
| 872 data->append(originalData->data() + data->size(), |
| 873 originalData->size() - data->size()); |
| 874 ASSERT_EQ(originalData->size(), data->size()); |
| 875 |
| 876 auto decoder = createDecoder(); |
| 877 decoder->setData(data.get(), true); |
| 878 |
| 879 ASSERT_EQ(4u, decoder->frameCount()); |
| 880 ASSERT_FALSE(decoder->failed()); |
| 881 |
| 882 auto* frame = decoder->frameBufferAtIndex(0); |
| 883 ASSERT_TRUE(frame); |
| 884 ASSERT_EQ(ImageFrame::DisposeOverwriteBgcolor, frame->getDisposalMethod()); |
| 885 |
| 886 frame = decoder->frameBufferAtIndex(1); |
| 887 ASSERT_TRUE(frame); |
| 888 ASSERT_FALSE(decoder->failed()); |
| 889 ASSERT_NE(IntRect({}, decoder->size()), frame->originalFrameRect()); |
| 890 ASSERT_EQ(kNotFound, frame->requiredPreviousFrameIndex()); |
| 891 |
| 892 const auto hash = hashBitmap(frame->bitmap()); |
| 893 |
| 894 // Now decode starting from frame 1. |
| 895 decoder = createDecoder(); |
| 896 decoder->setData(data.get(), true); |
| 897 |
| 898 frame = decoder->frameBufferAtIndex(1); |
| 899 ASSERT_TRUE(frame); |
| 900 EXPECT_EQ(hash, hashBitmap(frame->bitmap())); |
| 901 } |
| 902 |
| 903 // If the first frame is subset from IHDR (only allowed if the first frame is |
| 904 // not the default image), the decoder has to destroy the png_struct it used |
| 905 // for parsing so it can use a modified IHDR. |
| 906 TEST(AnimatedPNGTests, SubsetFromIHDR) { |
| 907 const char* pngFile = |
| 908 "/LayoutTests/images/resources/" |
| 909 "png-animated-idat-not-part-of-animation.png"; |
| 910 auto originalData = readFile(pngFile); |
| 911 ASSERT_FALSE(originalData->isEmpty()); |
| 912 |
| 913 const size_t fcTLOffset = 2519u; |
| 914 auto data = SharedBuffer::create(originalData->data(), fcTLOffset); |
| 915 |
| 916 const size_t fcTLSize = 38u; |
| 917 png_byte fcTL[fcTLSize]; |
| 918 memcpy(fcTL, originalData->data() + fcTLOffset, fcTLSize); |
| 919 // Modify to have a subset frame (yOffset 1, height 34 out of 35). |
| 920 writeUint32(34, fcTL + 16u); |
| 921 writeUint32(1, fcTL + 24u); |
| 922 writeUint32(3972842751, fcTL + 34u); |
| 923 data->append(reinterpret_cast<const char*>(fcTL), fcTLSize); |
| 924 |
| 925 // Append the rest of the data. |
| 926 // Note: If PNGImageDecoder changes to reject an image with too many |
| 927 // rows, the fdAT data will need to be modified as well. |
| 928 data->append(originalData->data() + fcTLOffset + fcTLSize, |
| 929 originalData->size() - data->size()); |
| 930 ASSERT_EQ(originalData->size(), data->size()); |
| 931 |
| 932 // This will test both byte by byte and using the full data, and compare. |
| 933 testByteByByteDecode(createDecoder, data.get(), 1, cAnimationNone); |
| 934 } |
| 935 |
127 // Static PNG tests | 936 // Static PNG tests |
128 | 937 |
129 TEST(StaticPNGTests, repetitionCountTest) { | 938 TEST(StaticPNGTests, repetitionCountTest) { |
130 testRepetitionCount("/LayoutTests/images/resources/png-simple.png", | 939 testRepetitionCount("/LayoutTests/images/resources/png-simple.png", |
131 cAnimationNone); | 940 cAnimationNone); |
132 } | 941 } |
133 | 942 |
134 TEST(StaticPNGTests, sizeTest) { | 943 TEST(StaticPNGTests, sizeTest) { |
135 testSize("/LayoutTests/images/resources/png-simple.png", IntSize(111, 29)); | 944 testSize("/LayoutTests/images/resources/png-simple.png", IntSize(111, 29)); |
136 } | 945 } |
137 | 946 |
138 TEST(StaticPNGTests, MetaDataTest) { | 947 TEST(StaticPNGTests, MetaDataTest) { |
139 const size_t expectedFrameCount = 1; | 948 const size_t expectedFrameCount = 1; |
140 const size_t expectedDuration = 0; | 949 const size_t expectedDuration = 0; |
141 auto decoder = | 950 auto decoder = |
142 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png"); | 951 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png"); |
143 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); | 952 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); |
144 EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0)); | 953 EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0)); |
145 } | 954 } |
146 | 955 |
| 956 TEST(StaticPNGTests, InvalidIHDRChunk) { |
| 957 testMissingDataBreaksDecoding("/LayoutTests/images/resources/png-simple.png", |
| 958 20u, 2u); |
| 959 } |
| 960 |
147 TEST(StaticPNGTests, ProgressiveDecoding) { | 961 TEST(StaticPNGTests, ProgressiveDecoding) { |
148 testProgressiveDecoding(&createDecoder, | 962 testProgressiveDecoding(&createDecoder, |
149 "/LayoutTests/images/resources/png-simple.png", 11u); | 963 "/LayoutTests/images/resources/png-simple.png", 11u); |
150 } | 964 } |
151 | 965 |
152 TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) { | 966 TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData) { |
153 testProgressiveDecodingContinuesAfterFullData( | 967 testProgressiveDecodingContinuesAfterFullData( |
154 "/LayoutTests/images/resources/png-simple.png", 1000u); | 968 "/LayoutTests/images/resources/png-simple.png", 1000u); |
155 } | 969 } |
156 | 970 |
157 TEST(StaticPNGTests, FailureDuringDecodingInvalidatesDecoder) { | 971 TEST(StaticPNGTests, FailureDuringDecodingInvalidatesDecoder) { |
158 testFailureDuringDecode( | 972 testFailureDuringDecode( |
159 "/LayoutTests/images/resources/png-simple.png", | 973 "/LayoutTests/images/resources/png-simple.png", |
160 85u, // idat offset for frame index 0 | 974 85u, // idat offset for frame index 0 |
161 0u, // try to decode frame index 0 | 975 0u, // try to decode frame index 0 |
162 true, // expect the decoder to be invalidated after the failure | |
163 1u); // expected frame count before failure | 976 1u); // expected frame count before failure |
164 } | 977 } |
165 | 978 |
166 // For static images, frameIsCompleteAtIndex(0) should return true if and only | 979 TEST(PNGTests, VerifyFrameCompleteBehavior) { |
167 // if the frame is successfully decoded, not when it is fully received. | 980 struct { |
168 TEST(StaticPNGTests, VerifyFrameCompleteBehavior) { | 981 const char* name; |
169 auto decoder = | 982 size_t expectedFrameCount; |
170 createDecoderWithPngData("/LayoutTests/images/resources/png-simple.png"); | 983 size_t offsetInFirstFrame; |
171 EXPECT_EQ(1u, decoder->frameCount()); | 984 } gRecs[] = { |
172 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); | 985 {"/LayoutTests/images/resources/" |
173 EXPECT_EQ(ImageFrame::FrameComplete, | 986 "png-animated-three-independent-frames.png", |
174 decoder->frameBufferAtIndex(0)->getStatus()); | 987 3u, 150u}, |
175 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | 988 {"/LayoutTests/images/resources/" |
| 989 "png-animated-idat-part-of-animation.png", |
| 990 4u, 160u}, |
| 991 |
| 992 {"/LayoutTests/images/resources/png-simple.png", 1u, 700u}, |
| 993 {"/LayoutTests/images/resources/lenna.png", 1u, 40000u}, |
| 994 }; |
| 995 for (const auto& rec : gRecs) { |
| 996 auto fullData = readFile(rec.name); |
| 997 ASSERT_TRUE(fullData.get()); |
| 998 |
| 999 // Create with enough data for part of the first frame. |
| 1000 auto decoder = createDecoder(); |
| 1001 auto data = SharedBuffer::create(fullData->data(), rec.offsetInFirstFrame); |
| 1002 decoder->setData(data.get(), false); |
| 1003 |
| 1004 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); |
| 1005 |
| 1006 // Parsing the size is not enough to mark the frame as complete. |
| 1007 EXPECT_TRUE(decoder->isSizeAvailable()); |
| 1008 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); |
| 1009 |
| 1010 const auto partialFrameCount = decoder->frameCount(); |
| 1011 EXPECT_EQ(1u, partialFrameCount); |
| 1012 |
| 1013 // Frame is not complete, even after decoding partially. |
| 1014 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); |
| 1015 auto* frame = decoder->frameBufferAtIndex(0); |
| 1016 ASSERT_TRUE(frame); |
| 1017 EXPECT_NE(ImageFrame::FrameComplete, frame->getStatus()); |
| 1018 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); |
| 1019 |
| 1020 decoder->setData(fullData.get(), true); |
| 1021 |
| 1022 // With full data, parsing the size still does not mark a frame as |
| 1023 // complete. |
| 1024 EXPECT_TRUE(decoder->isSizeAvailable()); |
| 1025 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); |
| 1026 |
| 1027 const auto frameCount = decoder->frameCount(); |
| 1028 ASSERT_EQ(rec.expectedFrameCount, frameCount); |
| 1029 |
| 1030 if (frameCount > 1u) { |
| 1031 // After parsing (the full file), all frames are complete. |
| 1032 for (size_t i = 0; i < frameCount; ++i) |
| 1033 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(i)); |
| 1034 } else { |
| 1035 // A single frame image is not reported complete until decoding. |
| 1036 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0)); |
| 1037 } |
| 1038 |
| 1039 frame = decoder->frameBufferAtIndex(0); |
| 1040 ASSERT_TRUE(frame); |
| 1041 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus()); |
| 1042 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); |
| 1043 } |
176 } | 1044 } |
177 | 1045 |
178 }; // namespace blink | 1046 }; // namespace blink |
OLD | NEW |