Bug 867774 - Make RasterImage's frame blending helper functions threadsafe. r=jrmuizel
authorJoe Drew <joe@drew.ca>
Fri, 14 Jun 2013 09:42:01 -0400
changeset 142826 f0380ec657f82a13f37732d1b4c8d99d99c2ce78
parent 142825 5b3196ad66f40b7ca578461155f279fa0c6fca8e
child 142827 7158e859e28c9fbb642a6c0ff84a4262bbec6935
push idunknown
push userunknown
push dateunknown
reviewersjrmuizel
bugs867774
milestone24.0a1
Bug 867774 - Make RasterImage's frame blending helper functions threadsafe. r=jrmuizel
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/imgFrame.cpp
image/src/imgFrame.h
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -44,16 +44,18 @@
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/gfx/Scale.h"
 
 #include "GeckoProfiler.h"
 #include <algorithm>
 
+#include "pixman.h"
+
 using namespace mozilla;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
 // a mask for flags that will affect the decoding
 #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
 #define DECODE_FLAGS_DEFAULT 0
 
@@ -2240,206 +2242,206 @@ RasterImage::DoComposite(nsIntRect* aDir
   mAnim->lastCompositedFrameIndex = aNextFrameIndex;
 
   return NS_OK;
 }
 
 //******************************************************************************
 // Fill aFrame with black. Does also clears the mask.
 void
-RasterImage::ClearFrame(imgFrame *aFrame)
+RasterImage::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect)
 {
-  if (!aFrame)
-    return;
-
-  nsresult rv = aFrame->LockImageData();
-  if (NS_FAILED(rv))
+  if (!aFrameData)
     return;
 
-  nsRefPtr<gfxASurface> surf;
-  aFrame->GetSurface(getter_AddRefs(surf));
-
-  // Erase the surface to transparent
-  gfxContext ctx(surf);
-  ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
-  ctx.Paint();
-
-  aFrame->UnlockImageData();
+  memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
+}
+
+void
+RasterImage::ClearFrame(imgFrame* aFrame)
+{
+  AutoFrameLocker lock(aFrame);
+  if (lock.Succeeded()) {
+    ClearFrame(aFrame->GetImageData(), aFrame->GetRect());
+  }
 }
 
 //******************************************************************************
 void
-RasterImage::ClearFrame(imgFrame *aFrame, nsIntRect &aRect)
+RasterImage::ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect, const nsIntRect& aRectToClear)
 {
-  if (!aFrame || aRect.width <= 0 || aRect.height <= 0)
+  if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
+      aRectToClear.width <= 0 || aRectToClear.height <= 0) {
     return;
-
-  nsresult rv = aFrame->LockImageData();
-  if (NS_FAILED(rv))
+  }
+
+  nsIntRect toClear = aFrameRect.Intersect(aRectToClear);
+  if (toClear.IsEmpty()) {
     return;
-
-  nsRefPtr<gfxASurface> surf;
-  aFrame->GetSurface(getter_AddRefs(surf));
-
-  // Erase the destination rectangle to transparent
-  gfxContext ctx(surf);
-  ctx.SetOperator(gfxContext::OPERATOR_CLEAR);
-  ctx.Rectangle(gfxRect(aRect.x, aRect.y, aRect.width, aRect.height));
-  ctx.Fill();
-
-  aFrame->UnlockImageData();
+  }
+
+  uint32_t bytesPerRow = aFrameRect.width * 4;
+  for (int row = toClear.y; row < toClear.height; ++row) {
+    memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0, toClear.width * 4);
+  }
 }
 
+void
+RasterImage::ClearFrame(imgFrame* aFrame, const nsIntRect& aRectToClear)
+{
+  AutoFrameLocker lock(aFrame);
+  if (lock.Succeeded()) {
+    ClearFrame(aFrame->GetImageData(), aFrame->GetRect(), aRectToClear);
+  }
+}
 
 //******************************************************************************
 // Whether we succeed or fail will not cause a crash, and there's not much
 // we can do about a failure, so there we don't return a nsresult
 bool
