Bug 1359527. Add DrawTarget::IntoLuminance. r=jrmuizel,Bas
☠☠ backed out by 75b68c6105e1 ☠ ☠
authorMason Chang <mcang@mozilla.com>
Sun, 28 May 2017 15:27:59 -0400
changeset 409190 e12fa325e11235635eaa49d553f89b8023e26c98
parent 409178 d1743afbf7ac41e11a73e7e765904150e3b3c426
child 409191 7660dd818a354f9d47f9eaa333b1a9548fe0bac7
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, Bas
bugs1359527
milestone55.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 1359527. Add DrawTarget::IntoLuminance. r=jrmuizel,Bas This lets us use the luminance effect with Direct2D to avoid having to read back. It will also let us avoid reading back with DrawTargetRecording.
gfx/2d/2D.h
gfx/2d/DrawTarget.cpp
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
gfx/2d/LuminanceNEON.cpp
gfx/2d/LuminanceNEON.h
gfx/2d/Types.h
gfx/2d/moz.build
layout/svg/moz.build
layout/svg/nsSVGMaskFrame.cpp
layout/svg/nsSVGMaskFrame.h
layout/svg/nsSVGMaskFrameNEON.cpp
layout/svg/nsSVGMaskFrameNEON.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -889,16 +889,21 @@ public:
   virtual bool IsCaptureDT() const { return false; }
 
   /**
    * Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget.
    * Multiple calls to Snapshot() without any drawing operations in between will
    * normally return the same SourceSurface object.
    */
   virtual already_AddRefed<SourceSurface> Snapshot() = 0;
