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/gif/GIFImageDecoder.h" | |
34 | |
35 #include "platform/SharedBuffer.h" | |
36 #include "public/platform/Platform.h" | |
37 #include "public/platform/WebData.h" | |
38 #include "public/platform/WebSize.h" | |
39 #include "public/platform/WebUnitTestSupport.h" | |
40 #include "wtf/OwnPtr.h" | |
41 #include "wtf/PassOwnPtr.h" | |
42 #include "wtf/StringHasher.h" | |
43 #include "wtf/Vector.h" | |
44 #include <gtest/gtest.h> | |
45 | |
46 using namespace WebCore; | |
47 using namespace blink; | |
48 | |
49 namespace { | |
50 | |
51 PassRefPtr<SharedBuffer> readFile(const char* fileName) | |
52 { | |
53 String filePath = Platform::current()->unitTestSupport()->webKitRootDir(); | |
54 filePath.append(fileName); | |
55 | |
56 return Platform::current()->unitTestSupport()->readFromFile(filePath); | |
57 } | |
58 | |
59 PassOwnPtr<GIFImageDecoder> createDecoder() | |
60 { | |
61 return adoptPtr(new GIFImageDecoder(ImageSource::AlphaNotPremultiplied, Imag
eSource::GammaAndColorProfileApplied, ImageDecoder::noDecodedImageByteLimit)); | |
62 } | |
63 | |
64 unsigned hashSkBitmap(const SkBitmap& bitmap) | |
65 { | |
66 return StringHasher::hashMemory(bitmap.getPixels(), bitmap.getSize()); | |
67 } | |
68 | |
69 void createDecodingBaseline(SharedBuffer* data, Vector<unsigned>* baselineHashes
) | |
70 { | |
71 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
72 decoder->setData(data, true); | |
73 size_t frameCount = decoder->frameCount(); | |
74 for (size_t i = 0; i < frameCount; ++i) { | |
75 ImageFrame* frame = decoder->frameBufferAtIndex(i); | |
76 baselineHashes->append(hashSkBitmap(frame->getSkBitmap())); | |
77 } | |
78 } | |
79 | |
80 void testRandomFrameDecode(const char* gifFile) | |
81 { | |
82 SCOPED_TRACE(gifFile); | |
83 | |
84 RefPtr<SharedBuffer> fullData = readFile(gifFile); | |
85 ASSERT_TRUE(fullData.get()); | |
86 Vector<unsigned> baselineHashes; | |
87 createDecodingBaseline(fullData.get(), &baselineHashes); | |
88 size_t frameCount = baselineHashes.size(); | |
89 | |
90 // Random decoding should get the same results as sequential decoding. | |
91 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
92 decoder->setData(fullData.get(), true); | |
93 const size_t skippingStep = 5; | |
94 for (size_t i = 0; i < skippingStep; ++i) { | |
95 for (size_t j = i; j < frameCount; j += skippingStep) { | |
96 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j); | |
97 ImageFrame* frame = decoder->frameBufferAtIndex(j); | |
98 EXPECT_EQ(baselineHashes[j], hashSkBitmap(frame->getSkBitmap())); | |
99 } | |
100 } | |
101 | |
102 // Decoding in reverse order. | |
103 decoder = createDecoder(); | |
104 decoder->setData(fullData.get(), true); | |
105 for (size_t i = frameCount; i; --i) { | |
106 SCOPED_TRACE(testing::Message() << "Reverse i:" << i); | |
107 ImageFrame* frame = decoder->frameBufferAtIndex(i - 1); | |
108 EXPECT_EQ(baselineHashes[i - 1], hashSkBitmap(frame->getSkBitmap())); | |
109 } | |
110 } | |
111 | |
112 void testRandomDecodeAfterClearFrameBufferCache(const char* gifFile) | |
113 { | |
114 SCOPED_TRACE(gifFile); | |
115 | |
116 RefPtr<SharedBuffer> data = readFile(gifFile); | |
117 ASSERT_TRUE(data.get()); | |
118 Vector<unsigned> baselineHashes; | |
119 createDecodingBaseline(data.get(), &baselineHashes); | |
120 size_t frameCount = baselineHashes.size(); | |
121 | |
122 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
123 decoder->setData(data.get(), true); | |
124 for (size_t clearExceptFrame = 0; clearExceptFrame < frameCount; ++clearExce
ptFrame) { | |
125 decoder->clearCacheExceptFrame(clearExceptFrame); | |
126 const size_t skippingStep = 5; | |
127 for (size_t i = 0; i < skippingStep; ++i) { | |
128 for (size_t j = 0; j < frameCount; j += skippingStep) { | |
129 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" <<
j); | |
130 ImageFrame* frame = decoder->frameBufferAtIndex(j); | |
131 EXPECT_EQ(baselineHashes[j], hashSkBitmap(frame->getSkBitmap()))
; | |
132 } | |
133 } | |
134 } | |
135 } | |
136 | |
137 } // namespace | |
138 | |
139 TEST(GIFImageDecoderTest, decodeTwoFrames) | |
140 { | |
141 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
142 | |
143 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
144 ASSERT_TRUE(data.get()); | |
145 decoder->setData(data.get(), true); | |
146 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
147 | |
148 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
149 uint32_t generationID0 = frame->getSkBitmap().getGenerationID(); | |
150 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
151 EXPECT_EQ(16, frame->getSkBitmap().width()); | |
152 EXPECT_EQ(16, frame->getSkBitmap().height()); | |
153 | |
154 frame = decoder->frameBufferAtIndex(1); | |
155 uint32_t generationID1 = frame->getSkBitmap().getGenerationID(); | |
156 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
157 EXPECT_EQ(16, frame->getSkBitmap().width()); | |
158 EXPECT_EQ(16, frame->getSkBitmap().height()); | |
159 EXPECT_TRUE(generationID0 != generationID1); | |
160 | |
161 EXPECT_EQ(2u, decoder->frameCount()); | |
162 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
163 } | |
164 | |
165 TEST(GIFImageDecoderTest, parseAndDecode) | |
166 { | |
167 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
168 | |
169 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
170 ASSERT_TRUE(data.get()); | |
171 decoder->setData(data.get(), true); | |
172 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
173 | |
174 // This call will parse the entire file. | |
175 EXPECT_EQ(2u, decoder->frameCount()); | |
176 | |
177 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
178 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
179 EXPECT_EQ(16, frame->getSkBitmap().width()); | |
180 EXPECT_EQ(16, frame->getSkBitmap().height()); | |
181 | |
182 frame = decoder->frameBufferAtIndex(1); | |
183 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
184 EXPECT_EQ(16, frame->getSkBitmap().width()); | |
185 EXPECT_EQ(16, frame->getSkBitmap().height()); | |
186 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
187 } | |
188 | |
189 TEST(GIFImageDecoderTest, parseByteByByte) | |
190 { | |
191 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
192 | |
193 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
194 ASSERT_TRUE(data.get()); | |
195 | |
196 size_t frameCount = 0; | |
197 | |
198 // Pass data to decoder byte by byte. | |
199 for (size_t length = 1; length <= data->size(); ++length) { | |
200 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), lengt
h); | |
201 decoder->setData(tempData.get(), length == data->size()); | |
202 | |
203 EXPECT_LE(frameCount, decoder->frameCount()); | |
204 frameCount = decoder->frameCount(); | |
205 } | |
206 | |
207 EXPECT_EQ(2u, decoder->frameCount()); | |
208 | |
209 decoder->frameBufferAtIndex(0); | |
210 decoder->frameBufferAtIndex(1); | |
211 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
212 } | |
213 | |
214 TEST(GIFImageDecoderTest, parseAndDecodeByteByByte) | |
215 { | |
216 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
217 | |
218 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated-gif-with-offsets.gif"); | |
219 ASSERT_TRUE(data.get()); | |
220 | |
221 size_t frameCount = 0; | |
222 size_t framesDecoded = 0; | |
223 | |
224 // Pass data to decoder byte by byte. | |
225 for (size_t length = 1; length <= data->size(); ++length) { | |
226 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), lengt
h); | |
227 decoder->setData(tempData.get(), length == data->size()); | |
228 | |
229 EXPECT_LE(frameCount, decoder->frameCount()); | |
230 frameCount = decoder->frameCount(); | |
231 | |
232 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
233 if (frame && frame->status() == ImageFrame::FrameComplete && framesDecod
ed < frameCount) | |
234 ++framesDecoded; | |
235 } | |
236 | |
237 EXPECT_EQ(5u, decoder->frameCount()); | |
238 EXPECT_EQ(5u, framesDecoded); | |
239 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
240 } | |
241 | |
242 TEST(GIFImageDecoderTest, brokenSecondFrame) | |
243 { | |
244 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
245 | |
246 RefPtr<SharedBuffer> data = readFile("/Source/web/tests/data/broken.gif"); | |
247 ASSERT_TRUE(data.get()); | |
248 decoder->setData(data.get(), true); | |
249 | |
250 // One frame is detected but cannot be decoded. | |
251 EXPECT_EQ(1u, decoder->frameCount()); | |
252 ImageFrame* frame = decoder->frameBufferAtIndex(1); | |
253 EXPECT_FALSE(frame); | |
254 } | |
255 | |
256 TEST(GIFImageDecoderTest, progressiveDecode) | |
257 { | |
258 RefPtr<SharedBuffer> fullData = readFile("/Source/web/tests/data/radient.gif
"); | |
259 ASSERT_TRUE(fullData.get()); | |
260 const size_t fullLength = fullData->size(); | |
261 | |
262 OwnPtr<GIFImageDecoder> decoder; | |
263 ImageFrame* frame; | |
264 | |
265 Vector<unsigned> truncatedHashes; | |
266 Vector<unsigned> progressiveHashes; | |
267 | |
268 // Compute hashes when the file is truncated. | |
269 const size_t increment = 1; | |
270 for (size_t i = 1; i <= fullLength; i += increment) { | |
271 decoder = createDecoder(); | |
272 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
273 decoder->setData(data.get(), i == fullLength); | |
274 frame = decoder->frameBufferAtIndex(0); | |
275 if (!frame) { | |
276 truncatedHashes.append(0); | |
277 continue; | |
278 } | |
279 truncatedHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
280 } | |
281 | |
282 // Compute hashes when the file is progressively decoded. | |
283 decoder = createDecoder(); | |
284 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
285 for (size_t i = 1; i <= fullLength; i += increment) { | |
286 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
287 decoder->setData(data.get(), i == fullLength); | |
288 frame = decoder->frameBufferAtIndex(0); | |
289 if (!frame) { | |
290 progressiveHashes.append(0); | |
291 continue; | |
292 } | |
293 progressiveHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
294 } | |
295 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); | |
296 | |
297 bool match = true; | |
298 for (size_t i = 0; i < truncatedHashes.size(); ++i) { | |
299 if (truncatedHashes[i] != progressiveHashes[i]) { | |
300 match = false; | |
301 break; | |
302 } | |
303 } | |
304 EXPECT_TRUE(match); | |
305 } | |
306 | |
307 TEST(GIFImageDecoderTest, allDataReceivedTruncation) | |
308 { | |
309 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
310 | |
311 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
312 ASSERT_TRUE(data.get()); | |
313 | |
314 ASSERT_GE(data->size(), 10u); | |
315 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
316 decoder->setData(tempData.get(), true); | |
317 | |
318 EXPECT_EQ(2u, decoder->frameCount()); | |
319 EXPECT_FALSE(decoder->failed()); | |
320 | |
321 decoder->frameBufferAtIndex(0); | |
322 EXPECT_FALSE(decoder->failed()); | |
323 decoder->frameBufferAtIndex(1); | |
324 EXPECT_TRUE(decoder->failed()); | |
325 } | |
326 | |
327 TEST(GIFImageDecoderTest, frameIsComplete) | |
328 { | |
329 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
330 | |
331 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
332 ASSERT_TRUE(data.get()); | |
333 decoder->setData(data.get(), true); | |
334 | |
335 EXPECT_EQ(2u, decoder->frameCount()); | |
336 EXPECT_FALSE(decoder->failed()); | |
337 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
338 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
339 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
340 } | |
341 | |
342 TEST(GIFImageDecoderTest, frameIsCompleteLoading) | |
343 { | |
344 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
345 | |
346 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
347 ASSERT_TRUE(data.get()); | |
348 | |
349 ASSERT_GE(data->size(), 10u); | |
350 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
351 decoder->setData(tempData.get(), false); | |
352 | |
353 EXPECT_EQ(2u, decoder->frameCount()); | |
354 EXPECT_FALSE(decoder->failed()); | |
355 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
356 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(1)); | |
357 | |
358 decoder->setData(data.get(), true); | |
359 EXPECT_EQ(2u, decoder->frameCount()); | |
360 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
361 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
362 } | |
363 | |
364 TEST(GIFImageDecoderTest, badTerminator) | |
365 { | |
366 RefPtr<SharedBuffer> referenceData = readFile("/Source/web/tests/data/radien
t.gif"); | |
367 RefPtr<SharedBuffer> testData = readFile("/Source/web/tests/data/radient-bad
-terminator.gif"); | |
368 ASSERT_TRUE(referenceData.get()); | |
369 ASSERT_TRUE(testData.get()); | |
370 | |
371 OwnPtr<GIFImageDecoder> referenceDecoder(createDecoder()); | |
372 referenceDecoder->setData(referenceData.get(), true); | |
373 EXPECT_EQ(1u, referenceDecoder->frameCount()); | |
374 ImageFrame* referenceFrame = referenceDecoder->frameBufferAtIndex(0); | |
375 ASSERT(referenceFrame); | |
376 | |
377 OwnPtr<GIFImageDecoder> testDecoder(createDecoder()); | |
378 testDecoder->setData(testData.get(), true); | |
379 EXPECT_EQ(1u, testDecoder->frameCount()); | |
380 ImageFrame* testFrame = testDecoder->frameBufferAtIndex(0); | |
381 ASSERT(testFrame); | |
382 | |
383 EXPECT_EQ(hashSkBitmap(referenceFrame->getSkBitmap()), hashSkBitmap(testFram
e->getSkBitmap())); | |
384 } | |
385 | |
386 TEST(GIFImageDecoderTest, updateRequiredPreviousFrameAfterFirstDecode) | |
387 { | |
388 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
389 | |
390 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/animated-10color.gif"); | |
391 ASSERT_TRUE(fullData.get()); | |
392 | |
393 // Give it data that is enough to parse but not decode in order to check the
status | |
394 // of requiredPreviousFrameIndex before decoding. | |
395 size_t partialSize = 1; | |
396 do { | |
397 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
398 decoder->setData(data.get(), false); | |
399 ++partialSize; | |
400 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
401 | |
402 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameIn
dex()); | |
403 unsigned frameCount = decoder->frameCount(); | |
404 for (size_t i = 1; i < frameCount; ++i) | |
405 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIn
dex()); | |
406 | |
407 decoder->setData(fullData.get(), true); | |
408 for (size_t i = 0; i < frameCount; ++i) | |
409 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(i)->requiredPreviousFra
meIndex()); | |
410 } | |
411 | |
412 TEST(GIFImageDecoderTest, randomFrameDecode) | |
413 { | |
414 // Single frame image. | |
415 testRandomFrameDecode("/Source/web/tests/data/radient.gif"); | |
416 // Multiple frame images. | |
417 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-gif-with-
offsets.gif"); | |
418 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-10color.g
if"); | |
419 } | |
420 | |
421 TEST(GIFImageDecoderTest, randomDecodeAfterClearFrameBufferCache) | |
422 { | |
423 // Single frame image. | |
424 testRandomDecodeAfterClearFrameBufferCache("/Source/web/tests/data/radient.g
if"); | |
425 // Multiple frame images. | |
426 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/animated-gif-with-offsets.gif"); | |
427 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/animated-10color.gif"); | |
428 } | |
429 | |
430 TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache) | |
431 { | |
432 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/animated-10color.gif"); | |
433 ASSERT_TRUE(fullData.get()); | |
434 Vector<unsigned> baselineHashes; | |
435 createDecodingBaseline(fullData.get(), &baselineHashes); | |
436 size_t frameCount = baselineHashes.size(); | |
437 | |
438 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
439 | |
440 // Let frame 0 be partially decoded. | |
441 size_t partialSize = 1; | |
442 do { | |
443 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
444 decoder->setData(data.get(), false); | |
445 ++partialSize; | |
446 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
447 | |
448 // Skip to the last frame and clear. | |
449 decoder->setData(fullData.get(), true); | |
450 EXPECT_EQ(frameCount, decoder->frameCount()); | |
451 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1); | |
452 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitma
p())); | |
453 decoder->clearCacheExceptFrame(kNotFound); | |
454 | |
455 // Resume decoding of the first frame. | |
456 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); | |
457 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status()); | |
458 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap())); | |
459 } | |
460 | |
461 // The first LZW codes in the image are invalid values that try to create a loop | |
462 // in the dictionary. Decoding should fail, but not infinitely loop or corrupt m
emory. | |
463 TEST(GIFImageDecoderTest, badInitialCode) | |
464 { | |
465 RefPtr<SharedBuffer> testData = readFile("/Source/platform/image-decoders/te
sting/bad-initial-code.gif"); | |
466 ASSERT_TRUE(testData.get()); | |
467 | |
468 OwnPtr<GIFImageDecoder> testDecoder(createDecoder()); | |
469 testDecoder->setData(testData.get(), true); | |
470 EXPECT_EQ(1u, testDecoder->frameCount()); | |
471 ASSERT_TRUE(testDecoder->frameBufferAtIndex(0)); | |
472 EXPECT_TRUE(testDecoder->failed()); | |
473 } | |
474 | |
475 // The image has an invalid LZW code that exceeds dictionary size. Decoding shou
ld fail. | |
476 TEST(GIFImageDecoderTest, badCode) | |
477 { | |
478 RefPtr<SharedBuffer> testData = readFile("/Source/platform/image-decoders/te
sting/bad-code.gif"); | |
479 ASSERT_TRUE(testData.get()); | |
480 | |
481 OwnPtr<GIFImageDecoder> testDecoder(createDecoder()); | |
482 testDecoder->setData(testData.get(), true); | |
483 EXPECT_EQ(1u, testDecoder->frameCount()); | |
484 ASSERT_TRUE(testDecoder->frameBufferAtIndex(0)); | |
485 EXPECT_TRUE(testDecoder->failed()); | |
486 } | |
487 | |
488 TEST(GIFImageDecoderTest, invalidDisposalMethod) | |
489 { | |
490 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
491 | |
492 // The image has 2 frames, with disposal method 4 and 5, respectively. | |
493 RefPtr<SharedBuffer> data = readFile("/Source/web/tests/data/invalid-disposa
l-method.gif"); | |
494 ASSERT_TRUE(data.get()); | |
495 decoder->setData(data.get(), true); | |
496 | |
497 EXPECT_EQ(2u, decoder->frameCount()); | |
498 // Disposal method 4 is converted to ImageFrame::DisposeOverwritePrevious. | |
499 EXPECT_EQ(ImageFrame::DisposeOverwritePrevious, decoder->frameBufferAtIndex(
0)->disposalMethod()); | |
500 // Disposal method 5 is ignored. | |
501 EXPECT_EQ(ImageFrame::DisposeNotSpecified, decoder->frameBufferAtIndex(1)->d
isposalMethod()); | |
502 } | |
OLD | NEW |