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 "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 // If 'parseErrorExpected' is true, error is expected during parse (frameCount() | |
162 // call); else error is expected during decode (frameBufferAtIndex() call). | |
163 void testInvalidImage(const char* webpFile, bool parseErrorExpected) | |
164 { | |
165 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
166 | |
167 RefPtr<SharedBuffer> data = readFile(webpFile); | |
168 ASSERT_TRUE(data.get()); | |
169 decoder->setData(data.get(), true); | |
170 | |
171 if (parseErrorExpected) | |
172 EXPECT_EQ(0u, decoder->frameCount()); | |
173 else | |
174 EXPECT_LT(0u, decoder->frameCount()); | |
175 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
176 EXPECT_FALSE(frame); | |
177 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
178 } | |
179 | |
180 } // namespace | |
181 | |
182 TEST(AnimatedWebPTests, uniqueGenerationIDs) | |
183 { | |
184 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
185 | |
186 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
187 ASSERT_TRUE(data.get()); | |
188 decoder->setData(data.get(), true); | |
189 | |
190 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
191 uint32_t generationID0 = frame->getSkBitmap().getGenerationID(); | |
192 frame = decoder->frameBufferAtIndex(1); | |
193 uint32_t generationID1 = frame->getSkBitmap().getGenerationID(); | |
194 | |
195 EXPECT_TRUE(generationID0 != generationID1); | |
196 } | |
197 | |
198 TEST(AnimatedWebPTests, verifyAnimationParametersTransparentImage) | |
199 { | |
200 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
201 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
202 | |
203 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
204 ASSERT_TRUE(data.get()); | |
205 decoder->setData(data.get(), true); | |
206 | |
207 const int canvasWidth = 11; | |
208 const int canvasHeight = 29; | |
209 const struct AnimParam { | |
210 int xOffset, yOffset, width, height; | |
211 ImageFrame::DisposalMethod disposalMethod; | |
212 ImageFrame::AlphaBlendSource alphaBlendSource; | |
213 unsigned duration; | |
214 bool hasAlpha; | |
215 } frameParameters[] = { | |
216 { 0, 0, 11, 29, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFr
ame, 1000u, true }, | |
217 { 2, 10, 7, 17, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFr
ame, 500u, true }, | |
218 { 2, 2, 7, 16, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFra
me, 1000u, true }, | |
219 }; | |
220 | |
221 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
222 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
223 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
224 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
225 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
226 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
227 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
228 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
229 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
230 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
231 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
232 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
233 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
234 } | |
235 | |
236 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
237 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
238 } | |
239 | |
240 TEST(AnimatedWebPTests, verifyAnimationParametersOpaqueFramesTransparentBackgrou
nd) | |
241 { | |
242 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
243 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
244 | |
245 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated-opaque.webp"); | |
246 ASSERT_TRUE(data.get()); | |
247 decoder->setData(data.get(), true); | |
248 | |
249 const int canvasWidth = 94; | |
250 const int canvasHeight = 87; | |
251 const struct AnimParam { | |
252 int xOffset, yOffset, width, height; | |
253 ImageFrame::DisposalMethod disposalMethod; | |
254 ImageFrame::AlphaBlendSource alphaBlendSource; | |
255 unsigned duration; | |
256 bool hasAlpha; | |
257 } frameParameters[] = { | |
258 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendA
topPreviousFrame, 1000u, true }, | |
259 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
260 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
261 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
262 }; | |
263 | |
264 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
265 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
266 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
267 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
268 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
269 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
270 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
271 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
272 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
273 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
274 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
275 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
276 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
277 } | |
278 | |
279 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
280 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
281 } | |
282 | |
283 TEST(AnimatedWebPTests, verifyAnimationParametersBlendOverwrite) | |
284 { | |
285 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
286 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
287 | |
288 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated-no-blend.webp"); | |
289 ASSERT_TRUE(data.get()); | |
290 decoder->setData(data.get(), true); | |
291 | |
292 const int canvasWidth = 94; | |
293 const int canvasHeight = 87; | |
294 const struct AnimParam { | |
295 int xOffset, yOffset, width, height; | |
296 ImageFrame::DisposalMethod disposalMethod; | |
297 ImageFrame::AlphaBlendSource alphaBlendSource; | |
298 unsigned duration; | |
299 bool hasAlpha; | |
300 } frameParameters[] = { | |
301 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendA
topBgcolor, 1000u, true }, | |
302 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
303 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
304 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
305 }; | |
306 | |
307 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
308 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
309 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
310 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
311 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
312 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
313 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
314 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
315 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
316 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
317 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
318 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
319 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
320 } | |
321 | |
322 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
323 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
324 } | |
325 | |
326 TEST(AnimatedWebPTests, parseAndDecodeByteByByte) | |
327 { | |
328 const struct TestImage { | |
329 const char* filename; | |
330 unsigned frameCount; | |
331 int repetitionCount; | |
332 } testImages[] = { | |
333 { "/LayoutTests/fast/images/resources/webp-animated.webp", 3u, cAnimatio
nLoopInfinite }, | |
334 { "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp", 13u,
32000 }, | |
335 }; | |
336 | |
337 for (size_t i = 0; i < ARRAY_SIZE(testImages); ++i) { | |
338 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
339 RefPtr<SharedBuffer> data = readFile(testImages[i].filename); | |
340 ASSERT_TRUE(data.get()); | |
341 | |
342 size_t frameCount = 0; | |
343 size_t framesDecoded = 0; | |
344 | |
345 // Pass data to decoder byte by byte. | |
346 for (size_t length = 1; length <= data->size(); ++length) { | |
347 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), l
ength); | |
348 decoder->setData(tempData.get(), length == data->size()); | |
349 | |
350 EXPECT_LE(frameCount, decoder->frameCount()); | |
351 frameCount = decoder->frameCount(); | |
352 | |
353 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
354 if (frame && frame->status() == ImageFrame::FrameComplete && framesD
ecoded < frameCount) | |
355 ++framesDecoded; | |
356 } | |
357 | |
358 EXPECT_EQ(testImages[i].frameCount, decoder->frameCount()); | |
359 EXPECT_EQ(testImages[i].frameCount, framesDecoded); | |
360 EXPECT_EQ(testImages[i].repetitionCount, decoder->repetitionCount()); | |
361 } | |
362 } | |
363 | |
364 TEST(AnimatedWebPTests, invalidImages) | |
365 { | |
366 // ANMF chunk size is smaller than ANMF header size. | |
367 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp.w
ebp", true); | |
368 // One of the frame rectangles extends outside the image boundary. | |
369 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp3.
webp", true); | |
370 } | |
371 | |
372 TEST(AnimatedWebPTests, truncatedLastFrame) | |
373 { | |
374 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
375 | |
376 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/inv
alid-animated-webp2.webp"); | |
377 ASSERT_TRUE(data.get()); | |
378 decoder->setData(data.get(), true); | |
379 | |
380 unsigned frameCount = 8; | |
381 EXPECT_EQ(frameCount, decoder->frameCount()); | |
382 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
383 EXPECT_FALSE(frame); | |
384 frame = decoder->frameBufferAtIndex(0); | |
385 EXPECT_FALSE(frame); | |
386 } | |
387 | |
388 TEST(AnimatedWebPTests, truncatedInBetweenFrame) | |
389 { | |
390 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
391 | |
392 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/invalid-animated-webp4.webp"); | |
393 ASSERT_TRUE(fullData.get()); | |
394 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), fullData-
>size() - 1); | |
395 decoder->setData(data.get(), false); | |
396 | |
397 ImageFrame* frame = decoder->frameBufferAtIndex(2); | |
398 EXPECT_FALSE(frame); | |
399 } | |
400 | |
401 // Reproduce a crash that used to happen for a specific file with specific seque
nce of method calls. | |
402 TEST(AnimatedWebPTests, reproCrash) | |
403 { | |
404 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
405 | |
406 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/invalid_vp8_vp8x.webp"); | |
407 ASSERT_TRUE(fullData.get()); | |
408 | |
409 // Parse partial data up to which error in bitstream is not detected. | |
410 const size_t partialSize = 32768; | |
411 ASSERT_GT(fullData->size(), partialSize); | |
412 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSi
ze); | |
413 decoder->setData(data.get(), false); | |
414 EXPECT_EQ(1u, decoder->frameCount()); | |
415 | |
416 // Parse full data now. The error in bitstream should now be detected. | |
417 decoder->setData(fullData.get(), true); | |
418 EXPECT_EQ(0u, decoder->frameCount()); | |
419 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
420 EXPECT_FALSE(frame); | |
421 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
422 } | |
423 | |
424 TEST(AnimatedWebPTests, progressiveDecode) | |
425 { | |
426 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated.webp"); | |
427 ASSERT_TRUE(fullData.get()); | |
428 const size_t fullLength = fullData->size(); | |
429 | |
430 OwnPtr<WEBPImageDecoder> decoder; | |
431 ImageFrame* frame; | |
432 | |
433 Vector<unsigned> truncatedHashes; | |
434 Vector<unsigned> progressiveHashes; | |
435 | |
436 // Compute hashes when the file is truncated. | |
437 const size_t increment = 1; | |
438 for (size_t i = 1; i <= fullLength; i += increment) { | |
439 decoder = createDecoder(); | |
440 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
441 decoder->setData(data.get(), i == fullLength); | |
442 frame = decoder->frameBufferAtIndex(0); | |
443 if (!frame) { | |
444 truncatedHashes.append(0); | |
445 continue; | |
446 } | |
447 truncatedHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
448 } | |
449 | |
450 // Compute hashes when the file is progressively decoded. | |
451 decoder = createDecoder(); | |
452 for (size_t i = 1; i <= fullLength; i += increment) { | |
453 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
454 decoder->setData(data.get(), i == fullLength); | |
455 frame = decoder->frameBufferAtIndex(0); | |
456 if (!frame) { | |
457 progressiveHashes.append(0); | |
458 continue; | |
459 } | |
460 progressiveHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
461 } | |
462 | |
463 bool match = true; | |
464 for (size_t i = 0; i < truncatedHashes.size(); ++i) { | |
465 if (truncatedHashes[i] != progressiveHashes[i]) { | |
466 match = false; | |
467 break; | |
468 } | |
469 } | |
470 EXPECT_TRUE(match); | |
471 } | |
472 | |
473 TEST(AnimatedWebPTests, frameIsCompleteAndDuration) | |
474 { | |
475 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
476 | |
477 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
478 ASSERT_TRUE(data.get()); | |
479 | |
480 ASSERT_GE(data->size(), 10u); | |
481 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
482 decoder->setData(tempData.get(), false); | |
483 | |
484 EXPECT_EQ(2u, decoder->frameCount()); | |
485 EXPECT_FALSE(decoder->failed()); | |
486 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
487 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); | |
488 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
489 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); | |
490 | |
491 decoder->setData(data.get(), true); | |
492 EXPECT_EQ(3u, decoder->frameCount()); | |
493 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
494 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); | |
495 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
496 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); | |
497 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(2)); | |
498 EXPECT_EQ(1000.0, decoder->frameDurationAtIndex(2)); | |
499 } | |
500 | |
501 TEST(AnimatedWebPTests, updateRequiredPreviousFrameAfterFirstDecode) | |
502 { | |
503 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
504 | |
505 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated.webp"); | |
506 ASSERT_TRUE(fullData.get()); | |
507 | |
508 // Give it data that is enough to parse but not decode in order to check the
status | |
509 // of requiredPreviousFrameIndex before decoding. | |
510 size_t partialSize = 1; | |
511 do { | |
512 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
513 decoder->setData(data.get(), false); | |
514 ++partialSize; | |
515 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
516 | |
517 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameIn
dex()); | |
518 unsigned frameCount = decoder->frameCount(); | |
519 for (size_t i = 1; i < frameCount; ++i) | |
520 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIn
dex()); | |
521 | |
522 decoder->setData(fullData.get(), true); | |
523 for (size_t i = 0; i < frameCount; ++i) | |
524 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(i)->requiredPreviousFra
meIndex()); | |
525 } | |
526 | |
527 TEST(AnimatedWebPTests, randomFrameDecode) | |
528 { | |
529 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated.webp
"); | |
530 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-opaq
ue.webp"); | |
531 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-larg
e.webp"); | |
532 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-icc-
xmp.webp"); | |
533 } | |
534 | |
535 TEST(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) | |
536 { | |
537 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated.webp"); | |
538 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-opaque.webp"); | |
539 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-large.webp"); | |
540 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-icc-xmp.webp"); | |
541 } | |
542 | |
543 TEST(AnimatedWebPTests, resumePartialDecodeAfterClearFrameBufferCache) | |
544 { | |
545 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated-large.webp"); | |
546 ASSERT_TRUE(fullData.get()); | |
547 Vector<unsigned> baselineHashes; | |
548 createDecodingBaseline(fullData.get(), &baselineHashes); | |
549 size_t frameCount = baselineHashes.size(); | |
550 | |
551 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
552 | |
553 // Let frame 0 be partially decoded. | |
554 size_t partialSize = 1; | |
555 do { | |
556 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
557 decoder->setData(data.get(), false); | |
558 ++partialSize; | |
559 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
560 | |
561 // Skip to the last frame and clear. | |
562 decoder->setData(fullData.get(), true); | |
563 EXPECT_EQ(frameCount, decoder->frameCount()); | |
564 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1); | |
565 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitma
p())); | |
566 decoder->clearCacheExceptFrame(kNotFound); | |
567 | |
568 // Resume decoding of the first frame. | |
569 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); | |
570 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status()); | |
571 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap())); | |
572 } | |
573 | |
574 TEST(AnimatedWebPTests, decodeAfterReallocatingData) | |
575 { | |
576 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-ani
mated.webp"); | |
577 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-ani
mated-icc-xmp.webp"); | |
578 } | |
579 | |
580 TEST(StaticWebPTests, truncatedImage) | |
581 { | |
582 // VP8 data is truncated. | |
583 testInvalidImage("/LayoutTests/fast/images/resources/truncated.webp", false)
; | |
584 // Chunk size in RIFF header doesn't match the file size. | |
585 testInvalidImage("/LayoutTests/fast/images/resources/truncated2.webp", true)
; | |
586 } | |
587 | |
588 TEST(StaticWebPTests, notAnimated) | |
589 { | |
590 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
591 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-color-profile-lossy.webp"); | |
592 ASSERT_TRUE(data.get()); | |
593 decoder->setData(data.get(), true); | |
594 EXPECT_EQ(1u, decoder->frameCount()); | |
595 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); | |
596 } | |
OLD | NEW |