-RasterImage::CopyFrameImage(imgFrame *aSrcFrame,
-                            imgFrame *aDstFrame)
+RasterImage::CopyFrameImage(uint8_t *aDataSrc, const nsIntRect& aRectSrc,
+                            uint8_t *aDataDest, const nsIntRect& aRectDest)
 {
-  uint8_t* aDataSrc;
-  uint8_t* aDataDest;
-  uint32_t aDataLengthSrc;
-  uint32_t aDataLengthDest;
-
-  if (!aSrcFrame || !aDstFrame)
-    return false;
-
-  AutoFrameLocker dstLock(aDstFrame);
-  AutoFrameLocker srcLock(aSrcFrame);
-
-  if (!srcLock.Succeeded() || !dstLock.Succeeded()) {
+  uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
+  uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
+
+  if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
     return false;
   }
 
-  // Copy Image Over
-  aSrcFrame->GetImageData(&aDataSrc, &aDataLengthSrc);
-  aDstFrame->GetImageData(&aDataDest, &aDataLengthDest);
-  if (!aDataDest || !aDataSrc || aDataLengthDest != aDataLengthSrc) {
-    return false;
-  }
-
-  memcpy(aDataDest, aDataSrc, aDataLengthSrc);
+  memcpy(aDataDest, aDataSrc, dataLengthDest);
 
   return true;
 }
 
-//******************************************************************************
-/*
- * aSrc is the current frame being drawn,
- * aDst is the composition frame where the current frame is drawn into.
- * aSrcRect is the size of the current frame, and the position of that frame
- *          in the composition frame.
- */
+bool
+RasterImage::CopyFrameImage(imgFrame* aSrc, imgFrame* aDst)
+{
+  AutoFrameLocker srclock(aSrc);
+  AutoFrameLocker dstlock(aDst);
+  if (!srclock.Succeeded() || !dstlock.Succeeded()) {
+    return false;
+  }
+
+  return CopyFrameImage(aSrc->GetImageData(), aSrc->GetRect(),
+                        aDst->GetImageData(), aDst->GetRect());
+}
+
 nsresult
-RasterImage::DrawFrameTo(imgFrame *aSrc,
-                         imgFrame *aDst,
-                         nsIntRect& aSrcRect)
+RasterImage::DrawFrameTo(uint8_t *aSrcData, const nsIntRect& aSrcRect,
+                         uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
+                         uint8_t *aDstPixels, const nsIntRect& aDstRect,
+                         FrameBlendMethod aBlendMethod)
 {
-  NS_ENSURE_ARG_POINTER(aSrc);
-  NS_ENSURE_ARG_POINTER(aDst);
-
-  AutoFrameLocker srcLock(aSrc);
-  AutoFrameLocker dstLock(aDst);
-
-  nsIntRect dstRect = aDst->GetRect();
+  NS_ENSURE_ARG_POINTER(aSrcData);
+  NS_ENSURE_ARG_POINTER(aDstPixels);
 
   // According to both AGIF and APNG specs, offsets are unsigned
   if (aSrcRect.x < 0 || aSrcRect.y < 0) {
     NS_WARNING("RasterImage::DrawFrameTo: negative offsets not allowed");
     return NS_ERROR_FAILURE;
   }
   // Outside the destination frame, skip it
-  if ((aSrcRect.x > dstRect.width) || (aSrcRect.y > dstRect.height)) {
+  if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
     return NS_OK;
   }
 
-  if (aSrc->GetIsPaletted()) {
+  if (aSrcPaletteLength) {
     // Larger than the destination frame, clip it
-    int32_t width = std::min(aSrcRect.width, dstRect.width - aSrcRect.x);
-    int32_t height = std::min(aSrcRect.height, dstRect.height - aSrcRect.y);
+    int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
+    int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);
 
     // The clipped image must now fully fit within destination image frame
     NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
-                 (aSrcRect.x + width <= dstRect.width) &&
-                 (aSrcRect.y + height <= dstRect.height),
+                 (aSrcRect.x + width <= aDstRect.width) &&
+                 (aSrcRect.y + height <= aDstRect.height),
                 "RasterImage::DrawFrameTo: Invalid aSrcRect");
 
     // clipped image size may be smaller than source, but not larger
     NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
                  "RasterImage::DrawFrameTo: source must be smaller than dest");
 
