Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoderTest.cpp

Issue 2930513004: [WIP] Move ImageDecoders to SkCodec
Patch Set: Adding check for decoder creation Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698