Bug 1337111 - Part 3. Land groundwork for new blended animation gtests. r=tnikkel
authorAndrew Osmond <aosmond@mozilla.com>
Mon, 17 Sep 2018 15:06:29 -0400
changeset 492583 a826a94ae5dd12bfb01edde50dc9a37e4c1e6e3b
parent 492582 e72e07baca921e697a6d79aa8488e2a0aecb1364
child 492584 8cd5c4f23ee8ca0ae719c53ee9b7e9229a2de38d
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1337111
milestone64.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 1337111 - Part 3. Land groundwork for new blended animation gtests. r=tnikkel
image/Decoder.h
image/DecoderFactory.cpp
image/DecoderFactory.h
image/ImageOps.cpp
image/test/gtest/Common.cpp
image/test/gtest/Common.h
image/test/gtest/TestDecoders.cpp
image/test/gtest/TestMetadata.cpp
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -448,16 +448,17 @@ public:
   bool HasFrameToTake() const { return mHasFrameToTake; }
   void ClearHasFrameToTake() {
     MOZ_ASSERT(mHasFrameToTake);
     mHasFrameToTake = false;
   }
 
 protected:
   friend class AutoRecordDecoderTelemetry;
+  friend class DecoderTestHelper;
   friend class nsICODecoder;
   friend class PalettedSurfaceSink;
   friend class SurfaceSink;
 
   virtual ~Decoder();
 
   /*
    * Internal hooks. Decoder implementations may override these and
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -327,16 +327,17 @@ DecoderFactory::CreateDecoderForICOResou
 
   return decoder.forget();
 }
 
 /* static */ already_AddRefed<Decoder>
 DecoderFactory::CreateAnonymousDecoder(DecoderType aType,
                                        NotNull<SourceBuffer*> aSourceBuffer,
                                        const Maybe<IntSize>& aOutputSize,
+                                       DecoderFlags aDecoderFlags,
                                        SurfaceFlags aSurfaceFlags)
 {
   if (aType == DecoderType::UNKNOWN) {
     return nullptr;
   }
 
   RefPtr<Decoder> decoder =
     GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false);
