Bug 1000640 - Handle non-premult alpha in Layers. - r=mattwoodrow
☠☠ backed out by ad28fb0d24e6 ☠ ☠
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 04 Jun 2014 15:20:24 -0700
changeset 205930 f644eda342c2322de69fddb5a6f47582e03ab6cd
parent 205929 90b3655df3b9b78d594fd2b5a41695eeda679a33
child 205931 9d1dd5a9be4f70248bcb69db330168f06c92b21d
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1000640
milestone32.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 1000640 - Handle non-premult alpha in Layers. - r=mattwoodrow
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContextGL.cpp
gfx/layers/CopyableCanvasLayer.cpp
gfx/layers/Effects.h
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -1366,17 +1366,17 @@ WebGLContext::GetSurfaceSnapshot(bool* a
     if (aPremultAlpha) {
         *aPremultAlpha = true;
     }
     bool srcPremultAlpha = mOptions.premultipliedAlpha;
     if (!srcPremultAlpha) {
         if (aPremultAlpha) {
             *aPremultAlpha = false;
         } else {
-            gfxUtils::PremultiplyDataSurface(surf);
+            gfxUtils::PremultiplyDataSurface(surf, surf);
         }
     }
 
     RefPtr<DrawTarget> dt =
         Factory::CreateDrawTarget(BackendType::CAIRO,
                                   IntSize(mWidth, mHeight),
                                   SurfaceFormat::B8G8R8A8);
 
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -2525,17 +2525,27 @@ WebGLContext::SurfaceFromElementResultTo
         return NS_OK;
     RefPtr<DataSourceSurface> data = res.mSourceSurface->GetDataSurface();
     if (!data) {
         // SurfaceFromElement lied!
         return NS_OK;
     }
 
     if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) {
-      data = gfxUtils::UnpremultiplyDataSurface(data);
+        switch (data->GetFormat()) {
+        case SurfaceFormat::B8G8R8X8:
+            // No alpha, so de-facto premult'd.
+            break;
+        case SurfaceFormat::B8G8R8A8:
+            data = gfxUtils::CreateUnpremultipliedDataSurface(data);
+            break;
+        default:
+            MOZ_ASSERT(false, "Format unsupported.");
+            break;
+        }
     }
 
     // We disallow loading cross-domain images and videos that have not been validated
     // with CORS as WebGL textures. The reason for doing that is that timing
     // attacks on WebGL shaders are able to retrieve approximations of the
     // pixel values in WebGL textures; see bug 655987.
     //
     // To prevent a loophole where a Canvas2D would be used as a proxy to load
--- a/gfx/layers/CopyableCanvasLayer.cpp
+++ b/gfx/layers/CopyableCanvasLayer.cpp
@@ -122,17 +122,17 @@ CopyableCanvasLayer::UpdateTarget(DrawTa
       int32_t destStride;
       SurfaceFormat destFormat;
       if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
         if (destSize == readSize && destFormat == format) {
           RefPtr<DataSourceSurface> data =
             Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
           mGLContext->Screen()->Readback(sharedSurf, data);
           if (needsPremult) {
-              gfxUtils::PremultiplyDataSurface(data);
+              gfxUtils::PremultiplyDataSurface(data, data);
           }
           aDestTarget->ReleaseBits(destData);
           return;
         }
         aDestTarget->ReleaseBits(destData);
       }
     }
 
