Backed out changeset 8681fc7a918b (bug 997014)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 17 Apr 2014 08:43:38 +0200
changeset 179360 72dd3220abbee756db6af6ec9c3f0fa6cc6971ad
parent 179359 cf6ccdc2e849e6718b95d355f5f33034c4b39b9f
child 179361 f5b8418508dd9499225db408128283723e54b55c
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
bugs997014
milestone31.0a1
backs out8681fc7a918b146128ce41581464acb70d0d9a6b
Backed out changeset 8681fc7a918b (bug 997014)
content/canvas/public/nsICanvasRenderingContextInternal.h
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -9,18 +9,18 @@
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "GraphicsFilter.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x06166dd1, 0xd540, 0x4f29, \
-  { 0x91, 0x5c, 0x08, 0x7d, 0xce, 0x1f, 0x79, 0x59 } }
+{ 0xf74397d9, 0x25d9, 0x43ed, \
+  { 0xb4, 0x6a, 0xf5, 0x4e, 0xa1, 0x17, 0xae, 0x6e } }
 
 class gfxContext;
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -36,16 +36,20 @@ class SourceSurface;
 
 class nsICanvasRenderingContextInternal : public nsISupports {
 public:
   typedef mozilla::layers::CanvasLayer CanvasLayer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
 
+  enum {
+    RenderFlagPremultAlpha = 0x1
+  };
+
   void SetCanvasElement(mozilla::dom::HTMLCanvasElement* aParentCanvas)
   {
     mCanvasElement = aParentCanvas;
   }
   mozilla::dom::HTMLCanvasElement* GetParentObject() const
   {
     return mCanvasElement;
   }
@@ -57,16 +61,21 @@ public:
 #endif
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
+  // Render the canvas at the origin of the given gfxContext
+  NS_IMETHOD Render(gfxContext *ctx,
+                    GraphicsFilter aFilter,
+                    uint32_t aFlags = RenderFlagPremultAlpha) = 0;
+
   // Creates an image buffer. Returns null on failure.
   virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
 
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1049,16 +1049,61 @@ CanvasRenderingContext2D::SetIsIPC(bool 
     mIPC = isIPC;
     ClearTarget();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32_t aFlags)
+{
+  nsresult rv = NS_OK;
+
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<gfxASurface> surface;
+
+  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
+
+  pat->SetFilter(aFilter);
+  pat->SetExtend(gfxPattern::EXTEND_PAD);
+
+  gfxContext::GraphicsOperator op = ctx->CurrentOperator();
+  if (mOpaque)
+      ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+
+  // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
+  // pixel alignment for this stuff!
+  ctx->NewPath();
+  ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
+  ctx->Fill();
+
+  if (mOpaque)
+      ctx->SetOperator(op);
+
+  if (!(aFlags & RenderFlagPremultAlpha)) {
+      nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
+      nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
+      MOZ_ASSERT(gis, "If non-premult alpha, must be able to get image surface!");
+
+      gfxUtils::UnpremultiplyImageSurface(gis);
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
 CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
 {
   if (aOptions.isNullOrUndefined()) {
     return NS_OK;
   }
 
   ContextAttributes2D attributes;
   NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -452,16 +452,19 @@ public:
 #ifdef DEBUG
     virtual int32_t GetWidth() const MOZ_OVERRIDE;
     virtual int32_t GetHeight() const MOZ_OVERRIDE;
 #endif
   // nsICanvasRenderingContextInternal
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
   NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE;
 
+  NS_IMETHOD Render(gfxContext *ctx,
+                    GraphicsFilter aFilter,
+                    uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
   NS_IMETHOD GetInputStream(const char* aMimeType,
                             const char16_t* aEncoderOptions,
                             nsIInputStream **aStream) MOZ_OVERRIDE;
   NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
 
   mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) MOZ_OVERRIDE
   {
     EnsureTarget();
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -620,16 +620,58 @@ WebGLContext::SetDimensions(int32_t widt
     MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
     MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
     MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
 
     reporter.SetSuccessful();
     return NS_OK;
 }
 
+NS_IMETHODIMP
+WebGLContext::Render(gfxContext *ctx, GraphicsFilter f, uint32_t aFlags)
+{
+    if (!gl)
+        return NS_OK;
+
+    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                                                         gfxImageFormat::ARGB32);
+    if (surf->CairoStatus() != 0)
+        return NS_ERROR_FAILURE;
+
+    gl->MakeCurrent();
+    ReadScreenIntoImageSurface(gl, surf);
+
+    bool srcPremultAlpha = mOptions.premultipliedAlpha;
+    bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha;
+
+    if (!srcPremultAlpha && dstPremultAlpha) {
+        gfxUtils::PremultiplyImageSurface(surf);
+    } else if (srcPremultAlpha && !dstPremultAlpha) {
+        gfxUtils::UnpremultiplyImageSurface(surf);
+    }
+    surf->MarkDirty();
+
+    nsRefPtr<gfxPattern> pat = new gfxPattern(surf);
+    pat->SetFilter(f);
+
+    // Pixels from ReadPixels will be "upside down" compared to
+    // what cairo wants, so draw with a y-flip and a translte to
+    // flip them.
+    gfxMatrix m;
+    m.Translate(gfxPoint(0.0, mHeight));
+    m.Scale(1.0, -1.0);
+    pat->SetMatrix(m);
+
+    ctx->NewPath();
+    ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
+    ctx->Fill();
+
+    return NS_OK;
+}
+
 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
 {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     // some mobile devices can't have more than 8 GL contexts overall
     const size_t kMaxWebGLContextsPerPrincipal = 2;
     const size_t kMaxWebGLContexts             = 4;
 #else
     const size_t kMaxWebGLContextsPerPrincipal = 16;
@@ -711,53 +753,54 @@ void WebGLContext::LoseOldestWebGLContex
 }
 
 void
 WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
 {
     *aImageBuffer = nullptr;
     *aFormat = 0;
 
-    // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
-    bool premult;
-    RefPtr<SourceSurface> snapshot =
-      GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
-    if (!snapshot) {
-        return;
-    }
-    MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
+    nsRefPtr<gfxImageSurface> imgsurf =
+        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                            gfxImageFormat::ARGB32);
 
-    RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
-
-    DataSourceSurface::MappedSurface map;
-    if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+    if (!imgsurf || imgsurf->CairoStatus()) {
         return;
     }
 
-    static const fallible_t fallible = fallible_t();
-    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
-    if (!imageBuffer) {
-        dataSurface->Unmap();
+    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+    if (!ctx || ctx->HasError()) {
         return;
     }
-    memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
 
-    dataSurface->Unmap();
+    // Use Render() to make sure that appropriate y-flip gets applied
+    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
+    nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
+    if (NS_FAILED(rv)) {
+        return;
+    }
 
     int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
     if (!mOptions.premultipliedAlpha) {
         // We need to convert to INPUT_FORMAT_RGBA, otherwise
         // we are automatically considered premult, and unpremult'd.
         // Yes, it is THAT silly.
         // Except for different lossy conversions by color,
         // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
+        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
         format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
+    static const fallible_t fallible = fallible_t();
+    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+    if (!imageBuffer) {
+        return;
+    }
+    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
+
     *aImageBuffer = imageBuffer;
     *aFormat = format;
 }
 
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const char16_t* aEncoderOptions,
                              nsIInputStream **aStream)
@@ -1334,18 +1377,17 @@ WebGLContext::MakeContextCurrent() const
 
 mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
 WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
 {
     if (!gl)
         return nullptr;
 
     nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                                                         gfxImageFormat::ARGB32,
-                                                         mWidth * 4, 0, false);
+                                                         gfxImageFormat::ARGB32);
     if (surf->CairoStatus() != 0) {
         return nullptr;
     }
 
     gl->MakeCurrent();
     ReadScreenIntoImageSurface(gl, surf);
 
     if (aPremultAlpha) {
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -160,16 +160,19 @@ public:
     virtual int32_t GetWidth() const MOZ_OVERRIDE;
     virtual int32_t GetHeight() const MOZ_OVERRIDE;
 #endif
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset() MOZ_OVERRIDE
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
+    NS_IMETHOD Render(gfxContext *ctx,
+                      GraphicsFilter f,
+                      uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
     virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const char16_t* aEncoderOptions,
                               nsIInputStream **aStream) MOZ_OVERRIDE;
     NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha) MOZ_OVERRIDE;
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -241,34 +241,16 @@ gfxUtils::ConvertBGRAtoRGBA(gfxImageSurf
             dst[0] = src[2];
             dst[1] = src[1];
             dst[2] = src[0];
             dst[3] = src[3];
         }
     }
 }
 
-void
-gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
-{
-    uint8_t *src = aData;
-    uint8_t *srcEnd = src + aLength;
-
-    uint8_t buffer[4];
-    for (; src != srcEnd; src += 4) {
-        buffer[0] = src[2];
-        buffer[1] = src[1];
-        buffer[2] = src[0];
-
-        src[0] = buffer[0];
-        src[1] = buffer[1];
-        src[2] = buffer[2];
-    }
-}
-
 static bool
 IsSafeImageTransformComponent(gfxFloat aValue)
 {
   return aValue >= -32768 && aValue <= 32767;
 }
 
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
 /**
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -43,17 +43,16 @@ public:
     static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface,
                                         gfxImageSurface *aDestSurface = nullptr);
     static void UnpremultiplyImageSurface(gfxImageSurface *aSurface,
                                           gfxImageSurface *aDestSurface = nullptr);
     static mozilla::TemporaryRef<DataSourceSurface> UnpremultiplyDataSurface(DataSourceSurface* aSurface);
 
     static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
                                   gfxImageSurface *aDestSurface = nullptr);
-    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.
      *
      * The input parameters here usually come from the output of our image
      * snapping algorithm in nsLayoutUtils.cpp.