-    if (!srcLock.Succeeded() || !dstLock.Succeeded())
-      return NS_ERROR_FAILURE;
-
     // Get pointers to image data
-    uint32_t size;
-    uint8_t *srcPixels;
-    uint32_t *colormap;
-    uint32_t *dstPixels;
-
-    aSrc->GetImageData(&srcPixels, &size);
-    aSrc->GetPaletteData(&colormap, &size);
-    aDst->GetImageData((uint8_t **)&dstPixels, &size);
-    if (!srcPixels || !dstPixels || !colormap) {
-      return NS_ERROR_FAILURE;
-    }
+    uint8_t *srcPixels = aSrcData + aSrcPaletteLength;
+    uint32_t *dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
+    uint32_t *colormap = reinterpret_cast<uint32_t*>(aSrcData);
 
     // Skip to the right offset
-    dstPixels += aSrcRect.x + (aSrcRect.y * dstRect.width);
-    if (!aSrc->GetHasAlpha()) {
+    dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
+    if (!aSrcHasAlpha) {
       for (int32_t r = height; r > 0; --r) {
         for (int32_t c = 0; c < width; c++) {
           dstPixels[c] = colormap[srcPixels[c]];
         }
         // Go to the next row in the source resp. destination image
         srcPixels += aSrcRect.width;
-        dstPixels += dstRect.width;
+        dstPixels += aDstRect.width;
       }
     } else {
       for (int32_t r = height; r > 0; --r) {
         for (int32_t c = 0; c < width; c++) {
           const uint32_t color = colormap[srcPixels[c]];
           if (color)
             dstPixels[c] = color;
         }
         // Go to the next row in the source resp. destination image
         srcPixels += aSrcRect.width;
-        dstPixels += dstRect.width;
+        dstPixels += aDstRect.width;
       }
     }
-
-    return NS_OK;
+  } else {
+    pixman_image_t* src = pixman_image_create_bits(aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
+                                                   aSrcRect.width,
+                                                   aSrcRect.height,
+                                                   reinterpret_cast<uint32_t*>(aSrcData),
+                                                   aSrcRect.width * 4);
+    pixman_image_t* dst = pixman_image_create_bits(PIXMAN_a8r8g8b8,
+                                                   aDstRect.width,
+                                                   aDstRect.height,
+                                                   reinterpret_cast<uint32_t*>(aDstPixels),
+                                                   aDstRect.width * 4);
+
+    pixman_image_composite32(aBlendMethod == kBlendSource ? PIXMAN_OP_SRC : PIXMAN_OP_OVER,
+                             src,
+                             nullptr,
+                             dst,
+                             0, 0,
+                             0, 0,
+                             aSrcRect.x, aSrcRect.y,
+                             aDstRect.width, aDstRect.height);
+
+    pixman_image_unref(src);
+    pixman_image_unref(dst);
   }
 
-  nsRefPtr<gfxPattern> srcPatt;
-  aSrc->GetPattern(getter_AddRefs(srcPatt));
-
-  nsRefPtr<gfxASurface> dstSurf;
-  aDst->GetSurface(getter_AddRefs(dstSurf));
-
-  gfxContext dst(dstSurf);
-  dst.Translate(gfxPoint(aSrcRect.x, aSrcRect.y));
-  dst.Rectangle(gfxRect(0, 0, aSrcRect.width, aSrcRect.height), true);
-
-  // first clear the surface if the blend flag says so
-  int32_t blendMethod = aSrc->GetBlendMethod();
-  if (blendMethod == kBlendSource) {
-    gfxContext::GraphicsOperator defaultOperator = dst.CurrentOperator();
-    dst.SetOperator(gfxContext::OPERATOR_CLEAR);
-    dst.Fill();
-    dst.SetOperator(defaultOperator);
-  }
-  dst.SetPattern(srcPatt);
-  dst.Paint();
-
   return NS_OK;
 }
 