@@ -140,17 +140,17 @@ CopyableCanvasLayer::UpdateTarget(DrawTa
     if (sharedSurf->Type() == SharedSurfaceType::Basic && !needsPremult) {
       SharedSurface_Basic* sharedSurf_Basic = SharedSurface_Basic::Cast(sharedSurf);
       resultSurf = sharedSurf_Basic->GetData();
     } else {
       RefPtr<DataSourceSurface> data = GetTempSurface(readSize, format);
       // Readback handles Flush/MarkDirty.
       mGLContext->Screen()->Readback(sharedSurf, data);
       if (needsPremult) {
-        gfxUtils::PremultiplyDataSurface(data);
+        gfxUtils::PremultiplyDataSurface(data, data);
       }
       resultSurf = data;
     }
     MOZ_ASSERT(resultSurf);
 
     if (aDestTarget) {
       aDestTarget->CopySurface(resultSurf,
                                IntRect(0, 0, readSize.width, readSize.height),
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -67,17 +67,17 @@ struct TexturedEffect : public Effect
   {}
 
   virtual const char* Name() = 0;
   virtual void PrintInfo(nsACString& aTo, const char* aPrefix);
 
   gfx::Rect mTextureCoords;
   TextureSource* mTexture;
   bool mPremultiplied;
-  gfx::Filter mFilter;;
+  gfx::Filter mFilter;
 };
 
 // Support an alpha mask.
 struct EffectMask : public Effect
 {
   EffectMask(TextureSource *aMaskTexture,
              gfx::IntSize aSize,
              const gfx::Matrix4x4 &aMaskTransform)
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -30,120 +30,280 @@ using namespace mozilla::gfx;
 static const uint8_t PremultiplyValue(uint8_t a, uint8_t v) {
     return gfxUtils::sPremultiplyTable[a*256+v];
 }
 
 static const uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) {
     return gfxUtils::sUnpremultiplyTable[a*256+v];
 }
 
-void
-gfxUtils::PremultiplyDataSurface(DataSourceSurface *aSurface)
+static void
+PremultiplyData(const uint8_t* srcData,
+                size_t srcStride,  // row-to-row stride in bytes
+                uint8_t* destData,
+                size_t destStride, // row-to-row stride in bytes
+                size_t pixelWidth,
+                size_t rowCount)
+{
+    MOZ_ASSERT(srcData && destData);
+
+    for (size_t y = 0; y < rowCount; ++y) {
+        const uint8_t* src  = srcData  + y * srcStride;
+        uint8_t* dest       = destData + y * destStride;
+
+        for (size_t x = 0; x < pixelWidth; ++x) {
+#ifdef IS_LITTLE_ENDIAN
+            uint8_t b = *src++;
+            uint8_t g = *src++;
+            uint8_t r = *src++;
+            uint8_t a = *src++;
+
+            *dest++ = PremultiplyValue(a, b);
+            *dest++ = PremultiplyValue(a, g);
+            *dest++ = PremultiplyValue(a, r);
+            *dest++ = a;
+#else
+            uint8_t a = *src++;
+            uint8_t r = *src++;
+            uint8_t g = *src++;
+            uint8_t b = *src++;
+
+            *dest++ = a;
+            *dest++ = PremultiplyValue(a, r);
+            *dest++ = PremultiplyValue(a, g);
+            *dest++ = PremultiplyValue(a, b);
+#endif
+        }
+    }
+}
+static void
+UnpremultiplyData(const uint8_t* srcData,
+                  size_t srcStride,  // row-to-row stride in bytes
+                  uint8_t* destData,
+                  size_t destStride, // row-to-row stride in bytes
+                  size_t pixelWidth,
+                  size_t rowCount)
 {
-    // Only premultiply ARGB32
-    if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
-        return;
+    MOZ_ASSERT(srcData && destData);
+
+    for (size_t y = 0; y < rowCount; ++y) {
+        const uint8_t* src  = srcData  + y * srcStride;
+        uint8_t* dest       = destData + y * destStride;
+
+        for (size_t x = 0; x < pixelWidth; ++x) {
+#ifdef IS_LITTLE_ENDIAN
+            uint8_t b = *src++;
+            uint8_t g = *src++;
+            uint8_t r = *src++;
+            uint8_t a = *src++;
+
+            *dest++ = UnpremultiplyValue(a, b);
+            *dest++ = UnpremultiplyValue(a, g);
+            *dest++ = UnpremultiplyValue(a, r);
+            *dest++ = a;
+#else
+            uint8_t a = *src++;
+            uint8_t r = *src++;
+            uint8_t g = *src++;
+            uint8_t b = *src++;
+
+            *dest++ = a;
+            *dest++ = UnpremultiplyValue(a, r);
+            *dest++ = UnpremultiplyValue(a, g);
+            *dest++ = UnpremultiplyValue(a, b);
+#endif
+        }
+    }
+}
+
+static bool
+MapSrcDest(DataSourceSurface* srcSurf,
+           DataSourceSurface* destSurf,
+           DataSourceSurface::MappedSurface* out_srcMap,
+           DataSourceSurface::MappedSurface* out_destMap)
+{
+    MOZ_ASSERT(srcSurf && destSurf);
+    MOZ_ASSERT(out_srcMap && out_destMap);
+
+    if (srcSurf->GetFormat()  != SurfaceFormat::B8G8R8A8 ||
+        destSurf->GetFormat() != SurfaceFormat::B8G8R8A8)
+    {
+        MOZ_ASSERT(false, "Only operate on BGRA8 surfs.");
+        return false;
+    }
+
+    if (srcSurf->GetSize().width  != destSurf->GetSize().width ||
+        srcSurf->GetSize().height != destSurf->GetSize().height)
+    {
+        MOZ_ASSERT(false, "Width and height must match.");
+        return false;
     }
 
-    DataSourceSurface::MappedSurface map;
-    if (!aSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
-        return;
+    if (srcSurf == destSurf) {
+        DataSourceSurface::MappedSurface map;
+        if (!srcSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
+            NS_WARNING("Couldn't Map srcSurf/destSurf.");
+            return false;
+        }
+
+        *out_srcMap = map;
+        *out_destMap = map;
+        return true;
     }
-    MOZ_ASSERT(map.mStride == aSurface->GetSize().width * 4,
-               "Source surface stride isn't tightly packed");
-
-    uint8_t *src = map.mData;
-    uint8_t *dst = map.mData;
 
-    uint32_t dim = aSurface->GetSize().width * aSurface->GetSize().height;
-    for (uint32_t i = 0; i < dim; ++i) {
-#ifdef IS_LITTLE_ENDIAN
-        uint8_t b = *src++;
-        uint8_t g = *src++;
-        uint8_t r = *src++;
-        uint8_t a = *src++;
+    // Map src for reading.
+    DataSourceSurface::MappedSurface srcMap;
+    if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
+        NS_WARNING("Couldn't Map srcSurf.");
+        return false;
+    }
 
-        *dst++ = PremultiplyValue(a, b);
-        *dst++ = PremultiplyValue(a, g);
-        *dst++ = PremultiplyValue(a, r);
-        *dst++ = a;
-#else
-        uint8_t a = *src++;
-        uint8_t r = *src++;
-        uint8_t g = *src++;
-        uint8_t b = *src++;
-
-        *dst++ = a;
-        *dst++ = PremultiplyValue(a, r);
-        *dst++ = PremultiplyValue(a, g);
-        *dst++ = PremultiplyValue(a, b);
-#endif
+    // Map dest for writing.
+    DataSourceSurface::MappedSurface destMap;
+    if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+        NS_WARNING("Couldn't Map aDest.");
+        srcSurf->Unmap();
+        return false;
     }
 
-    aSurface->Unmap();
+    *out_srcMap = srcMap;
+    *out_destMap = destMap;
+    return true;
+}
+
+static void
+UnmapSrcDest(DataSourceSurface* srcSurf,
+             DataSourceSurface* destSurf)
+{
+    if (srcSurf == destSurf) {
+        srcSurf->Unmap();
+    } else {
+        srcSurf->Unmap();
+        destSurf->Unmap();
+    }
+}
+
+bool
+gfxUtils::PremultiplyDataSurface(DataSourceSurface* srcSurf,
+                                 DataSourceSurface* destSurf)
+{
+    MOZ_ASSERT(srcSurf && destSurf);
+
+    DataSourceSurface::MappedSurface srcMap;
+    DataSourceSurface::MappedSurface destMap;
+    if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
+        return false;
+
+    PremultiplyData(srcMap.mData, srcMap.mStride,
+                    destMap.mData, destMap.mStride,
+                    srcSurf->GetSize().width,
+                    srcSurf->GetSize().height);
+
+    UnmapSrcDest(srcSurf, destSurf);
+    return true;
+}
+
+bool
+gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
+                                   DataSourceSurface* destSurf)
+{
+    MOZ_ASSERT(srcSurf && destSurf);
+
+    DataSourceSurface::MappedSurface srcMap;
+    DataSourceSurface::MappedSurface destMap;
+    if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
+        return false;
+
+    UnpremultiplyData(srcMap.mData, srcMap.mStride,
+                      destMap.mData, destMap.mStride,
+                      srcSurf->GetSize().width,
+                      srcSurf->GetSize().height);
+
+    UnmapSrcDest(srcSurf, destSurf);
+    return true;
+}
+
+static bool
+MapSrcAndCreateMappedDest(DataSourceSurface* srcSurf,
+                          RefPtr<DataSourceSurface>* out_destSurf,
+                          DataSourceSurface::MappedSurface* out_srcMap,
+                          DataSourceSurface::MappedSurface* out_destMap)
+{
+    MOZ_ASSERT(srcSurf);
+    MOZ_ASSERT(out_destSurf && out_srcMap && out_destMap);
+
+    if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8) {
+        MOZ_ASSERT(false, "Only operate on BGRA8.");
+        return false;
+    }
+
+    // Ok, map source for reading.
+    DataSourceSurface::MappedSurface srcMap;
+    if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
+        MOZ_ASSERT(false, "Couldn't Map srcSurf.");
+        return false;
+    }
+
+    // Make our dest surface based on the src.
+    RefPtr<DataSourceSurface> destSurf =
+        Factory::CreateDataSourceSurfaceWithStride(srcSurf->GetSize(),
+                                                   srcSurf->GetFormat(),
+                                                   srcMap.mStride);
+
+    DataSourceSurface::MappedSurface destMap;
+    if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+        MOZ_ASSERT(false, "Couldn't Map destSurf.");
+        srcSurf->Unmap();
+        return false;
+    }
+
+    *out_destSurf = destSurf;
+    *out_srcMap = srcMap;
+    *out_destMap = destMap;
+    return true;
 }
 
 TemporaryRef<DataSourceSurface>
-gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* aSurface)
+gfxUtils::CreatePremultipliedDataSurface(DataSourceSurface* srcSurf)
 {
-    // Only premultiply ARGB32
-    if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
-        return aSurface;
-    }
-
-    DataSourceSurface::MappedSurface map;
-    if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
-        return nullptr;
-    }
-
-    RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurfaceWithStride(aSurface->GetSize(),
-                                                                                aSurface->GetFormat(),
-                                                                                map.mStride);
-
+    RefPtr<DataSourceSurface> destSurf;
+    DataSourceSurface::MappedSurface srcMap;
     DataSourceSurface::MappedSurface destMap;
-    if (!dest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
-        aSurface->Unmap();
-        return nullptr;
+    if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
+        MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
+        return srcSurf;
     }
 
-    uint8_t *src = map.mData;
-    uint8_t *dst = destMap.mData;
-
-    for (int32_t i = 0; i < aSurface->GetSize().height; ++i) {
-        uint8_t *srcRow = src + (i * map.mStride);
-        uint8_t *dstRow = dst + (i * destMap.mStride);
+    PremultiplyData(srcMap.mData, srcMap.mStride,
+                    destMap.mData, destMap.mStride,
+                    srcSurf->GetSize().width,
+                    srcSurf->GetSize().height);
 
-        for (int32_t j = 0; j < aSurface->GetSize().width; ++j) {
-#ifdef IS_LITTLE_ENDIAN
-          uint8_t b = *srcRow++;
-          uint8_t g = *srcRow++;
-          uint8_t r = *srcRow++;
-          uint8_t a = *srcRow++;
+    UnmapSrcDest(srcSurf, destSurf);
+    return destSurf;
+}
 
-          *dstRow++ = UnpremultiplyValue(a, b);
-          *dstRow++ = UnpremultiplyValue(a, g);
-          *dstRow++ = UnpremultiplyValue(a, r);
-          *dstRow++ = a;
-#else
-          uint8_t a = *srcRow++;
-          uint8_t r = *srcRow++;
-          uint8_t g = *srcRow++;
-          uint8_t b = *srcRow++;
-
-          *dstRow++ = a;
-          *dstRow++ = UnpremultiplyValue(a, r);
-          *dstRow++ = UnpremultiplyValue(a, g);
-          *dstRow++ = UnpremultiplyValue(a, b);
-#endif
-        }
+TemporaryRef<DataSourceSurface>
+gfxUtils::CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf)
+{
+    RefPtr<DataSourceSurface> destSurf;
+    DataSourceSurface::MappedSurface srcMap;
+    DataSourceSurface::MappedSurface destMap;
+    if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
+        MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
+        return srcSurf;
     }
 
