Bug 1551088 - Part 7. Add gtests for SwizzleFilter. r=tnikkel
authorAndrew Osmond <aosmond@mozilla.com>
Tue, 24 Sep 2019 20:42:48 +0000
changeset 494823 3e6f25b21f8cd1dcbee12112e34eee2f07b3cecc
parent 494822 5d72c8de4dafd142392b65e11cf22faba5064ccb
child 494824 9ef391e20fa6d7c0a0f097bda04d61f1d4d4d8f2
push id114131
push userdluca@mozilla.com
push dateThu, 26 Sep 2019 09:47:34 +0000
treeherdermozilla-inbound@1dc1a755079a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1551088
milestone71.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 1551088 - Part 7. Add gtests for SwizzleFilter. r=tnikkel Differential Revision: https://phabricator.services.mozilla.com/D46450
image/test/gtest/Common.cpp
image/test/gtest/Common.h
image/test/gtest/TestSwizzleFilter.cpp
image/test/gtest/moz.build
--- a/image/test/gtest/Common.cpp
+++ b/image/test/gtest/Common.cpp
@@ -162,38 +162,24 @@ already_AddRefed<nsIInputStream> LoadFil
 
 bool IsSolidColor(SourceSurface* aSurface, BGRAColor aColor,
                   uint8_t aFuzz /* = 0 */) {
   IntSize size = aSurface->GetSize();
   return RectIsSolidColor(aSurface, IntRect(0, 0, size.width, size.height),
                           aColor, aFuzz);
 }
 
-bool IsSolidPalettedColor(Decoder* aDecoder, uint8_t aColor) {
-  RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
-  return PalettedRectIsSolidColor(aDecoder, currentFrame->GetRect(), aColor);
-}
-
 bool RowsAreSolidColor(SourceSurface* aSurface, int32_t aStartRow,
                        int32_t aRowCount, BGRAColor aColor,
                        uint8_t aFuzz /* = 0 */) {
   IntSize size = aSurface->GetSize();
   return RectIsSolidColor(
       aSurface, IntRect(0, aStartRow, size.width, aRowCount), aColor, aFuzz);
 }
 
-bool PalettedRowsAreSolidColor(Decoder* aDecoder, int32_t aStartRow,
-                               int32_t aRowCount, uint8_t aColor) {
-  RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
-  IntRect frameRect = currentFrame->GetRect();
-  IntRect solidColorRect(frameRect.X(), aStartRow, frameRect.Width(),
-                         aRowCount);
-  return PalettedRectIsSolidColor(aDecoder, solidColorRect, aColor);
-}
-
 bool RectIsSolidColor(SourceSurface* aSurface, const IntRect& aRect,
                       BGRAColor aColor, uint8_t aFuzz /* = 0 */) {
   IntSize surfaceSize = aSurface->GetSize();
   IntRect rect =
       aRect.Intersect(IntRect(0, 0, surfaceSize.width, surfaceSize.height));
 
   RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
   ASSERT_TRUE_OR_RETURN(dataSurface != nullptr, false);
@@ -223,52 +209,16 @@ bool RectIsSolidColor(SourceSurface* aSu
         ASSERT_EQ_OR_RETURN(pmColor.mAlpha, data[i + 3], false);
       }
     }
   }
 
   return true;
 }
 
