OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "platform/image-decoders/png/PNGImageDecoder.h" | |
6 | |
7 #include "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 | |
OLD | NEW |