+
+  // Snapshots the contents and returns an alpha mask
+  // based on the RGB values.
+  virtual already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
+                                                              float aOpacity);
   virtual IntSize GetSize() = 0;
 
   /**
    * If possible returns the bits to this DrawTarget for direct manipulation. While
    * the bits is locked any modifications to this DrawTarget is forbidden.
    * Release takes the original data pointer for safety.
    */
   virtual bool LockBits(uint8_t** aData, IntSize* aSize,
--- a/gfx/2d/DrawTarget.cpp
+++ b/gfx/2d/DrawTarget.cpp
@@ -4,19 +4,182 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "2D.h"
 #include "Logging.h"
 #include "PathHelpers.h"
 
 #include "DrawTargetCapture.h"
 
+#ifdef BUILD_ARM_NEON
+#include "mozilla/arm.h"
+#include "LuminanceNEON.h"
+#endif
+
 namespace mozilla {
 namespace gfx {
 
+/**
+ * Byte offsets of channels in a native packed gfxColor or cairo image surface.
+ */
+#ifdef IS_BIG_ENDIAN
+#define GFX_ARGB32_OFFSET_A 0
+#define GFX_ARGB32_OFFSET_R 1
+#define GFX_ARGB32_OFFSET_G 2
+#define GFX_ARGB32_OFFSET_B 3
+#else
+#define GFX_ARGB32_OFFSET_A 3
+#define GFX_ARGB32_OFFSET_R 2
+#define GFX_ARGB32_OFFSET_G 1
+#define GFX_ARGB32_OFFSET_B 0
+#endif
+
+// c = n / 255
+// c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
+static const uint8_t gsRGBToLinearRGBMap[256] = {
+  0,   0,   0,   0,   0,   0,   0,   1,
+  1,   1,   1,   1,   1,   1,   1,   1,
+  1,   1,   2,   2,   2,   2,   2,   2,
+  2,   2,   3,   3,   3,   3,   3,   3,
+  4,   4,   4,   4,   4,   5,   5,   5,
+  5,   6,   6,   6,   6,   7,   7,   7,
+  8,   8,   8,   8,   9,   9,   9,  10,
+ 10,  10,  11,  11,  12,  12,  12,  13,
+ 13,  13,  14,  14,  15,  15,  16,  16,
+ 17,  17,  17,  18,  18,  19,  19,  20,
+ 20,  21,  22,  22,  23,  23,  24,  24,
+ 25,  25,  26,  27,  27,  28,  29,  29,
+ 30,  30,  31,  32,  32,  33,  34,  35,
+ 35,  36,  37,  37,  38,  39,  40,  41,
+ 41,  42,  43,  44,  45,  45,  46,  47,
+ 48,  49,  50,  51,  51,  52,  53,  54,
+ 55,  56,  57,  58,  59,  60,  61,  62,
+ 63,  64,  65,  66,  67,  68,  69,  70,
+ 71,  72,  73,  74,  76,  77,  78,  79,
+ 80,  81,  82,  84,  85,  86,  87,  88,
+ 90,  91,  92,  93,  95,  96,  97,  99,
+100, 101, 103, 104, 105, 107, 108, 109,
+111, 112, 114, 115, 116, 118, 119, 121,
+122, 124, 125, 127, 128, 130, 131, 133,
+134, 136, 138, 139, 141, 142, 144, 146,
+147, 149, 151, 152, 154, 156, 157, 159,
+161, 163, 164, 166, 168, 170, 171, 173,
+175, 177, 179, 181, 183, 184, 186, 188,
+190, 192, 194, 196, 198, 200, 202, 204,
+206, 208, 210, 212, 214, 216, 218, 220,
+222, 224, 226, 229, 231, 233, 235, 237,
+239, 242, 244, 246, 248, 250, 253, 255
+};
+
+static void
+ComputesRGBLuminanceMask(const uint8_t *aSourceData,
+                         int32_t aSourceStride,
+                         uint8_t *aDestData,
+                         int32_t aDestStride,
+                         const IntSize &aSize,
+                         float aOpacity)
+{
+#ifdef BUILD_ARM_NEON
+  if (mozilla::supports_neon()) {
+    ComputesRGBLuminanceMask_NEON(aSourceData, aSourceStride,
+                                  aDestData, aDestStride,
+                                  aSize, aOpacity);
+    return;
+  }
+#endif
+
+  int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
+  int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
+  int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
+  int32_t sourceOffset = aSourceStride - 4 * aSize.width;
+  const uint8_t *sourcePixel = aSourceData;
+  int32_t destOffset = aDestStride - aSize.width;
+  uint8_t *destPixel = aDestData;
+
+  for (int32_t y = 0; y < aSize.height; y++) {
+    for (int32_t x = 0; x < aSize.width; x++) {
+      uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
+
+      if (a) {
+        *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] +
+                      greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
+                      blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
+      } else {
+        *destPixel = 0;
+      }
+      sourcePixel += 4;
+      destPixel++;
+    }
+    sourcePixel += sourceOffset;
+    destPixel += destOffset;
+  }
+}
+
+static void
+ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData,
+                              int32_t aSourceStride,
+                              uint8_t *aDestData,
+                              int32_t aDestStride,
+                              const IntSize &aSize,
+                              float aOpacity)
+{
+  int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
+  int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
+  int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
+  int32_t sourceOffset = aSourceStride - 4 * aSize.width;
+  const uint8_t *sourcePixel = aSourceData;
+  int32_t destOffset = aDestStride - aSize.width;
+  uint8_t *destPixel = aDestData;
+
+  for (int32_t y = 0; y < aSize.height; y++) {
+    for (int32_t x = 0; x < aSize.width; x++) {
+      uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
+
+      // unpremultiply
+      if (a) {
+        if (a == 255) {
+          /* sRGB -> linearRGB -> intensity */
+          *destPixel =
+            static_cast<uint8_t>
+                       ((gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] *
+                         redFactor +
+                         gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] *
+                         greenFactor +
+                         gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] *
+                         blueFactor) >> 8);
+        } else {
+          uint8_t tempPixel[4];
+          tempPixel[GFX_ARGB32_OFFSET_B] =
+            (255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a;
+          tempPixel[GFX_ARGB32_OFFSET_G] =
+            (255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a;
+          tempPixel[GFX_ARGB32_OFFSET_R] =
+            (255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a;
+
+          /* sRGB -> linearRGB -> intensity */
+          *destPixel =
+            static_cast<uint8_t>
+                       (((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] *
+                          redFactor +
+                          gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] *
+                          greenFactor +
+                          gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] *
+                          blueFactor) >> 8) * (a / 255.0f));
+        }
+      } else {
+        *destPixel = 0;
+      }
+      sourcePixel += 4;
+      destPixel++;
+    }
+    sourcePixel += sourceOffset;
+    destPixel += destOffset;
+  }
+}
+
 already_AddRefed<DrawTargetCapture>
 DrawTarget::CreateCaptureDT(const IntSize& aSize)
 {
   RefPtr<DrawTargetCaptureImpl> dt = new DrawTargetCaptureImpl();
 
   if (!dt->Init(aSize, this)) {
     gfxWarning() << "Failed to initialize Capture DrawTarget!";
     return nullptr;
@@ -59,11 +222,56 @@ DrawTarget::StrokeGlyphs(ScaledFont* aFo
                          const StrokeOptions& aStrokeOptions,
                          const DrawOptions& aOptions,
                          const GlyphRenderingOptions* aRenderingOptions)
 {
   RefPtr<Path> path = aFont->GetPathForGlyphs(aBuffer, this);
   Stroke(path, aPattern, aStrokeOptions, aOptions);
 }
 
+already_AddRefed<SourceSurface>
+DrawTarget::IntoLuminanceSource(LuminanceType aMaskType, float aOpacity)
+{
+  RefPtr<SourceSurface> surface = Snapshot();
+  IntSize size = surface->GetSize();
+
+  RefPtr<DataSourceSurface> maskSurface = surface->GetDataSurface();
+  DataSourceSurface::MappedSurface map;
+  if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+    return nullptr;
+  }
+
+  // Create alpha channel mask for output
+  RefPtr<DataSourceSurface> destMaskSurface =
+    Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
+  if (!destMaskSurface) {
+    return nullptr;
+  }
+  DataSourceSurface::MappedSurface destMap;
+  if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+    return nullptr;
+  }
+
+  switch (aMaskType) {
+    case LuminanceType::LUMINANCE:
+    {
+      ComputesRGBLuminanceMask(map.mData, map.mStride,
+                               destMap.mData, destMap.mStride,
+                               size, aOpacity);
+      break;
+    }
+    case LuminanceType::LINEARRGB:
+    {
+      ComputeLinearRGBLuminanceMask(map.mData, map.mStride,
+                                    destMap.mData, destMap.mStride,
+                                    size, aOpacity);
+      break;
+    }
+  }
+
+  maskSurface->Unmap();
+  destMaskSurface->Unmap();
+
+  return destMaskSurface.forget();
+}
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -94,16 +94,47 @@ DrawTargetD2D1::Snapshot()
   Flush();
 
   mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
 
   RefPtr<SourceSurface> snapshot(mSnapshot);
   return snapshot.forget();
 }
 
