Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: Source/WebKit/chromium/tests/GIFImageDecoderTest.cpp

Issue 15350006: Decode GIF image frames on demand. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 14 matching lines...) Expand all
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 #include "config.h" 31 #include "config.h"
32 32
33 #include "core/platform/image-decoders/gif/GIFImageDecoder.h" 33 #include "core/platform/image-decoders/gif/GIFImageDecoder.h"
34 34
35 #include <gtest/gtest.h>
36 #include "core/platform/FileSystem.h" 35 #include "core/platform/FileSystem.h"
37 #include "core/platform/SharedBuffer.h" 36 #include "core/platform/SharedBuffer.h"
37 #include "wtf/OwnPtr.h"
38 #include "wtf/PassOwnPtr.h"
39 #include "wtf/StringHasher.h"
40 #include "wtf/Vector.h"
41 #include <gtest/gtest.h>
38 #include <public/Platform.h> 42 #include <public/Platform.h>
39 #include <public/WebData.h> 43 #include <public/WebData.h>
40 #include <public/WebSize.h> 44 #include <public/WebSize.h>
41 #include <public/WebUnitTestSupport.h> 45 #include <public/WebUnitTestSupport.h>
42 #include <wtf/OwnPtr.h>
43 #include <wtf/PassOwnPtr.h>
44 #include <wtf/StringHasher.h>
45 #include <wtf/Vector.h>
46 46
47 using namespace WebCore; 47 using namespace WebCore;
48 using namespace WebKit; 48 using namespace WebKit;
49 49
50 namespace { 50 namespace {
51 51
52 #if !OS(ANDROID) 52 #if !OS(ANDROID)
53 53
54 static PassRefPtr<SharedBuffer> readFile(const char* fileName) 54 static PassRefPtr<SharedBuffer> readFile(const char* fileName)
55 { 55 {
(...skipping 15 matching lines...) Expand all
71 static PassOwnPtr<GIFImageDecoder> createDecoder() 71 static PassOwnPtr<GIFImageDecoder> createDecoder()
72 { 72 {
73 return adoptPtr(new GIFImageDecoder(ImageSource::AlphaNotPremultiplied, Imag eSource::GammaAndColorProfileApplied)); 73 return adoptPtr(new GIFImageDecoder(ImageSource::AlphaNotPremultiplied, Imag eSource::GammaAndColorProfileApplied));
74 } 74 }
75 75
76 static unsigned hashSkBitmap(const SkBitmap& bitmap) 76 static unsigned hashSkBitmap(const SkBitmap& bitmap)
77 { 77 {
78 return StringHasher::hashMemory(bitmap.getPixels(), bitmap.getSize()); 78 return StringHasher::hashMemory(bitmap.getPixels(), bitmap.getSize());
79 } 79 }
80 80
81 static void createDecodingBaseline(SharedBuffer* data, Vector<unsigned>* baselin eHashes)
82 {
83 OwnPtr<GIFImageDecoder> decoder(createDecoder());
84 decoder->setData(data, true);
85 size_t frameCount = decoder->frameCount();
86 for (size_t i = 0; i < frameCount; ++i) {
87 ImageFrame* frame = decoder->frameBufferAtIndex(i);
88 baselineHashes->append(hashSkBitmap(frame->getSkBitmap()));
89 }
90 }
91
81 TEST(GIFImageDecoderTest, decodeTwoFrames) 92 TEST(GIFImageDecoderTest, decodeTwoFrames)
82 { 93 {
83 OwnPtr<GIFImageDecoder> decoder(createDecoder()); 94 OwnPtr<GIFImageDecoder> decoder(createDecoder());
84 95
85 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani mated.gif"); 96 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani mated.gif");
86 ASSERT_TRUE(data.get()); 97 ASSERT_TRUE(data.get());
87 decoder->setData(data.get(), true); 98 decoder->setData(data.get(), true);
88 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); 99 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount());
89 100
90 ImageFrame* frame = decoder->frameBufferAtIndex(0); 101 ImageFrame* frame = decoder->frameBufferAtIndex(0);
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 TEST(GIFImageDecoderTest, parseByteByByte) 139 TEST(GIFImageDecoderTest, parseByteByByte)
129 { 140 {
130 OwnPtr<GIFImageDecoder> decoder(createDecoder()); 141 OwnPtr<GIFImageDecoder> decoder(createDecoder());
131 142
132 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani mated.gif"); 143 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani mated.gif");
133 ASSERT_TRUE(data.get()); 144 ASSERT_TRUE(data.get());
134 145
135 size_t frameCount = 0; 146 size_t frameCount = 0;
136 147
137 // Pass data to decoder byte by byte. 148 // Pass data to decoder byte by byte.
138 for (unsigned length = 1; length <= data->size(); ++length) { 149 for (size_t length = 1; length <= data->size(); ++length) {
139 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), lengt h); 150 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), lengt h);
140 decoder->setData(tempData.get(), length == data->size()); 151 decoder->setData(tempData.get(), length == data->size());
141 152
142 EXPECT_LE(frameCount, decoder->frameCount()); 153 EXPECT_LE(frameCount, decoder->frameCount());
143 frameCount = decoder->frameCount(); 154 frameCount = decoder->frameCount();
144 } 155 }
145 156
146 EXPECT_EQ(2u, decoder->frameCount()); 157 EXPECT_EQ(2u, decoder->frameCount());
147 158
148 decoder->frameBufferAtIndex(0); 159 decoder->frameBufferAtIndex(0);
149 decoder->frameBufferAtIndex(1); 160 decoder->frameBufferAtIndex(1);
150 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); 161 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount());
151 } 162 }
152 163
153 TEST(GIFImageDecoderTest, parseAndDecodeByteByByte) 164 TEST(GIFImageDecoderTest, parseAndDecodeByteByByte)
154 { 165 {
155 OwnPtr<GIFImageDecoder> decoder(createDecoder()); 166 OwnPtr<GIFImageDecoder> decoder(createDecoder());
156 167
157 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani mated-gif-with-offsets.gif"); 168 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani mated-gif-with-offsets.gif");
158 ASSERT_TRUE(data.get()); 169 ASSERT_TRUE(data.get());
159 170
160 size_t frameCount = 0; 171 size_t frameCount = 0;
161 size_t framesDecoded = 0; 172 size_t framesDecoded = 0;
162 173
163 // Pass data to decoder byte by byte. 174 // Pass data to decoder byte by byte.
164 for (unsigned length = 1; length <= data->size(); ++length) { 175 for (size_t length = 1; length <= data->size(); ++length) {
165 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), lengt h); 176 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), lengt h);
166 decoder->setData(tempData.get(), length == data->size()); 177 decoder->setData(tempData.get(), length == data->size());
167 178
168 EXPECT_LE(frameCount, decoder->frameCount()); 179 EXPECT_LE(frameCount, decoder->frameCount());
169 frameCount = decoder->frameCount(); 180 frameCount = decoder->frameCount();
170 181
171 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); 182 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1);
172 if (frame && frame->status() == ImageFrame::FrameComplete && framesDecod ed < frameCount) 183 if (frame && frame->status() == ImageFrame::FrameComplete && framesDecod ed < frameCount)
173 ++framesDecoded; 184 ++framesDecoded;
174 } 185 }
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 EXPECT_FALSE(decoder->failed()); 301 EXPECT_FALSE(decoder->failed());
291 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); 302 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0));
292 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(1)); 303 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(1));
293 304
294 decoder->setData(data.get(), true); 305 decoder->setData(data.get(), true);
295 EXPECT_EQ(2u, decoder->frameCount()); 306 EXPECT_EQ(2u, decoder->frameCount());
296 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); 307 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0));
297 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); 308 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1));
298 } 309 }
299 310
311 TEST(GIFImageDecoderTest, updateRequiredPreviousFrameAfterFirstDecode)
312 {
313 OwnPtr<GIFImageDecoder> decoder(createDecoder());
314
315 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources /animated-10color.gif");
316 ASSERT_TRUE(fullData.get());
317
318 // Give it data that is enough to parse but not decode in order to check the status
319 // of requiredPreviousFrameIndex before decoding.
320 size_t partialSize = 1;
321 do {
322 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti alSize);
323 decoder->setData(data.get(), false);
324 ++partialSize;
325 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status() == ImageFrame::FrameEmpty);
326
327 EXPECT_EQ(notFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameInd ex());
328 unsigned frameCount = decoder->frameCount();
329 for (size_t i = 1; i < frameCount; ++i)
330 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIn dex());
331
332 decoder->setData(fullData.get(), true);
333 for (size_t i = 0; i < frameCount; ++i)
334 EXPECT_EQ(notFound, decoder->frameBufferAtIndex(i)->requiredPreviousFram eIndex());
335 }
336
337 void testRandomFrameDecode(const char* gifFile)
338 {
339 SCOPED_TRACE(gifFile);
340
341 RefPtr<SharedBuffer> fullData = readFile(gifFile);
342 ASSERT_TRUE(fullData.get());
343 Vector<unsigned> baselineHashes;
344 createDecodingBaseline(fullData.get(), &baselineHashes);
345 size_t frameCount = baselineHashes.size();
346
347 // Random decoding should get the same results as sequential decoding.
348 OwnPtr<GIFImageDecoder> decoder(createDecoder());
349 decoder->setData(fullData.get(), true);
350 const size_t skippingStep = 5;
351 for (size_t i = 0; i < skippingStep; ++i) {
352 for (size_t j = i; j < frameCount; j += skippingStep) {
353 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j);
354 ImageFrame* frame = decoder->frameBufferAtIndex(j);
355 EXPECT_EQ(baselineHashes[j], hashSkBitmap(frame->getSkBitmap()));
356 }
357 }
358
359 // Decoding in reverse order.
360 decoder = createDecoder();
361 decoder->setData(fullData.get(), true);
362 for (size_t i = frameCount; i; --i) {
363 SCOPED_TRACE(testing::Message() << "Reverse i:" << i);
364 ImageFrame* frame = decoder->frameBufferAtIndex(i - 1);
365 EXPECT_EQ(baselineHashes[i - 1], hashSkBitmap(frame->getSkBitmap()));
366 }
367 }
368
369 TEST(GIFImageDecoderTest, randomFrameDecode)
370 {
371 // Single frame image.
372 testRandomFrameDecode("/Source/WebKit/chromium/tests/data/radient.gif");
373 // Multiple frame image.
374 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-gif-with- offsets.gif");
375 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-10color.g if");
376 }
377
378 void testRandomDecodeAfterClearFrameBufferCache(const char* gifFile)
379 {
380 SCOPED_TRACE(gifFile);
381
382 RefPtr<SharedBuffer> data = readFile(gifFile);
383 ASSERT_TRUE(data.get());
384 Vector<unsigned> baselineHashes;
385 createDecodingBaseline(data.get(), &baselineHashes);
386 size_t frameCount = baselineHashes.size();
387
388 OwnPtr<GIFImageDecoder> decoder(createDecoder());
389 for (size_t clearExceptFrame = 0; clearExceptFrame < frameCount; ++clearExce ptFrame) {
390 decoder->clearFrameBufferCache(clearExceptFrame);
391 const size_t skippingStep = 5;
392 for (size_t i = 0; i < skippingStep; ++i) {
393 for (size_t j = 0; j < frameCount; j += skippingStep) {
394 SCOPED_TRACE(testing::Message() << "Random i:" << i << " j:" << j);
395 ImageFrame* frame = decoder->frameBufferAtIndex(j);
396 EXPECT_EQ(baselineHashes[j], hashSkBitmap(frame->getSkBitmap())) ;
397 }
398 }
399 }
400 }
401
402 TEST(GIFImageDecoderTest, randomDecodeAfterClearFrameBufferCache)
403 {
404 // Single frame image.
405 testRandomFrameDecode("/Source/WebKit/chromium/tests/data/radient.gif");
406 // Multiple frame image.
407 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-gif-with- offsets.gif");
408 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-10color.g if");
409 }
410
411 TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache)
412 {
413 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources /animated-10color.gif");
414 ASSERT_TRUE(fullData.get());
415 Vector<unsigned> baselineHashes;
416 createDecodingBaseline(fullData.get(), &baselineHashes);
417 size_t frameCount = baselineHashes.size();
418
419 OwnPtr<GIFImageDecoder> decoder(createDecoder());
420
421 // Let frame 0 be partially decoded.
422 size_t partialSize = 1;
423 do {
424 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti alSize);
425 decoder->setData(data.get(), false);
426 ++partialSize;
427 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status() == ImageFrame::FrameEmpty);
428
429 // Skip to the last frame and clear.
430 decoder->setData(fullData.get(), true);
431 EXPECT_EQ(frameCount, decoder->frameCount());
432 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1);
433 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitma p()));
434 decoder->clearFrameBufferCache(notFound);
435
436 // Resume decoding of the first frame.
437 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0);
438 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status());
439 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap()));
440 }
441
300 #endif 442 #endif
301 443
302 } // namespace 444 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698