From 25c5294a43b2d79fe42667d0301d4d5815749e1a Mon Sep 17 00:00:00 2001 From: Morris Tseng Date: Fri, 18 Dec 2015 14:52:17 +0800 Subject: [PATCH] Bug 1172796 - Part 5: Implements OffscreenCanvas::TransferImageBitmap. r=roc r=smaug --HG-- extra : commitid : 4XzkGVfRcHJ --- dom/canvas/ImageBitmap.cpp | 29 ++++++++ dom/canvas/ImageBitmap.h | 6 ++ dom/canvas/OffscreenCanvas.cpp | 27 ++++++++ dom/canvas/OffscreenCanvas.h | 6 ++ dom/canvas/test/mochitest.ini | 2 + dom/canvas/test/offscreencanvas.js | 21 ++++++ .../test_offscreencanvas_toimagebitmap.html | 69 +++++++++++++++++++ dom/webidl/OffscreenCanvas.webidl | 2 + layout/base/nsLayoutUtils.cpp | 38 ++++++++++ layout/base/nsLayoutUtils.h | 6 ++ 10 files changed, 206 insertions(+) create mode 100644 dom/canvas/test/test_offscreencanvas_toimagebitmap.html diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index ee6e62b31e68..ad91f568f2e7 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -518,6 +518,35 @@ ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal, return ret.forget(); } +/* static */ already_AddRefed +ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal, + OffscreenCanvas& aOffscreenCanvas, + ErrorResult& aRv) +{ + // Check origin-clean. + if (aOffscreenCanvas.IsWriteOnly()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + + nsLayoutUtils::SurfaceFromElementResult res = + nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas, + nsLayoutUtils::SFE_WANT_FIRST_FRAME); + + RefPtr surface = res.GetSourceSurface(); + + if (NS_WARN_IF(!surface)) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return nullptr; + } + + RefPtr data = + CreateImageFromSurface(surface); + + RefPtr ret = new ImageBitmap(aGlobal, data); + return ret.forget(); +} + /* static */ already_AddRefed ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl, const Maybe& aCropRect, ErrorResult& aRv) diff --git a/dom/canvas/ImageBitmap.h b/dom/canvas/ImageBitmap.h index b4639ba902a5..9ac76e15748f 100644 --- a/dom/canvas/ImageBitmap.h +++ b/dom/canvas/ImageBitmap.h @@ -34,6 +34,7 @@ class Image; } namespace dom { +class OffscreenCanvas; namespace workers { class WorkerStructuredCloneClosure; @@ -103,6 +104,11 @@ public: static already_AddRefed CreateFromCloneData(nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData); + static already_AddRefed + CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal, + OffscreenCanvas& aOffscreenCanvas, + ErrorResult& aRv); + static already_AddRefed Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc, const Maybe& aCropRect, ErrorResult& aRv); diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index cd83dccb3d1b..e579799b77fe 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -8,6 +8,7 @@ #include "mozilla/dom/OffscreenCanvasBinding.h" #include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerScope.h" #include "mozilla/layers/AsyncCanvasRenderer.h" #include "mozilla/layers/CanvasClient.h" #include "mozilla/layers/ImageBridgeChild.h" @@ -213,6 +214,23 @@ OffscreenCanvas::ToCloneData() mCompositorBackendType, mNeutered, mIsWriteOnly); } +already_AddRefed +OffscreenCanvas::TransferToImageBitmap() +{ + ErrorResult rv; + RefPtr result = ImageBitmap::CreateFromOffscreenCanvas(GetGlobalObject(), *this, rv); + + // Clear the content. + if ((mCurrentContextType == CanvasContextType::WebGL1 || + mCurrentContextType == CanvasContextType::WebGL2)) + { + WebGLContext* webGL = static_cast(mCurrentContext.get()); + webGL->ClearScreen(); + } + + return result.forget(); +} + already_AddRefed OffscreenCanvas::ToBlob(JSContext* aCx, const nsAString& aType, @@ -280,6 +298,15 @@ OffscreenCanvas::ToBlob(JSContext* aCx, return promise.forget(); } +already_AddRefed +OffscreenCanvas::GetSurfaceSnapshot(bool* aPremultAlpha) +{ + if (!mCurrentContext) { + return nullptr; + } + + return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha); +} nsCOMPtr OffscreenCanvas::GetGlobalObject() diff --git a/dom/canvas/OffscreenCanvas.h b/dom/canvas/OffscreenCanvas.h index d2491e03ccc6..520149e55b09 100644 --- a/dom/canvas/OffscreenCanvas.h +++ b/dom/canvas/OffscreenCanvas.h @@ -26,6 +26,7 @@ class CanvasClient; namespace dom { class Blob; +class ImageBitmap; // This is helper class for transferring OffscreenCanvas to worker thread. // Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen- @@ -109,6 +110,9 @@ public: } } + already_AddRefed + TransferToImageBitmap(); + already_AddRefed ToBlob(JSContext* aCx, const nsAString& aType, @@ -120,6 +124,8 @@ public: return mCurrentContext; } + already_AddRefed GetSurfaceSnapshot(bool* aPremultAlpha = nullptr); + static already_AddRefed CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData); diff --git a/dom/canvas/test/mochitest.ini b/dom/canvas/test/mochitest.ini index 63bce5190627..f3653d922fdb 100644 --- a/dom/canvas/test/mochitest.ini +++ b/dom/canvas/test/mochitest.ini @@ -270,6 +270,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965 [test_filter.html] [test_offscreencanvas_toblob.html] tags = offscreencanvas +[test_offscreencanvas_toimagebitmap.html] +tags = offscreencanvas [test_offscreencanvas_basic_webgl.html] tags = offscreencanvas [test_offscreencanvas_dynamic_fallback.html] diff --git a/dom/canvas/test/offscreencanvas.js b/dom/canvas/test/offscreencanvas.js index e5fd7384a28e..f530bc280caf 100644 --- a/dom/canvas/test/offscreencanvas.js +++ b/dom/canvas/test/offscreencanvas.js @@ -39,6 +39,14 @@ function sendBlob(blob) { postMessageGeneral({type: "blob", blob: blob}); } +function sendImageBitmap(img) { + if (port) { + port.postMessage({type: "imagebitmap", bitmap: img}); + } else { + postMessage({type: "imagebitmap", bitmap: img}); + } +} + //-------------------------------------------------------------------- // WebGL Drawing Functions //-------------------------------------------------------------------- @@ -243,6 +251,19 @@ function entryFunction(testStr, subtests, offscreenCanvas) { }); } //------------------------------------------------------------------------ + // Test toImageBitmap + //------------------------------------------------------------------------ + else if (test == "webgl_imagebitmap") { + draw = createDrawFunc(canvas); + if (!draw) { + return; + } + + draw("", false); + var imgBitmap = canvas.transferToImageBitmap(); + sendImageBitmap(imgBitmap); + } + //------------------------------------------------------------------------ // Canvas Size Change from Worker //------------------------------------------------------------------------ else if (test == "webgl_changesize") { diff --git a/dom/canvas/test/test_offscreencanvas_toimagebitmap.html b/dom/canvas/test/test_offscreencanvas_toimagebitmap.html new file mode 100644 index 000000000000..a7dd30eab470 --- /dev/null +++ b/dom/canvas/test/test_offscreencanvas_toimagebitmap.html @@ -0,0 +1,69 @@ + + + +WebGL in OffscreenCanvas + + + + + + + + + + diff --git a/dom/webidl/OffscreenCanvas.webidl b/dom/webidl/OffscreenCanvas.webidl index 2e2c27bf5d08..ffe2b91473f1 100644 --- a/dom/webidl/OffscreenCanvas.webidl +++ b/dom/webidl/OffscreenCanvas.webidl @@ -19,6 +19,8 @@ interface OffscreenCanvas : EventTarget { [Throws] nsISupports? getContext(DOMString contextId, optional any contextOptions = null); + + ImageBitmap transferToImageBitmap(); [Throws] Promise toBlob(optional DOMString type = "", optional any encoderOptions); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 4398c9006355..d67cbcab316e 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -6886,6 +6886,44 @@ nsLayoutUtils::IsReallyFixedPos(nsIFrame* aFrame) parentType == nsGkAtoms::pageContentFrame; } +nsLayoutUtils::SurfaceFromElementResult +nsLayoutUtils::SurfaceFromOffscreenCanvas(OffscreenCanvas* aOffscreenCanvas, + uint32_t aSurfaceFlags, + DrawTarget* aTarget) +{ + SurfaceFromElementResult result; + + bool* isPremultiplied = nullptr; + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { + isPremultiplied = &result.mIsPremultiplied; + } + + nsIntSize size = aOffscreenCanvas->GetWidthHeight(); + + result.mSourceSurface = aOffscreenCanvas->GetSurfaceSnapshot(isPremultiplied); + if (!result.mSourceSurface) { + // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just + // draw nothing, so return an empty surface. + DrawTarget *ref = aTarget ? aTarget : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height), + SurfaceFormat::B8G8R8A8); + if (dt) { + result.mSourceSurface = dt->Snapshot(); + } + } else if (aTarget) { + RefPtr opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); + if (opt) { + result.mSourceSurface = opt; + } + } + + result.mHasSize = true; + result.mSize = size; + result.mIsWriteOnly = aOffscreenCanvas->IsWriteOnly(); + + return result; +} + nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, uint32_t aSurfaceFlags, diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 06fa26df6f8a..0ecf54ff66d6 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -74,6 +74,7 @@ class Element; class HTMLImageElement; class HTMLCanvasElement; class HTMLVideoElement; +class OffscreenCanvas; class Selection; } // namespace dom namespace gfx { @@ -2115,6 +2116,11 @@ public: const RefPtr& GetSourceSurface(); }; + // This function can be called on any thread. + static SurfaceFromElementResult + SurfaceFromOffscreenCanvas(mozilla::dom::OffscreenCanvas *aOffscreenCanvas, + uint32_t aSurfaceFlags = 0, + DrawTarget *aTarget = nullptr); static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::Element *aElement, uint32_t aSurfaceFlags = 0, DrawTarget *aTarget = nullptr);