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