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