| 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 "platform/image-decoders/gif/GIFImageDecoder.h" | |
| 32 | |
| 33 #include <memory> | |
| 34 #include "platform/SharedBuffer.h" | |
| 35 #include "platform/image-decoders/ImageDecoderTestHelpers.h" | |
| 36 #include "platform/wtf/PtrUtil.h" | |
| 37 #include "platform/wtf/Vector.h" | |
| 38 #include "public/platform/WebData.h" | |
| 39 #include "public/platform/WebSize.h" | |
| 40 #include "testing/gtest/include/gtest/gtest.h" | |
| 41 | |
| 42 namespace blink { | |
| 43 | |
| 44 namespace { | |
| 45 | |
| 46 const char kLayoutTestResourcesDir[] = "LayoutTests/images/resources"; | |
| 47 | |
| 48 std::unique_ptr<ImageDecoder> CreateDecoder() { | |
| 49 return WTF::WrapUnique( | |
| 50 new GIFImageDecoder(ImageDecoder::kAlphaNotPremultiplied, | |
| 51 ColorBehavior::TransformToTargetForTesting(), | |
| 52 ImageDecoder::kNoDecodedImageByteLimit)); | |
| 53 } | |
| 54 | |
| 55 void TestRepetitionCount(const char* dir, | |
| 56 const char* file, | |
| 57 int expected_repetition_count) { | |
| 58 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 59 RefPtr<SharedBuffer> data = ReadFile(dir, file); | |
| 60 ASSERT_TRUE(data.Get()); | |
| 61 decoder->SetData(data.Get(), true); | |
| 62 EXPECT_EQ(kAnimationLoopOnce, | |
| 63 decoder->RepetitionCount()); // Default value before decode. | |
| 64 | |
| 65 for (size_t i = 0; i < decoder->FrameCount(); ++i) { | |
| 66 ImageFrame* frame = decoder->FrameBufferAtIndex(i); | |
| 67 EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); | |
| 68 } | |
| 69 | |
| 70 EXPECT_EQ(expected_repetition_count, | |
| 71 decoder->RepetitionCount()); // Expected value after decode. | |
| 72 } | |
| 73 | |
| 74 } // anonymous namespace | |
| 75 | |
| 76 TEST(GIFImageDecoderTest, decodeTwoFrames) { | |
| 77 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 78 | |
| 79 RefPtr<SharedBuffer> data = ReadFile(kLayoutTestResourcesDir, "animated.gif"); | |
| 80 ASSERT_TRUE(data.Get()); | |
| 81 decoder->SetData(data.Get(), true); | |
| 82 EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount()); | |
| 83 | |
| 84 ImageFrame* frame = decoder->FrameBufferAtIndex(0); | |
| 85 uint32_t generation_id0 = frame->Bitmap().getGenerationID(); | |
| 86 EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); | |
| 87 EXPECT_EQ(16, frame->Bitmap().width()); | |
| 88 EXPECT_EQ(16, frame->Bitmap().height()); | |
| 89 | |
| 90 frame = decoder->FrameBufferAtIndex(1); | |
| 91 uint32_t generation_id1 = frame->Bitmap().getGenerationID(); | |
| 92 EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); | |
| 93 EXPECT_EQ(16, frame->Bitmap().width()); | |
| 94 EXPECT_EQ(16, frame->Bitmap().height()); | |
| 95 EXPECT_TRUE(generation_id0 != generation_id1); | |
| 96 | |
| 97 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 98 EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount()); | |
| 99 } | |
| 100 | |
| 101 TEST(GIFImageDecoderTest, parseAndDecode) { | |
| 102 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 103 | |
| 104 RefPtr<SharedBuffer> data = ReadFile(kLayoutTestResourcesDir, "animated.gif"); | |
| 105 ASSERT_TRUE(data.Get()); | |
| 106 decoder->SetData(data.Get(), true); | |
| 107 EXPECT_EQ(kAnimationLoopOnce, decoder->RepetitionCount()); | |
| 108 | |
| 109 // This call will parse the entire file. | |
| 110 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 111 | |
| 112 ImageFrame* frame = decoder->FrameBufferAtIndex(0); | |
| 113 EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); | |
| 114 EXPECT_EQ(16, frame->Bitmap().width()); | |
| 115 EXPECT_EQ(16, frame->Bitmap().height()); | |
| 116 | |
| 117 frame = decoder->FrameBufferAtIndex(1); | |
| 118 EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); | |
| 119 EXPECT_EQ(16, frame->Bitmap().width()); | |
| 120 EXPECT_EQ(16, frame->Bitmap().height()); | |
| 121 EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount()); | |
| 122 } | |
| 123 | |
| 124 TEST(GIFImageDecoderTest, parseByteByByte) { | |
| 125 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 126 | |
| 127 RefPtr<SharedBuffer> data = ReadFile(kLayoutTestResourcesDir, "animated.gif"); | |
| 128 ASSERT_TRUE(data.Get()); | |
| 129 | |
| 130 size_t frame_count = 0; | |
| 131 | |
| 132 // Pass data to decoder byte by byte. | |
| 133 for (size_t length = 1; length <= data->size(); ++length) { | |
| 134 RefPtr<SharedBuffer> temp_data = SharedBuffer::Create(data->Data(), length); | |
| 135 decoder->SetData(temp_data.Get(), length == data->size()); | |
| 136 | |
| 137 EXPECT_LE(frame_count, decoder->FrameCount()); | |
| 138 frame_count = decoder->FrameCount(); | |
| 139 } | |
| 140 | |
| 141 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 142 | |
| 143 decoder->FrameBufferAtIndex(0); | |
| 144 decoder->FrameBufferAtIndex(1); | |
| 145 EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount()); | |
| 146 } | |
| 147 | |
| 148 TEST(GIFImageDecoderTest, parseAndDecodeByteByByte) { | |
| 149 TestByteByByteDecode(&CreateDecoder, kLayoutTestResourcesDir, | |
| 150 "animated-gif-with-offsets.gif", 5u, | |
| 151 kAnimationLoopInfinite); | |
| 152 } | |
| 153 | |
| 154 TEST(GIFImageDecoderTest, brokenSecondFrame) { | |
| 155 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 156 | |
| 157 RefPtr<SharedBuffer> data = ReadFile(kDecodersTestingDir, "broken.gif"); | |
| 158 ASSERT_TRUE(data.Get()); | |
| 159 decoder->SetData(data.Get(), true); | |
| 160 | |
| 161 // One frame is detected but cannot be decoded. | |
| 162 EXPECT_EQ(1u, decoder->FrameCount()); | |
| 163 ImageFrame* frame = decoder->FrameBufferAtIndex(1); | |
| 164 EXPECT_FALSE(frame); | |
| 165 } | |
| 166 | |
| 167 TEST(GIFImageDecoderTest, progressiveDecode) { | |
| 168 TestProgressiveDecoding(&CreateDecoder, kDecodersTestingDir, "radient.gif"); | |
| 169 } | |
| 170 | |
| 171 TEST(GIFImageDecoderTest, allDataReceivedTruncation) { | |
| 172 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 173 | |
| 174 RefPtr<SharedBuffer> data = ReadFile(kLayoutTestResourcesDir, "animated.gif"); | |
| 175 ASSERT_TRUE(data.Get()); | |
| 176 | |
| 177 ASSERT_GE(data->size(), 10u); | |
| 178 RefPtr<SharedBuffer> temp_data = | |
| 179 SharedBuffer::Create(data->Data(), data->size() - 10); | |
| 180 decoder->SetData(temp_data.Get(), true); | |
| 181 | |
| 182 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 183 EXPECT_FALSE(decoder->Failed()); | |
| 184 | |
| 185 decoder->FrameBufferAtIndex(0); | |
| 186 EXPECT_FALSE(decoder->Failed()); | |
| 187 decoder->FrameBufferAtIndex(1); | |
| 188 EXPECT_TRUE(decoder->Failed()); | |
| 189 } | |
| 190 | |
| 191 TEST(GIFImageDecoderTest, frameIsComplete) { | |
| 192 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 193 | |
| 194 RefPtr<SharedBuffer> data = ReadFile(kLayoutTestResourcesDir, "animated.gif"); | |
| 195 ASSERT_TRUE(data.Get()); | |
| 196 decoder->SetData(data.Get(), true); | |
| 197 | |
| 198 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 199 EXPECT_FALSE(decoder->Failed()); | |
| 200 EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0)); | |
| 201 EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(1)); | |
| 202 EXPECT_EQ(kAnimationLoopInfinite, decoder->RepetitionCount()); | |
| 203 } | |
| 204 | |
| 205 TEST(GIFImageDecoderTest, frameIsCompleteLoading) { | |
| 206 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 207 | |
| 208 RefPtr<SharedBuffer> data = ReadFile(kLayoutTestResourcesDir, "animated.gif"); | |
| 209 ASSERT_TRUE(data.Get()); | |
| 210 | |
| 211 ASSERT_GE(data->size(), 10u); | |
| 212 RefPtr<SharedBuffer> temp_data = | |
| 213 SharedBuffer::Create(data->Data(), data->size() - 10); | |
| 214 decoder->SetData(temp_data.Get(), false); | |
| 215 | |
| 216 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 217 EXPECT_FALSE(decoder->Failed()); | |
| 218 EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0)); | |
| 219 EXPECT_FALSE(decoder->FrameIsCompleteAtIndex(1)); | |
| 220 | |
| 221 decoder->SetData(data.Get(), true); | |
| 222 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 223 EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(0)); | |
| 224 EXPECT_TRUE(decoder->FrameIsCompleteAtIndex(1)); | |
| 225 } | |
| 226 | |
| 227 TEST(GIFImageDecoderTest, badTerminator) { | |
| 228 RefPtr<SharedBuffer> reference_data = | |
| 229 ReadFile(kDecodersTestingDir, "radient.gif"); | |
| 230 RefPtr<SharedBuffer> test_data = | |
| 231 ReadFile(kDecodersTestingDir, "radient-bad-terminator.gif"); | |
| 232 ASSERT_TRUE(reference_data.Get()); | |
| 233 ASSERT_TRUE(test_data.Get()); | |
| 234 | |
| 235 std::unique_ptr<ImageDecoder> reference_decoder = CreateDecoder(); | |
| 236 reference_decoder->SetData(reference_data.Get(), true); | |
| 237 EXPECT_EQ(1u, reference_decoder->FrameCount()); | |
| 238 ImageFrame* reference_frame = reference_decoder->FrameBufferAtIndex(0); | |
| 239 DCHECK(reference_frame); | |
| 240 | |
| 241 std::unique_ptr<ImageDecoder> test_decoder = CreateDecoder(); | |
| 242 test_decoder->SetData(test_data.Get(), true); | |
| 243 EXPECT_EQ(1u, test_decoder->FrameCount()); | |
| 244 ImageFrame* test_frame = test_decoder->FrameBufferAtIndex(0); | |
| 245 DCHECK(test_frame); | |
| 246 | |
| 247 EXPECT_EQ(HashBitmap(reference_frame->Bitmap()), | |
| 248 HashBitmap(test_frame->Bitmap())); | |
| 249 } | |
| 250 | |
| 251 TEST(GIFImageDecoderTest, updateRequiredPreviousFrameAfterFirstDecode) { | |
| 252 TestUpdateRequiredPreviousFrameAfterFirstDecode( | |
| 253 &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif"); | |
| 254 } | |
| 255 | |
| 256 TEST(GIFImageDecoderTest, randomFrameDecode) { | |
| 257 // Single frame image. | |
| 258 TestRandomFrameDecode(&CreateDecoder, kDecodersTestingDir, "radient.gif"); | |
| 259 // Multiple frame images. | |
| 260 TestRandomFrameDecode(&CreateDecoder, kLayoutTestResourcesDir, | |
| 261 "animated-gif-with-offsets.gif"); | |
| 262 TestRandomFrameDecode(&CreateDecoder, kLayoutTestResourcesDir, | |
| 263 "animated-10color.gif"); | |
| 264 } | |
| 265 | |
| 266 TEST(GIFImageDecoderTest, randomDecodeAfterClearFrameBufferCache) { | |
| 267 // Single frame image. | |
| 268 TestRandomDecodeAfterClearFrameBufferCache( | |
| 269 &CreateDecoder, kDecodersTestingDir, "radient.gif"); | |
| 270 // Multiple frame images. | |
| 271 TestRandomDecodeAfterClearFrameBufferCache( | |
| 272 &CreateDecoder, kLayoutTestResourcesDir, "animated-gif-with-offsets.gif"); | |
| 273 TestRandomDecodeAfterClearFrameBufferCache( | |
| 274 &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif"); | |
| 275 } | |
| 276 | |
| 277 TEST(GIFImageDecoderTest, resumePartialDecodeAfterClearFrameBufferCache) { | |
| 278 TestResumePartialDecodeAfterClearFrameBufferCache( | |
| 279 &CreateDecoder, kLayoutTestResourcesDir, "animated-10color.gif"); | |
| 280 } | |
| 281 | |
| 282 // The first LZW codes in the image are invalid values that try to create a loop | |
| 283 // in the dictionary. Decoding should fail, but not infinitely loop or corrupt | |
| 284 // memory. | |
| 285 TEST(GIFImageDecoderTest, badInitialCode) { | |
| 286 RefPtr<SharedBuffer> test_data = | |
| 287 ReadFile(kDecodersTestingDir, "bad-initial-code.gif"); | |
| 288 ASSERT_TRUE(test_data.Get()); | |
| 289 | |
| 290 std::unique_ptr<ImageDecoder> test_decoder = CreateDecoder(); | |
| 291 test_decoder->SetData(test_data.Get(), true); | |
| 292 EXPECT_EQ(1u, test_decoder->FrameCount()); | |
| 293 ASSERT_TRUE(test_decoder->FrameBufferAtIndex(0)); | |
| 294 EXPECT_TRUE(test_decoder->Failed()); | |
| 295 } | |
| 296 | |
| 297 // The image has an invalid LZW code that exceeds dictionary size. Decoding | |
| 298 // should fail. | |
| 299 TEST(GIFImageDecoderTest, badCode) { | |
| 300 RefPtr<SharedBuffer> test_data = | |
| 301 ReadFile(kDecodersTestingDir, "bad-code.gif"); | |
| 302 ASSERT_TRUE(test_data.Get()); | |
| 303 | |
| 304 std::unique_ptr<ImageDecoder> test_decoder = CreateDecoder(); | |
| 305 test_decoder->SetData(test_data.Get(), true); | |
| 306 EXPECT_EQ(1u, test_decoder->FrameCount()); | |
| 307 ASSERT_TRUE(test_decoder->FrameBufferAtIndex(0)); | |
| 308 EXPECT_TRUE(test_decoder->Failed()); | |
| 309 } | |
| 310 | |
| 311 TEST(GIFImageDecoderTest, invalidDisposalMethod) { | |
| 312 std::unique_ptr<ImageDecoder> decoder = CreateDecoder(); | |
| 313 | |
| 314 // The image has 2 frames, with disposal method 4 and 5, respectively. | |
| 315 RefPtr<SharedBuffer> data = | |
| 316 ReadFile(kDecodersTestingDir, "invalid-disposal-method.gif"); | |
| 317 ASSERT_TRUE(data.Get()); | |
| 318 decoder->SetData(data.Get(), true); | |
| 319 | |
| 320 EXPECT_EQ(2u, decoder->FrameCount()); | |
| 321 // Disposal method 4 is converted to ImageFrame::DisposeOverwritePrevious. | |
| 322 EXPECT_EQ(ImageFrame::kDisposeOverwritePrevious, | |
| 323 decoder->FrameBufferAtIndex(0)->GetDisposalMethod()); | |
| 324 // Disposal method 5 is ignored. | |
| 325 EXPECT_EQ(ImageFrame::kDisposeNotSpecified, | |
| 326 decoder->FrameBufferAtIndex(1)->GetDisposalMethod()); | |
| 327 } | |
| 328 | |
| 329 TEST(GIFImageDecoderTest, firstFrameHasGreaterSizeThanScreenSize) { | |
| 330 RefPtr<SharedBuffer> full_data = ReadFile( | |
| 331 kDecodersTestingDir, "first-frame-has-greater-size-than-screen-size.gif"); | |
| 332 ASSERT_TRUE(full_data.Get()); | |
| 333 | |
| 334 std::unique_ptr<ImageDecoder> decoder; | |
| 335 IntSize frame_size; | |
| 336 | |
| 337 // Compute hashes when the file is truncated. | |
| 338 for (size_t i = 1; i <= full_data->size(); ++i) { | |
| 339 decoder = CreateDecoder(); | |
| 340 RefPtr<SharedBuffer> data = SharedBuffer::Create(full_data->Data(), i); | |
| 341 decoder->SetData(data.Get(), i == full_data->size()); | |
| 342 | |
| 343 if (decoder->IsSizeAvailable() && !frame_size.Width() && | |
| 344 !frame_size.Height()) { | |
| 345 frame_size = decoder->DecodedSize(); | |
| 346 continue; | |
| 347 } | |
| 348 | |
| 349 ASSERT_EQ(frame_size.Width(), decoder->DecodedSize().Width()); | |
| 350 ASSERT_EQ(frame_size.Height(), decoder->DecodedSize().Height()); | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 TEST(GIFImageDecoderTest, verifyRepetitionCount) { | |
| 355 TestRepetitionCount(kLayoutTestResourcesDir, "full2loop.gif", 2); | |
| 356 TestRepetitionCount(kDecodersTestingDir, "radient.gif", kAnimationNone); | |
| 357 } | |
| 358 | |
| 359 TEST(GIFImageDecoderTest, bitmapAlphaType) { | |
| 360 RefPtr<SharedBuffer> full_data = ReadFile(kDecodersTestingDir, "radient.gif"); | |
| 361 ASSERT_TRUE(full_data.Get()); | |
| 362 | |
| 363 // Empirically chosen truncation size: | |
| 364 // a) large enough to produce a partial frame && | |
| 365 // b) small enough to not fully decode the frame | |
| 366 const size_t kTruncateSize = 800; | |
| 367 ASSERT_TRUE(kTruncateSize < full_data->size()); | |
| 368 RefPtr<SharedBuffer> partial_data = | |
| 369 SharedBuffer::Create(full_data->Data(), kTruncateSize); | |
| 370 | |
| 371 std::unique_ptr<ImageDecoder> premul_decoder = WTF::WrapUnique( | |
| 372 new GIFImageDecoder(ImageDecoder::kAlphaPremultiplied, | |
| 373 ColorBehavior::TransformToTargetForTesting(), | |
| 374 ImageDecoder::kNoDecodedImageByteLimit)); | |
| 375 std::unique_ptr<ImageDecoder> unpremul_decoder = WTF::WrapUnique( | |
| 376 new GIFImageDecoder(ImageDecoder::kAlphaNotPremultiplied, | |
| 377 ColorBehavior::TransformToTargetForTesting(), | |
| 378 ImageDecoder::kNoDecodedImageByteLimit)); | |
| 379 | |
| 380 // Partially decoded frame => the frame alpha type is unknown and should | |
| 381 // reflect the requested format. | |
| 382 premul_decoder->SetData(partial_data.Get(), false); | |
| 383 ASSERT_TRUE(premul_decoder->FrameCount()); | |
| 384 unpremul_decoder->SetData(partial_data.Get(), false); | |
| 385 ASSERT_TRUE(unpremul_decoder->FrameCount()); | |
| 386 ImageFrame* premul_frame = premul_decoder->FrameBufferAtIndex(0); | |
| 387 EXPECT_TRUE(premul_frame && | |
| 388 premul_frame->GetStatus() != ImageFrame::kFrameComplete); | |
| 389 EXPECT_EQ(premul_frame->Bitmap().alphaType(), kPremul_SkAlphaType); | |
| 390 ImageFrame* unpremul_frame = unpremul_decoder->FrameBufferAtIndex(0); | |
| 391 EXPECT_TRUE(unpremul_frame && | |
| 392 unpremul_frame->GetStatus() != ImageFrame::kFrameComplete); | |
| 393 EXPECT_EQ(unpremul_frame->Bitmap().alphaType(), kUnpremul_SkAlphaType); | |
| 394 | |
| 395 // Fully decoded frame => the frame alpha type is known (opaque). | |
| 396 premul_decoder->SetData(full_data.Get(), true); | |
| 397 ASSERT_TRUE(premul_decoder->FrameCount()); | |
| 398 unpremul_decoder->SetData(full_data.Get(), true); | |
| 399 ASSERT_TRUE(unpremul_decoder->FrameCount()); | |
| 400 premul_frame = premul_decoder->FrameBufferAtIndex(0); | |
| 401 EXPECT_TRUE(premul_frame && | |
| 402 premul_frame->GetStatus() == ImageFrame::kFrameComplete); | |
| 403 EXPECT_EQ(premul_frame->Bitmap().alphaType(), kOpaque_SkAlphaType); | |
| 404 unpremul_frame = unpremul_decoder->FrameBufferAtIndex(0); | |
| 405 EXPECT_TRUE(unpremul_frame && | |
| 406 unpremul_frame->GetStatus() == ImageFrame::kFrameComplete); | |
| 407 EXPECT_EQ(unpremul_frame->Bitmap().alphaType(), kOpaque_SkAlphaType); | |
| 408 } | |
| 409 | |
| 410 namespace { | |
| 411 // Needed to exercise ImageDecoder::SetMemoryAllocator, but still does the | |
| 412 // default allocation. | |
| 413 class Allocator final : public SkBitmap::Allocator { | |
| 414 bool allocPixelRef(SkBitmap* dst, SkColorTable* ctable) override { | |
| 415 return dst->tryAllocPixels(ctable); | |
| 416 } | |
| 417 }; | |
| 418 } | |
| 419 | |
| 420 // Ensure that calling SetMemoryAllocator does not short-circuit | |
| 421 // InitializeNewFrame. | |
| 422 TEST(GIFImageDecoderTest, externalAllocator) { | |
| 423 auto data = ReadFile(kLayoutTestResourcesDir, "boston.gif"); | |
| 424 ASSERT_TRUE(data.Get()); | |
| 425 | |
| 426 auto decoder = CreateDecoder(); | |
| 427 decoder->SetData(data.Get(), true); | |
| 428 | |
| 429 Allocator allocator; | |
| 430 decoder->SetMemoryAllocator(&allocator); | |
| 431 EXPECT_EQ(1u, decoder->FrameCount()); | |
| 432 ImageFrame* frame = decoder->FrameBufferAtIndex(0); | |
| 433 decoder->SetMemoryAllocator(nullptr); | |
| 434 | |
| 435 ASSERT_TRUE(frame); | |
| 436 EXPECT_EQ(IntRect(IntPoint(), decoder->Size()), frame->OriginalFrameRect()); | |
| 437 EXPECT_FALSE(frame->HasAlpha()); | |
| 438 } | |
| 439 | |
| 440 } // namespace blink | |
| OLD | NEW |