Bug 1172796 - Part 5: Implements OffscreenCanvas::TransferImageBitmap. r=roc r=smaug

This commit is contained in:
Morris Tseng 2015-12-18 14:52:17 +08:00
parent 5648f31da4
commit 688e35290a
10 changed files with 206 additions and 0 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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()

View File

@ -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);

View File

@ -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]

View File

@ -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") {

View 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>

View File

@ -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);

View File

@ -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,

View File

@ -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);