+void
+DrawTargetD2D1::EnsureLuminanceEffect()
+{
+  if (mLuminanceEffect.get()) {
+    return;
+  }
+
+  HRESULT hr = mDC->CreateEffect(CLSID_D2D1LuminanceToAlpha,
+                                 getter_AddRefs(mLuminanceEffect));
+  if (FAILED(hr)) {
+    gfxWarning() << "Failed to create luminance effect. Code: " << hexa(hr);
+  }
+}
+
+already_AddRefed<SourceSurface>
+DrawTargetD2D1::IntoLuminanceSource(LuminanceType aLuminanceType, float aOpacity)
+{
+  if (aLuminanceType != LuminanceType::LUMINANCE) {
+    return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
+  }
+
+  // Create the luminance effect
+  EnsureLuminanceEffect();
+  mLuminanceEffect->SetInput(0, mBitmap);
+
+  RefPtr<ID2D1Image> luminanceOutput;
+  mLuminanceEffect->GetOutput(getter_AddRefs(luminanceOutput));
+
+ return MakeAndAddRef<SourceSurfaceD2D1>(luminanceOutput, mDC, SurfaceFormat::A8, mSize);
+}
+
 // Command lists are kept around by device contexts until EndDraw is called,
 // this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
 // are expensive though, especially relatively when little work is done, so
 // we try to reduce the amount of times we execute these purges.
 static const uint32_t kPushedLayersBeforePurge = 25;
 
 void
 DrawTargetD2D1::Flush()
