mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1172796 - Part 5: Implements OffscreenCanvas::TransferImageBitmap. r=roc r=smaug
--HG-- extra : commitid : 4XzkGVfRcHJ
This commit is contained in:
parent
4493d55b04
commit
25c5294a43
@ -518,6 +518,35 @@ ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ImageBitmap>
|
||||
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<SourceSurface> surface = res.GetSourceSurface();
|
||||
|
||||
if (NS_WARN_IF(!surface)) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<layers::Image> data =
|
||||
CreateImageFromSurface(surface);
|
||||
|
||||
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ImageBitmap>
|
||||
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
|
||||
const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
|
||||
|
@ -34,6 +34,7 @@ class Image;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
class OffscreenCanvas;
|
||||
|
||||
namespace workers {
|
||||
class WorkerStructuredCloneClosure;
|
||||
@ -103,6 +104,11 @@ public:
|
||||
static already_AddRefed<ImageBitmap>
|
||||
CreateFromCloneData(nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData);
|
||||
|
||||
static already_AddRefed<ImageBitmap>
|
||||
CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
|
||||
OffscreenCanvas& aOffscreenCanvas,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
|
||||
const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
|
||||
|
@ -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<ImageBitmap>
|
||||
OffscreenCanvas::TransferToImageBitmap()
|
||||
{
|
||||
ErrorResult rv;
|
||||
RefPtr<ImageBitmap> result = ImageBitmap::CreateFromOffscreenCanvas(GetGlobalObject(), *this, rv);
|
||||
|
||||
// Clear the content.
|
||||
if ((mCurrentContextType == CanvasContextType::WebGL1 ||
|
||||
mCurrentContextType == CanvasContextType::WebGL2))
|
||||
{
|
||||
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
|
||||
webGL->ClearScreen();
|
||||
}
|
||||
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
OffscreenCanvas::ToBlob(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
@ -280,6 +298,15 @@ OffscreenCanvas::ToBlob(JSContext* aCx,
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::SourceSurface>
|
||||
OffscreenCanvas::GetSurfaceSnapshot(bool* aPremultAlpha)
|
||||
{
|
||||
if (!mCurrentContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject>
|
||||
OffscreenCanvas::GetGlobalObject()
|
||||
|
@ -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<ImageBitmap>
|
||||
TransferToImageBitmap();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ToBlob(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
@ -120,6 +124,8 @@ public:
|
||||
return mCurrentContext;
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
|
||||
|
||||
static already_AddRefed<OffscreenCanvas>
|
||||
CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData);
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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") {
|
||||
|
69
dom/canvas/test/test_offscreencanvas_toimagebitmap.html
Normal file
69
dom/canvas/test/test_offscreencanvas_toimagebitmap.html
Normal file
@ -0,0 +1,69 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<canvas id="c2" width="64" height="64"></canvas>
|
||||
<canvas id="c-ref" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "imagebitmap") {
|
||||
// testing toBlob
|
||||
// Fill c-ref with green color.
|
||||
var c = document.getElementById("c-ref");
|
||||
var ctx = c.getContext("2d");
|
||||
ctx.rect(0, 0, 64, 64);
|
||||
ctx.fillStyle = "#00FF00";
|
||||
ctx.fill();
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
|
||||
bitmapRenderer.transferImageBitmap(msg.bitmap);
|
||||
|
||||
ok(c.toDataURL() == htmlCanvas.toDataURL(),
|
||||
"imagebitmap should return a 64x64 green square");
|
||||
|
||||
// The ownership of msg.bitmap should be transferred to canvas "c" when
|
||||
// we called transferImageBitmap. So we test if the ownership is actually
|
||||
// transferred here.
|
||||
var htmlCanvas = document.getElementById("c2");
|
||||
var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
|
||||
bitmapRenderer.transferImageBitmap(msg.bitmap);
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { c2.toDataURL(); },
|
||||
"ImageBitmap has been transferred, toDataURL will throw.");
|
||||
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
worker.postMessage({test: 'webgl_imagebitmap'});
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,8 @@ interface OffscreenCanvas : EventTarget {
|
||||
[Throws]
|
||||
nsISupports? getContext(DOMString contextId,
|
||||
optional any contextOptions = null);
|
||||
|
||||
ImageBitmap transferToImageBitmap();
|
||||
[Throws]
|
||||
Promise<Blob> toBlob(optional DOMString type = "",
|
||||
optional any encoderOptions);
|
||||
|
@ -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<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (dt) {
|
||||
result.mSourceSurface = dt->Snapshot();
|
||||
}
|
||||
} else if (aTarget) {
|
||||
RefPtr<SourceSurface> 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,
|
||||
|
@ -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<mozilla::gfx::SourceSurface>& 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);
|
||||
|
Loading…
Reference in New Issue
Block a user