+nsresult
+RasterImage::DrawFrameTo(imgFrame* aSrc, imgFrame* aDst, const nsIntRect& aSrcRect)
+{
+  AutoFrameLocker srclock(aSrc);
+  AutoFrameLocker dstlock(aDst);
+  if (!srclock.Succeeded() || !dstlock.Succeeded()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (aSrc->GetIsPaletted()) {
+    return DrawFrameTo(reinterpret_cast<uint8_t*>(aSrc->GetPaletteData()),
+                       aSrcRect, aSrc->PaletteDataLength(),
+                       aSrc->GetHasAlpha(), aDst->GetImageData(),
+                       aDst->GetRect(),
+                       FrameBlendMethod(aSrc->GetBlendMethod()));
+  }
+
+  return DrawFrameTo(aSrc->GetImageData(), aSrcRect,
+                     0, aSrc->GetHasAlpha(),
+                     aDst->GetImageData(), aDst->GetRect(),
+                     FrameBlendMethod(aSrc->GetBlendMethod()));
+}
 
 /********* Methods to implement lazy allocation of nsIProperties object *************/
 NS_IMETHODIMP
 RasterImage::Get(const char *prop, const nsIID & iid, void * *result)
 {
   if (!mProperties)
     return NS_ERROR_FAILURE;
   return mProperties->Get(prop, iid, result);
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -670,39 +670,53 @@ private:
    */
   nsresult DoComposite(nsIntRect* aDirtyRect,
                        imgFrame* aPrevFrame,
                        imgFrame* aNextFrame,
                        int32_t aNextFrameIndex);
 
   /** Clears an area of <aFrame> with transparent black.
    *
-   * @param aFrame Target Frame
+   * @param aFrameData Target Frame data
+   * @param aFrameRect The rectangle of the data pointed ot by aFrameData
    *
    * @note Does also clears the transparancy mask
    */
+  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect);
   static void ClearFrame(imgFrame* aFrame);
 
   //! @overload
-  static void ClearFrame(imgFrame* aFrame, nsIntRect &aRect);
+  static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect, const nsIntRect &aRectToClear);
+  static void ClearFrame(imgFrame* aFrame, const nsIntRect& aRectToClear);
 
   //! Copy one frames's image and mask into another
-  static bool CopyFrameImage(imgFrame *aSrcFrame,
-                               imgFrame *aDstFrame);
+  static bool CopyFrameImage(uint8_t *aDataSrc, const nsIntRect& aRectSrc,
+                             uint8_t *aDataDest, const nsIntRect& aRectDest);
+  static bool CopyFrameImage(imgFrame* aSrc, imgFrame* aDst);
 
-  /** Draws one frames's image to into another,
-   * at the position specified by aRect
+  /**
+   * Draws one frames's image to into another, at the position specified by
+   * aSrcRect.
    *
-   * @param aSrcFrame  Frame providing the source image
-   * @param aDstFrame  Frame where the image is drawn into
-   * @param aRect      The position and size to draw the image
+   * @aSrcData the raw data of the current frame being drawn
+   * @aSrcRect the size of the source frame, and the position of that frame in
+   *           the composition frame
+   * @aSrcPaletteLength the length (in bytes) of the palette at the beginning
+   *                    of the source data (0 if image is not paletted)
+   * @aSrcHasAlpha whether the source data represents an image with alpha
+   * @aDstPixels the raw data of the composition frame where the current frame
+   *             is drawn into (32-bit ARGB)
+   * @aDstRect the size of the composition frame
+   * @aBlendMethod the blend method for how to blend src on the composition frame.
    */