@@ -345,24 +346,17 @@ DecoderFactory::CreateAnonymousDecoder(D
   // Initialize the decoder.
   decoder->SetMetadataDecode(false);
   decoder->SetIterator(aSourceBuffer->Iterator());
 
   // Anonymous decoders are always transient; we don't want to optimize surfaces
   // or do any other expensive work that might be wasted.
   DecoderFlags decoderFlags = DecoderFlags::IMAGE_IS_TRANSIENT;
 
-  // Without an image, the decoder can't store anything in the SurfaceCache, so
-  // callers will only be able to retrieve the most recent frame via
-  // Decoder::GetCurrentFrame(). That means that anonymous decoders should
-  // always be first-frame-only decoders, because nobody ever wants the *last*
-  // frame.
-  decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY;
-
-  decoder->SetDecoderFlags(decoderFlags);
+  decoder->SetDecoderFlags(aDecoderFlags | decoderFlags);
   decoder->SetSurfaceFlags(aSurfaceFlags);
 
   // Set an output size for downscale-during-decode if requested.
   if (aOutputSize) {
     decoder->SetOutputSize(*aOutputSize);
   }
 
   if (NS_FAILED(decoder->Init())) {
--- a/image/DecoderFactory.h
+++ b/image/DecoderFactory.h
@@ -172,23 +172,25 @@ public:
    *
    * @param aType Which type of decoder to create - JPEG, PNG, etc.
    * @param aSourceBuffer The SourceBuffer which the decoder will read its data
    *                      from.
    * @param aOutputSize If Some(), the output size for the decoder. If this is
    *                    smaller than the intrinsic size, the decoder will
    *                    downscale the image. If Nothing(), the output size will
    *                    be the intrinsic size.
+   * @param aDecoderFlags Flags specifying the behavior of this decoder.
    * @param aSurfaceFlags Flags specifying the type of output this decoder
    *                      should produce.
    */
   static already_AddRefed<Decoder>
   CreateAnonymousDecoder(DecoderType aType,
                          NotNull<SourceBuffer*> aSourceBuffer,
                          const Maybe<gfx::IntSize>& aOutputSize,
+                         DecoderFlags aDecoderFlags,
                          SurfaceFlags aSurfaceFlags);
 
   /**
    * Creates and initializes an anonymous metadata decoder (one which isn't
    * associated with an Image object). This decoder will only decode the image's
    * header, extracting metadata like the size of the image. No actual image
    * data will be decoded and no surfaces will be allocated.
    *
--- a/image/ImageOps.cpp
+++ b/image/ImageOps.cpp
@@ -220,17 +220,19 @@ ImageOps::DecodeToSurface(ImageBuffer* a
   }
 
   // Create a decoder.
   DecoderType decoderType =
     DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
   RefPtr<Decoder> decoder =
     DecoderFactory::CreateAnonymousDecoder(decoderType,
                                            WrapNotNull(sourceBuffer),
-                                           aSize, ToSurfaceFlags(aFlags));
+                                           aSize,
+                                           DecoderFlags::FIRST_FRAME_ONLY,
+                                           ToSurfaceFlags(aFlags));
   if (!decoder) {
     return nullptr;
   }
 
   // Run the decoder synchronously.
   RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder));
   task->Run();
   if (!decoder->GetDecodeDone() || decoder->HasError()) {
--- a/image/test/gtest/Common.cpp
+++ b/image/test/gtest/Common.cpp
@@ -190,30 +190,31 @@ RectIsSolidColor(SourceSurface* aSurface
   DataSourceSurface::ScopedMap mapping(dataSurface,
                                        DataSourceSurface::MapType::READ);
   ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false);
   ASSERT_EQ_OR_RETURN(mapping.GetStride(), surfaceSize.width * 4, false);
 
   uint8_t* data = mapping.GetData();
   ASSERT_TRUE_OR_RETURN(data != nullptr, false);
 
+  BGRAColor pmColor = aColor.Premultiply();
   int32_t rowLength = mapping.GetStride();
   for (int32_t row = rect.Y(); row < rect.YMost(); ++row) {
     for (int32_t col = rect.X(); col < rect.XMost(); ++col) {
       int32_t i = row * rowLength + col * 4;
       if (aFuzz != 0) {
-        ASSERT_LE_OR_RETURN(abs(aColor.mBlue - data[i + 0]), aFuzz, false);
-        ASSERT_LE_OR_RETURN(abs(aColor.mGreen - data[i + 1]), aFuzz, false);
-        ASSERT_LE_OR_RETURN(abs(aColor.mRed - data[i + 2]), aFuzz, false);
-        ASSERT_LE_OR_RETURN(abs(aColor.mAlpha - data[i + 3]), aFuzz, false);
+        ASSERT_LE_OR_RETURN(abs(pmColor.mBlue - data[i + 0]), aFuzz, false);
+        ASSERT_LE_OR_RETURN(abs(pmColor.mGreen - data[i + 1]), aFuzz, false);
+        ASSERT_LE_OR_RETURN(abs(pmColor.mRed - data[i + 2]), aFuzz, false);
+        ASSERT_LE_OR_RETURN(abs(pmColor.mAlpha - data[i + 3]), aFuzz, false);
       } else {
-        ASSERT_EQ_OR_RETURN(aColor.mBlue,  data[i + 0], false);
-        ASSERT_EQ_OR_RETURN(aColor.mGreen, data[i + 1], false);
-        ASSERT_EQ_OR_RETURN(aColor.mRed,   data[i + 2], false);
-        ASSERT_EQ_OR_RETURN(aColor.mAlpha, data[i + 3], false);
+        ASSERT_EQ_OR_RETURN(pmColor.mBlue,  data[i + 0], false);
+        ASSERT_EQ_OR_RETURN(pmColor.mGreen, data[i + 1], false);
+        ASSERT_EQ_OR_RETURN(pmColor.mRed,   data[i + 2], false);
+        ASSERT_EQ_OR_RETURN(pmColor.mAlpha, data[i + 3], false);
       }
     }
   }
 
   return true;
 }
 
 bool
@@ -295,16 +296,17 @@ RowHasPixels(SourceSurface* aSurface,
 already_AddRefed<Decoder>
 CreateTrivialDecoder()
 {
   gfxPrefs::GetSingleton();
   DecoderType decoderType = DecoderFactory::GetDecoderType("image/gif");
   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
   RefPtr<Decoder> decoder =
     DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(),
+                                           DefaultDecoderFlags(),
                                            DefaultSurfaceFlags());
   return decoder.forget();
 }
 
 void
 AssertCorrectPipelineFinalState(SurfaceFilter* aFilter,
                                 const gfx::IntRect& aInputSpaceRect,
                                 const gfx::IntRect& aOutputSpaceRect)
--- a/image/test/gtest/Common.h
+++ b/image/test/gtest/Common.h
@@ -5,16 +5,17 @@
 
 #ifndef mozilla_image_test_gtest_Common_h
 #define mozilla_image_test_gtest_Common_h
 
 #include <vector>
 
 #include "gtest/gtest.h"
 
+#include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/2D.h"
 #include "Decoder.h"
 #include "gfxColor.h"
 #include "imgITools.h"
 #include "nsCOMPtr.h"
 #include "SurfacePipe.h"
@@ -70,34 +71,53 @@ struct ImageTestCase
   gfx::IntSize mOutputSize;
   uint32_t mFlags;
 };
 
 struct BGRAColor
 {
   BGRAColor() : BGRAColor(0, 0, 0, 0) { }
 
-  BGRAColor(uint8_t aBlue, uint8_t aGreen, uint8_t aRed, uint8_t aAlpha)
+  BGRAColor(uint8_t aBlue, uint8_t aGreen, uint8_t aRed, uint8_t aAlpha, bool aPremultiplied = false)
     : mBlue(aBlue)
     , mGreen(aGreen)
     , mRed(aRed)
     , mAlpha(aAlpha)
+    , mPremultiplied(aPremultiplied)
   { }
 
   static BGRAColor Green() { return BGRAColor(0x00, 0xFF, 0x00, 0xFF); }
   static BGRAColor Red()   { return BGRAColor(0x00, 0x00, 0xFF, 0xFF); }
   static BGRAColor Blue()   { return BGRAColor(0xFF, 0x00, 0x00, 0xFF); }
   static BGRAColor Transparent() { return BGRAColor(0x00, 0x00, 0x00, 0x00); }
 
-  uint32_t AsPixel() const { return gfxPackedPixel(mAlpha, mRed, mGreen, mBlue); }
+  BGRAColor Premultiply() const
+  {
+    if (!mPremultiplied) {
+      return BGRAColor(gfxPreMultiply(mBlue, mAlpha),
+                       gfxPreMultiply(mGreen, mAlpha),
+                       gfxPreMultiply(mRed, mAlpha),
+                       mAlpha,
+                       true);
+    }
+    return *this;
+  }
+
+  uint32_t AsPixel() const {
+    if (!mPremultiplied) {
+      return gfxPackedPixel(mAlpha, mRed, mGreen, mBlue);
+    }
+    return gfxPackedPixelNoPreMultiply(mAlpha, mRed, mGreen, mBlue);
+  }
 
   uint8_t mBlue;
   uint8_t mGreen;
   uint8_t mRed;
   uint8_t mAlpha;
+  bool mPremultiplied;
 };
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // General Helpers
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -236,30 +256,38 @@ already_AddRefed<Decoder> CreateTrivialD
  * it to the provided lambda @aFunc. Assertions that the pipeline is constructly
  * correctly and cleanup of any allocated surfaces is handled automatically.
  *
  * @param aDecoder The decoder to use for allocating surfaces.
  * @param aFunc The lambda function to pass the filter pipeline to.
  * @param aConfigs The configuration for the pipeline.
  */
 template <typename Func, typename... Configs>
-void WithFilterPipeline(Decoder* aDecoder, Func aFunc, const Configs&... aConfigs)
+void WithFilterPipeline(Decoder* aDecoder, Func aFunc, bool aFinish, const Configs&... aConfigs)
 {
   auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
   nsresult rv = pipe->Configure(aConfigs...);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
 
   aFunc(aDecoder, pipe.get());
 
-  RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
-  if (currentFrame) {
-    currentFrame->Finish();
+  if (aFinish) {
+    RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
+    if (currentFrame) {
+      currentFrame->Finish();
+    }
   }
 }
 
+template <typename Func, typename... Configs>
+void WithFilterPipeline(Decoder* aDecoder, Func aFunc, const Configs&... aConfigs)
+{
+  WithFilterPipeline(aDecoder, aFunc, true, aConfigs...);
+}
+
 /**
  * Creates a pipeline of SurfaceFilters from a list of Config structs and
  * asserts that configuring it fails. Cleanup of any allocated surfaces is
  * handled automatically.
  *
  * @param aDecoder The decoder to use for allocating surfaces.
  * @param aConfigs The configuration for the pipeline.
  */
@@ -364,16 +392,41 @@ void CheckWritePixels(Decoder* aDecoder,
 void CheckPalettedWritePixels(Decoder* aDecoder,
                               SurfaceFilter* aFilter,
                               const Maybe<gfx::IntRect>& aOutputRect = Nothing(),
                               const Maybe<gfx::IntRect>& aInputRect = Nothing(),
                               const Maybe<gfx::IntRect>& aInputWriteRect = Nothing(),
                               const Maybe<gfx::IntRect>& aOutputWriteRect = Nothing(),
                               uint8_t aFuzz = 0);
 
+///////////////////////////////////////////////////////////////////////////////
+// Decoder Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+// Friend class of Decoder to access internals for tests.
+class MOZ_STACK_CLASS DecoderTestHelper final
+{
+public:
+  explicit DecoderTestHelper(Decoder* aDecoder)
+    : mDecoder(aDecoder)
+  { }
+
+  void PostIsAnimated(FrameTimeout aTimeout)
+  {
+    mDecoder->PostIsAnimated(aTimeout);
+  }
+
+  void PostFrameStop(Opacity aOpacity)
+  {
+    mDecoder->PostFrameStop(aOpacity);
+  }
+
+private:
+  Decoder* mDecoder;
+};
 
 ///////////////////////////////////////////////////////////////////////////////
 // Test Data
 ///////////////////////////////////////////////////////////////////////////////
 
 ImageTestCase GreenPNGTestCase();
 ImageTestCase GreenGIFTestCase();
 ImageTestCase GreenJPGTestCase();
--- a/image/test/gtest/TestDecoders.cpp
+++ b/image/test/gtest/TestDecoders.cpp
@@ -110,16 +110,17 @@ void WithSingleChunkDecode(const ImageTe
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   sourceBuffer->Complete(NS_OK);
 
   // Create a decoder.
   DecoderType decoderType =
     DecoderFactory::GetDecoderType(aTestCase.mMimeType);
   RefPtr<Decoder> decoder =
     DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, aOutputSize,
+                                           DecoderFlags::FIRST_FRAME_ONLY,
                                            DefaultSurfaceFlags());
   ASSERT_TRUE(decoder != nullptr);
   RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder));
 
   // Run the full decoder synchronously.
   task->Run();
 
   // Call the lambda to verify the expected results.
@@ -147,16 +148,17 @@ CheckDecoderMultiChunk(const ImageTestCa
 
   // Create a SourceBuffer and a decoder.
   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
   sourceBuffer->ExpectLength(length);
   DecoderType decoderType =
     DecoderFactory::GetDecoderType(aTestCase.mMimeType);
   RefPtr<Decoder> decoder =
     DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(),
+                                           DecoderFlags::FIRST_FRAME_ONLY,
                                            DefaultSurfaceFlags());
   ASSERT_TRUE(decoder != nullptr);
   RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder));
 
   for (uint64_t read = 0; read < length ; ++read) {
     uint64_t available = 0;
     rv = inputStream->Available(&available);
     ASSERT_TRUE(available > 0);
--- a/image/test/gtest/TestMetadata.cpp
+++ b/image/test/gtest/TestMetadata.cpp
@@ -103,16 +103,17 @@ CheckMetadata(const ImageTestCase& aTest
   EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
 
   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
             bool(metadataProgress & FLAG_IS_ANIMATED));
 
   // Create a full decoder, so we can compare the result.
   decoder =
     DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(),
+                                           DecoderFlags::FIRST_FRAME_ONLY,
                                            DefaultSurfaceFlags());
   ASSERT_TRUE(decoder != nullptr);
   task = new AnonymousDecodingTask(WrapNotNull(decoder));
 
   if (aBMPWithinICO == BMPWithinICO::YES) {
     static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
   }