@@ -815,36 +846,37 @@ DrawTargetD2D1::PushLayer(bool aOpaque, 
 
   if (aOpaque) {
     options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
   }
   if (aCopyBackground) {
     options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
   }
 
-  RefPtr<ID2D1BitmapBrush> mask;
-
+  RefPtr<ID2D1ImageBrush> mask;
   Matrix maskTransform = aMaskTransform;
+  RefPtr<ID2D1PathGeometry> clip;
 
-  RefPtr<ID2D1PathGeometry> clip;
   if (aMask) {
+    RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
     mDC->SetTransform(D2D1::IdentityMatrix());
     mTransformDirty = true;
 
-    RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
-
     // The mask is given in user space. Our layer will apply it in device space.
     maskTransform = maskTransform * mTransform;
 
     if (image) {
-      RefPtr<ID2D1Bitmap> bitmap;
-      image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
-
-      mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask));
-      MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface.
+      IntSize maskSize = aMask->GetSize();
+      HRESULT hr = mDC->CreateImageBrush(image,
+                                         D2D1::ImageBrushProperties(D2D1::RectF(0, 0, maskSize.width, maskSize.height)),
+                                         D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)),
+                                         getter_AddRefs(mask));
+      if (FAILED(hr)) {
+        gfxWarning() <<"[D2D1.1] Failed to create a ImageBrush, code: " << hexa(hr);
+      }
 
       factory()->CreatePathGeometry(getter_AddRefs(clip));
       RefPtr<ID2D1GeometrySink> sink;
       clip->Open(getter_AddRefs(sink));
       AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height));
       sink->Close();
     } else {
       gfxCriticalError() << "Failed to get image for mask surface!";
@@ -1864,17 +1896,16 @@ DrawTargetD2D1::CreateBrushForPattern(co
 }
 
 already_AddRefed<ID2D1Image>
 DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
                                    ExtendMode aExtendMode, const IntRect* aSourceRect,
                                    bool aUserSpace)
 {
   RefPtr<ID2D1Image> image;
-
   switch (aSurface->GetType()) {
   case SurfaceType::D2D1_1_IMAGE:
     {
       SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface);
       image = surf->GetImage();
       AddDependencyOnSource(surf);
     }
     break;
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -31,16 +31,18 @@ class DrawTargetD2D1 : public DrawTarget
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetD2D1, override)
   DrawTargetD2D1();
   virtual ~DrawTargetD2D1();
 
   virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
   virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
   virtual already_AddRefed<SourceSurface> Snapshot() override;
+  virtual already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
+                                                              float aOpacity) override;
   virtual IntSize GetSize() override { return mSize; }
 
   virtual void Flush() override;
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
                            const DrawSurfaceOptions &aSurfOptions,
                            const DrawOptions &aOptions) override;
@@ -290,14 +292,18 @@ private:
   // If we resolve the current command list before this happens
   // we can avoid the subsequent hang. (See bug 1293586)
   bool mDidComplexBlendWithListInList;
 
   static ID2D1Factory1 *mFactory;
   static IDWriteFactory *mDWriteFactory;
   // This value is uesed to verify if the DrawTarget is created by a stale device.
   uint32_t mDeviceSeq;