-  static nsresult DrawFrameTo(imgFrame *aSrcFrame,
-                              imgFrame *aDstFrame,
-                              nsIntRect& aRect);
+  static nsresult DrawFrameTo(uint8_t *aSrcData, const nsIntRect& aSrcRect,
+                              uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
+                              uint8_t *aDstPixels, const nsIntRect& aDstRect,
+                              FrameBlendMethod aBlendMethod);
+  static nsresult DrawFrameTo(imgFrame* aSrc, imgFrame* aDst, const nsIntRect& aSrcRect);
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
                                   uint8_t **imageData, uint32_t *imageLength,
                                   uint32_t **paletteData, uint32_t *paletteLength,
                                   imgFrame** aRetFrame);
   nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
                             gfxASurface::gfxImageFormat aFormat, uint8_t aPaletteDepth,
                             uint8_t **imageData, uint32_t *imageLength,
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -544,16 +544,24 @@ void imgFrame::GetImageData(uint8_t **aD
   else if (mPalettedImageData)
     *aData = mPalettedImageData + PaletteDataLength();
   else
     *aData = nullptr;
 
   *length = GetImageDataLength();
 }
 
+uint8_t* imgFrame::GetImageData() const
+{
+  uint8_t *data;
+  uint32_t length;
+  GetImageData(&data, &length);
+  return data;
+}
+
 bool imgFrame::GetIsPaletted() const
 {
   return mPalettedImageData != nullptr;
 }
 
 bool imgFrame::GetHasAlpha() const
 {
   return mFormat == gfxASurface::ImageFormatARGB32;
@@ -567,16 +575,24 @@ void imgFrame::GetPaletteData(uint32_t *
     *aPalette = nullptr;
     *length = 0;
   } else {
     *aPalette = (uint32_t *) mPalettedImageData;
     *length = PaletteDataLength();
   }
 }
 
+uint32_t* imgFrame::GetPaletteData() const
+{
+  uint32_t* data;
+  uint32_t length;
+  GetPaletteData(&data, &length);
+  return data;
+}
+
 nsresult imgFrame::LockImageData()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks");
   if (mLockCount < 0) {
     return NS_ERROR_FAILURE;
   }
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -43,17 +43,19 @@ public:
   nsIntRect GetRect() const;
   gfxASurface::gfxImageFormat GetFormat() const;
   bool GetNeedsBackground() const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;
   bool GetIsPaletted() const;
   bool GetHasAlpha() const;
   void GetImageData(uint8_t **aData, uint32_t *length) const;
+  uint8_t* GetImageData() const;
   void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
+  uint32_t* GetPaletteData() const;
 
   int32_t GetTimeout() const;
   void SetTimeout(int32_t aTimeout);
 
   int32_t GetFrameDisposalMethod() const;
   void SetFrameDisposalMethod(int32_t aFrameDisposalMethod);
   int32_t GetBlendMethod() const;
   void SetBlendMethod(int32_t aBlendMethod);
@@ -100,22 +102,22 @@ public:
     return mImageSurface;
   }
 
   size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
            gfxASurface::MemoryLocation aLocation,
            nsMallocSizeOfFun aMallocSizeOf) const;
 
   uint8_t GetPaletteDepth() const { return mPaletteDepth; }
-
-private: // methods
   uint32_t PaletteDataLength() const {
     return ((1 << mPaletteDepth) * sizeof(uint32_t));
   }
 
+private: // methods
+
   struct SurfaceWithFormat {
     nsRefPtr<gfxDrawable> mDrawable;
     gfxImageSurface::gfxImageFormat mFormat;
     SurfaceWithFormat() {}
     SurfaceWithFormat(gfxDrawable* aDrawable, gfxImageSurface::gfxImageFormat aFormat)
      : mDrawable(aDrawable), mFormat(aFormat) {}
     bool IsValid() { return !!mDrawable; }
   };