-bool PalettedRectIsSolidColor(Decoder* aDecoder, const IntRect& aRect,
-                              uint8_t aColor) {
-  RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
-  uint8_t* imageData;
-  uint32_t imageLength;
-  currentFrame->GetImageData(&imageData, &imageLength);
-  ASSERT_TRUE_OR_RETURN(imageData, false);
-
-  // Clamp to the frame rect. If any pixels outside the frame rect are included,
-  // we immediately fail, because such pixels don't have any "color" in the
-  // sense this function measures - they're transparent, and that doesn't
-  // necessarily correspond to any color palette index at all.
-  IntRect frameRect = currentFrame->GetRect();
-  ASSERT_EQ_OR_RETURN(imageLength, uint32_t(frameRect.Area()), false);
-  IntRect rect = aRect.Intersect(frameRect);
-  ASSERT_EQ_OR_RETURN(rect.Area(), aRect.Area(), false);
-
-  // Translate |rect| by |frameRect.TopLeft()| to reflect the fact that the
-  // frame rect's offset doesn't actually mean anything in terms of the
-  // in-memory representation of the surface. The image data starts at the upper
-  // left corner of the frame rect, in other words.
-  rect -= frameRect.TopLeft();
-
-  // Walk through the image data and make sure that the entire rect has the
-  // palette index |aColor|.
-  int32_t rowLength = frameRect.Width();
-  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;
-      ASSERT_EQ_OR_RETURN(aColor, imageData[i], false);
-    }
-  }
-
-  return true;
-}
-
 bool RowHasPixels(SourceSurface* aSurface, int32_t aRow,
                   const vector<BGRAColor>& aPixels) {
   ASSERT_GE_OR_RETURN(aRow, 0, false);
 
   IntSize surfaceSize = aSurface->GetSize();
   ASSERT_EQ_OR_RETURN(aPixels.size(), size_t(surfaceSize.width), false);
   ASSERT_LT_OR_RETURN(aRow, surfaceSize.height, false);
 
@@ -367,153 +317,74 @@ void CheckGeneratedSurface(SourceSurface
 
   // Check that the area below the output rect is the outer color. (Region 'E'.)
   const int32_t heightBelow = surfaceSize.height - aRect.YMost();
   EXPECT_TRUE(RectIsSolidColor(
       aSurface, IntRect(0, aRect.YMost(), surfaceSize.width, heightBelow),
       aOuterColor, aFuzz));
 }
 
-void CheckGeneratedPalettedImage(Decoder* aDecoder, const IntRect& aRect) {
-  RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
-  IntSize imageSize = currentFrame->GetSize();
-
-  // This diagram shows how the surface is divided into regions that the code
-  // below tests for the correct content. The output rect is the bounds of the
-  // region labeled 'C'.
-  //
-  // +---------------------------+
-  // |             A             |
-  // +---------+--------+--------+
-  // |    B    |   C    |   D    |
-  // +---------+--------+--------+
-  // |             E             |
-  // +---------------------------+
-
-  // Check that the output rect itself is all 255's. (Region 'C'.)
-  EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, aRect, 255));
-
-  // Check that the area above the output rect is all 0's. (Region 'A'.)
-  EXPECT_TRUE(PalettedRectIsSolidColor(
-      aDecoder, IntRect(0, 0, imageSize.width, aRect.Y()), 0));
-
-  // Check that the area to the left of the output rect is all 0's. (Region
-  // 'B'.)
-  EXPECT_TRUE(PalettedRectIsSolidColor(
-      aDecoder, IntRect(0, aRect.Y(), aRect.X(), aRect.YMost()), 0));
-
-  // Check that the area to the right of the output rect is all 0's. (Region
-  // 'D'.)
-  const int32_t widthOnRight = imageSize.width - aRect.XMost();
-  EXPECT_TRUE(PalettedRectIsSolidColor(
-      aDecoder, IntRect(aRect.XMost(), aRect.Y(), widthOnRight, aRect.YMost()),
-      0));
-
-  // Check that the area below the output rect is transparent. (Region 'E'.)
-  const int32_t heightBelow = imageSize.height - aRect.YMost();
-  EXPECT_TRUE(PalettedRectIsSolidColor(
-      aDecoder, IntRect(0, aRect.YMost(), imageSize.width, heightBelow), 0));
-}
-
 void CheckWritePixels(Decoder* aDecoder, SurfaceFilter* aFilter,
                       const Maybe<IntRect>& aOutputRect /* = Nothing() */,
                       const Maybe<IntRect>& aInputRect /* = Nothing() */,
                       const Maybe<IntRect>& aInputWriteRect /* = Nothing() */,
                       const Maybe<IntRect>& aOutputWriteRect /* = Nothing() */,
                       uint8_t aFuzz /* = 0 */) {
-  IntRect outputRect = aOutputRect.valueOr(IntRect(0, 0, 100, 100));
-  IntRect inputRect = aInputRect.valueOr(IntRect(0, 0, 100, 100));
-  IntRect inputWriteRect = aInputWriteRect.valueOr(inputRect);
-  IntRect outputWriteRect = aOutputWriteRect.valueOr(outputRect);
-
-  // Fill the image.
-  int32_t count = 0;
-  auto result = aFilter->WritePixels<uint32_t>([&] {
-    ++count;
-    return AsVariant(BGRAColor::Green().AsPixel());
-  });
-  EXPECT_EQ(WriteState::FINISHED, result);
-  EXPECT_EQ(inputWriteRect.Width() * inputWriteRect.Height(), count);
-
-  AssertCorrectPipelineFinalState(aFilter, inputRect, outputRect);
-
-  // Attempt to write more data and make sure nothing changes.
-  const int32_t oldCount = count;
-  result = aFilter->WritePixels<uint32_t>([&] {
-    ++count;
-    return AsVariant(BGRAColor::Green().AsPixel());
-  });
-  EXPECT_EQ(oldCount, count);
-  EXPECT_EQ(WriteState::FINISHED, result);
-  EXPECT_TRUE(aFilter->IsSurfaceFinished());
-  Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
-  EXPECT_TRUE(invalidRect.isNothing());
-
-  // Attempt to advance to the next row and make sure nothing changes.
-  aFilter->AdvanceRow();
-  EXPECT_TRUE(aFilter->IsSurfaceFinished());
-  invalidRect = aFilter->TakeInvalidRect();
-  EXPECT_TRUE(invalidRect.isNothing());
-
-  // Check that the generated image is correct.
-  CheckGeneratedImage(aDecoder, outputWriteRect, aFuzz);
+  CheckTransformedWritePixels(aDecoder, aFilter, BGRAColor::Green(),
+                              BGRAColor::Green(), aOutputRect, aInputRect,
+                              aInputWriteRect, aOutputWriteRect, aFuzz);
 }
 