+
+  // List of effects we use
+  void EnsureLuminanceEffect();
+  RefPtr<ID2D1Effect> mLuminanceEffect;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
new file mode 100644
--- /dev/null
+++ b/gfx/2d/LuminanceNEON.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 <arm_neon.h>
+#include "LuminanceNEON.h"
+
+using namespace mozilla::gfx;
+
+/**
+ * Byte offsets of channels in a native packed gfxColor or cairo image surface.
+ */
+#ifdef IS_BIG_ENDIAN
+#define GFX_ARGB32_OFFSET_A 0
+#define GFX_ARGB32_OFFSET_R 1
+#define GFX_ARGB32_OFFSET_G 2
+#define GFX_ARGB32_OFFSET_B 3
+#else
+#define GFX_ARGB32_OFFSET_A 3
+#define GFX_ARGB32_OFFSET_R 2
+#define GFX_ARGB32_OFFSET_G 1
+#define GFX_ARGB32_OFFSET_B 0
+#endif
+
+
+void
+ComputesRGBLuminanceMask_NEON(const uint8_t *aSourceData,
+                              int32_t aSourceStride,
+                              uint8_t *aDestData,
+                              int32_t aDestStride,
+                              const IntSize &aSize,
+                              float aOpacity)
+{
+  int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
+  int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
+  int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
+  const uint8_t *sourcePixel = aSourceData;
+  int32_t sourceOffset = aSourceStride - 4 * aSize.width;
+  uint8_t *destPixel = aDestData;
+  int32_t destOffset = aDestStride - aSize.width;
+
+  sourcePixel = aSourceData;
+  int32_t remainderWidth = aSize.width % 8;
+  int32_t roundedWidth = aSize.width - remainderWidth;
+  uint16x8_t temp;
+  uint8x8_t gray;
+  uint8x8_t redVector = vdup_n_u8(redFactor);
+  uint8x8_t greenVector = vdup_n_u8(greenFactor);
+  uint8x8_t blueVector = vdup_n_u8(blueFactor);
+  uint8x8_t fullBitVector = vdup_n_u8(255);
+  uint8x8_t oneVector = vdup_n_u8(1);
+  for (int32_t y = 0; y < aSize.height; y++) {
+    // Calculate luminance by neon with 8 pixels per loop
+    for (int32_t x = 0; x < roundedWidth; x += 8) {
+      uint8x8x4_t argb  = vld4_u8(sourcePixel);
+      temp = vmull_u8(argb.val[GFX_ARGB32_OFFSET_R], redVector); // temp = red * redFactor
+      temp = vmlal_u8(temp, argb.val[GFX_ARGB32_OFFSET_G], greenVector); // temp += green * greenFactor
+      temp = vmlal_u8(temp, argb.val[GFX_ARGB32_OFFSET_B], blueVector); // temp += blue * blueFactor
+      gray = vshrn_n_u16(temp, 8); // gray = temp >> 8
+
+      // Check alpha value
+      uint8x8_t alphaVector = vtst_u8(argb.val[GFX_ARGB32_OFFSET_A], fullBitVector);
+      gray = vmul_u8(gray, vand_u8(alphaVector, oneVector));
+
+      // Put the result to the 8 pixels
+      vst1_u8(destPixel, gray);
+      sourcePixel += 8 * 4;
+      destPixel += 8;
+    }
+
+    // Calculate the rest pixels of the line by cpu
+    for (int32_t x = 0; x < remainderWidth; x++) {
+      if (sourcePixel[GFX_ARGB32_OFFSET_A] > 0) {
+        *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R]+
+                      greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
+                      blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
+      } else {
+        *destPixel = 0;
+      }
+      sourcePixel += 4;
+      destPixel++;
+    }
+    sourcePixel += sourceOffset;
+    destPixel += destOffset;
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/2d/LuminanceNEON.h
@@ -0,0 +1,19 @@
+/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef __LUMINANCENEON_H__
+#define __LUMINANCENEON_H__
+
+#include "mozilla/gfx/Point.h"
+
+void
+ComputesRGBLuminanceMask_NEON(const uint8_t *aSourceData,
+                              int32_t aSourceStride,
+                              uint8_t *aDestData,
+                              int32_t aDestStride,
+                              const mozilla::gfx::IntSize &aSize,
+                              float aOpacity);
+
+#endif /* __LUMINANCENEON_H__ */
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -273,16 +273,22 @@ enum class CapStyle : int8_t {
   SQUARE
 };
 
 enum class SamplingBounds : int8_t {
   UNBOUNDED,
   BOUNDED
 };
 
+// Moz2d version for SVG mask types
+enum class LuminanceType : int8_t {
+  LUMINANCE,
+  LINEARRGB,
+};
+
 /* Color is stored in non-premultiplied form */
 struct Color
 {
 public:
   Color()
     : r(0.0f), g(0.0f), b(0.0f), a(0.0f)
   {}
   Color(Float aR, Float aG, Float aB, Float aA)
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -215,19 +215,21 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     SOURCES += [
         'MacIOSurface.cpp',
         'QuartzSupport.mm',
     ]
 
 if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
     SOURCES += [
         'BlurNEON.cpp',
+        'LuminanceNEON.cpp',
         'SwizzleNEON.cpp',
     ]
     SOURCES['BlurNEON.cpp'].flags += CONFIG['NEON_FLAGS']
+    SOURCES['LuminanceNEON.cpp'].flags += CONFIG['NEON_FLAGS']
     SOURCES['SwizzleNEON.cpp'].flags += CONFIG['NEON_FLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 for var in ('USE_CAIRO', 'MOZ2D_HAS_MOZ_CAIRO'):
     DEFINES[var] = True
--- a/layout/svg/moz.build
+++ b/layout/svg/moz.build
@@ -61,20 +61,16 @@ UNIFIED_SOURCES += [
     'SVGFELeafFrame.cpp',
     'SVGFEUnstyledLeafFrame.cpp',
     'SVGGeometryFrame.cpp',
     'SVGImageContext.cpp',
     'SVGTextFrame.cpp',
     'SVGViewFrame.cpp',
 ]
 
-if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
-    SOURCES += ['nsSVGMaskFrameNEON.cpp']
-    SOURCES['nsSVGMaskFrameNEON.cpp'].flags += CONFIG['NEON_FLAGS']
-
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../../widget',
     '../base',
     '../generic',
     '../painting',
     '../style',
     '../xul',
--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -9,180 +9,49 @@
 // Keep others in (case-insensitive) order:
 #include "AutoReferenceChainGuard.h"
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsSVGEffects.h"
 #include "mozilla/dom/SVGMaskElement.h"
-#ifdef BUILD_ARM_NEON
-#include "mozilla/arm.h"
-#include "nsSVGMaskFrameNEON.h"
-#endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
-// c = n / 255
-// c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
-static const uint8_t gsRGBToLinearRGBMap[256] = {
-  0,   0,   0,   0,   0,   0,   0,   1,
-  1,   1,   1,   1,   1,   1,   1,   1,
-  1,   1,   2,   2,   2,   2,   2,   2,
-  2,   2,   3,   3,   3,   3,   3,   3,
-  4,   4,   4,   4,   4,   5,   5,   5,
-  5,   6,   6,   6,   6,   7,   7,   7,
-  8,   8,   8,   8,   9,   9,   9,  10,
- 10,  10,  11,  11,  12,  12,  12,  13,
- 13,  13,  14,  14,  15,  15,  16,  16,
- 17,  17,  17,  18,  18,  19,  19,  20,
- 20,  21,  22,  22,  23,  23,  24,  24,
- 25,  25,  26,  27,  27,  28,  29,  29,
- 30,  30,  31,  32,  32,  33,  34,  35,
- 35,  36,  37,  37,  38,  39,  40,  41,
- 41,  42,  43,  44,  45,  45,  46,  47,
- 48,  49,  50,  51,  51,  52,  53,  54,
- 55,  56,  57,  58,  59,  60,  61,  62,
- 63,  64,  65,  66,  67,  68,  69,  70,
- 71,  72,  73,  74,  76,  77,  78,  79,
- 80,  81,  82,  84,  85,  86,  87,  88,
- 90,  91,  92,  93,  95,  96,  97,  99,
-100, 101, 103, 104, 105, 107, 108, 109,
-111, 112, 114, 115, 116, 118, 119, 121,
-122, 124, 125, 127, 128, 130, 131, 133,
-134, 136, 138, 139, 141, 142, 144, 146,
-147, 149, 151, 152, 154, 156, 157, 159,
-161, 163, 164, 166, 168, 170, 171, 173,
-175, 177, 179, 181, 183, 184, 186, 188,
-190, 192, 194, 196, 198, 200, 202, 204,
-206, 208, 210, 212, 214, 216, 218, 220,
-222, 224, 226, 229, 231, 233, 235, 237,
-239, 242, 244, 246, 248, 250, 253, 255
-};
-
-static void
-ComputesRGBLuminanceMask(const uint8_t *aSourceData,
-                         int32_t aSourceStride,
-                         uint8_t *aDestData,
-                         int32_t aDestStride,
-                         const IntSize &aSize,
-                         float aOpacity)
-{
-#ifdef BUILD_ARM_NEON
-  if (mozilla::supports_neon()) {
-    ComputesRGBLuminanceMask_NEON(aSourceData, aSourceStride,
-                                  aDestData, aDestStride,
-                                  aSize, aOpacity);
-    return;
-  }
-#endif
-
-  int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
-  int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
-  int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
-  int32_t sourceOffset = aSourceStride - 4 * aSize.width;
-  const uint8_t *sourcePixel = aSourceData;
-  int32_t destOffset = aDestStride - aSize.width;
-  uint8_t *destPixel = aDestData;
-
-  for (int32_t y = 0; y < aSize.height; y++) {
-    for (int32_t x = 0; x < aSize.width; x++) {
-      uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
-
-      if (a) {
-        *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] +
-                      greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
-                      blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
-      } else {
-        *destPixel = 0;
-      }
-      sourcePixel += 4;
-      destPixel++;
-    }
-    sourcePixel += sourceOffset;
-    destPixel += destOffset;
-  }
-}
-
-static void
-ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData,
-                              int32_t aSourceStride,
-                              uint8_t *aDestData,
-                              int32_t aDestStride,
-                              const IntSize &aSize,
-                              float aOpacity)
-{
-  int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
-  int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
-  int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
-  int32_t sourceOffset = aSourceStride - 4 * aSize.width;
-  const uint8_t *sourcePixel = aSourceData;
-  int32_t destOffset = aDestStride - aSize.width;
-  uint8_t *destPixel = aDestData;
-
-  for (int32_t y = 0; y < aSize.height; y++) {
-    for (int32_t x = 0; x < aSize.width; x++) {
-      uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
-
-      // unpremultiply
-      if (a) {
-        if (a == 255) {
-          /* sRGB -> linearRGB -> intensity */
-          *destPixel =
-            static_cast<uint8_t>
-                       ((gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] *
-                         redFactor +
-                         gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] *
-                         greenFactor +
-                         gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] *
-                         blueFactor) >> 8);
-        } else {
-          uint8_t tempPixel[4];
-          tempPixel[GFX_ARGB32_OFFSET_B] =
-            (255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a;
-          tempPixel[GFX_ARGB32_OFFSET_G] =
-            (255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a;
-          tempPixel[GFX_ARGB32_OFFSET_R] =
-            (255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a;
-
-          /* sRGB -> linearRGB -> intensity */
-          *destPixel =
-            static_cast<uint8_t>
-                       (((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] *
-                          redFactor +
-                          gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] *
-                          greenFactor +
-                          gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] *
-                          blueFactor) >> 8) * (a / 255.0f));
-        }
-      } else {
-        *destPixel = 0;
-      }
-      sourcePixel += 4;
-      destPixel++;
-    }
-    sourcePixel += sourceOffset;
-    destPixel += destOffset;
-  }
-}
-
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGMaskFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame)
 
