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

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

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

Powered by Google App Engine
This is Rietveld 408576698