Bug 982480 - Add support + test case for context alpha attribute. r=roc
authorRik Cabanier <cabanier@adobe.com>
Sun, 16 Mar 2014 15:34:14 -0400
changeset 192060 e25f1c48cf0c5836e01f1e47d165101fdcabf6e6
parent 192059 1387783130960ef31af3886edb2a0bc1eb1866ba
child 192061 5870a4beef4d29fae596c5abb71cedafea1753b7
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs982480
milestone30.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 982480 - Add support + test case for context alpha attribute. r=roc
content/canvas/public/nsICanvasRenderingContextInternal.h
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/src/WebGLContext.h
content/canvas/test/test_canvas.html
content/html/content/src/HTMLCanvasElement.cpp
dom/webidl/CanvasRenderingContext2D.webidl
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -92,16 +92,17 @@ public:
   // of the canvas at the time it was called.
   virtual mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() = 0;
 
   // If this context is opaque, the backing store of the canvas should
   // be created as opaque; all compositing operators should assume the
   // dst alpha is always 1.0.  If this is never called, the context
   // defaults to false (not opaque).
   NS_IMETHOD SetIsOpaque(bool isOpaque) = 0;
+  virtual bool GetIsOpaque() = 0;
 
   // Invalidate this context and release any held resources, in preperation
   // for possibly reinitializing with SetDimensions/InitializeWithSurface.
   NS_IMETHOD Reset() = 0;
 
   // Return the CanvasLayer for this context, creating
   // one for the given layer manager if not available.
   virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -983,16 +983,20 @@ CanvasRenderingContext2D::InitializeWith
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetIsOpaque(bool isOpaque)
 {
   if (isOpaque != mOpaque) {
     mOpaque = isOpaque;
     ClearTarget();
   }
 
+  if (mOpaque) {
+    EnsureTarget();
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetIsIPC(bool isIPC)
 {
   if (isIPC != mIPC) {
     mIPC = isIPC;
@@ -1057,16 +1061,20 @@ CanvasRenderingContext2D::SetContextOpti
   ContextAttributes2D attributes;
   NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
 
   if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
     // Use software when there is going to be a lot of readback
     mForceSoftware = attributes.mWillReadFrequently;
   }
 
+  if (!attributes.mAlpha) {
+    SetIsOpaque(true);
+  }
+
   return NS_OK;
 }
 
 void
 CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
                                          int32_t* aFormat)
 {
   *aImageBuffer = nullptr;
@@ -3802,16 +3810,40 @@ CanvasRenderingContext2D::GetImageDataAr
   }
 
   // NOTE! dst is the same as src, and this relies on reading
   // from src and advancing that ptr before writing to dst.
   // NOTE! I'm not sure that it is, I think this comment might have been
   // inherited from Thebes canvas and is no longer true
   uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
 
+  if (mOpaque) {
+    for (int32_t j = 0; j < dstWriteRect.height; ++j) {
+      for (int32_t i = 0; i < dstWriteRect.width; ++i) {
+        // XXX Is there some useful swizzle MMX we can use here?
+#if MOZ_LITTLE_ENDIAN
+        uint8_t b = *src++;
+        uint8_t g = *src++;
+        uint8_t r = *src++;
+        src++;
+#else
+        src++;
+        uint8_t r = *src++;
+        uint8_t g = *src++;
+        uint8_t b = *src++;
+#endif
+        *dst++ = r;
+        *dst++ = g;
+        *dst++ = b;
+        *dst++ = 255;
+      }
+      src += srcStride - (dstWriteRect.width * 4);
+      dst += (aWidth * 4) - (dstWriteRect.width * 4);
+    }
+  } else
   for (int32_t j = 0; j < dstWriteRect.height; ++j) {
     for (int32_t i = 0; i < dstWriteRect.width; ++i) {
       // XXX Is there some useful swizzle MMX we can use here?
 #if MOZ_LITTLE_ENDIAN
       uint8_t b = *src++;
       uint8_t g = *src++;
       uint8_t r = *src++;
       uint8_t a = *src++;
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -403,16 +403,17 @@ public:
                             const char16_t* aEncoderOptions,
                             nsIInputStream **aStream) MOZ_OVERRIDE;
   NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
 
   mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE
   { EnsureTarget(); return mTarget->Snapshot(); }
 
   NS_IMETHOD SetIsOpaque(bool isOpaque) MOZ_OVERRIDE;
+  bool GetIsOpaque() MOZ_OVERRIDE { return mOpaque; }
   NS_IMETHOD Reset() MOZ_OVERRIDE;
   already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                                CanvasLayer *aOldLayer,
                                                LayerManager *aManager) MOZ_OVERRIDE;
   virtual bool ShouldForceInactiveLayer(LayerManager *aManager) MOZ_OVERRIDE;
   void MarkContextClean() MOZ_OVERRIDE;
   NS_IMETHOD SetIsIPC(bool isIPC) MOZ_OVERRIDE;
   // this rect is in canvas device space
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -173,16 +173,17 @@ public:
     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() MOZ_OVERRIDE;
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
+    bool GetIsOpaque() MOZ_OVERRIDE { return false; }
     NS_IMETHOD SetContextOptions(JSContext* aCx,
                                  JS::Handle<JS::Value> aOptions) MOZ_OVERRIDE;
 
     NS_IMETHOD SetIsIPC(bool b) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack,
                     int32_t x, int32_t y, int32_t w, int32_t h)
                     { return NS_ERROR_NOT_IMPLEMENTED; }
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -21572,16 +21572,34 @@ function test_linedash() {
   isPixel(ctx, 120, 25, 0, 255, 0, 255, 0);
   isPixel(ctx, 115, 40, 0, 0, 0, 0, 0);
   isPixel(ctx, 105, 40, 0, 255, 0, 255, 0);
   isPixel(ctx, 90, 35, 0, 0, 0, 0, 0);
   isPixel(ctx, 90, 25, 0, 255, 0, 255, 0);
 }
 </script>
 