+static LuminanceType
+GetLuminanceType(uint8_t aNSMaskType)
+{
+  switch (aNSMaskType) {
+    case NS_STYLE_MASK_TYPE_LUMINANCE:
+      return LuminanceType::LUMINANCE;
+    case NS_STYLE_COLOR_INTERPOLATION_LINEARRGB:
+      return LuminanceType::LINEARRGB;
+    default:
+    {
+      NS_WARNING("Unknown SVG mask type, defaulting to luminance");
+      return LuminanceType::LUMINANCE;
+    }
+  }
+}
+
 already_AddRefed<SourceSurface>
 nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams)
 {
   // Make sure we break reference loops and over long reference chains:
   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
   AutoReferenceChainGuard refChainGuard(this, &mInUse,
                                         &sRefChainLengthCounter);
   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
@@ -255,52 +124,28 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(Ma
       m = static_cast<nsSVGElement*>(kid->GetContent())->
             PrependLocalTransformsTo(m, eUserSpaceToParent);
     }
     nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
   }
 
   RefPtr<SourceSurface> surface;
   if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
-    RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
+    if (StyleSVG()->mColorInterpolation ==
+        NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
+      maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
+    }
+
+    RefPtr<SourceSurface> maskSnapshot =
+    maskDT->IntoLuminanceSource(GetLuminanceType(maskType), aParams.opacity);
     if (!maskSnapshot) {
       return nullptr;
     }
 
