| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 #include "config.h" | |
| 32 | |
| 33 #include "platform/image-decoders/webp/WEBPImageDecoder.h" | |
| 34 | |
| 35 #include "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() | |
| 62 { | |
| 63 return adoptPtr(new WEBPImageDecoder(ImageSource::AlphaNotPremultiplied, Ima
geSource::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; ++clearExce
ptFrame) { | |
| 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 // If 'parseErrorExpected' is true, error is expected during parse (frameCount() | |
| 162 // call); else error is expected during decode (frameBufferAtIndex() call). | |
| 163 void testInvalidImage(const char* webpFile, bool parseErrorExpected) | |
| 164 { | |
| 165 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 166 | |
| 167 RefPtr<SharedBuffer> data = readFile(webpFile); | |
| 168 ASSERT_TRUE(data.get()); | |
| 169 decoder->setData(data.get(), true); | |
| 170 | |
| 171 if (parseErrorExpected) | |
| 172 EXPECT_EQ(0u, decoder->frameCount()); | |
| 173 else | |
| 174 EXPECT_LT(0u, decoder->frameCount()); | |
| 175 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
| 176 EXPECT_FALSE(frame); | |
| 177 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
| 178 } | |
| 179 | |
| 180 } // namespace | |
| 181 | |
| 182 TEST(AnimatedWebPTests, uniqueGenerationIDs) | |
| 183 { | |
| 184 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 185 | |
| 186 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
| 187 ASSERT_TRUE(data.get()); | |
| 188 decoder->setData(data.get(), true); | |
| 189 | |
| 190 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
| 191 uint32_t generationID0 = frame->getSkBitmap().getGenerationID(); | |
| 192 frame = decoder->frameBufferAtIndex(1); | |
| 193 uint32_t generationID1 = frame->getSkBitmap().getGenerationID(); | |
| 194 | |
| 195 EXPECT_TRUE(generationID0 != generationID1); | |
| 196 } | |
| 197 | |
| 198 TEST(AnimatedWebPTests, verifyAnimationParametersTransparentImage) | |
| 199 { | |
| 200 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 201 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
| 202 | |
| 203 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
| 204 ASSERT_TRUE(data.get()); | |
| 205 decoder->setData(data.get(), true); | |
| 206 | |
| 207 const int canvasWidth = 11; | |
| 208 const int canvasHeight = 29; | |
| 209 const struct AnimParam { | |
| 210 int xOffset, yOffset, width, height; | |
| 211 ImageFrame::DisposalMethod disposalMethod; | |
| 212 ImageFrame::AlphaBlendSource alphaBlendSource; | |
| 213 unsigned duration; | |
| 214 bool hasAlpha; | |
| 215 } frameParameters[] = { | |
| 216 { 0, 0, 11, 29, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFr
ame, 1000u, true }, | |
| 217 { 2, 10, 7, 17, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFr
ame, 500u, true }, | |
| 218 { 2, 2, 7, 16, ImageFrame::DisposeKeep, ImageFrame::BlendAtopPreviousFra
me, 1000u, true }, | |
| 219 }; | |
| 220 | |
| 221 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
| 222 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
| 223 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
| 224 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
| 225 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
| 226 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
| 227 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
| 228 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
| 229 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
| 230 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
| 231 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
| 232 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
| 233 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
| 234 } | |
| 235 | |
| 236 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
| 237 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
| 238 } | |
| 239 | |
| 240 TEST(AnimatedWebPTests, verifyAnimationParametersOpaqueFramesTransparentBackgrou
nd) | |
| 241 { | |
| 242 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 243 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
| 244 | |
| 245 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated-opaque.webp"); | |
| 246 ASSERT_TRUE(data.get()); | |
| 247 decoder->setData(data.get(), true); | |
| 248 | |
| 249 const int canvasWidth = 94; | |
| 250 const int canvasHeight = 87; | |
| 251 const struct AnimParam { | |
| 252 int xOffset, yOffset, width, height; | |
| 253 ImageFrame::DisposalMethod disposalMethod; | |
| 254 ImageFrame::AlphaBlendSource alphaBlendSource; | |
| 255 unsigned duration; | |
| 256 bool hasAlpha; | |
| 257 } frameParameters[] = { | |
| 258 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendA
topPreviousFrame, 1000u, true }, | |
| 259 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
| 260 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
| 261 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopPreviousFrame, 1000u, true }, | |
| 262 }; | |
| 263 | |
| 264 for (size_t i = 0; i < ARRAY_SIZE(frameParameters); ++i) { | |
| 265 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); | |
| 266 EXPECT_EQ(ImageFrame::FrameComplete, frame->status()); | |
| 267 EXPECT_EQ(canvasWidth, frame->getSkBitmap().width()); | |
| 268 EXPECT_EQ(canvasHeight, frame->getSkBitmap().height()); | |
| 269 EXPECT_EQ(frameParameters[i].xOffset, frame->originalFrameRect().x()); | |
| 270 EXPECT_EQ(frameParameters[i].yOffset, frame->originalFrameRect().y()); | |
| 271 EXPECT_EQ(frameParameters[i].width, frame->originalFrameRect().width()); | |
| 272 EXPECT_EQ(frameParameters[i].height, frame->originalFrameRect().height()
); | |
| 273 EXPECT_EQ(frameParameters[i].disposalMethod, frame->disposalMethod()); | |
| 274 EXPECT_EQ(frameParameters[i].alphaBlendSource, frame->alphaBlendSource()
); | |
| 275 EXPECT_EQ(frameParameters[i].duration, frame->duration()); | |
| 276 EXPECT_EQ(frameParameters[i].hasAlpha, frame->hasAlpha()); | |
| 277 } | |
| 278 | |
| 279 EXPECT_EQ(ARRAY_SIZE(frameParameters), decoder->frameCount()); | |
| 280 EXPECT_EQ(cAnimationLoopInfinite, decoder->repetitionCount()); | |
| 281 } | |
| 282 | |
| 283 TEST(AnimatedWebPTests, verifyAnimationParametersBlendOverwrite) | |
| 284 { | |
| 285 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 286 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
| 287 | |
| 288 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated-no-blend.webp"); | |
| 289 ASSERT_TRUE(data.get()); | |
| 290 decoder->setData(data.get(), true); | |
| 291 | |
| 292 const int canvasWidth = 94; | |
| 293 const int canvasHeight = 87; | |
| 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 { 4, 10, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::BlendA
topBgcolor, 1000u, true }, | |
| 302 { 34, 30, 33, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
| 303 { 62, 50, 32, 32, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 1000u, true }, | |
| 304 { 10, 54, 32, 33, ImageFrame::DisposeOverwriteBgcolor, ImageFrame::Blend
AtopBgcolor, 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, parseAndDecodeByteByByte) | |
| 327 { | |
| 328 const struct TestImage { | |
| 329 const char* filename; | |
| 330 unsigned frameCount; | |
| 331 int repetitionCount; | |
| 332 } testImages[] = { | |
| 333 { "/LayoutTests/fast/images/resources/webp-animated.webp", 3u, cAnimatio
nLoopInfinite }, | |
| 334 { "/LayoutTests/fast/images/resources/webp-animated-icc-xmp.webp", 13u,
32000 }, | |
| 335 }; | |
| 336 | |
| 337 for (size_t i = 0; i < ARRAY_SIZE(testImages); ++i) { | |
| 338 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 339 RefPtr<SharedBuffer> data = readFile(testImages[i].filename); | |
| 340 ASSERT_TRUE(data.get()); | |
| 341 | |
| 342 size_t frameCount = 0; | |
| 343 size_t framesDecoded = 0; | |
| 344 | |
| 345 // Pass data to decoder byte by byte. | |
| 346 for (size_t length = 1; length <= data->size(); ++length) { | |
| 347 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), l
ength); | |
| 348 decoder->setData(tempData.get(), length == data->size()); | |
| 349 | |
| 350 EXPECT_LE(frameCount, decoder->frameCount()); | |
| 351 frameCount = decoder->frameCount(); | |
| 352 | |
| 353 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
| 354 if (frame && frame->status() == ImageFrame::FrameComplete && framesD
ecoded < frameCount) | |
| 355 ++framesDecoded; | |
| 356 } | |
| 357 | |
| 358 EXPECT_EQ(testImages[i].frameCount, decoder->frameCount()); | |
| 359 EXPECT_EQ(testImages[i].frameCount, framesDecoded); | |
| 360 EXPECT_EQ(testImages[i].repetitionCount, decoder->repetitionCount()); | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 TEST(AnimatedWebPTests, invalidImages) | |
| 365 { | |
| 366 // ANMF chunk size is smaller than ANMF header size. | |
| 367 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp.w
ebp", true); | |
| 368 // One of the frame rectangles extends outside the image boundary. | |
| 369 testInvalidImage("/LayoutTests/fast/images/resources/invalid-animated-webp3.
webp", true); | |
| 370 } | |
| 371 | |
| 372 TEST(AnimatedWebPTests, truncatedLastFrame) | |
| 373 { | |
| 374 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 375 | |
| 376 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/inv
alid-animated-webp2.webp"); | |
| 377 ASSERT_TRUE(data.get()); | |
| 378 decoder->setData(data.get(), true); | |
| 379 | |
| 380 unsigned frameCount = 8; | |
| 381 EXPECT_EQ(frameCount, decoder->frameCount()); | |
| 382 ImageFrame* frame = decoder->frameBufferAtIndex(frameCount - 1); | |
| 383 EXPECT_FALSE(frame); | |
| 384 frame = decoder->frameBufferAtIndex(0); | |
| 385 EXPECT_FALSE(frame); | |
| 386 } | |
| 387 | |
| 388 TEST(AnimatedWebPTests, truncatedInBetweenFrame) | |
| 389 { | |
| 390 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 391 | |
| 392 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/invalid-animated-webp4.webp"); | |
| 393 ASSERT_TRUE(fullData.get()); | |
| 394 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), fullData-
>size() - 1); | |
| 395 decoder->setData(data.get(), false); | |
| 396 | |
| 397 ImageFrame* frame = decoder->frameBufferAtIndex(2); | |
| 398 EXPECT_FALSE(frame); | |
| 399 } | |
| 400 | |
| 401 // Reproduce a crash that used to happen for a specific file with specific seque
nce of method calls. | |
| 402 TEST(AnimatedWebPTests, reproCrash) | |
| 403 { | |
| 404 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 405 | |
| 406 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/invalid_vp8_vp8x.webp"); | |
| 407 ASSERT_TRUE(fullData.get()); | |
| 408 | |
| 409 // Parse partial data up to which error in bitstream is not detected. | |
| 410 const size_t partialSize = 32768; | |
| 411 ASSERT_GT(fullData->size(), partialSize); | |
| 412 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), partialSi
ze); | |
| 413 decoder->setData(data.get(), false); | |
| 414 EXPECT_EQ(1u, decoder->frameCount()); | |
| 415 | |
| 416 // Parse full data now. The error in bitstream should now be detected. | |
| 417 decoder->setData(fullData.get(), true); | |
| 418 EXPECT_EQ(0u, decoder->frameCount()); | |
| 419 ImageFrame* frame = decoder->frameBufferAtIndex(0); | |
| 420 EXPECT_FALSE(frame); | |
| 421 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); | |
| 422 } | |
| 423 | |
| 424 TEST(AnimatedWebPTests, progressiveDecode) | |
| 425 { | |
| 426 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated.webp"); | |
| 427 ASSERT_TRUE(fullData.get()); | |
| 428 const size_t fullLength = fullData->size(); | |
| 429 | |
| 430 OwnPtr<WEBPImageDecoder> decoder; | |
| 431 ImageFrame* frame; | |
| 432 | |
| 433 Vector<unsigned> truncatedHashes; | |
| 434 Vector<unsigned> progressiveHashes; | |
| 435 | |
| 436 // Compute hashes when the file is truncated. | |
| 437 const size_t increment = 1; | |
| 438 for (size_t i = 1; i <= fullLength; i += increment) { | |
| 439 decoder = createDecoder(); | |
| 440 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
| 441 decoder->setData(data.get(), i == fullLength); | |
| 442 frame = decoder->frameBufferAtIndex(0); | |
| 443 if (!frame) { | |
| 444 truncatedHashes.append(0); | |
| 445 continue; | |
| 446 } | |
| 447 truncatedHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
| 448 } | |
| 449 | |
| 450 // Compute hashes when the file is progressively decoded. | |
| 451 decoder = createDecoder(); | |
| 452 for (size_t i = 1; i <= fullLength; i += increment) { | |
| 453 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); | |
| 454 decoder->setData(data.get(), i == fullLength); | |
| 455 frame = decoder->frameBufferAtIndex(0); | |
| 456 if (!frame) { | |
| 457 progressiveHashes.append(0); | |
| 458 continue; | |
| 459 } | |
| 460 progressiveHashes.append(hashSkBitmap(frame->getSkBitmap())); | |
| 461 } | |
| 462 | |
| 463 bool match = true; | |
| 464 for (size_t i = 0; i < truncatedHashes.size(); ++i) { | |
| 465 if (truncatedHashes[i] != progressiveHashes[i]) { | |
| 466 match = false; | |
| 467 break; | |
| 468 } | |
| 469 } | |
| 470 EXPECT_TRUE(match); | |
| 471 } | |
| 472 | |
| 473 TEST(AnimatedWebPTests, frameIsCompleteAndDuration) | |
| 474 { | |
| 475 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 476 | |
| 477 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-animated.webp"); | |
| 478 ASSERT_TRUE(data.get()); | |
| 479 | |
| 480 ASSERT_GE(data->size(), 10u); | |
| 481 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), data->siz
e() - 10); | |
| 482 decoder->setData(tempData.get(), false); | |
| 483 | |
| 484 EXPECT_EQ(2u, decoder->frameCount()); | |
| 485 EXPECT_FALSE(decoder->failed()); | |
| 486 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
| 487 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); | |
| 488 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
| 489 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); | |
| 490 | |
| 491 decoder->setData(data.get(), true); | |
| 492 EXPECT_EQ(3u, decoder->frameCount()); | |
| 493 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(0)); | |
| 494 EXPECT_EQ(1000, decoder->frameDurationAtIndex(0)); | |
| 495 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(1)); | |
| 496 EXPECT_EQ(500, decoder->frameDurationAtIndex(1)); | |
| 497 EXPECT_TRUE(decoder->frameIsCompleteAtIndex(2)); | |
| 498 EXPECT_EQ(1000.0, decoder->frameDurationAtIndex(2)); | |
| 499 } | |
| 500 | |
| 501 TEST(AnimatedWebPTests, updateRequiredPreviousFrameAfterFirstDecode) | |
| 502 { | |
| 503 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 504 | |
| 505 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated.webp"); | |
| 506 ASSERT_TRUE(fullData.get()); | |
| 507 | |
| 508 // Give it data that is enough to parse but not decode in order to check the
status | |
| 509 // of requiredPreviousFrameIndex before decoding. | |
| 510 size_t partialSize = 1; | |
| 511 do { | |
| 512 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
| 513 decoder->setData(data.get(), false); | |
| 514 ++partialSize; | |
| 515 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
| 516 | |
| 517 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(0)->requiredPreviousFrameIn
dex()); | |
| 518 unsigned frameCount = decoder->frameCount(); | |
| 519 for (size_t i = 1; i < frameCount; ++i) | |
| 520 EXPECT_EQ(i - 1, decoder->frameBufferAtIndex(i)->requiredPreviousFrameIn
dex()); | |
| 521 | |
| 522 decoder->setData(fullData.get(), true); | |
| 523 for (size_t i = 0; i < frameCount; ++i) | |
| 524 EXPECT_EQ(kNotFound, decoder->frameBufferAtIndex(i)->requiredPreviousFra
meIndex()); | |
| 525 } | |
| 526 | |
| 527 TEST(AnimatedWebPTests, randomFrameDecode) | |
| 528 { | |
| 529 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated.webp
"); | |
| 530 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-opaq
ue.webp"); | |
| 531 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-larg
e.webp"); | |
| 532 testRandomFrameDecode("/LayoutTests/fast/images/resources/webp-animated-icc-
xmp.webp"); | |
| 533 } | |
| 534 | |
| 535 TEST(AnimatedWebPTests, randomDecodeAfterClearFrameBufferCache) | |
| 536 { | |
| 537 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated.webp"); | |
| 538 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-opaque.webp"); | |
| 539 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-large.webp"); | |
| 540 testRandomDecodeAfterClearFrameBufferCache("/LayoutTests/fast/images/resourc
es/webp-animated-icc-xmp.webp"); | |
| 541 } | |
| 542 | |
| 543 TEST(AnimatedWebPTests, resumePartialDecodeAfterClearFrameBufferCache) | |
| 544 { | |
| 545 RefPtr<SharedBuffer> fullData = readFile("/LayoutTests/fast/images/resources
/webp-animated-large.webp"); | |
| 546 ASSERT_TRUE(fullData.get()); | |
| 547 Vector<unsigned> baselineHashes; | |
| 548 createDecodingBaseline(fullData.get(), &baselineHashes); | |
| 549 size_t frameCount = baselineHashes.size(); | |
| 550 | |
| 551 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 552 | |
| 553 // Let frame 0 be partially decoded. | |
| 554 size_t partialSize = 1; | |
| 555 do { | |
| 556 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), parti
alSize); | |
| 557 decoder->setData(data.get(), false); | |
| 558 ++partialSize; | |
| 559 } while (!decoder->frameCount() || decoder->frameBufferAtIndex(0)->status()
== ImageFrame::FrameEmpty); | |
| 560 | |
| 561 // Skip to the last frame and clear. | |
| 562 decoder->setData(fullData.get(), true); | |
| 563 EXPECT_EQ(frameCount, decoder->frameCount()); | |
| 564 ImageFrame* lastFrame = decoder->frameBufferAtIndex(frameCount - 1); | |
| 565 EXPECT_EQ(baselineHashes[frameCount - 1], hashSkBitmap(lastFrame->getSkBitma
p())); | |
| 566 decoder->clearCacheExceptFrame(kNotFound); | |
| 567 | |
| 568 // Resume decoding of the first frame. | |
| 569 ImageFrame* firstFrame = decoder->frameBufferAtIndex(0); | |
| 570 EXPECT_EQ(ImageFrame::FrameComplete, firstFrame->status()); | |
| 571 EXPECT_EQ(baselineHashes[0], hashSkBitmap(firstFrame->getSkBitmap())); | |
| 572 } | |
| 573 | |
| 574 TEST(AnimatedWebPTests, decodeAfterReallocatingData) | |
| 575 { | |
| 576 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-ani
mated.webp"); | |
| 577 testDecodeAfterReallocatingData("/LayoutTests/fast/images/resources/webp-ani
mated-icc-xmp.webp"); | |
| 578 } | |
| 579 | |
| 580 TEST(StaticWebPTests, truncatedImage) | |
| 581 { | |
| 582 // VP8 data is truncated. | |
| 583 testInvalidImage("/LayoutTests/fast/images/resources/truncated.webp", false)
; | |
| 584 // Chunk size in RIFF header doesn't match the file size. | |
| 585 testInvalidImage("/LayoutTests/fast/images/resources/truncated2.webp", true)
; | |
| 586 } | |
| 587 | |
| 588 TEST(StaticWebPTests, notAnimated) | |
| 589 { | |
| 590 OwnPtr<WEBPImageDecoder> decoder = createDecoder(); | |
| 591 RefPtr<SharedBuffer> data = readFile("/LayoutTests/fast/images/resources/web
p-color-profile-lossy.webp"); | |
| 592 ASSERT_TRUE(data.get()); | |
| 593 decoder->setData(data.get(), true); | |
| 594 EXPECT_EQ(1u, decoder->frameCount()); | |
| 595 EXPECT_EQ(cAnimationNone, decoder->repetitionCount()); | |
| 596 } | |
| OLD | NEW |