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 | |
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 | |
OLD | NEW |