-    aSurface->Unmap();
-    dest->Unmap();
-    return dest;
+    UnpremultiplyData(srcMap.mData, srcMap.mStride,
+                      destMap.mData, destMap.mStride,
+                      srcSurf->GetSize().width,
+                      srcSurf->GetSize().height);
+
+    UnmapSrcDest(srcSurf, destSurf);
+    return destSurf;
 }
 
 void
 gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
 {
     MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!");
 
     uint8_t *src = aData;
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -35,18 +35,25 @@ public:
      * to aDestSurface or back into aSourceSurface if aDestSurface is null.
      *
      * If aDestSurface is given, it must have identical format, dimensions, and
      * stride as the source.
      *
      * If the source is not gfxImageFormat::ARGB32, no operation is performed.  If
      * aDestSurface is given, the data is copied over.
      */
-    static void PremultiplyDataSurface(DataSourceSurface *aSurface);
-    static mozilla::TemporaryRef<DataSourceSurface> UnpremultiplyDataSurface(DataSourceSurface* aSurface);
+    static bool PremultiplyDataSurface(DataSourceSurface* srcSurf,
+                                       DataSourceSurface* destSurf);
+    static bool UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
+                                         DataSourceSurface* destSurf);
+
+    static mozilla::TemporaryRef<DataSourceSurface>
+      CreatePremultipliedDataSurface(DataSourceSurface* srcSurf);
+    static mozilla::TemporaryRef<DataSourceSurface>
+      CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf);
 
     static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength);
 
     /**
      * Draw something drawable while working around limitations like bad support
      * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
      * extreme user-space-to-image-space transforms.
      *