| 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/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 for (size_t i = 1; i <= fullLength; i += increment) { | |
| 285 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
| 286 decoder->setData(data.get(), i == fullLength); | |
| 287 frame = decoder->frameBufferAtIndex(0); | |
| 288 if (!frame) { | |
| 289 progressiveHashes.append(0); | |
| 290 continue; | |
| 291 } | |
| 292 progressiveHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
| 293 } | |
| 294 | |
| 295 bool match = true; | |
| 296 for (size_t i = 0; i < truncatedHashes.size(); ++i) { | |
| 297 if (truncatedHashes[i] != progressiveHashes[i]) { | |
| 298 match = false; | |
| 299 break; | |
| 300 } | |
| 301 } | |
| 302 EXPECT_TRUE(match); | |
| 303 } | |
| 304 | |
| 305 TEST(GIFImageDecoderTest, allDataReceivedTruncation) | |
| 306 { | |
| 307 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
| 308 | |
| 309 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
| 310 ASSERT_TRUE(data.get()); | |
| 311 | |
| 312 ASSERT_GE(data->size(), 10u); | |
| 313 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
| 314 decoder->setData(tempData.get(), true); | |
| 315 | |
| 316 EXPECT_EQ(2u, decoder->frameCount()); | |
| 317 EXPECT_FALSE(decoder->failed()); | |
| 318 | |
| 319 decoder->frameBufferAtIndex(0); | |
| 320 EXPECT_FALSE(decoder->failed()); | |
| 321 decoder->frameBufferAtIndex(1); | |
| 322 EXPECT_TRUE(decoder->failed()); | |
| 323 } | |
| 324 | |
| 325 TEST(GIFImageDecoderTest, frameIsComplete) | |
| 326 { | |
| 327 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
| 328 | |
| 329 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
| 330 ASSERT_TRUE(data.get()); | |
| 331 decoder->setData(data.get(), true); | |
| 332 | |
| 333 EXPECT_EQ(2u, decoder->frameCount()); | |
| 334 EXPECT_FALSE(decoder->failed()); | |
| 335 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
| 336 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
| 337 } | |
| 338 | |
| 339 TEST(GIFImageDecoderTest, frameIsCompleteLoading) | |
| 340 { | |
| 341 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
| 342 | |
| 343 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/ani
mated.gif"); | |
| 344 ASSERT_TRUE(data.get()); | |
| 345 | |
| 346 ASSERT_GE(data->size(), 10u); | |
| 347 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
| 348 decoder->setData(tempData.get(), false); | |
| 349 | |
| 350 EXPECT_EQ(2u, decoder->frameCount()); | |
| 351 EXPECT_FALSE(decoder->failed()); | |
| 352 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
| 353 EXPECT_FALSE(decoder->frameIsCompleteAtIndex(1)); | |
| 354 | |
| 355 decoder->setData(data.get(), true); | |
| 356 EXPECT_EQ(2u, decoder->frameCount()); | |
| 357 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
| 358 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
| 359 } | |
| 360 | |
| 361 TEST(GIFImageDecoderTest, badTerminator) | |
| 362 { | |
| 363 RefPtr<SharedBuffer> referenceData = readFile("/Source/web/tests/data/radien
t.gif"); | |
| 364 RefPtr<SharedBuffer> testData = readFile("/Source/web/tests/data/radient-bad
-terminator.gif"); | |
| 365 ASSERT_TRUE(referenceData.get()); | |
| 366 ASSERT_TRUE(testData.get()); | |
| 367 | |
| 368 OwnPtr<GIFImageDecoder> referenceDecoder(createDecoder()); | |
| 369 referenceDecoder->setData(referenceData.get(), true); | |
| 370 EXPECT_EQ(1u, referenceDecoder->frameCount()); | |
| 371 ImageFrame* referenceFrame = referenceDecoder->frameBufferAtIndex(0); | |
| 372 ASSERT(referenceFrame); | |
| 373 | |
| 374 OwnPtr<GIFImageDecoder> testDecoder(createDecoder()); | |
| 375 testDecoder->setData(testData.get(), true); | |
| 376 EXPECT_EQ(1u, testDecoder->frameCount()); | |
| 377 ImageFrame* testFrame = testDecoder->frameBufferAtIndex(0); | |
| 378 ASSERT(testFrame); | |
| 379 | |
| 380 EXPECT_EQ(hashSkBitmap(referenceFrame->getSkBitmap()), hashSkBitmap(testFram
e->getSkBitmap())); | |
| 381 } | |
| 382 | |
| 383 TEST(GIFImageDecoderTest, updateRequiredPreviousFrameAfterFirstDecode) | |
| 384 { | |
| 385 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
| 386 | |
| 387 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/animated-10color.gif"); | |
| 388 ASSERT_TRUE(fullData.get()); | |
| 389 | |
| 390 // Give it data that is enough to parse but not decode in order to check the
status | |
| 391 // of requiredPreviousFrameIndex before decoding. | |
| 392 size_t partialSize = 1; | |
| 393 do { | |
| 394 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
| 395 decoder->setData(data.get(), false); | |
| 396 ++partialSize; | |
| 397 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
| 398 | |
| 399 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameIn
dex()); | |
| 400 unsigned frameCount = decoder->frameCount(); | |
| 401 for (size_t i = 1; i < frameCount; ++i) | |
| 402 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIn
dex()); | |
| 403 | |
| 404 decoder->setData(fullData.get(), true); | |
| 405 for (size_t i = 0; i < frameCount; ++i) | |
| 406 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(i)->requiredPreviousFra
meIndex()); | |
| 407 } | |
| 408 | |
| 409 TEST(GIFImageDecoderTest, randomFrameDecode) | |
| 410 { | |
| 411 // Single frame image. | |
| 412 testRandomFrameDecode("/Source/web/tests/data/radient.gif"); | |
| 413 // Multiple frame images. | |
| 414 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-gif-with-
offsets.gif"); | |
| 415 testRandomFrameDecode("/LayoutTests/fast/images/resources/animated-10color.g
if"); | |
| 416 } | |
| 417 | |
| 418 TEST(GIFImageDecoderTest, randomDecodeAfterClearFrameBufferCache) | |
| 419 { | |
| 420 // Single frame image. | |
| 421 testRandomDecodeAfterClearFrameBufferCache("/Source/web/tests/data/radient.g
if"); | |
| 422 // Multiple frame images. | |
| 423 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/animated-gif-with-offsets.gif"); | |
| 424 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/animated-10color.gif"); | |
| 425 } | |
| 426 | |
| 427 TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache) | |
| 428 { | |
| 429 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/animated-10color.gif"); | |
| 430 ASSERT_TRUE(fullData.get()); | |
| 431 Vector<unsigned> baselineHashes; | |
| 432 createDecodingBaseline(fullData.get(), &baselineHashes); | |
| 433 size_t frameCount = baselineHashes.size(); | |
| 434 | |
| 435 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
| 436 | |
| 437 // Let frame 0 be partially decoded. | |
| 438 size_t partialSize = 1; | |
| 439 do { | |
| 440 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
| 441 decoder->setData(data.get(), false); | |
| 442 ++partialSize; | |
| 443 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
| 444 | |
| 445 // Skip to the last frame and clear. | |
| 446 decoder->setData(fullData.get(), true); | |
| 447 EXPECT_EQ(frameCount, decoder->frameCount()); | |
| 448 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1); | |
| 449 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitma
p())); | |
| 450 decoder->clearCacheExceptFrame(kNotFound); | |
| 451 | |
| 452 // Resume decoding of the first frame. | |
| 453 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); | |
| 454 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status()); | |
| 455 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap())); | |
| 456 } | |
| 457 | |
| 458 // The first LZW codes in the image are invalid values that try to create a loop | |
| 459 // in the dictionary. Decoding should fail, but not infinitely loop or corrupt m
emory. | |
| 460 TEST(GIFImageDecoderTest, badInitialCode) | |
| 461 { | |
| 462 RefPtr<SharedBuffer> testData = readFile("/Source/core/platform/image-decode
rs/testing/bad-initial-code.gif"); | |
| 463 ASSERT_TRUE(testData.get()); | |
| 464 | |
| 465 OwnPtr<GIFImageDecoder> testDecoder(createDecoder()); | |
| 466 testDecoder->setData(testData.get(), true); | |
| 467 EXPECT_EQ(1u, testDecoder->frameCount()); | |
| 468 ASSERT_TRUE(testDecoder->frameBufferAtIndex(0)); | |
| 469 EXPECT_TRUE(testDecoder->failed()); | |
| 470 } | |
| 471 | |
| 472 // The image has an invalid LZW code that exceeds dictionary size. Decoding shou
ld fail. | |
| 473 TEST(GIFImageDecoderTest, badCode) | |
| 474 { | |
| 475 RefPtr<SharedBuffer> testData = readFile("/Source/core/platform/image-decode
rs/testing/bad-code.gif"); | |
| 476 ASSERT_TRUE(testData.get()); | |
| 477 | |
| 478 OwnPtr<GIFImageDecoder> testDecoder(createDecoder()); | |
| 479 testDecoder->setData(testData.get(), true); | |
| 480 EXPECT_EQ(1u, testDecoder->frameCount()); | |
| 481 ASSERT_TRUE(testDecoder->frameBufferAtIndex(0)); | |
| 482 EXPECT_TRUE(testDecoder->failed()); | |
| 483 } | |
| 484 | |
| 485 TEST(GIFImageDecoderTest, invalidDisposalMethod) | |
| 486 { | |
| 487 OwnPtr<GIFImageDecoder> decoder = createDecoder(); | |
| 488 | |
| 489 // The image has 2 frames, with disposal method 4 and 5, respectively. | |
| 490 RefPtr<SharedBuffer> data = readFile("/Source/web/tests/data/invalid-disposa
l-method.gif"); | |
| 491 ASSERT_TRUE(data.get()); | |
| 492 decoder->setData(data.get(), true); | |
| 493 | |
| 494 EXPECT_EQ(2u, decoder->frameCount()); | |
| 495 // Disposal method 4 is converted to ImageFrame::DisposeOverwritePrevious. | |
| 496 EXPECT_EQ(ImageFrame::DisposeOverwritePrevious, decoder->frameBufferAtIndex(
0)->disposalMethod()); | |
| 497 // Disposal method 5 is ignored. | |
| 498 EXPECT_EQ(ImageFrame::DisposeNotSpecified, decoder->frameBufferAtIndex(1)->d
isposalMethod()); | |
| 499 } | |
| OLD | NEW |