+<p>Canvas test: test_opaque</p>
+<canvas id="c688" width="150" height="50"></canvas>
+<script type="text/javascript">
+
+function test_opaque() {
+  var c = document.getElementById("c688");
+  var ctx = c.getContext("2d", {alpha: false});
+  ctx.fillStyle = "green";
+  ctx.fillRect(0,0,10,10);
+  ctx.fillStyle = "rgba(255,0,0,.5)";
+  ctx.fillRect(10,0,10,10);
+
+  isPixel(ctx, 20, 20, 0, 0, 0, 255, 0);
+  isPixel(ctx, 5, 5, 0, 128, 0, 255, 0);
+  isPixel(ctx, 15, 5, 128, 0, 0, 255, 0);
+}
+</script>
+
 <script>
 
 function asyncTestsDone() {
 	if (isDone_test_2d_drawImage_animated_apng &&
 		isDone_test_2d_drawImage_animated_gif) {
 		SimpleTest.finish();
 	} else {
 		setTimeout(asyncTestsDone, 500);
@@ -24868,16 +24886,22 @@ function runTests() {
   ok(false, "unexpected exception thrown in: test_getImageData_after_zero_canvas");
  }
  try {
   test_linedash();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_linedash");
  }
+try {
+  test_opaque();
+ } catch(e) {
+  throw e;
+  ok(false, "unexpected exception thrown in: test_opaque");
+ }
  try {
   // run this test last since it replaces the getContext method
   test_type_replace();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_type_replace");
  }
  
  //run the asynchronous tests
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -776,17 +776,17 @@ HTMLCanvasElement::MozGetIPCContext(cons
 nsresult
 HTMLCanvasElement::UpdateContext(JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions)
 {
   if (!mCurrentContext)
     return NS_OK;
 
   nsIntSize sz = GetWidthHeight();
 
-  nsresult rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
+  nsresult rv = mCurrentContext->SetIsOpaque(HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque));
   if (NS_FAILED(rv)) {
     mCurrentContext = nullptr;
     mCurrentContextId.Truncate();
     return rv;
   }
 
   rv = mCurrentContext->SetContextOptions(aCx, aNewContextOptions);
   if (NS_FAILED(rv)) {
@@ -898,16 +898,20 @@ HTMLCanvasElement::GetContextAtIndex(int
     return mCurrentContext;
 
   return nullptr;
 }
 
 bool
 HTMLCanvasElement::GetIsOpaque()
 {
+  if (mCurrentContext) {
+    return mCurrentContext->GetIsOpaque();
+  }
+
   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
 }
 
 already_AddRefed<CanvasLayer>
 HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                   CanvasLayer *aOldLayer,
                                   LayerManager *aManager)
 {
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -11,16 +11,18 @@
  * and create derivative works of this document.
  */
 
 enum CanvasWindingRule { "nonzero", "evenodd" };
 
 dictionary ContextAttributes2D {
   // whether or not we're planning to do a lot of readback operations
   boolean willReadFrequently = false;
+  // signal if the canvas contains an alpha channel
+  boolean alpha = true;
 };
 
 dictionary HitRegionOptions {
   DOMString id = "";
   Element? control = null;
 };
 
 interface CanvasRenderingContext2D {