Bug 1293472 (Part 4) - Test that single-frame and animated decodes can coexist for the same image. r=edwin
authorSeth Fowler <mark.seth.fowler@gmail.com>
Thu, 18 Aug 2016 01:00:23 -0700
changeset 351489 856b1b82372a8273af0c327bb5b6ce5e8e7ff359
parent 351488 cfb1f1eeceb36952195fd255d5a7a5f017771cbd
child 351490 f6a32bf33e423d7bb2fadc1c100c0de317170b54
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1293472
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1293472 (Part 4) - Test that single-frame and animated decodes can coexist for the same image. r=edwin
image/test/gtest/TestDecoders.cpp
--- a/image/test/gtest/TestDecoders.cpp
+++ b/image/test/gtest/TestDecoders.cpp
@@ -360,16 +360,247 @@ TEST_F(ImageDecoders, CorruptICOWithBadB
   CheckDecoderSingleChunk(CorruptICOWithBadBMPHeightTestCase());
 }
 
 TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightMultiChunk)
 {
   CheckDecoderMultiChunk(CorruptICOWithBadBMPHeightTestCase());
 }
 
+TEST_F(ImageDecoders, AnimatedGIFWithFRAME_FIRST)
+{
+  ImageTestCase testCase = GreenFirstFrameAnimatedGIFTestCase();
+
+  // Verify that we can decode this test case and retrieve the first frame using
+  // imgIContainer::FRAME_FIRST. This ensures that we correctly trigger a
+  // single-frame decode rather than an animated decode when
+  // imgIContainer::FRAME_FIRST is requested.
+
+  // Create an image.
+  RefPtr<Image> image =
+    ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType));
+  ASSERT_TRUE(!image->HasError());
+
+  nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
+  ASSERT_TRUE(inputStream);
+
+  // Figure out how much data we have.
+  uint64_t length;
+  nsresult rv = inputStream->Available(&length);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Write the data into the image.
+  rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
+                                   static_cast<uint32_t>(length));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Let the image know we've sent all the data.
+  rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
+  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+
+  // Lock the image so its surfaces don't disappear during the test.
+  image->LockImage();
+
+  // Use GetFrame() to force a sync decode of the image, specifying FRAME_FIRST
+  // to ensure that we don't get an animated decode.
+  RefPtr<SourceSurface> surface =
+    image->GetFrame(imgIContainer::FRAME_FIRST,
+                    imgIContainer::FLAG_SYNC_DECODE);
+
+  // Ensure that the image's metadata meets our expectations.
+  IntSize imageSize(0, 0);
+  rv = image->GetWidth(&imageSize.width);
+  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  rv = image->GetHeight(&imageSize.height);
+  EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+  EXPECT_EQ(testCase.mSize.width, imageSize.width);
+  EXPECT_EQ(testCase.mSize.height, imageSize.height);
+
+  Progress imageProgress = tracker->GetProgress();
+
+  EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
+  EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
+
+  // Ensure that we decoded the static version of the image.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eStatic));
+    ASSERT_EQ(MatchType::EXACT, result.Type());
+    EXPECT_TRUE(bool(result.Surface()));
+  }
+
+  // Ensure that we didn't decode the animated version of the image.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eAnimated));
+    ASSERT_EQ(MatchType::NOT_FOUND, result.Type());
+  }
+
+  // Use GetFrame() to force a sync decode of the image, this time specifying
+  // FRAME_CURRENT to ensure that we get an animated decode.
+  RefPtr<SourceSurface> animatedSurface =
+    image->GetFrame(imgIContainer::FRAME_CURRENT,
+                    imgIContainer::FLAG_SYNC_DECODE);
+
+  // Ensure that we decoded both frames of the animated version of the image.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eAnimated));
+    ASSERT_EQ(MatchType::EXACT, result.Type());
+
+    EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
+    EXPECT_TRUE(bool(result.Surface()));
+
+    EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1)));
+    EXPECT_TRUE(bool(result.Surface()));
+  }
+
+  // Ensure that the static version is still around.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eStatic));
+    ASSERT_EQ(MatchType::EXACT, result.Type());
+    EXPECT_TRUE(bool(result.Surface()));
+  }
+}
+
+TEST_F(ImageDecoders, AnimatedGIFWithFRAME_CURRENT)
+{
+  ImageTestCase testCase = GreenFirstFrameAnimatedGIFTestCase();
+
+  // Verify that we can decode this test case and retrieve the entire sequence
+  // of frames using imgIContainer::FRAME_CURRENT. This ensures that we
+  // correctly trigger an animated decode rather than a single-frame decode when
+  // imgIContainer::FRAME_CURRENT is requested.
+
+  // Create an image.
+  RefPtr<Image> image =
+    ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType));
+  ASSERT_TRUE(!image->HasError());
+
+  nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
+  ASSERT_TRUE(inputStream);
+
+  // Figure out how much data we have.
+  uint64_t length;
+  nsresult rv = inputStream->Available(&length);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Write the data into the image.
+  rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
+                                   static_cast<uint32_t>(length));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Let the image know we've sent all the data.
+  rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
+  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+
+  // Lock the image so its surfaces don't disappear during the test.
+  image->LockImage();
+
+  // Use GetFrame() to force a sync decode of the image, specifying
+  // FRAME_CURRENT to ensure we get an animated decode.
+  RefPtr<SourceSurface> surface =
+    image->GetFrame(imgIContainer::FRAME_CURRENT,
+                    imgIContainer::FLAG_SYNC_DECODE);
+
+  // Ensure that the image's metadata meets our expectations.
+  IntSize imageSize(0, 0);
+  rv = image->GetWidth(&imageSize.width);
+  EXPECT_TRUE(NS_SUCCEEDED(rv));
+  rv = image->GetHeight(&imageSize.height);
+  EXPECT_TRUE(NS_SUCCEEDED(rv));
+
+  EXPECT_EQ(testCase.mSize.width, imageSize.width);
+  EXPECT_EQ(testCase.mSize.height, imageSize.height);
+
+  Progress imageProgress = tracker->GetProgress();
+
+  EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
+  EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
+
+  // Ensure that we decoded both frames of the animated version of the image.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eAnimated));
+    ASSERT_EQ(MatchType::EXACT, result.Type());
+
+    EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
+    EXPECT_TRUE(bool(result.Surface()));
+
+    EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1)));
+    EXPECT_TRUE(bool(result.Surface()));
+  }
+
+  // Ensure that we didn't decode the static version of the image.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eStatic));
+    ASSERT_EQ(MatchType::NOT_FOUND, result.Type());
+  }
+
+  // Use GetFrame() to force a sync decode of the image, this time specifying
+  // FRAME_FIRST to ensure that we get a single-frame decode.
+  RefPtr<SourceSurface> animatedSurface =
+    image->GetFrame(imgIContainer::FRAME_FIRST,
+                    imgIContainer::FLAG_SYNC_DECODE);
+
+  // Ensure that we decoded the static version of the image.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eStatic));
+    ASSERT_EQ(MatchType::EXACT, result.Type());
+    EXPECT_TRUE(bool(result.Surface()));
+  }
+
+  // Ensure that both frames of the animated version are still around.
+  {
+    LookupResult result =
+      SurfaceCache::Lookup(ImageKey(image.get()),
+                           RasterSurfaceKey(imageSize,
+                                            DefaultSurfaceFlags(),
+                                            PlaybackType::eAnimated));
+    ASSERT_EQ(MatchType::EXACT, result.Type());
+
+    EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
+    EXPECT_TRUE(bool(result.Surface()));
+
+    EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1)));
+    EXPECT_TRUE(bool(result.Surface()));
+  }
+}
+
 TEST_F(ImageDecoders, AnimatedGIFWithExtraImageSubBlocks)
 {
   ImageTestCase testCase = ExtraImageSubBlocksAnimatedGIFTestCase();
 
   // Verify that we can decode this test case and get two frames, even though
   // there are extra image sub blocks between the first and second frame. The
   // extra data shouldn't confuse the decoder or cause the decode to fail.