-    RefPtr<DataSourceSurface> maskSurface = maskSnapshot->GetDataSurface();
-    DataSourceSurface::MappedSurface map;
-    if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) {
-      return nullptr;
-    }
-
-    // Create alpha channel mask for output
-    RefPtr<DataSourceSurface> destMaskSurface =
-      Factory::CreateDataSourceSurface(maskSurfaceSize, SurfaceFormat::A8);
-    if (!destMaskSurface) {
-      return nullptr;
-    }
-    DataSourceSurface::MappedSurface destMap;
-    if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
-      return nullptr;
-    }
-
-    if (StyleSVG()->mColorInterpolation ==
-        NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
-      ComputeLinearRGBLuminanceMask(map.mData, map.mStride,
-                                    destMap.mData, destMap.mStride,
-                                    maskSurfaceSize, aParams.opacity);
-    } else {
-      ComputesRGBLuminanceMask(map.mData, map.mStride,
-                               destMap.mData, destMap.mStride,
-                               maskSurfaceSize, aParams.opacity);
-    }
-
-    maskSurface->Unmap();
-    destMaskSurface->Unmap();
-    surface = destMaskSurface.forget();
+    surface = maskSnapshot.forget();
   } else {
     maskDT->SetTransform(Matrix());
     maskDT->FillRect(Rect(0, 0, maskSurfaceSize.width, maskSurfaceSize.height), ColorPattern(Color(1.0f, 1.0f, 1.0f, aParams.opacity)), DrawOptions(1, CompositionOp::OP_IN));
     RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
     if (!maskSnapshot) {
       return nullptr;
     }
     surface = maskSnapshot.forget();
--- a/layout/svg/nsSVGMaskFrame.h
+++ b/layout/svg/nsSVGMaskFrame.h
@@ -11,31 +11,16 @@
 #include "mozilla/RefPtr.h"
 #include "gfxPattern.h"
 #include "gfxMatrix.h"
 #include "nsSVGContainerFrame.h"
 #include "nsSVGUtils.h"
 
 class gfxContext;
 
-/**
- * Byte offsets of channels in a native packed gfxColor or cairo image surface.
- */
-#ifdef IS_BIG_ENDIAN
-#define GFX_ARGB32_OFFSET_A 0
-#define GFX_ARGB32_OFFSET_R 1
-#define GFX_ARGB32_OFFSET_G 2
-#define GFX_ARGB32_OFFSET_B 3
-#else
-#define GFX_ARGB32_OFFSET_A 3
-#define GFX_ARGB32_OFFSET_R 2
-#define GFX_ARGB32_OFFSET_G 1
-#define GFX_ARGB32_OFFSET_B 0
-#endif
-
 class nsSVGMaskFrame final : public nsSVGContainerFrame
 {
   friend nsIFrame*
   NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::SourceSurface SourceSurface;
   typedef mozilla::image::imgDrawingParams imgDrawingParams;
deleted file mode 100644
--- a/layout/svg/nsSVGMaskFrameNEON.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nsSVGMaskFrameNEON.h"
-#include "nsSVGMaskFrame.h"
-#include <arm_neon.h>
-
-using namespace mozilla::gfx;
-
-void
-ComputesRGBLuminanceMask_NEON(const uint8_t *aSourceData,
-                              int32_t aSourceStride,
-                              uint8_t *aDestData,
-                              int32_t aDestStride,
-                              const IntSize &aSize,
-                              float aOpacity)
-{
-  int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
-  int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
-  int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
-  const uint8_t *sourcePixel = aSourceData;
-  int32_t sourceOffset = aSourceStride - 4 * aSize.width;
-  uint8_t *destPixel = aDestData;
-  int32_t destOffset = aDestStride - aSize.width;
-
-  sourcePixel = aSourceData;
-  int32_t remainderWidth = aSize.width % 8;
-  int32_t roundedWidth = aSize.width - remainderWidth;
-  uint16x8_t temp;
-  uint8x8_t gray;
-  uint8x8_t redVector = vdup_n_u8(redFactor);
-  uint8x8_t greenVector = vdup_n_u8(greenFactor);
-  uint8x8_t blueVector = vdup_n_u8(blueFactor);
-  uint8x8_t fullBitVector = vdup_n_u8(255);
-  uint8x8_t oneVector = vdup_n_u8(1);
-  for (int32_t y = 0; y < aSize.height; y++) {
-    // Calculate luminance by neon with 8 pixels per loop
-    for (int32_t x = 0; x < roundedWidth; x += 8) {
-      uint8x8x4_t argb  = vld4_u8(sourcePixel);
-      temp = vmull_u8(argb.val[GFX_ARGB32_OFFSET_R], redVector); // temp = red * redFactor
-      temp = vmlal_u8(temp, argb.val[GFX_ARGB32_OFFSET_G], greenVector); // temp += green * greenFactor
-      temp = vmlal_u8(temp, argb.val[GFX_ARGB32_OFFSET_B], blueVector); // temp += blue * blueFactor
-      gray = vshrn_n_u16(temp, 8); // gray = temp >> 8
-
-      // Check alpha value
-      uint8x8_t alphaVector = vtst_u8(argb.val[GFX_ARGB32_OFFSET_A], fullBitVector);
-      gray = vmul_u8(gray, vand_u8(alphaVector, oneVector));
-
-      // Put the result to the 8 pixels
-      vst1_u8(destPixel, gray);
-      sourcePixel += 8 * 4;
-      destPixel += 8;
-    }
-
-    // Calculate the rest pixels of the line by cpu
-    for (int32_t x = 0; x < remainderWidth; x++) {
-      if (sourcePixel[GFX_ARGB32_OFFSET_A] > 0) {
-        *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R]+
-                      greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
-                      blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
-      } else {
-        *destPixel = 0;
-      }
-      sourcePixel += 4;
-      destPixel++;
-    }
-    sourcePixel += sourceOffset;
-    destPixel += destOffset;
-  }
-}
-
deleted file mode 100644
--- a/layout/svg/nsSVGMaskFrameNEON.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#ifndef __NS_SVGMASKFRAMENEON_H__
-#define __NS_SVGMASKFRAMENEON_H__
-
-#include "mozilla/gfx/Point.h"
-
-void
-ComputesRGBLuminanceMask_NEON(const uint8_t *aSourceData,
-                              int32_t aSourceStride,
-                              uint8_t *aDestData,
-                              int32_t aDestStride,
-                              const mozilla::gfx::IntSize &aSize,
-                              float aOpacity);
-
-#endif /* __NS_SVGMASKFRAMENEON_H__ */