OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 | |
31 #include "config.h" | |
32 | |
33 #include "core/platform/image-decoders/webp/WEBPImageDecoder.h" | |
34 | |
35 #include "RuntimeEnabledFeatures.h" | |
36 #include "platform/SharedBuffer.h" | |
37 #include "public/platform/Platform.h" | |
38 #include "public/platform/WebData.h" | |
39 #include "public/platform/WebSize.h" | |
40 #include "public/platform/WebUnitTestSupport.h" | |
41 #include "wtf/OwnPtr.h" | |
42 #include "wtf/PassOwnPtr.h" | |
43 #include "wtf/StringHasher.h" | |
44 #include "wtf/Vector.h" | |
45 #include "wtf/dtoa/utils.h" | |
46 #include <gtest/gtest.h> | |
47 | |
48 using namespace WebCore; | |
49 using namespace blink; | |
50 | |
51 namespace { | |
52 | |
53 PassRefPtr<SharedBuffer> readFile(const char* fileName) | |
54 { | |
55 String filePath = Platform::current()->unitTestSupport()->webKitRootDir(); | |
56 filePath.append(fileName); | |
57 | |
58 return Platform::current()->unitTestSupport()->readFromFile(filePath); | |
59 } | |
60 | |
61 PassOwnPtr<WEBPImageDecoder> createDecoder() | |
62 { | |
63 return adoptPtr(new WEBPImageDecoder(ImageSource::AlphaNotPremultiplied, Ima
geSource::GammaAndColorProfileApplied, ImageDecoder::noDecodedImageByteLimit)); | |
64 } | |
65 | |
66 unsigned hashSkBitmap(const SkBitmap& bitmap) | |
67 { | |
68 return StringHasher::hashMemory(bitmap.getPixels(), bitmap.getSize()); | |
69 } | |
70 | |
71 void createDecodingBaseline(SharedBuffer* data, Vector<unsigned>* baselineHashes
) | |
72 { | |
73 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
74 decoder->setData(data, true); | |
75 size_t frameCount = decoder->frameCount(); | |
76 for (size_t i = 0; i < frameCount; ++i) { | |
77 ImageFrame* frame = decoder->frameBufferAtIndex(i); | |
78 baselineHashes->append(hashSkBitmap(frame->getSkBitmap())); | |
79 } | |
80 } | |
81 | |
82 void testRandomFrameDecode(const char* webpFile) | |
83 { | |
84 SCOPED_TRACE(webpFile); | |
85 | |
86 RefPtr<SharedBuffer> fullData = readFile(webpFile); | |
87 ASSERT_TRUE(fullData.get()); | |
88 Vector<unsigned> baselineHashes; | |
89 createDecodingBaseline(fullData.get(), &baselineHashes); | |
90 size_t frameCount = baselineHashes.size(); | |
91 | |
92 // Random decoding should get the same results as sequential decoding. | |
93 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
94 decoder->setData(fullData.get(), true); | |
95 const size_t skippingStep = 5; | |
96 for (size_t i = 0; i < skippingStep; ++i) { | |
97 for (size_t j = i; j < frameCount; j += skippingStep) { | |
98 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j); | |
99 ImageFrame* frame = decoder->frameBufferAtIndex(j); | |
100 EXPECT_EQ(baselineHashes[j], hashSkBitmap(frame->getSkBitmap())); | |
101 } | |
102 } | |
103 | |
104 // Decoding in reverse order. | |
105 decoder = createDecoder(); | |
106 decoder->setData(fullData.get(), true); | |
107 for (size_t i = frameCount; i; --i) { | |
108 SCOPED_TRACE(testing::Message() << "Reverse i:" << i); | |
109 ImageFrame* frame = decoder->frameBufferAtIndex(i - 1); | |
110 EXPECT_EQ(baselineHashes[i - 1], hashSkBitmap(frame->getSkBitmap())); | |
111 } | |
112 } | |
113 | |
114 void testRandomDecodeAfterClearFrameBufferCache(const char* webpFile) | |
115 { | |
116 SCOPED_TRACE(webpFile); | |
117 | |
118 RefPtr<SharedBuffer> data = readFile(webpFile); | |
119 ASSERT_TRUE(data.get()); | |
120 Vector<unsigned> baselineHashes; | |
121 createDecodingBaseline(data.get(), &baselineHashes); | |
122 size_t frameCount = baselineHashes.size(); | |
123 | |
124 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
125 decoder->setData(data.get(), true); | |
126 for (size_t clearExceptFrame = 0; clearExceptFrame < frameCount; ++clearExce
ptFrame) { | |
127 decoder->clearCacheExceptFrame(clearExceptFrame); | |
128 const size_t skippingStep = 5; | |
129 for (size_t i = 0; i < skippingStep; ++i) { | |
130 for (size_t j = 0; j < frameCount; j += skippingStep) { | |
131 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" <<
j); | |
132 ImageFrame* frame = decoder->frameBufferAtIndex(j); | |
133 EXPECT_EQ(baselineHashes[j], hashSkBitmap(frame->getSkBitmap()))
; | |
134 } | |
135 } | |
136 } | |
137 } | |
138 | |
139 void testDecodeAfterReallocatingData(const char* webpFile) | |
140 { | |
141 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
142 RefPtr<SharedBuffer> data = readFile(webpFile); | |
143 ASSERT_TRUE(data.get()); | |
144 | |
145 // Parse from 'data'. | |
146 decoder->setData(data.get(), true); | |
147 size_t frameCount = decoder->frameCount(); | |
148 | |
149 // ... and then decode frames from 'reallocatedData'. | |
150 RefPtr<SharedBuffer> reallocatedData = data.get()->copy(); | |
151 ASSERT_TRUE(reallocatedData.get()); | |
152 data.clear(); | |
153 decoder->setData(reallocatedData.get(), true); | |
154 | |
155 for (size_t i = 0; i < frameCount; ++i) { | |
156 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
157 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
158 } | |
159 } | |
160 | |
161 void testInvalidImage(const char* webpFile) | |
162 { | |
163 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
164 | |
165 RefPtr<SharedBuffer> data = readFile(webpFile); | |
166 ASSERT_TRUE(data.get()); | |
167 decoder->setData(data.get(), true); | |
168 | |
169 EXPECT_EQ(0u, decoder->frameCount()); | |
170 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
171 EXPECT_FALSE(frame); | |
172 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
173 } | |
174 | |
175 } // namespace | |
176 | |
177 class AnimatedWebPTests : public ::testing::Test { | |
178 protected: | |
179 virtual void SetUp() | |
180 { | |
181 // Enable animated WebP for all the tests. | |
182 WebCore::RuntimeEnabledFeatures::setAnimatedWebPEnabled(true); | |
183 } | |
184 }; | |
185 | |
186 TEST_F(AnimatedWebPTests, uniqueGenerationIDs) | |
187 { | |
188 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
189 | |
190 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
191 ASSERT_TRUE(data.get()); | |
192 decoder->setData(data.get(), true); | |
193 | |
194 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
195 uint32_t generationID0 = frame->getSkBitmap().getGenerationID(); | |
196 frame = decoder->frameBufferAtIndex(1); | |
197 uint32_t generationID1 = frame->getSkBitmap().getGenerationID(); | |
198 | |
199 EXPECT_TRUE(generationID0 != generationID1); | |
200 } | |
201 | |
202 TEST_F(AnimatedWebPTests, verifyAnimationParametersTransparentImage) | |
203 { | |
204 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
205 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
206 | |
207 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
208 ASSERT_TRUE(data.get()); | |
209 decoder->setData(data.get(), true); | |
210 | |
211 const int canvasWidth = 11; | |
212 const int canvasHeight = 29; | |
213 const struct AnimParam { | |
214 int xOffset, yOffset, width, height; | |
215 ImageFrame::DisposalMethod disposalMethod; | |
216 ImageFrame::AlphaBlendSource alphaBlendSource; | |
217 unsigned duration; | |
218 bool hasAlpha; | |
219 } frameParameters[] = { | |
220 { 0, 0, 11, 29, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFr
ame, 1000u, true }, | |
221 { 2, 10, 7, 17, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFr
ame, 500u, true }, | |
222 { 2, 2, 7, 16, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFra
me, 1000u, true }, | |
223 }; | |
224 | |
225 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
226 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
227 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
228 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
229 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
230 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
231 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
232 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
233 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
234 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
235 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
236 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
237 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
238 } | |
239 | |
240 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
241 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
242 } | |
243 | |
244 TEST_F(AnimatedWebPTests, verifyAnimationParametersOpaqueFramesTransparentBackgr
ound) | |
245 { | |
246 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
247 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
248 | |
249 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated-opaque.webp"); | |
250 ASSERT_TRUE(data.get()); | |
251 decoder->setData(data.get(), true); | |
252 | |
253 const int canvasWidth = 94; | |
254 const int canvasHeight = 87; | |
255 const struct AnimParam { | |
256 int xOffset, yOffset, width, height; | |
257 ImageFrame::DisposalMethod disposalMethod; | |
258 ImageFrame::AlphaBlendSource alphaBlendSource; | |
259 unsigned duration; | |
260 bool hasAlpha; | |
261 } frameParameters[] = { | |
262 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendA
topPreviousFrame, 1000u, true }, | |
263 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
264 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
265 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
266 }; | |
267 | |
268 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
269 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
270 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
271 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
272 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
273 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
274 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
275 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
276 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
277 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
278 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
279 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
280 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
281 } | |
282 | |
283 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
284 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
285 } | |
286 | |
287 TEST_F(AnimatedWebPTests, verifyAnimationParametersBlendOverwrite) | |
288 { | |
289 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
290 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
291 | |
292 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated-no-blend.webp"); | |
293 ASSERT_TRUE(data.get()); | |
294 decoder->setData(data.get(), true); | |
295 | |
296 const int canvasWidth = 94; | |
297 const int canvasHeight = 87; | |
298 const struct AnimParam { | |
299 int xOffset, yOffset, width, height; | |
300 ImageFrame::DisposalMethod disposalMethod; | |
301 ImageFrame::AlphaBlendSource alphaBlendSource; | |
302 unsigned duration; | |
303 bool hasAlpha; | |
304 } frameParameters[] = { | |
305 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendA
topBgcolor, 1000u, true }, | |
306 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
307 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
308 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
309 }; | |
310 | |
311 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
312 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
313 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
314 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
315 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
316 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
317 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
318 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
319 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
320 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
321 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
322 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
323 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
324 } | |
325 | |
326 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
327 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
328 } | |
329 | |
330 TEST_F(AnimatedWebPTests, parseAndDecodeByteByByte) | |
331 { | |
332 const struct TestImage { | |
333 const char* filename; | |
334 unsigned frameCount; | |
335 int repetitionCount; | |
336 } testImages[] = { | |
337 { "/LayoutTests/fast/images/resources/webp-animated.webp", 3u, cAnimatio
nLoopInfinite }, | |
338 { "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp", 13u,
32000 }, | |
339 }; | |
340 | |
341 for (size_t i = 0; i < ARRAY_SIZE(testImages); ++i) { | |
342 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
343 RefPtr<SharedBuffer> data = readFile(testImages[i].filename); | |
344 ASSERT_TRUE(data.get()); | |
345 | |
346 size_t frameCount = 0; | |
347 size_t framesDecoded = 0; | |
348 | |
349 // Pass data to decoder byte by byte. | |
350 for (size_t length = 1; length <= data->size(); ++length) { | |
351 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), l
ength); | |
352 decoder->setData(tempData.get(), length == data->size()); | |
353 | |
354 EXPECT_LE(frameCount, decoder->frameCount()); | |
355 frameCount = decoder->frameCount(); | |
356 | |
357 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
358 if (frame && frame->status() == ImageFrame::FrameComplete && framesD
ecoded < frameCount) | |
359 ++framesDecoded; | |
360 } | |
361 | |
362 EXPECT_EQ(testImages[i].frameCount, decoder->frameCount()); | |
363 EXPECT_EQ(testImages[i].frameCount, framesDecoded); | |
364 EXPECT_EQ(testImages[i].repetitionCount, decoder->repetitionCount()); | |
365 } | |
366 } | |
367 | |
368 TEST_F(AnimatedWebPTests, invalidImages) | |
369 { | |
370 // ANMF chunk size is smaller than ANMF header size. | |
371 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp.w
ebp"); | |
372 // One of the frame rectangles extends outside the image boundary. | |
373 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp3.
webp"); | |
374 } | |
375 | |
376 TEST_F(AnimatedWebPTests, truncatedLastFrame) | |
377 { | |
378 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
379 | |
380 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/inv
alid-animated-webp2.webp"); | |
381 ASSERT_TRUE(data.get()); | |
382 decoder->setData(data.get(), true); | |
383 | |
384 unsigned frameCount = 8; | |
385 EXPECT_EQ(frameCount, decoder->frameCount()); | |
386 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
387 EXPECT_FALSE(frame); | |
388 frame = decoder->frameBufferAtIndex(0); | |
389 EXPECT_FALSE(frame); | |
390 } | |
391 | |
392 TEST_F(AnimatedWebPTests, truncatedInBetweenFrame) | |
393 { | |
394 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
395 | |
396 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/invalid-animated-webp4.webp"); | |
397 ASSERT_TRUE(fullData.get()); | |
398 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), fullData-
>size() - 1); | |
399 decoder->setData(data.get(), false); | |
400 | |
401 ImageFrame* frame = decoder->frameBufferAtIndex(2); | |
402 EXPECT_FALSE(frame); | |
403 } | |
404 | |
405 // Reproduce a crash that used to happen for a specific file with specific seque
nce of method calls. | |
406 TEST_F(AnimatedWebPTests, reproCrash) | |
407 { | |
408 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
409 | |
410 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/invalid_vp8_vp8x.webp"); | |
411 ASSERT_TRUE(fullData.get()); | |
412 | |
413 // Parse partial data up to which error in bitstream is not detected. | |
414 const size_t partialSize = 32768; | |
415 ASSERT_GT(fullData->size(), partialSize); | |
416 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSi
ze); | |
417 decoder->setData(data.get(), false); | |
418 EXPECT_EQ(1u, decoder->frameCount()); | |
419 | |
420 // Parse full data now. The error in bitstream should now be detected. | |
421 decoder->setData(fullData.get(), true); | |
422 EXPECT_EQ(0u, decoder->frameCount()); | |
423 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
424 EXPECT_FALSE(frame); | |
425 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
426 } | |
427 | |
428 TEST_F(AnimatedWebPTests, progressiveDecode) | |
429 { | |
430 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated.webp"); | |
431 ASSERT_TRUE(fullData.get()); | |
432 const size_t fullLength = fullData->size(); | |
433 | |
434 OwnPtr<WEBPImageDecoder> decoder; | |
435 ImageFrame* frame; | |
436 | |
437 Vector<unsigned> truncatedHashes; | |
438 Vector<unsigned> progressiveHashes; | |
439 | |
440 // Compute hashes when the file is truncated. | |
441 const size_t increment = 1; | |
442 for (size_t i = 1; i <= fullLength; i += increment) { | |
443 decoder = createDecoder(); | |
444 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
445 decoder->setData(data.get(), i == fullLength); | |
446 frame = decoder->frameBufferAtIndex(0); | |
447 if (!frame) { | |
448 truncatedHashes.append(0); | |
449 continue; | |
450 } | |
451 truncatedHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
452 } | |
453 | |
454 // Compute hashes when the file is progressively decoded. | |
455 decoder = createDecoder(); | |
456 for (size_t i = 1; i <= fullLength; i += increment) { | |
457 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
458 decoder->setData(data.get(), i == fullLength); | |
459 frame = decoder->frameBufferAtIndex(0); | |
460 if (!frame) { | |
461 progressiveHashes.append(0); | |
462 continue; | |
463 } | |
464 progressiveHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
465 } | |
466 | |
467 bool match = true; | |
468 for (size_t i = 0; i < truncatedHashes.size(); ++i) { | |
469 if (truncatedHashes[i] != progressiveHashes[i]) { | |
470 match = false; | |
471 break; | |
472 } | |
473 } | |
474 EXPECT_TRUE(match); | |
475 } | |
476 | |
477 TEST_F(AnimatedWebPTests, frameIsCompleteAndDuration) | |
478 { | |
479 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
480 | |
481 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
482 ASSERT_TRUE(data.get()); | |
483 | |
484 ASSERT_GE(data->size(), 10u); | |
485 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
486 decoder->setData(tempData.get(), false); | |
487 | |
488 EXPECT_EQ(2u, decoder->frameCount()); | |
489 EXPECT_FALSE(decoder->failed()); | |
490 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
491 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); | |
492 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
493 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); | |
494 | |
495 decoder->setData(data.get(), true); | |
496 EXPECT_EQ(3u, decoder->frameCount()); | |
497 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
498 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); | |
499 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
500 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); | |
501 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(2)); | |
502 EXPECT_EQ(1000.0, decoder->frameDurationAtIndex(2)); | |
503 } | |
504 | |
505 TEST_F(AnimatedWebPTests, updateRequiredPreviousFrameAfterFirstDecode) | |
506 { | |
507 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
508 | |
509 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated.webp"); | |
510 ASSERT_TRUE(fullData.get()); | |
511 | |
512 // Give it data that is enough to parse but not decode in order to check the
status | |
513 // of requiredPreviousFrameIndex before decoding. | |
514 size_t partialSize = 1; | |
515 do { | |
516 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
517 decoder->setData(data.get(), false); | |
518 ++partialSize; | |
519 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
520 | |
521 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameIn
dex()); | |
522 unsigned frameCount = decoder->frameCount(); | |
523 for (size_t i = 1; i < frameCount; ++i) | |
524 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIn
dex()); | |
525 | |
526 decoder->setData(fullData.get(), true); | |
527 for (size_t i = 0; i < frameCount; ++i) | |
528 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(i)->requiredPreviousFra
meIndex()); | |
529 } | |
530 | |
531 TEST_F(AnimatedWebPTests, randomFrameDecode) | |
532 { | |
533 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated.webp
"); | |
534 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-opaq
ue.webp"); | |
535 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-larg
e.webp"); | |
536 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-icc-
xmp.webp"); | |
537 } | |
538 | |
539 TEST_F(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) | |
540 { | |
541 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated.webp"); | |
542 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-opaque.webp"); | |
543 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-large.webp"); | |
544 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-icc-xmp.webp"); | |
545 } | |
546 | |
547 TEST_F(AnimatedWebPTests, resumePartialDecodeAfterClearFrameBufferCache) | |
548 { | |
549 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated-large.webp"); | |
550 ASSERT_TRUE(fullData.get()); | |
551 Vector<unsigned> baselineHashes; | |
552 createDecodingBaseline(fullData.get(), &baselineHashes); | |
553 size_t frameCount = baselineHashes.size(); | |
554 | |
555 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
556 | |
557 // Let frame 0 be partially decoded. | |
558 size_t partialSize = 1; | |
559 do { | |
560 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
561 decoder->setData(data.get(), false); | |
562 ++partialSize; | |
563 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
564 | |
565 // Skip to the last frame and clear. | |
566 decoder->setData(fullData.get(), true); | |
567 EXPECT_EQ(frameCount, decoder->frameCount()); | |
568 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1); | |
569 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitma
p())); | |
570 decoder->clearCacheExceptFrame(kNotFound); | |
571 | |
572 // Resume decoding of the first frame. | |
573 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); | |
574 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status()); | |
575 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap())); | |
576 } | |
577 | |
578 TEST_F(AnimatedWebPTests, decodeAfterReallocatingData) | |
579 { | |
580 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-ani
mated.webp"); | |
581 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-ani
mated-icc-xmp.webp"); | |
582 } | |
583 | |
584 TEST(StaticWebPTests, truncatedImage) | |
585 { | |
586 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
587 | |
588 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/tru
ncated.webp"); | |
589 ASSERT_TRUE(data.get()); | |
590 decoder->setData(data.get(), true); | |
591 | |
592 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
593 EXPECT_FALSE(frame); | |
594 } | |
OLD | NEW |