-void CheckPalettedWritePixels(
-    Decoder* aDecoder, SurfaceFilter* aFilter,
+void CheckTransformedWritePixels(
+    Decoder* aDecoder, SurfaceFilter* aFilter, const BGRAColor& aInputColor,
+    const BGRAColor& aOutputColor,
     const Maybe<IntRect>& aOutputRect /* = Nothing() */,
     const Maybe<IntRect>& aInputRect /* = Nothing() */,
     const Maybe<IntRect>& aInputWriteRect /* = Nothing() */,
     const Maybe<IntRect>& aOutputWriteRect /* = Nothing() */,
     uint8_t aFuzz /* = 0 */) {
   IntRect outputRect = aOutputRect.valueOr(IntRect(0, 0, 100, 100));
   IntRect inputRect = aInputRect.valueOr(IntRect(0, 0, 100, 100));
   IntRect inputWriteRect = aInputWriteRect.valueOr(inputRect);
   IntRect outputWriteRect = aOutputWriteRect.valueOr(outputRect);
 
   // Fill the image.
   int32_t count = 0;
-  auto result = aFilter->WritePixels<uint8_t>([&] {
+  auto result = aFilter->WritePixels<uint32_t>([&] {
     ++count;
-    return AsVariant(uint8_t(255));
+    return AsVariant(aInputColor.AsPixel());
   });
   EXPECT_EQ(WriteState::FINISHED, result);
   EXPECT_EQ(inputWriteRect.Width() * inputWriteRect.Height(), count);
 
   AssertCorrectPipelineFinalState(aFilter, inputRect, outputRect);
 
   // Attempt to write more data and make sure nothing changes.
   const int32_t oldCount = count;
-  result = aFilter->WritePixels<uint8_t>([&] {
+  result = aFilter->WritePixels<uint32_t>([&] {
     ++count;
-    return AsVariant(uint8_t(255));
+    return AsVariant(aInputColor.AsPixel());
   });
   EXPECT_EQ(oldCount, count);
   EXPECT_EQ(WriteState::FINISHED, result);
   EXPECT_TRUE(aFilter->IsSurfaceFinished());
   Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
   EXPECT_TRUE(invalidRect.isNothing());
 
   // Attempt to advance to the next row and make sure nothing changes.
   aFilter->AdvanceRow();
   EXPECT_TRUE(aFilter->IsSurfaceFinished());
   invalidRect = aFilter->TakeInvalidRect();
   EXPECT_TRUE(invalidRect.isNothing());
 
   // Check that the generated image is correct.
   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
-  uint8_t* imageData;
-  uint32_t imageLength;
-  currentFrame->GetImageData(&imageData, &imageLength);
-  ASSERT_TRUE(imageData != nullptr);
-  ASSERT_EQ(outputWriteRect.Width() * outputWriteRect.Height(),
-            int32_t(imageLength));
-  for (uint32_t i = 0; i < imageLength; ++i) {
-    ASSERT_EQ(uint8_t(255), imageData[i]);
-  }
+  RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
+  CheckGeneratedSurface(surface, outputWriteRect, aOutputColor,
+                        BGRAColor::Transparent(), aFuzz);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Test Data
 ///////////////////////////////////////////////////////////////////////////////
 
 ImageTestCase GreenPNGTestCase() {
   return ImageTestCase("green.png", "image/png", IntSize(100, 100));
--- a/image/test/gtest/Common.h
+++ b/image/test/gtest/Common.h
@@ -154,57 +154,37 @@ already_AddRefed<nsIInputStream> LoadFil
  * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
  * component. This may be necessary for tests that involve JPEG images or
  * downscaling.
  */
 bool IsSolidColor(gfx::SourceSurface* aSurface, BGRAColor aColor,
                   uint8_t aFuzz = 0);
 
 /**
- * @returns true if every pixel of @aDecoder's surface has the palette index
- * specified by @aColor.
- */
-bool IsSolidPalettedColor(Decoder* aDecoder, uint8_t aColor);
-
-/**
  * @returns true if every pixel in the range of rows specified by @aStartRow and
  * @aRowCount of @aSurface is @aColor.
  *
  * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
  * component. This may be necessary for tests that involve JPEG images or
  * downscaling.
  */
 bool RowsAreSolidColor(gfx::SourceSurface* aSurface, int32_t aStartRow,
                        int32_t aRowCount, BGRAColor aColor, uint8_t aFuzz = 0);
 
 /**
- * @returns true if every pixel in the range of rows specified by @aStartRow and
- * @aRowCount of @aDecoder's surface has the palette index specified by @aColor.
- */
-bool PalettedRowsAreSolidColor(Decoder* aDecoder, int32_t aStartRow,
-                               int32_t aRowCount, uint8_t aColor);
-
-/**
  * @returns true if every pixel in the rect specified by @aRect is @aColor.
  *
  * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
  * component. This may be necessary for tests that involve JPEG images or
  * downscaling.
  */
 bool RectIsSolidColor(gfx::SourceSurface* aSurface, const gfx::IntRect& aRect,
                       BGRAColor aColor, uint8_t aFuzz = 0);
 
 /**
- * @returns true if every pixel in the rect specified by @aRect has the palette
- * index specified by @aColor.
- */
-bool PalettedRectIsSolidColor(Decoder* aDecoder, const gfx::IntRect& aRect,
-                              uint8_t aColor);
-
-/**
  * @returns true if the pixels in @aRow of @aSurface match the pixels given in
  * @aPixels.
  */
 bool RowHasPixels(gfx::SourceSurface* aSurface, int32_t aRow,
                   const std::vector<BGRAColor>& aPixels);
 
 // ExpectNoResume is an IResumable implementation for use by tests that expect
 // Resume() to never get called.
@@ -239,19 +219,19 @@ class CountResumes : public IResumable {
 // SurfacePipe Helpers
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Creates a decoder with no data associated with, suitable for testing code
  * that requires a decoder to initialize or to allocate surfaces but doesn't
  * actually need the decoder to do any decoding.
  *
- * XXX(seth): We only need this because SurfaceSink and PalettedSurfaceSink
- * defer to the decoder for surface allocation. Once all decoders use
- * SurfacePipe we won't need to do that anymore and we can remove this function.
+ * XXX(seth): We only need this because SurfaceSink defer to the decoder for
+ * surface allocation. Once all decoders use SurfacePipe we won't need to do
+ * that anymore and we can remove this function.
  */
 already_AddRefed<Decoder> CreateTrivialDecoder();
 
 /**
  * Creates a pipeline of SurfaceFilters from a list of Config structs and passes
  * it to the provided lambda @aFunc. Assertions that the pipeline is constructly
  * correctly and cleanup of any allocated surfaces is handled automatically.
  *
@@ -345,29 +325,16 @@ void CheckGeneratedImage(Decoder* aDecod
  * @param aFuzz The amount of fuzz to use in pixel comparisons.
  */
 void CheckGeneratedSurface(gfx::SourceSurface* aSurface,
                            const gfx::IntRect& aRect,
                            const BGRAColor& aInnerColor,
                            const BGRAColor& aOuterColor, uint8_t aFuzz = 0);
 
 /**
- * Checks a generated paletted image for correctness. Reports any unexpected
- * deviation from the expected image as GTest failures.
- *
- * @param aDecoder The decoder which contains the image. The decoder's current
- *                 frame will be checked.
- * @param aRect The region in the space of the output surface that the filter
- *              pipeline will actually write to. It's expected that pixels in
- *              this region have a palette index of 255, while pixels outside
- *              this region have a palette index of 0.
- */
-void CheckGeneratedPalettedImage(Decoder* aDecoder, const gfx::IntRect& aRect);
-
-/**
  * Tests the result of calling WritePixels() using the provided SurfaceFilter
  * pipeline. The pipeline must be a normal (i.e., non-paletted) pipeline.
  *
  * The arguments are specified in the an order intended to minimize the number
  * of arguments that most test cases need to pass.
  *
  * @param aDecoder The decoder whose current frame will be written to.
  * @param aFilter The SurfaceFilter pipeline to use.
@@ -394,21 +361,23 @@ void CheckWritePixels(Decoder* aDecoder,
                       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);
 
 /**
  * Tests the result of calling WritePixels() using the provided SurfaceFilter
- * pipeline. The pipeline must be a paletted pipeline.
+ * pipeline. Allows for control over the input color to write, and the expected
+ * output color.
  * @see CheckWritePixels() for documentation of the arguments.
  */
-void CheckPalettedWritePixels(
-    Decoder* aDecoder, SurfaceFilter* aFilter,
+void CheckTransformedWritePixels(
+    Decoder* aDecoder, SurfaceFilter* aFilter, const BGRAColor& aInputColor,
+    const BGRAColor& aOutputColor,
     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
 ///////////////////////////////////////////////////////////////////////////////
new file mode 100644
--- /dev/null
+++ b/image/test/gtest/TestSwizzleFilter.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/gfx/2D.h"
+#include "Common.h"
+#include "Decoder.h"
+#include "DecoderFactory.h"
+#include "SurfaceFilters.h"
+#include "SurfacePipe.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::image;
+
+template <typename Func>
+void WithSwizzleFilter(const IntSize& aSize, SurfaceFormat aInputFormat,
+                       SurfaceFormat aOutputFormat, bool aPremultiplyAlpha,
+                       Func aFunc) {
+  RefPtr<Decoder> decoder = CreateTrivialDecoder();
+  ASSERT_TRUE(decoder != nullptr);
+
+  WithFilterPipeline(
+      decoder, std::forward<Func>(aFunc),
+      SwizzleConfig{aInputFormat, aOutputFormat, aPremultiplyAlpha},
+      SurfaceConfig{decoder, aSize, aOutputFormat, false});
+}
+
+TEST(ImageSwizzleFilter, WritePixels_RGBA_to_BGRA)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::R8G8B8A8, SurfaceFormat::B8G8R8A8,
+      false, [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(aDecoder, aFilter, BGRAColor::Blue(),
+                                    BGRAColor::Red());
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_RGBA_to_Premultiplied_BGRA)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::R8G8B8A8, SurfaceFormat::B8G8R8A8, true,
+      [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(
+            aDecoder, aFilter, BGRAColor(0x26, 0x00, 0x00, 0x7F, true),
+            BGRAColor(0x00, 0x00, 0x26, 0x7F), Nothing(), Nothing(), Nothing(),
+            Nothing(), /* aFuzz */ 1);
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_RGBA_to_BGRX)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::R8G8B8A8, SurfaceFormat::B8G8R8X8,
+      false, [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(aDecoder, aFilter,
+                                    BGRAColor(0x26, 0x00, 0x00, 0x7F, true),
+                                    BGRAColor(0x00, 0x00, 0x26, 0xFF));
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_RGBA_to_Premultiplied_BGRX)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::R8G8B8A8, SurfaceFormat::B8G8R8X8, true,
+      [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(aDecoder, aFilter,
+                                    BGRAColor(0x26, 0x00, 0x00, 0x7F, true),
+                                    BGRAColor(0x00, 0x00, 0x13, 0xFF));
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_RGBA_to_RGBX)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::R8G8B8A8, SurfaceFormat::R8G8B8X8,
+      false, [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(aDecoder, aFilter,
+                                    BGRAColor(0x00, 0x00, 0x26, 0x7F, true),
+                                    BGRAColor(0x00, 0x00, 0x26, 0xFF));
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_RGBA_to_Premultiplied_RGRX)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::R8G8B8A8, SurfaceFormat::R8G8B8X8, true,
+      [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(aDecoder, aFilter,
+                                    BGRAColor(0x00, 0x00, 0x26, 0x7F, true),
+                                    BGRAColor(0x00, 0x00, 0x13, 0xFF));
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_BGRA_to_BGRX)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8X8,
+      false, [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(aDecoder, aFilter,
+                                    BGRAColor(0x10, 0x26, 0x00, 0x7F, true),
+                                    BGRAColor(0x10, 0x26, 0x00, 0xFF));
+      });
+}
+
+TEST(ImageSwizzleFilter, WritePixels_BGRA_to_Premultiplied_BGRA)
+{
+  WithSwizzleFilter(
+      IntSize(100, 100), SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8, true,
+      [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+        CheckTransformedWritePixels(
+            aDecoder, aFilter, BGRAColor(0x10, 0x26, 0x00, 0x7F, true),
+            BGRAColor(0x10, 0x26, 0x00, 0x7F), Nothing(), Nothing(), Nothing(),
+            Nothing(), /* aFuzz */ 1);
+      });
+}
--- a/image/test/gtest/moz.build
+++ b/image/test/gtest/moz.build
@@ -14,16 +14,17 @@ UNIFIED_SOURCES = [
     'TestContainers.cpp',
     'TestCopyOnWrite.cpp',
     'TestDeinterlacingFilter.cpp',
     'TestFrameAnimator.cpp',
     'TestLoader.cpp',
     'TestRemoveFrameRectFilter.cpp',
     'TestStreamingLexer.cpp',
     'TestSurfaceSink.cpp',
+    'TestSwizzleFilter.cpp',
 ]
 
 # skip the test on windows10-aarch64, aarch64 due to 1544961
 if not(CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'aarch64'):
     UNIFIED_SOURCES += [
         'TestDecoders.cpp',
         'TestDecodersPerf.cpp',
         'TestDecodeToSurface.cpp',