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 "platform/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(ImageSource::AlphaOption alphaOption = ImageSource::AlphaNotPremultiplied) 62 { 63 return adoptPtr(new WEBPImageDecoder(alphaOption, ImageSource::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; ++clearExceptFrame) { 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 testByteByByteDecode(const char* webpFile, size_t expectedFrameCount, int expectedRepetitionCount) 162 { 163 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 164 RefPtr<SharedBuffer> data = readFile(webpFile); 165 ASSERT_TRUE(data.get()); 166 167 size_t frameCount = 0; 168 size_t framesDecoded = 0; 169 170 // Pass data to decoder byte by byte. 171 for (size_t length = 1; length <= data->size(); ++length) { 172 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), length); 173 decoder->setData(tempData.get(), length == data->size()); 174 175 EXPECT_LE(frameCount, decoder->frameCount()); 176 frameCount = decoder->frameCount(); 177 178 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); 179 if (frame && frame->status() == ImageFrame::FrameComplete && framesDecoded < frameCount) 180 ++framesDecoded; 181 182 if (decoder->failed()) 183 break; 184 } 185 186 EXPECT_FALSE(decoder->failed()); 187 EXPECT_EQ(expectedFrameCount, decoder->frameCount()); 188 EXPECT_EQ(expectedFrameCount, framesDecoded); 189 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); 190 } 191 192 // If 'parseErrorExpected' is true, error is expected during parse (frameCount() 193 // call); else error is expected during decode (frameBufferAtIndex() call). 194 void testInvalidImage(const char* webpFile, bool parseErrorExpected) 195 { 196 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 197 198 RefPtr<SharedBuffer> data = readFile(webpFile); 199 ASSERT_TRUE(data.get()); 200 decoder->setData(data.get(), true); 201 202 if (parseErrorExpected) 203 EXPECT_EQ(0u, decoder->frameCount()); 204 else 205 EXPECT_LT(0u, decoder->frameCount()); 206 ImageFrame* frame = decoder->frameBufferAtIndex(0); 207 EXPECT_FALSE(frame); 208 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); 209 } 210 211 uint32_t premultiplyColor(uint32_t c) 212 { 213 return SkPremultiplyARGBInline(SkGetPackedA32(c), SkGetPackedR32(c), SkGetPackedG32(c), SkGetPackedB32(c)); 214 } 215 216 void verifyFramesMatch(const char* webpFile, const ImageFrame* const a, ImageFrame* const b) 217 { 218 const SkBitmap& bitmapA = a->getSkBitmap(); 219 const SkBitmap& bitmapB = b->getSkBitmap(); 220 ASSERT_EQ(bitmapA.width(), bitmapB.width()); 221 ASSERT_EQ(bitmapA.height(), bitmapB.height()); 222 223 int maxDifference = 0; 224 for (int y = 0; y < bitmapA.height(); ++y) { 225 for (int x = 0; x < bitmapA.width(); ++x) { 226 uint32_t colorA = *bitmapA.getAddr32(x, y); 227 if (!a->premultiplyAlpha()) 228 colorA = premultiplyColor(colorA); 229 uint32_t colorB = *bitmapB.getAddr32(x, y); 230 if (!b->premultiplyAlpha()) 231 colorB = premultiplyColor(colorB); 232 uint8_t* pixelA = reinterpret_cast<uint8_t*>(&colorA); 233 uint8_t* pixelB = reinterpret_cast<uint8_t*>(&colorB); 234 for (int channel = 0; channel < 4; ++channel) { 235 const int difference = abs(pixelA[channel] - pixelB[channel]); 236 if (difference > maxDifference) 237 maxDifference = difference; 238 } 239 } 240 } 241 242 // Pre-multiplication could round the RGBA channel values. So, we declare 243 // that the frames match if the RGBA channel values differ by at most 2. 244 EXPECT_GE(2, maxDifference) << webpFile; 245 } 246 247 // Verify that result of alpha blending is similar for AlphaPremultiplied and AlphaNotPremultiplied cases. 248 void testAlphaBlending(const char* webpFile) 249 { 250 RefPtr<SharedBuffer> data = readFile(webpFile); 251 ASSERT_TRUE(data.get()); 252 253 OwnPtr<WEBPImageDecoder> decoderA = createDecoder(ImageSource::AlphaPremultiplied); 254 decoderA->setData(data.get(), true); 255 256 OwnPtr<WEBPImageDecoder> decoderB = createDecoder(ImageSource::AlphaNotPremultiplied); 257 decoderB->setData(data.get(), true); 258 259 size_t frameCount = decoderA->frameCount(); 260 ASSERT_EQ(frameCount, decoderB->frameCount()); 261 262 for (size_t i = 0; i < frameCount; ++i) 263 verifyFramesMatch(webpFile, decoderA->frameBufferAtIndex(i), decoderB->frameBufferAtIndex(i)); 264 } 265 266 } // namespace 267 268 TEST(AnimatedWebPTests, uniqueGenerationIDs) 269 { 270 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 271 272 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); 273 ASSERT_TRUE(data.get()); 274 decoder->setData(data.get(), true); 275 276 ImageFrame* frame = decoder->frameBufferAtIndex(0); 277 uint32_t generationID0 = frame->getSkBitmap().getGenerationID(); 278 frame = decoder->frameBufferAtIndex(1); 279 uint32_t generationID1 = frame->getSkBitmap().getGenerationID(); 280 281 EXPECT_TRUE(generationID0 != generationID1); 282 } 283 284 TEST(AnimatedWebPTests, verifyAnimationParametersTransparentImage) 285 { 286 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 287 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); 288 289 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); 290 ASSERT_TRUE(data.get()); 291 decoder->setData(data.get(), true); 292 293 const int canvasWidth = 11; 294 const int canvasHeight = 29; 295 const struct AnimParam { 296 int xOffset, yOffset, width, height; 297 ImageFrame::DisposalMethod disposalMethod; 298 ImageFrame::AlphaBlendSource alphaBlendSource; 299 unsigned duration; 300 bool hasAlpha; 301 } frameParameters[] = { 302 { 0, 0, 11, 29, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFrame, 1000u, true }, 303 { 2, 10, 7, 17, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFrame, 500u, true }, 304 { 2, 2, 7, 16, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFrame, 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, verifyAnimationParametersOpaqueFramesTransparentBackground) 327 { 328 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 329 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); 330 331 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); 332 ASSERT_TRUE(data.get()); 333 decoder->setData(data.get(), true); 334 335 const int canvasWidth = 94; 336 const int canvasHeight = 87; 337 const struct AnimParam { 338 int xOffset, yOffset, width, height; 339 ImageFrame::DisposalMethod disposalMethod; 340 ImageFrame::AlphaBlendSource alphaBlendSource; 341 unsigned duration; 342 bool hasAlpha; 343 } frameParameters[] = { 344 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopPreviousFrame, 1000u, true }, 345 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopPreviousFrame, 1000u, true }, 346 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopPreviousFrame, 1000u, true }, 347 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopPreviousFrame, 1000u, true }, 348 }; 349 350 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { 351 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); 352 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); 353 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); 354 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); 355 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); 356 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); 357 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); 358 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()); 359 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); 360 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()); 361 EXPECT_EQ(frameParameters[i].duration, frame->duration()); 362 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); 363 } 364 365 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); 366 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); 367 } 368 369 TEST(AnimatedWebPTests, verifyAnimationParametersBlendOverwrite) 370 { 371 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 372 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); 373 374 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-animated-no-blend.webp"); 375 ASSERT_TRUE(data.get()); 376 decoder->setData(data.get(), true); 377 378 const int canvasWidth = 94; 379 const int canvasHeight = 87; 380 const struct AnimParam { 381 int xOffset, yOffset, width, height; 382 ImageFrame::DisposalMethod disposalMethod; 383 ImageFrame::AlphaBlendSource alphaBlendSource; 384 unsigned duration; 385 bool hasAlpha; 386 } frameParameters[] = { 387 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopBgcolor, 1000u, true }, 388 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopBgcolor, 1000u, true }, 389 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopBgcolor, 1000u, true }, 390 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendAtopBgcolor, 1000u, true }, 391 }; 392 393 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { 394 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); 395 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); 396 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); 397 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); 398 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); 399 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); 400 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); 401 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()); 402 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); 403 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()); 404 EXPECT_EQ(frameParameters[i].duration, frame->duration()); 405 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); 406 } 407 408 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); 409 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); 410 } 411 412 TEST(AnimatedWebPTests, parseAndDecodeByteByByte) 413 { 414 testByteByByteDecode("/LayoutTests/fast/images/resources/webp-animated.webp", 3u, cAnimationLoopInfinite); 415 testByteByByteDecode("/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp", 13u, 31999); 416 } 417 418 TEST(AnimatedWebPTests, invalidImages) 419 { 420 // ANMF chunk size is smaller than ANMF header size. 421 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp.webp", true); 422 // One of the frame rectangles extends outside the image boundary. 423 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp3.webp", true); 424 } 425 426 TEST(AnimatedWebPTests, truncatedLastFrame) 427 { 428 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 429 430 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/invalid-animated-webp2.webp"); 431 ASSERT_TRUE(data.get()); 432 decoder->setData(data.get(), true); 433 434 size_t frameCount = 8; 435 EXPECT_EQ(frameCount, decoder->frameCount()); 436 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); 437 EXPECT_FALSE(frame); 438 frame = decoder->frameBufferAtIndex(0); 439 EXPECT_FALSE(frame); 440 } 441 442 TEST(AnimatedWebPTests, truncatedInBetweenFrame) 443 { 444 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 445 446 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/invalid-animated-webp4.webp"); 447 ASSERT_TRUE(fullData.get()); 448 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), fullData->size() - 1); 449 decoder->setData(data.get(), false); 450 451 ImageFrame* frame = decoder->frameBufferAtIndex(2); 452 EXPECT_FALSE(frame); 453 } 454 455 // Reproduce a crash that used to happen for a specific file with specific sequence of method calls. 456 TEST(AnimatedWebPTests, reproCrash) 457 { 458 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 459 460 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/invalid_vp8_vp8x.webp"); 461 ASSERT_TRUE(fullData.get()); 462 463 // Parse partial data up to which error in bitstream is not detected. 464 const size_t partialSize = 32768; 465 ASSERT_GT(fullData->size(), partialSize); 466 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSize); 467 decoder->setData(data.get(), false); 468 EXPECT_EQ(1u, decoder->frameCount()); 469 470 // Parse full data now. The error in bitstream should now be detected. 471 decoder->setData(fullData.get(), true); 472 EXPECT_EQ(0u, decoder->frameCount()); 473 ImageFrame* frame = decoder->frameBufferAtIndex(0); 474 EXPECT_FALSE(frame); 475 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); 476 } 477 478 TEST(AnimatedWebPTests, progressiveDecode) 479 { 480 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); 481 ASSERT_TRUE(fullData.get()); 482 const size_t fullLength = fullData->size(); 483 484 OwnPtr<WEBPImageDecoder> decoder; 485 ImageFrame* frame; 486 487 Vector<unsigned> truncatedHashes; 488 Vector<unsigned> progressiveHashes; 489 490 // Compute hashes when the file is truncated. 491 const size_t increment = 1; 492 for (size_t i = 1; i <= fullLength; i += increment) { 493 decoder = createDecoder(); 494 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); 495 decoder->setData(data.get(), i == fullLength); 496 frame = decoder->frameBufferAtIndex(0); 497 if (!frame) { 498 truncatedHashes.append(0); 499 continue; 500 } 501 truncatedHashes.append(hashSkBitmap(frame->getSkBitmap())); 502 } 503 504 // Compute hashes when the file is progressively decoded. 505 decoder = createDecoder(); 506 for (size_t i = 1; i <= fullLength; i += increment) { 507 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); 508 decoder->setData(data.get(), i == fullLength); 509 frame = decoder->frameBufferAtIndex(0); 510 if (!frame) { 511 progressiveHashes.append(0); 512 continue; 513 } 514 progressiveHashes.append(hashSkBitmap(frame->getSkBitmap())); 515 } 516 517 bool match = true; 518 for (size_t i = 0; i < truncatedHashes.size(); ++i) { 519 if (truncatedHashes[i] != progressiveHashes[i]) { 520 match = false; 521 break; 522 } 523 } 524 EXPECT_TRUE(match); 525 } 526 527 TEST(AnimatedWebPTests, frameIsCompleteAndDuration) 528 { 529 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 530 531 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); 532 ASSERT_TRUE(data.get()); 533 534 ASSERT_GE(data->size(), 10u); 535 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->size() - 10); 536 decoder->setData(tempData.get(), false); 537 538 EXPECT_EQ(2u, decoder->frameCount()); 539 EXPECT_FALSE(decoder->failed()); 540 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); 541 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); 542 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); 543 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); 544 545 decoder->setData(data.get(), true); 546 EXPECT_EQ(3u, decoder->frameCount()); 547 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); 548 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); 549 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); 550 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); 551 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(2)); 552 EXPECT_EQ(1000.0, decoder->frameDurationAtIndex(2)); 553 } 554 555 TEST(AnimatedWebPTests, updateRequiredPreviousFrameAfterFirstDecode) 556 { 557 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 558 559 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/webp-animated.webp"); 560 ASSERT_TRUE(fullData.get()); 561 562 // Give it data that is enough to parse but not decode in order to check the status 563 // of requiredPreviousFrameIndex before decoding. 564 size_t partialSize = 1; 565 do { 566 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSize); 567 decoder->setData(data.get(), false); 568 ++partialSize; 569 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status() == ImageFrame::FrameEmpty); 570 571 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameIndex()); 572 size_t frameCount = decoder->frameCount(); 573 for (size_t i = 1; i < frameCount; ++i) 574 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIndex()); 575 576 decoder->setData(fullData.get(), true); 577 for (size_t i = 0; i < frameCount; ++i) 578 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIndex()); 579 } 580 581 TEST(AnimatedWebPTests, randomFrameDecode) 582 { 583 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated.webp"); 584 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); 585 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-large.webp"); 586 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); 587 } 588 589 TEST(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) 590 { 591 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resources/webp-animated.webp"); 592 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resources/webp-animated-opaque.webp"); 593 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resources/webp-animated-large.webp"); 594 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); 595 } 596 597 TEST(AnimatedWebPTests, resumePartialDecodeAfterClearFrameBufferCache) 598 { 599 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources/webp-animated-large.webp"); 600 ASSERT_TRUE(fullData.get()); 601 Vector<unsigned> baselineHashes; 602 createDecodingBaseline(fullData.get(), &baselineHashes); 603 size_t frameCount = baselineHashes.size(); 604 605 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 606 607 // Let frame 0 be partially decoded. 608 size_t partialSize = 1; 609 do { 610 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSize); 611 decoder->setData(data.get(), false); 612 ++partialSize; 613 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status() == ImageFrame::FrameEmpty); 614 615 // Skip to the last frame and clear. 616 decoder->setData(fullData.get(), true); 617 EXPECT_EQ(frameCount, decoder->frameCount()); 618 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1); 619 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitmap())); 620 decoder->clearCacheExceptFrame(kNotFound); 621 622 // Resume decoding of the first frame. 623 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); 624 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status()); 625 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap())); 626 } 627 628 TEST(AnimatedWebPTests, decodeAfterReallocatingData) 629 { 630 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-animated.webp"); 631 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp"); 632 } 633 634 TEST(AnimatedWebPTests, alphaBlending) 635 { 636 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated.webp"); 637 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated-semitransparent1.webp"); 638 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated-semitransparent2.webp"); 639 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated-semitransparent3.webp"); 640 testAlphaBlending("/LayoutTests/fast/images/resources/webp-animated-semitransparent4.webp"); 641 } 642 643 TEST(StaticWebPTests, truncatedImage) 644 { 645 // VP8 data is truncated. 646 testInvalidImage("/LayoutTests/fast/images/resources/truncated.webp", false); 647 // Chunk size in RIFF header doesn't match the file size. 648 testInvalidImage("/LayoutTests/fast/images/resources/truncated2.webp", true); 649 } 650 651 TEST(StaticWebPTests, incrementalDecode) 652 { 653 // Regression test for a bug where some valid images were failing to decode incrementally. 654 testByteByByteDecode("/LayoutTests/fast/images/resources/crbug.364830.webp", 1u, cAnimationNone); 655 } 656 657 TEST(StaticWebPTests, notAnimated) 658 { 659 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); 660 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/webp-color-profile-lossy.webp"); 661 ASSERT_TRUE(data.get()); 662 decoder->setData(data.get(), true); 663 EXPECT_EQ(1u, decoder->frameCount()); 664 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); 665 } 666