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

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

Issue 2386453003: WIP: Implement APNG (Closed)
Patch Set: Add test to verify frameIsComplete behavior. Created 4 years 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 "png.h"
8 #include "platform/image-decoders/ImageDecoderTestHelpers.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include <memory>
11
12 // The image /LT/f/i/r/png-animated-idat-part-of-animation.png is modified in
13 // multiple tests to simulate erroneous PNGs. As a reference, the table below
14 // 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 // @TODO(joostouwerling) extend test image set with other image encodings, such
31 // as first frame fcTL and multiple chunks per frame.
32
33 namespace blink {
34
35 namespace {
36
37 std::unique_ptr<ImageDecoder> createDecoder(ImageDecoder::AlphaOption alphaOptio n)
38 {
39 return wrapUnique(new PNGImageDecoder(alphaOption,
40 ImageDecoder::ColorSpaceApplied,
41 ImageDecoder::noDecodedImageByteLimit));
42 }
43
44 std::unique_ptr<ImageDecoder> createDecoder()
45 {
46 return createDecoder(ImageDecoder::AlphaNotPremultiplied);
47 }
48
49 std::unique_ptr<ImageDecoder> createDecoderWithPngData(const char* pngFile)
50 {
51 auto decoder = createDecoder();
52 auto data = readFile(pngFile);
53 EXPECT_FALSE(data->isEmpty());
54 decoder->setData(data.get(), true);
55 return decoder;
56 }
57
58 void testSize(const char* pngFile, IntSize expectedSize)
59 {
60 auto decoder = createDecoderWithPngData(pngFile);
61 EXPECT_TRUE(decoder->isSizeAvailable());
62 EXPECT_EQ(expectedSize, decoder->size());
63 }
64
65 void writeUint32(uint32_t val, png_byte* data)
66 {
67 data[0] = val >> 24;
68 data[1] = val >> 16;
69 data[2] = val >> 8;
70 data[3] = val;
71 }
72
73 void testRepetitionCount(const char* pngFile, int expectedRepetitionCount)
74 {
75 auto decoder = createDecoderWithPngData(pngFile);
76 // Decode frame count should see the number of repetitions as well.
77 decoder->frameCount();
78 EXPECT_FALSE(decoder->failed());
79 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount());
80 }
81
82 // Test whether querying for the size of the image works if we present the
83 // data byte by byte.
84 void testSizeByteByByte(const char *pngFile, size_t bytesNeededToDecodeSize,
85 IntSize expectedSize)
86 {
87 auto decoder = createDecoder();
88 auto data = readFile(pngFile);
89 ASSERT_FALSE(data->isEmpty());
90 ASSERT_LT(bytesNeededToDecodeSize, data->size());
91
92 for (size_t length = 1; length <= bytesNeededToDecodeSize; length++) {
93 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(),
94 length);
95 decoder->setData(tempData.get(), false);
96
97 if (length < bytesNeededToDecodeSize) {
98 EXPECT_FALSE(decoder->isSizeAvailable());
99 EXPECT_TRUE(decoder->size().isEmpty());
100 EXPECT_FALSE(decoder->failed());
101 } else {
102 EXPECT_TRUE(decoder->isSizeAvailable());
103 EXPECT_EQ(expectedSize, decoder->size());
104 }
105 }
106 EXPECT_FALSE(decoder->failed());
107 }
108
109 struct PublicFrameInfo {
110 size_t duration;
111 IntRect frameRect;
112 ImageFrame::AlphaBlendSource alphaBlend;
113 ImageFrame::DisposalMethod disposalMethod;
114 };
115
116 // This is the frame data for the following PNG image:
117 // /LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png
118 static PublicFrameInfo pngAnimatedFrameInfo[] = {
119 {500, {IntPoint(0, 0), IntSize(5, 5)}, ImageFrame::BlendAtopBgcolor,
120 ImageFrame::DisposeKeep},
121 {900, {IntPoint(1, 1), IntSize(3, 1)}, ImageFrame::BlendAtopBgcolor,
122 ImageFrame::DisposeOverwriteBgcolor},
123 {2000, {IntPoint(1, 2), IntSize(3, 2)}, ImageFrame::BlendAtopPreviousFrame,
124 ImageFrame::DisposeKeep},
125 {1500, {IntPoint(1, 2), IntSize(3, 1)}, ImageFrame::BlendAtopBgcolor,
126 ImageFrame::DisposeKeep},
127 };
128
129 void compareFrameWithExpectation(const PublicFrameInfo& expected,
130 const ImageFrame* frame)
131 {
132 EXPECT_EQ(expected.duration, frame->duration());
133 EXPECT_EQ(expected.frameRect, frame->originalFrameRect());
134 EXPECT_EQ(expected.disposalMethod, frame->getDisposalMethod());
135 EXPECT_EQ(expected.alphaBlend, frame->getAlphaBlendSource());
136 }
137
138 // This function removes |length| bytes at |offset|, and then calls frameCount.
139 // It assumes the missing bytes should result in a failed decode because the
140 // parser jumps |length| bytes too far in the next chunk.
141 void testMissingDataBreaksDecoding(const char* pngFile, size_t offset,
142 size_t length)
143 {
144 auto decoder = createDecoder();
145 auto data = readFile(pngFile);
146 ASSERT_FALSE(data->isEmpty());
147
148 RefPtr<SharedBuffer> invalidData = SharedBuffer::create(data->data(),
149 offset);
150 invalidData->append(SharedBuffer::create(data->data() + offset + length,
151 data->size() - offset - length));
152 ASSERT_EQ(data->size() - length, invalidData->size());
153
154 decoder->setData(invalidData, true);
155 decoder->frameCount();
156 EXPECT_TRUE(decoder->failed());
157 }
158
159 // Decoding up to the indicated fcTL offset and then provide an fcTL with
160 // the wrong chunk size (20 instead of 26). It should break the decoder.
161 void testInvalidFctlSize(const char* pngFile,
162 size_t offsetFctl,
163 size_t expectedFrameCountBeforeFail,
164 bool expectedDecoderFailure)
165 {
166 auto data = readFile(pngFile);
167 ASSERT_FALSE(data->isEmpty());
168
169 auto decoder = createDecoder();
170 RefPtr<SharedBuffer> invalidData = SharedBuffer::create(data->data(),
171 offsetFctl);
172
173 // Test if this gives the correct frame count, before the fcTL is parsed.
174 decoder->setData(invalidData, false);
175 EXPECT_EQ(expectedFrameCountBeforeFail, decoder->frameCount());
176 ASSERT_FALSE(decoder->failed());
177
178 // Append the wrong size to the data stream
179 png_byte sizeChunk[4];
180 writeUint32(20, sizeChunk);
181 invalidData->append(reinterpret_cast<char*>(sizeChunk), 4u);
182
183 // Skip the size in the original data, but provide the rest of the fcTL,
184 // which is 4B of tag, 26B of data and 4B of CRC, totalling 34B.
185 invalidData->append(data->data() + offsetFctl + 4, 34u);
186
187 decoder->setData(invalidData, false);
188 decoder->frameCount();
189 EXPECT_EQ(expectedDecoderFailure, decoder->failed());
190 }
191
192 void testDifferentActlFrameCountIsIgnored(const char* pngFile,
193 size_t offsetActl,
194 size_t injectedFrameCount,
195 size_t expectedFrameCount)
196 {
197 // First make sure that this tests makes sense.
198 ASSERT_NE(injectedFrameCount, expectedFrameCount);
199
200 auto data = readFile(pngFile);
201 auto decoder = createDecoder();
202 ASSERT_FALSE(data->isEmpty());
203
204 RefPtr<SharedBuffer> diffActlData = SharedBuffer::create(data->data(),
205 offsetActl + 8);
206 // Write the injectedFrameCount to the stream
207 png_byte sizeChunk[4];
208 writeUint32(injectedFrameCount, sizeChunk);
209 diffActlData->append(reinterpret_cast<char*>(sizeChunk), 4u);
210 // Append the rest of the data. The first |offsetActl + 12| bytes that are
211 // already in diffActlData should not be appended again.
212 diffActlData->append(data->data() + offsetActl + 12,
213 data->size() - offsetActl - 12);
214
215 decoder->setData(diffActlData, true);
216 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
217 }
218
219 // Test if the frame bitmap hashes of truncated decoding are equal to the
220 // hashes found by incremental decoding.
221 void testProgressiveDecoding(const char *pngFile)
222 {
223 RefPtr<SharedBuffer> fullData = readFile(pngFile);
224 ASSERT_TRUE(fullData.get());
225 const size_t fullLength = fullData->size();
226
227 std::unique_ptr<ImageDecoder> decoder;
228 ImageFrame* frame;
229
230 Vector<unsigned> truncatedHashes;
231 Vector<unsigned> progressiveHashes;
232
233 // Compute hashes when the file is truncated.
234 const size_t increment = 13;
235 for (size_t i = 1; i <= fullLength; i += increment) {
236 decoder = createDecoder();
237 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i);
238 decoder->setData(data.get(), i == fullLength);
239 size_t frameCount = decoder->frameCount();
240 ASSERT_FALSE(decoder->failed());
241 if (frameCount == 0) {
242 truncatedHashes.append(0);
243 continue;
244 }
245 frame = decoder->frameBufferAtIndex(frameCount - 1);
246 if (!frame) {
247 truncatedHashes.append(0);
248 continue;
249 }
250 truncatedHashes.append(hashBitmap(frame->bitmap()));
251 }
252
253 // Compute hashes when the file is progressively decoded.
254 decoder = createDecoder();
255 for (size_t i = 1; i <= fullLength; i += increment) {
256 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i);
257 decoder->setData(data.get(), i == fullLength);
258 ASSERT_FALSE(decoder->failed());
259 size_t frameCount = decoder->frameCount();
260 if (frameCount == 0) {
261 progressiveHashes.append(0);
262 continue;
263 }
264 frame = decoder->frameBufferAtIndex(frameCount - 1);
265 if (!frame) {
266 progressiveHashes.append(0);
267 continue;
268 }
269 progressiveHashes.append(hashBitmap(frame->bitmap()));
270 }
271
272 for (size_t i = 0; i < truncatedHashes.size(); ++i)
273 ASSERT_EQ(truncatedHashes[i], progressiveHashes[i]);
274 }
275
276 // Check that providing the full data after the first frame was partially
277 // decoded properly continues where it left off.
278 void testProgressiveDecodingContinuesAfterFullData(const char* pngFile,
279 size_t offsetMidFirstFrame)
280 {
281 auto fullData = readFile(pngFile);
282 auto decoder = createDecoder();
283 ASSERT_FALSE(fullData->isEmpty());
284
285 RefPtr<SharedBuffer> partialData =
286 SharedBuffer::create(fullData->data(), offsetMidFirstFrame);
287 decoder->setData(partialData, false);
288
289 EXPECT_EQ(1u, decoder->frameCount());
290 ImageFrame* frame = decoder->frameBufferAtIndex(0);
291 EXPECT_EQ(frame->getStatus(), ImageFrame::FramePartial);
292 unsigned hashPartial = hashBitmap(frame->bitmap());
293
294 decoder->setData(fullData.get(), true);
295 frame = decoder->frameBufferAtIndex(0);
296 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete);
297 unsigned hashFull = hashBitmap(frame->bitmap());
298
299 EXPECT_FALSE(decoder->failed());
300 EXPECT_NE(hashFull, hashPartial);
301 }
302
303 // This test verifies that the frame buffer contents change when progressively
304 // decoding the first frame. It should change more than 1 time: once for the
305 // first data, and at least once more thereafter. If |offsetFirstFrameEnd| == 0,
306 // the test uses the full data size of the image for its value.
307 void testProgressiveDecodingChangesFrameBuffer(const char* pngFile,
308 size_t offsetFirstFrameEnd,
309 size_t step = 1u)
310 {
311 auto fullData = readFile(pngFile);
312 auto decoder = createDecoder();
313 ASSERT_FALSE(fullData->isEmpty());
314
315 if (offsetFirstFrameEnd == 0)
316 offsetFirstFrameEnd = fullData->size();
317
318 size_t numTimesBufferChanged = 0;
319 unsigned lastHash;
320
321 for (size_t length = 1; length <= offsetFirstFrameEnd; length += step) {
322 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(),
323 length);
324 decoder->setData(data, false);
325 ImageFrame* frame = decoder->frameBufferAtIndex(0);
326 if (!frame)
327 continue;
328 unsigned newHash = hashBitmap(frame->bitmap());
329 if (newHash != lastHash) {
330 lastHash = newHash;
331 numTimesBufferChanged++;
332 }
333 }
334 EXPECT_GT(numTimesBufferChanged, 1u);
335 }
336
337 // @TODO(joostouwerling) Pull this up to ImageDecoderTestHelper since it is also
338 // used in WEBPImageDecoderTest
339 void testRandomDecodeAfterClearFrameBufferCache(const char* pngFile) {
340 SCOPED_TRACE(pngFile);
341
342 RefPtr<SharedBuffer> data = readFile(pngFile);
343 ASSERT_TRUE(data.get());
344 Vector<unsigned> baselineHashes;
345 createDecodingBaseline(&createDecoder, data.get(), &baselineHashes);
346 const size_t frameCount = baselineHashes.size();
347
348 std::unique_ptr<ImageDecoder> decoder = createDecoder();
349 decoder->setData(data.get(), true);
350 for (size_t clearExceptFrame = 0; clearExceptFrame < frameCount;
351 ++clearExceptFrame) {
352 decoder->clearCacheExceptFrame(clearExceptFrame);
353 const size_t skippingStep = 2;
354 for (size_t i = 0; i < skippingStep; ++i) {
355 for (size_t j = 0; j < frameCount; j += skippingStep) {
356 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j);
357 ImageFrame* frame = decoder->frameBufferAtIndex(j);
358 EXPECT_EQ(baselineHashes[j], hashBitmap(frame->bitmap()));
359 }
360 }
361 }
362 }
363
364 uint32_t premultiplyColor(uint32_t c) {
365 return SkPremultiplyARGBInline(SkGetPackedA32(c), SkGetPackedR32(c),
366 SkGetPackedG32(c), SkGetPackedB32(c));
367 }
368
369 void verifyFramesMatch(const char* pngFile,
370 const ImageFrame* const a,
371 ImageFrame* const b) {
372 const SkBitmap& bitmapA = a->bitmap();
373 const SkBitmap& bitmapB = b->bitmap();
374 ASSERT_EQ(bitmapA.width(), bitmapB.width());
375 ASSERT_EQ(bitmapA.height(), bitmapB.height());
376
377 int maxDifference = 0;
378 for (int y = 0; y < bitmapA.height(); ++y) {
379 for (int x = 0; x < bitmapA.width(); ++x) {
380 uint32_t colorA = *bitmapA.getAddr32(x, y);
381 if (!a->premultiplyAlpha())
382 colorA = premultiplyColor(colorA);
383 uint32_t colorB = *bitmapB.getAddr32(x, y);
384 if (!b->premultiplyAlpha())
385 colorB = premultiplyColor(colorB);
386 uint8_t* pixelA = reinterpret_cast<uint8_t*>(&colorA);
387 uint8_t* pixelB = reinterpret_cast<uint8_t*>(&colorB);
388 for (int channel = 0; channel < 4; ++channel) {
389 const int difference = abs(pixelA[channel] - pixelB[channel]);
390 if (difference > maxDifference)
391 maxDifference = difference;
392 }
393 }
394 }
395
396 // Pre-multiplication could round the RGBA channel values. So, we declare
397 // that the frames match if the RGBA channel values differ by at most 2.
398 EXPECT_GE(2, maxDifference) << pngFile;
399 }
400
401 // Verifies that result of alpha blending is similar for AlphaPremultiplied and
402 // AlphaNotPremultiplied cases.
403 void testAlphaBlending(const char* pngFile) {
404 RefPtr<SharedBuffer> data = readFile(pngFile);
405 ASSERT_TRUE(data.get());
406
407 std::unique_ptr<ImageDecoder> decoderA =
408 createDecoder(ImageDecoder::AlphaPremultiplied);
409 decoderA->setData(data.get(), true);
410
411 std::unique_ptr<ImageDecoder> decoderB =
412 createDecoder(ImageDecoder::AlphaNotPremultiplied);
413 decoderB->setData(data.get(), true);
414
415 size_t frameCount = decoderA->frameCount();
416 ASSERT_EQ(frameCount, decoderB->frameCount());
417
418 for (size_t i = 0; i < frameCount; ++i)
419 verifyFramesMatch(pngFile, decoderA->frameBufferAtIndex(i),
420 decoderB->frameBufferAtIndex(i));
421 }
422
423 // Modify the frame data bytes for frame |frameIndex| so that decoding fails.
424 // Parsing should work fine, and is checked with |expectedFrameCountBefore|. If
425 // the failure should invalidate the decoder, |expectFailure| should be set to
426 // true. If not, |expectedFrameCountAfter| should indicate the new frame count
427 // after the failure.
428 void testFailureDuringDecode(const char *file,
429 size_t idatOffset,
430 size_t frameIndex,
431 bool expectFailure,
432 size_t expectedFrameCountBefore,
433 size_t expectedFrameCountAfter = 0u)
434 {
435 RefPtr<SharedBuffer> fullData = readFile(file);
436 ASSERT_FALSE(fullData->isEmpty());
437
438 // This is the offset where the frame data chunk frame |frameIndex| starts.
439 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(),
440 idatOffset + 8u);
441 // Repeat the first 8 bytes of the frame data. This should result in a
442 // succesfull parse, since frame data is not analyzed in that step, but
443 // should give an error during decoding.
444 data->append(fullData->data() + idatOffset, 8u);
445 data->append(fullData->data() + idatOffset + 16u,
446 fullData->size() - idatOffset - 16u);
447
448 auto decoder = createDecoder();
449 decoder->setData(data.get(), true);
450
451 EXPECT_EQ(expectedFrameCountBefore, decoder->frameCount());
452
453 const ImageFrame* const frame = decoder->frameBufferAtIndex(frameIndex);
454 EXPECT_EQ(expectFailure, decoder->failed());
455 if (!expectFailure) {
456 EXPECT_EQ(expectedFrameCountAfter, decoder->frameCount());
457 EXPECT_EQ(ImageFrame::FrameEmpty, frame->getStatus());
458 }
459 }
460
461 } // Anonymous namespace
462
463 // Animated PNG Tests
464
465 TEST(AnimatedPNGTests, sizeTest)
466 {
467 testSize("/LayoutTests/fast/images/resources/png-animated-idat-part-of-animati on.png", IntSize(5, 5));
468 testSize("/LayoutTests/fast/images/resources/png-animated-idat-not-part-of-ani mation.png", IntSize(227, 35));
469 }
470
471 TEST(AnimatedPNGTests, repetitionCountTest)
472 {
473 testRepetitionCount("/LayoutTests/fast/images/resources/png-animated-idat-part -of-animation.png", 7u);
474 // This is an "animated" image with only one frame, that is, the IDAT is
475 // ignored and there is one fdAT frame. so it should be considered
476 // non-animated.
477 testRepetitionCount("/LayoutTests/fast/images/resources/png-animated-idat-not- part-of-animation.png", cAnimationNone);
478 }
479
480 // Test if the decoded metdata corresponds to the defined expectations
481 TEST(AnimatedPNGTests, MetaDataTest)
482 {
483 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
484 constexpr size_t expectedFrameCount = 4;
485
486 auto decoder = createDecoderWithPngData(pngFile);
487 ASSERT_EQ(expectedFrameCount, decoder->frameCount());
488 for (size_t i = 0; i < expectedFrameCount; i++)
489 compareFrameWithExpectation(pngAnimatedFrameInfo[i],
490 decoder->frameBufferAtIndex(i));
491 }
492
493 TEST(AnimatedPNGTests, ByteByByteSizeAvailable)
494 {
495 testSizeByteByByte("/LayoutTests/fast/images/resources/png-animated-idat-part- of-animation.png",
496 141u, IntSize(5, 5));
497 testSizeByteByByte("/LayoutTests/fast/images/resources/png-animated-idat-not-p art-of-animation.png",
498 79u, IntSize(227, 35));
499 }
500
501 // Test whether the frame metadata decoding also works when we provide the data
502 // byte by byte. This should cover the case when the client does not provide
503 // all data at once. At given offsets, we expect frames to become available.
504 // This test checks whether that is the case, and if so, if the frame data is
505 // equal to what we expected.
506 TEST(AnimatedPNGTests, ByteByByteMetaData)
507 {
508 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
509 constexpr size_t expectedFrameCount = 4;
510
511 // These are the byte offsets where each frame should have been parsed.
512 // It boils down to the offset of the first fcTL / IEND after the last
513 // frame data chunk, plus 8 bytes for recognition. The exception on this is
514 // the first frame, which is reported when its first framedata is seen.
515 size_t frameOffsets[expectedFrameCount] = {141, 249, 322, 430};
516
517 auto decoder = createDecoder();
518 auto data = readFile(pngFile);
519 ASSERT_FALSE(data->isEmpty());
520 size_t framesParsed = 0;
521
522 for (size_t length = 1; length <= frameOffsets[expectedFrameCount - 1]; length ++) {
523 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(),
524 length);
525 decoder->setData(tempData.get(), false);
526 EXPECT_FALSE(decoder->failed());
527 if (length < frameOffsets[framesParsed]) {
528 EXPECT_EQ(framesParsed, decoder->frameCount());
529 } else {
530 ASSERT_EQ(framesParsed + 1, decoder->frameCount());
531 compareFrameWithExpectation(pngAnimatedFrameInfo[framesParsed],
532 decoder->frameBufferAtIndex(framesParsed));
533 framesParsed++;
534 }
535 }
536 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
537 EXPECT_FALSE(decoder->failed());
538 }
539
540 TEST(AnimatedPNGTests, TestRandomFrameDecode)
541 {
542 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/p ng-animated-idat-part-of-animation.png");
543 ASSERT_TRUE(fullData.get());
544 Vector<unsigned> baselineHashes;
545 createDecodingBaseline(&createDecoder, fullData.get(), &baselineHashes);
546 const size_t frameCount = baselineHashes.size();
547
548 // Random decoding should get the same results as sequential decoding.
549 std::unique_ptr<ImageDecoder> decoder = createDecoder();
550 decoder->setData(fullData.get(), true);
551 const size_t skippingStep = 2;
552 for (size_t i = 0; i < skippingStep; ++i) {
553 for (size_t j = i; j < frameCount; j += skippingStep) {
554 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j);
555 ImageFrame* frame = decoder->frameBufferAtIndex(j);
556 EXPECT_EQ(baselineHashes[j], hashBitmap(frame->bitmap()));
557 }
558 }
559
560 // Decoding in reverse order.
561 decoder = createDecoder();
562 decoder->setData(fullData.get(), true);
563 for (size_t i = frameCount; i; --i) {
564 SCOPED_TRACE(testing::Message() << "Reverse i:" << i);
565 ImageFrame* frame = decoder->frameBufferAtIndex(i - 1);
566 EXPECT_EQ(baselineHashes[i - 1], hashBitmap(frame->bitmap()));
567 }
568
569 }
570
571 TEST(AnimatedPNGTests, TestDecodeAfterReallocation)
572 {
573 std::unique_ptr<ImageDecoder> decoder = createDecoder();
574 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/png-a nimated-idat-part-of-animation.png");
575 ASSERT_TRUE(data.get());
576
577 // Parse from |data|.
578 decoder->setData(data.get(), true);
579 const size_t frameCount = decoder->frameCount();
580
581 // ... and then decode frames from |reallocatedData|.
582 RefPtr<SharedBuffer> reallocatedData = data.get()->copy();
583 ASSERT_TRUE(reallocatedData.get());
584 data.clear();
585 decoder->setData(reallocatedData.get(), true);
586
587 for (size_t i = 0; i < frameCount; ++i) {
588 const ImageFrame* const frame = decoder->frameBufferAtIndex(i);
589 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus());
590 }
591 }
592
593 TEST(AnimatedPNGTests, ProgressiveDecode)
594 {
595 testProgressiveDecoding("/LayoutTests/fast/images/resources/png-animated-idat- part-of-animation.png");
596 }
597
598 TEST(AnimatedPNGTests, ParseAndDecodeByteByByte)
599 {
600 testByteByByteDecode(&createDecoder,
601 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-anima tion.png",
602 4u, 7u);
603 }
604
605 TEST(AnimatedPNGTests, FailureDuringParsing)
606 {
607 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
608 auto data = readFile(pngFile);
609 ASSERT_FALSE(data->isEmpty());
610
611 // Test the first fcTL in the stream. Because no frame data has been set at
612 // this point, the expected frame count is zero. 95 bytes is just before the
613 // first fcTL chunk, at which the first frame is detected. The decoder should
614 // be in the failed state since no frames were parsed before this error.
615 testInvalidFctlSize("/LayoutTests/fast/images/resources/png-animated-idat-part -of-animation.png",
616 95u, 0u, true);
617
618 // Test for the third fcTL in the stream. This should be tested as well,
619 // since the first fcTL is parsed in PNGImageReader::parseSize() whereas
620 // later fcTLs are parsed in PNGImageReader::parse() as part of framecount.
621 // The expected frame count before the fcTL chunk is 1u, since the second
622 // frame is registered when the third fcTL (or IEND) is seen.
623 //
624 // This should *not* set the decoder to the failed state since the first
625 // frame was succesfully parsed, and we still want to show that frame.
626 testInvalidFctlSize("/LayoutTests/fast/images/resources/png-animated-idat-part -of-animation.png",
627 241u, 1u, false);
628
629 }
630
631 TEST(AnimatedPNGTests, FailureDuringParsingDueToMissingFctlData)
632 {
633 // The fcTL chunk starts at 95u, add 10u to get to the content data, and
634 // remove 5 bytes of data. This sets the decoder to the failed state since
635 // no frames were parsed yet.
636 testMissingDataBreaksDecoding(
637 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png" ,
638 105u, 5u);
639 }
640
641 TEST(AnimatedPNGTests, MissingActlResultsInNonAnimated)
642 {
643 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
644 auto data = readFile(pngFile);
645 auto decoder = createDecoder();
646 ASSERT_FALSE(data->isEmpty());
647
648 // Remove the acTL chunk from the stream.
649 const size_t offsetActl = 33;
650 RefPtr<SharedBuffer> noActlData = SharedBuffer::create(data->data(),
651 offsetActl);
652 noActlData->append(data->data() + offsetActl + 20,
653 data->size() - offsetActl - 20);
654
655 decoder->setData(noActlData, true);
656 EXPECT_EQ(1u, decoder->frameCount());
657 EXPECT_FALSE(decoder->failed());
658 }
659
660 // Test if the indicated frame count by the acTL is ignored if the actual
661 // number of frames is different. Test for higher and lower indicated number.
662 TEST(AnimatedPNGTests, differentActlFrameCountIsIgnored)
663 {
664 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
665 testDifferentActlFrameCountIsIgnored(pngFile, 33u, 2u, 4u);
666 testDifferentActlFrameCountIsIgnored(pngFile, 33u, 8u, 4u);
667 }
668
669 // Originally, the third frame has an offset of (1,2) and a size of (3,2). By
670 // changing the offset to (0,0) and the size to (4,4), a frame with not enough
671 // data is simulated. The expected result is that the decoder will only receive
672 // 1 row of data, since the data contains 6 pixels and each row is 4 pixels.
673 //
674 // When the decoder receives not enough data for a frame, the expected behavior
675 // is that nothing changes: the decoder should not be invalidated, and the
676 // frame should be considered successfully decoded. The reason for this is that
677 // the error could either be an invalid frame size, or an invalid data chunk.
678 // For now, the decoder does not take any action if not enough data is
679 // provided, as described in PNGImageDecoder::complete().
680 TEST(AnimatedPNGTests, VerifyIncompleteFrameDataDoesNotInvalidateDecoder)
681 {
682 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
683 auto data = readFile(pngFile);
684 auto decoder = createDecoder();
685 ASSERT_FALSE(data->isEmpty());
686
687 // Change the size and the offset of the frame, as described above. The size
688 // is stored in byte 12-19 of the fcTL chunk, and the offset in byte 20-27.
689 const size_t offsetThirdFctl = 241 + 12;
690 RefPtr<SharedBuffer> modifiedData = SharedBuffer::create(data->data(),
691 offsetThirdFctl);
692 png_byte rectChunk[16];
693 writeUint32(4, rectChunk);
694 writeUint32(4, rectChunk + 4);
695 writeUint32(0, rectChunk + 8);
696 writeUint32(0, rectChunk + 12);
697 modifiedData->append(const_cast<const char*>(
698 reinterpret_cast<char*>(rectChunk)), 16u);
699 modifiedData->append(data->data() + offsetThirdFctl + 16,
700 data->size() - offsetThirdFctl - 16);
701
702 decoder->setData(modifiedData, true);
703
704 // Verify that parsing the image results in the expected meta data.
705 IntSize expectedSize(5, 5);
706 const size_t expectedFrameCount = 4;
707 IntRect expectedFrameRect(IntPoint(0, 0), IntSize(4, 4));
708 EXPECT_TRUE(decoder->isSizeAvailable());
709 EXPECT_EQ(expectedSize, decoder->size());
710 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
711 ASSERT_FALSE(decoder->failed());
712
713 // Try to decode frame 3. This should not fail the decoder, and the frame
714 // should be FrameComplete.
715 const ImageFrame* const buffer = decoder->frameBufferAtIndex(2);
716 EXPECT_EQ(expectedFrameRect, buffer->originalFrameRect());
717 EXPECT_FALSE(decoder->failed());
718 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
719 EXPECT_EQ(ImageFrame::FrameComplete, buffer->getStatus());
720 }
721
722 // Originally, the third frame has an offset of (1,2) and a size of (3,2). By
723 // changing the offset to (4,4), the frame rect will extend the image size of
724 // 5x5. Verify that the frame is correctly clipped to a size of (1,1) without
725 // any decoding errors. Since this is the third frame, a decoding failure would
726 // result in the frame count being adjusted to 2, and the decoder would not be
727 // invalidated. Therefore, it is verified that after decoding frame 3 the
728 // decoder is still in a valid state and the frame count is still 4.
729 TEST(AnimatedPNGTests, VerifyFrameExtendsImageSizeClipsCorrectly)
730 {
731 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
732 auto data = readFile(pngFile);
733 auto decoder = createDecoder();
734 ASSERT_FALSE(data->isEmpty());
735
736 // The offset of the frame rect is located 20 bytes into the fcTL chunk, after
737 // the length, tag, sequence number, width and height fields (all 4B).
738 const size_t offsetThirdFctl = 241 + 20;
739 RefPtr<SharedBuffer> modifiedData = SharedBuffer::create(data->data(),
740 offsetThirdFctl);
741 png_byte offsetChunk[8];
742 writeUint32(4, offsetChunk);
743 writeUint32(4, offsetChunk + 4);
744 modifiedData->append(const_cast<const char*>(
745 reinterpret_cast<char*>(offsetChunk)), 8u);
746 modifiedData->append(data->data() + offsetThirdFctl + 8,
747 data->size() - offsetThirdFctl - 8);
748
749 decoder->setData(modifiedData, true);
750
751 // Verify that parsing the image results in the expected meta data.
752 IntSize expectedSize(5, 5);
753 const size_t expectedFrameCount = 4;
754 IntRect expectedFrameRect(IntPoint(4, 4), IntSize(1, 1));
755 EXPECT_TRUE(decoder->isSizeAvailable());
756 EXPECT_EQ(expectedSize, decoder->size());
757 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
758 ASSERT_FALSE(decoder->failed());
759
760 // Try to decode frame 3. This should not fail the decoder, and the frame
761 // should be FrameComplete.
762 const ImageFrame* const buffer = decoder->frameBufferAtIndex(2);
763 EXPECT_EQ(expectedFrameRect, buffer->originalFrameRect());
764 EXPECT_FALSE(decoder->failed());
765 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
766 EXPECT_EQ(ImageFrame::FrameComplete, buffer->getStatus());
767 }
768
769 TEST(AnimatedPNGTests, ProgressiveDecodingChangesFrameBuffer)
770 {
771 testProgressiveDecodingChangesFrameBuffer(
772 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png" , 249u);
773 }
774
775 TEST(AnimatedPNGTests, ProgressiveDecodingContinuesAfterFullData)
776 {
777 // 160u is a randomly chosen offset in the IDAT chunk of the first frame.
778 testProgressiveDecodingContinuesAfterFullData(
779 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png" , 160u);
780 }
781
782 TEST(AnimatedPNGTests, RandomDecodeAfterClearFrameBufferCache)
783 {
784 testRandomDecodeAfterClearFrameBufferCache(
785 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png" );
786 }
787
788 TEST(AnimatedPNGTests, VerifyAlphaBlending) {
789 testAlphaBlending(
790 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.png" );
791 }
792
793 // This tests if the frame count gets set correctly when parsing frameCount
794 // fails in one of the parsing queries.
795 //
796 // First, enough data is provided such that two frames should be registered.
797 // The decoder should at this point not be in the failed status.
798 //
799 // Then, we provide the rest of the data except for the last IEND chunk, but
800 // tell the decoder that this is all the data we have. The frame count should
801 // be three, since one extra frame should be discovered. The fourth frame
802 // should *not* be registered since the reader should not be able to determine
803 // where the frame ends. The decoder should *not* be in the failed state since
804 // there are three frames which can be shown.
805 TEST(AnimatedPNGTests, FailureMissingIendChunk)
806 {
807 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/p ng-animated-idat-part-of-animation.png");
808 ASSERT_FALSE(fullData->isEmpty());
809 auto decoder = createDecoder();
810
811 const size_t offsetTwoFrames = 249;
812 const size_t expectedFramesAfter249Bytes = 2;
813 RefPtr<SharedBuffer> tempData = SharedBuffer::create(fullData->data(),
814 offsetTwoFrames);
815 decoder->setData(tempData.get(), false);
816 EXPECT_EQ(expectedFramesAfter249Bytes, decoder->frameCount());
817 EXPECT_FALSE(decoder->failed());
818
819 // Provide the rest of the data except for the last IEND chunk.
820 const size_t expectedFramesAfterAllExpect12Bytes = 3;
821 tempData = SharedBuffer::create(fullData->data(), fullData->size() - 12);
822 decoder->setData(tempData.get(), true);
823 EXPECT_EQ(expectedFramesAfterAllExpect12Bytes, decoder->frameCount());
824 EXPECT_FALSE(decoder->failed());
825 }
826
827 TEST(AnimatedPNGTests, VerifyFrameCountChangesOnDecodingFailure)
828 {
829 testFailureDuringDecode(
830 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.pn g",
831 279u, // idat offset for frame index 2
832 2u, // try to decode frame index 2
833 false, // expect the decoder to *not* be invalidated after the failure
834 4u, // expected frame count before failure
835 2u); // expected frame count after failure
836
837 testFailureDuringDecode(
838 "/LayoutTests/fast/images/resources/png-animated-idat-part-of-animation.pn g",
839 133u, // idat offset for frame index 0
840 0u, // try to decode frame index 0
841 true, // expect the decoder to be invalidated after the failure
842 4u); // expected frame count before failure
843 }
844
845 // Verify that a malformatted PNG, where the IEND appears before any frame data
846 // (IDAT), invalidates the decoder.
847 TEST(AnimatedPNGTests, VerifyIENDBeforeIDATInvalidatesDecoder)
848 {
849 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/p ng-animated-idat-part-of-animation.png");
850 ASSERT_FALSE(fullData->isEmpty());
851 auto decoder = createDecoder();
852
853 const size_t offsetIDAT = 133;
854 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(),
855 offsetIDAT);
856 data->append(fullData->data() + fullData->size() - 12u, 12u);
857 data->append(fullData->data() + offsetIDAT, fullData->size() - offsetIDAT);
858 decoder->setData(data.get(), true);
859
860 const size_t expectedFrameCount = 0u;
861 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
862 EXPECT_TRUE(decoder->failed());
863 }
864
865 TEST(AnimatedPNGTests, VerifyFrameCompleteBehavior)
866 {
867 const char* pngFile = "/LayoutTests/fast/images/resources/png-animated-idat-pa rt-of-animation.png";
868 RefPtr<SharedBuffer> fullData = readFile(pngFile);
869 ASSERT_FALSE(fullData->isEmpty());
870 auto decoder = createDecoder();
871 decoder->setData(fullData.get(), true);
872
873 const size_t expectedFrameCount = 4;
874 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
875 ASSERT_FALSE(decoder->failed());
876
877 // The first frame is only considered complete if it has been fully decoded.
scroggo_chromium 2016/11/29 16:30:52 I think this behavior is wrong. I've gone back to
joostouwerling 2016/12/02 16:08:42 Yes, I agree. I think it is for the best to return
878 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0));
879 // Non-first frames are considered complete when they're fully received.
880 for (size_t index = 1; index < expectedFrameCount; index++)
881 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1));
scroggo_chromium 2016/11/29 16:30:52 I think you meant to pass index to frameIsComplete
joostouwerling 2016/12/02 16:08:42 Done.
882 // After decoding, the frame should be considered complete.
883 decoder->frameBufferAtIndex(0);
884 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0));
885
886 // Also test that a progressive decode of frame 0 only returns true on
scroggo_chromium 2016/11/29 16:30:52 Again, I think this is the wrong behavior.
joostouwerling 2016/12/02 16:08:42 Acknowledged.
887 // frameIsCompleteAtIndex when it is fully decoded.
888 decoder = createDecoder();
889 const size_t offsetMidwayIDAT = 160;
890 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(),
891 offsetMidwayIDAT);
892 decoder->setData(data.get(), false);
893 const ImageFrame* frame = decoder->frameBufferAtIndex(0);
894 EXPECT_EQ(ImageFrame::FramePartial, frame->getStatus());
895 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(0));
896 // With the full data provided, the status should switch to complete.
897 decoder->setData(fullData.get(), true);
898 frame = decoder->frameBufferAtIndex(0);
899 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus());
900 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0));
901 }
902
903 // Static PNG tests
904
905 TEST(StaticPNGTests, repetitionCountTest)
906 {
907 testRepetitionCount("/LayoutTests/fast/images/resources/png-simple.png",
908 cAnimationNone);
909 }
910
911 TEST(StaticPNGTests, sizeTest)
912 {
913 testSize("/LayoutTests/fast/images/resources/png-simple.png",
914 IntSize(111, 29));
915 }
916
917 TEST(StaticPNGTests, MetaDataTest)
918 {
919 const size_t expectedFrameCount = 1;
920 const size_t expectedDuration = 0;
921 auto decoder = createDecoderWithPngData("/LayoutTests/fast/images/resources/pn g-simple.png");
922 EXPECT_EQ(expectedFrameCount, decoder->frameCount());
923 EXPECT_EQ(expectedDuration, decoder->frameDurationAtIndex(0));
924 }
925
926 TEST(StaticPNGTests, InvalidIHDRChunk)
927 {
928 testMissingDataBreaksDecoding(
929 "/LayoutTests/fast/images/resources/png-simple.png", 20u, 2u);
930 }
931
932 TEST(StaticPNGTests, ProgressiveDecoding)
933 {
934 testProgressiveDecoding("/LayoutTests/fast/images/resources/png-simple.png");
935 }
936
937 TEST(StaticPNGTests, ProgressiveDecodingChangesFrameBuffer)
938 {
939 testProgressiveDecodingChangesFrameBuffer(
940 "/LayoutTests/fast/images/resources/png-simple.png", 0u, 5u);
941 }
942
943 TEST(StaticPNGTests, ProgressiveDecodingContinuesAfterFullData)
944 {
945 testProgressiveDecodingContinuesAfterFullData(
946 "/LayoutTests/fast/images/resources/png-simple.png", 1000u);
947 }
948
949 TEST(StaticPNGTests, FailureDuringDecodingInvalidatesDecoder)
950 {
951 testFailureDuringDecode(
952 "/LayoutTests/fast/images/resources/png-simple.png",
953 85u, // idat offset for frame index 0
954 0u, // try to decode frame index 0
955 true, // expect the decoder to be invalidated after the failure
956 1u); // expected frame count before failure
957 }
958
959 }; // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698