Bug 1172796 - Part 7: Implements ImageBitmapRenderingContext. r=roc r=smaug

This commit is contained in:
Morris Tseng 2015-12-18 14:52:17 +08:00
parent 597a19a423
commit b14d03ab1a
21 changed files with 690 additions and 43 deletions

View File

@ -5579,9 +5579,9 @@ CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
return mBufferProvider;
}
already_AddRefed<CanvasLayer>
already_AddRefed<Layer>
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
Layer *aOldLayer,
LayerManager *aManager)
{
if (mOpaque || mIsSkiaGL) {
@ -5624,8 +5624,10 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
data.mBufferProvider = provider;
}
if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
RefPtr<CanvasLayer> ret = aOldLayer;
if (userData &&
userData->IsForContext(this) &&
static_cast<CanvasLayer*>(aOldLayer)->IsDataValid(data)) {
RefPtr<Layer> ret = aOldLayer;
return ret.forget();
}
}

View File

@ -459,9 +459,9 @@ public:
bool GetIsOpaque() override { return mOpaque; }
NS_IMETHOD Reset() override;
mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
LayerManager *aManager) override;
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager) override;
virtual bool ShouldForceInactiveLayer(LayerManager *aManager) override;
void MarkContextClean() override;
void MarkContextCleanForFrameCapture() override;

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CanvasRenderingContextHelper.h"
#include "ImageBitmapRenderingContext.h"
#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/Telemetry.h"
@ -151,6 +152,11 @@ CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
return nullptr;
break;
case CanvasContextType::ImageBitmap:
ret = new ImageBitmapRenderingContext();
break;
}
MOZ_ASSERT(ret);

View File

@ -25,7 +25,8 @@ enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
WebGL1,
WebGL2
WebGL2,
ImageBitmap
};
/**

View File

@ -61,6 +61,11 @@ GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_typ
}
}
if (str.EqualsLiteral("bitmaprenderer")) {
*out_type = dom::CanvasContextType::ImageBitmap;
return true;
}
return false;
}

View File

@ -505,6 +505,14 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
return surface.forget();
}
already_AddRefed<layers::Image>
ImageBitmap::TransferAsImage()
{
RefPtr<layers::Image> image = mData;
Close();
return image.forget();
}
ImageBitmapCloneData*
ImageBitmap::ToCloneData()
{

View File

@ -100,6 +100,13 @@ public:
already_AddRefed<gfx::SourceSurface>
PrepareForDrawTarget(gfx::DrawTarget* aTarget);
/*
* Transfer ownership of buffer to caller. So this function call
* Close() implicitly.
*/
already_AddRefed<layers::Image>
TransferAsImage();
ImageBitmapCloneData*
ToCloneData();

View File

@ -0,0 +1,303 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageBitmapRenderingContext.h"
#include "mozilla/dom/ImageBitmapRenderingContextBinding.h"
#include "ImageContainer.h"
#include "ImageLayers.h"
namespace mozilla {
namespace dom {
ImageBitmapRenderingContext::ImageBitmapRenderingContext()
: mWidth(0)
, mHeight(0)
{
}
ImageBitmapRenderingContext::~ImageBitmapRenderingContext()
{
RemovePostRefreshObserver();
}
JSObject*
ImageBitmapRenderingContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ImageBitmapRenderingContextBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<layers::Image>
ImageBitmapRenderingContext::ClipToIntrinsicSize()
{
if (!mImage) {
return nullptr;
}
// If image is larger than canvas intrinsic size, clip it to the intrinsic size.
RefPtr<gfx::SourceSurface> surface;
RefPtr<layers::Image> result;
if (mWidth < mImage->GetSize().width ||
mHeight < mImage->GetSize().height) {
surface = MatchWithIntrinsicSize();
} else {
surface = mImage->GetAsSourceSurface();
}
result = new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface);
return result.forget();
}
void
ImageBitmapRenderingContext::TransferImageBitmap(ImageBitmap& aImageBitmap)
{
Reset();
mImage = aImageBitmap.TransferAsImage();
if (!mImage) {
return;
}
Redraw(gfxRect(0, 0, mWidth, mHeight));
}
int32_t
ImageBitmapRenderingContext::GetWidth() const
{
return mWidth;
}
int32_t
ImageBitmapRenderingContext::GetHeight() const
{
return mHeight;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
{
mWidth = aWidth;
mHeight = aHeight;
return NS_OK;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::InitializeWithSurface(nsIDocShell* aDocShell,
gfxASurface* aSurface,
int32_t aWidth,
int32_t aHeight)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
already_AddRefed<DataSourceSurface>
ImageBitmapRenderingContext::MatchWithIntrinsicSize()
{
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
RefPtr<DataSourceSurface> temp =
Factory::CreateDataSourceSurface(IntSize(mWidth, mHeight), surface->GetFormat());
if (!temp) {
return nullptr;
}
DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
if (!map.IsMapped()) {
return nullptr;
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
map.GetData(),
temp->GetSize(),
map.GetStride(),
temp->GetFormat());
if (!dt) {
return nullptr;
}
dt->ClearRect(Rect(0, 0, mWidth, mHeight));
dt->CopySurface(surface,
IntRect(0, 0, surface->GetSize().width,
surface->GetSize().height),
IntPoint(0, 0));
return temp.forget();
}
mozilla::UniquePtr<uint8_t[]>
ImageBitmapRenderingContext::GetImageBuffer(int32_t* aFormat)
{
*aFormat = 0;
if (!mImage) {
return nullptr;
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
RefPtr<DataSourceSurface> data = surface->GetDataSurface();
if (!data) {
return nullptr;
}
if (data->GetSize() != IntSize(mWidth, mHeight)) {
data = MatchWithIntrinsicSize();
}
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
return SurfaceToPackedBGRA(data);
}
NS_IMETHODIMP
ImageBitmapRenderingContext::GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream)
{
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += aMimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder) {
return NS_ERROR_FAILURE;
}
int32_t format = 0;
UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
if (!imageBuffer) {
return NS_ERROR_FAILURE;
}
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(), format,
encoder, aEncoderOptions, aStream);
}
already_AddRefed<mozilla::gfx::SourceSurface>
ImageBitmapRenderingContext::GetSurfaceSnapshot(bool* aPremultAlpha)
{
if (!mImage) {
return nullptr;
}
if (aPremultAlpha) {
*aPremultAlpha = true;
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
if (surface->GetSize() != IntSize(mWidth, mHeight)) {
return MatchWithIntrinsicSize();
}
return surface.forget();
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque)
{
return NS_OK;
}
bool
ImageBitmapRenderingContext::GetIsOpaque()
{
return false;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::Reset()
{
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
mImage = nullptr;
return NS_OK;
}
already_AddRefed<Layer>
ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager)
{
if (!mImage) {
// No DidTransactionCallback will be received, so mark the context clean
// now so future invalidations will be dispatched.
MarkContextClean();
return nullptr;
}
RefPtr<ImageLayer> imageLayer;
if (aOldLayer) {
imageLayer = static_cast<ImageLayer*>(aOldLayer);
} else {
imageLayer = aManager->CreateImageLayer();
}
RefPtr<ImageContainer> imageContainer = imageLayer->GetContainer();
if (!imageContainer) {
imageContainer = aManager->CreateImageContainer();
imageLayer->SetContainer(imageContainer);
}
nsAutoTArray<ImageContainer::NonOwningImage, 1> imageList;
RefPtr<layers::Image> image = ClipToIntrinsicSize();
imageList.AppendElement(ImageContainer::NonOwningImage(image));
imageContainer->SetCurrentImages(imageList);
return imageLayer.forget();
}
void
ImageBitmapRenderingContext::MarkContextClean()
{
}
NS_IMETHODIMP
ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty)
{
if (!mCanvasElement) {
return NS_OK;
}
mozilla::gfx::Rect rect = ToRect(aDirty);
mCanvasElement->InvalidateCanvasContent(&rect);
return NS_OK;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC)
{
return NS_OK;
}
void
ImageBitmapRenderingContext::DidRefresh()
{
}
void
ImageBitmapRenderingContext::MarkContextCleanForFrameCapture()
{
}
bool
ImageBitmapRenderingContext::IsContextCleanForFrameCapture()
{
return true;
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmapRenderingContext,
mCanvasElement,
mOffscreenCanvas)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
}
}

View File

@ -0,0 +1,97 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ImageBitmapRenderingContext_h
#define ImageBitmapRenderingContext_h
#include "nsICanvasRenderingContextInternal.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace gfx {
class DataSourceSurface;
class SourceSurface;
}
namespace layers {
class Image;
class ImageContainer;
}
namespace dom {
/**
* The purpose of ImageBitmapRenderingContext is to provide a faster and efficient
* way to display ImageBitmap. Simply call TransferImageBitmap() then we'll transfer
* the surface of ImageBitmap to this context and then to use it to display.
*
* See more details in spec: https://wiki.whatwg.org/wiki/OffscreenCanvas
*/
class ImageBitmapRenderingContext final :
public nsICanvasRenderingContextInternal,
public nsWrapperCache
{
virtual ~ImageBitmapRenderingContext();
public:
ImageBitmapRenderingContext();
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// nsISupports interface + CC
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ImageBitmapRenderingContext)
void TransferImageBitmap(ImageBitmap& aImageBitmap);
// nsICanvasRenderingContextInternal
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
NS_IMETHOD InitializeWithSurface(nsIDocShell* aDocShell,
gfxASurface* aSurface,
int32_t aWidth,
int32_t aHeight) override;
virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream) override;
virtual already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override;
NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
virtual bool GetIsOpaque() override;
NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager) override;
virtual void MarkContextClean() override;
NS_IMETHOD Redraw(const gfxRect& aDirty) override;
NS_IMETHOD SetIsIPC(bool aIsIPC) override;
virtual void DidRefresh() override;
virtual void MarkContextCleanForFrameCapture() override;
virtual bool IsContextCleanForFrameCapture() override;
protected:
already_AddRefed<gfx::DataSourceSurface> MatchWithIntrinsicSize();
already_AddRefed<layers::Image> ClipToIntrinsicSize();
int32_t mWidth;
int32_t mHeight;
RefPtr<layers::Image> mImage;
};
}
}
#endif /* ImageBitmapRenderingContext_h */

View File

@ -121,7 +121,8 @@ OffscreenCanvas::GetContext(JSContext* aCx,
}
if (!(contextType == CanvasContextType::WebGL1 ||
contextType == CanvasContextType::WebGL2))
contextType == CanvasContextType::WebGL2 ||
contextType == CanvasContextType::ImageBitmap))
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
@ -138,28 +139,31 @@ OffscreenCanvas::GetContext(JSContext* aCx,
}
if (mCanvasRenderer) {
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
gl::GLContext* gl = webGL->GL();
mCanvasRenderer->mContext = mCurrentContext;
mCanvasRenderer->SetActiveThread();
mCanvasRenderer->mGLContext = gl;
mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
if (contextType == CanvasContextType::WebGL1 ||
contextType == CanvasContextType::WebGL2) {
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
gl::GLContext* gl = webGL->GL();
mCanvasRenderer->mContext = mCurrentContext;
mCanvasRenderer->SetActiveThread();
mCanvasRenderer->mGLContext = gl;
mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
if (ImageBridgeChild::IsCreated()) {
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
mCanvasClient = ImageBridgeChild::GetSingleton()->
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
mCanvasRenderer->SetCanvasClient(mCanvasClient);
if (ImageBridgeChild::IsCreated()) {
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
mCanvasClient = ImageBridgeChild::GetSingleton()->
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
mCanvasRenderer->SetCanvasClient(mCanvasClient);
gl::GLScreenBuffer* screen = gl->Screen();
gl::SurfaceCaps caps = screen->mCaps;
auto forwarder = mCanvasClient->GetForwarder();
gl::GLScreenBuffer* screen = gl->Screen();
gl::SurfaceCaps caps = screen->mCaps;
auto forwarder = mCanvasClient->GetForwarder();
UniquePtr<gl::SurfaceFactory> factory =
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
UniquePtr<gl::SurfaceFactory> factory =
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
if (factory)
screen->Morph(Move(factory));
if (factory)
screen->Morph(Move(factory));
}
}
}

View File

@ -1131,9 +1131,9 @@ private:
RefPtr<HTMLCanvasElement> mCanvas;
};
already_AddRefed<layers::CanvasLayer>
already_AddRefed<layers::Layer>
WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
CanvasLayer* oldLayer,
Layer* oldLayer,
LayerManager* manager)
{
if (IsContextLost())
@ -1141,7 +1141,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
if (!mResetLayer && oldLayer &&
oldLayer->HasUserData(&gWebGLLayerUserData)) {
RefPtr<layers::CanvasLayer> ret = oldLayer;
RefPtr<layers::Layer> ret = oldLayer;
return ret.forget();
}

View File

@ -331,8 +331,8 @@ public:
return ActiveBoundTextureForTarget(texTarget);
}
already_AddRefed<CanvasLayer>
GetCanvasLayer(nsDisplayListBuilder* builder, CanvasLayer* oldLayer,
already_AddRefed<Layer>
GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer,
LayerManager* manager) override;
// Note that 'clean' here refers to its invalidation state, not the

View File

@ -34,6 +34,7 @@ EXPORTS.mozilla.dom += [
'CanvasRenderingContextHelper.h',
'CanvasUtils.h',
'ImageBitmap.h',
'ImageBitmapRenderingContext.h',
'ImageBitmapSource.h',
'ImageData.h',
'OffscreenCanvas.h',
@ -50,6 +51,7 @@ UNIFIED_SOURCES += [
'DocumentRendererChild.cpp',
'DocumentRendererParent.cpp',
'ImageBitmap.cpp',
'ImageBitmapRenderingContext.cpp',
'ImageData.cpp',
'OffscreenCanvas.cpp',
]

View File

@ -26,6 +26,7 @@ class nsDisplayListBuilder;
namespace mozilla {
namespace layers {
class CanvasLayer;
class Layer;
class LayerManager;
} // namespace layers
namespace gfx {
@ -39,6 +40,7 @@ class nsICanvasRenderingContextInternal :
{
public:
typedef mozilla::layers::CanvasLayer CanvasLayer;
typedef mozilla::layers::Layer Layer;
typedef mozilla::layers::LayerManager LayerManager;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
@ -129,9 +131,9 @@ public:
// Return the CanvasLayer for this context, creating
// one for the given layer manager if not available.
virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* builder,
CanvasLayer *oldLayer,
LayerManager *manager) = 0;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* builder,
Layer *oldLayer,
LayerManager *manager) = 0;
// Return true if the canvas should be forced to be "inactive" to ensure
// it can be drawn to the screen even if it's too large to be blitted by

View File

@ -218,6 +218,7 @@ skip-if = toolkit != 'cocoa'
[test_2d.path.rect.zero.6.html]
disabled = bug 407107
[test_2d.strokeRect.zero.5.html]
[test_bitmaprenderer.html]
[test_bug232227.html]
[test_bug613794.html]
[test_bug753758.html]

View File

@ -0,0 +1,163 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<script type="text/js-worker">
function ok(expect, msg) {
postMessage({"type": "status", status: !!expect, msg: msg});
}
onmessage = function(event) {
var bitmap = event.data.bitmap;
ok(!!bitmap, "Get the ImageBitmap from the main script.");
var offscreenCanvas = new OffscreenCanvas(64, 64);
var ctx = offscreenCanvas.getContext('bitmaprenderer');
ok(!!ctx, "Get bitmaprenderer context on worker.");
ctx.transferImageBitmap(bitmap);
var resultBitmap = offscreenCanvas.transferToImageBitmap();
postMessage({"type": "bitmap", bitmap: resultBitmap}, [resultBitmap]);
}
</script>
<script>
SimpleTest.waitForExplicitFinish();
function createCanvas(width, height) {
var htmlCanvas = document.createElement('canvas');
htmlCanvas.width = width;
htmlCanvas.height = height;
document.body.appendChild(htmlCanvas);
return htmlCanvas;
}
function runTest(canvasWidth, canvasHeight, nextTest) {
var canvas1 = createCanvas(canvasWidth, canvasHeight);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
var canvasRef = createCanvas(90, 90);
var ctx = canvasRef.getContext("2d");
// Clear with black transparent first
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(0, 0, 90, 90);
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
createImageBitmap(canvas1).then(function(bmp) {
document.body.removeChild(canvas1);
var canvas2 = createCanvas(90, 90);
var ctx2 = canvas2.getContext("bitmaprenderer");
ctx2.transferImageBitmap(bmp);
ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
// Exam render result
canvasRef.style.display = "none";
canvas2.style.display = "block";
var snapshot = snapshotWindow(window);
canvasRef.style.display = "block";
canvas2.style.display = "none";
var snapshotRef = snapshotWindow(window);
var results = compareSnapshots(snapshot, snapshotRef, true);
ok(results[0], "Screenshots should be the same");
document.body.removeChild(canvasRef);
document.body.removeChild(canvas2);
nextTest();
});
}
function scaleTest() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "#00FF00";
ctx2.fillRect(0, 0, 64, 64);
var p1 = createImageBitmap(canvas1);
var p2 = createImageBitmap(canvas2);
Promise.all([p1, p2]).then(function(bitmaps) {
document.body.removeChild(canvas1);
document.body.removeChild(canvas2);
// Create a large canvas then shrink.
var canvas3 = createCanvas(128, 128);
var ctx3 = canvas3.getContext("bitmaprenderer");
ctx3.transferImageBitmap(bitmaps[0]);
var snapshotLargeRef = snapshotWindow(window);
canvas3.width = 32;
canvas3.height = 32;
var snapshotSmall = snapshotWindow(window);
document.body.removeChild(canvas3);
// Create a small canvas then grow.
var canvas4 = createCanvas(32, 32);
var ctx4 = canvas4.getContext("bitmaprenderer");
ctx4.transferImageBitmap(bitmaps[1]);
var snapshotSmallRef = snapshotWindow(window);
canvas4.width = 128;
canvas4.height = 128;
var snapshotLarge = snapshotWindow(window);
document.body.removeChild(canvas4);
var resultsLarge = compareSnapshots(snapshotLarge, snapshotLargeRef, true);
ok(resultsLarge[0], "Screenshots should be the same");
var resultsSmall = compareSnapshots(snapshotSmall, snapshotSmallRef, true);
ok(resultsSmall[0], "Screenshots should be the same");
runTestOnWorker();
});
}
function runTestOnWorker() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
var worker = new Worker(window.URL.createObjectURL(blob));
createImageBitmap(canvas1).then(function(bmp) {
worker.postMessage({bitmap: bmp}, [bmp]);
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "bitmap") {
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext('bitmaprenderer');
ctx2.transferImageBitmap(event.data.bitmap);
ok(canvas1.toDataURL() == canvas2.toDataURL(), 'toDataURL should be the same');
SimpleTest.finish();
}
}
});
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
]}, runTest.bind(this, 64, 64, runTest.bind(this, 128, 128, scaleTest)));
</script>
</body>
</html>

View File

@ -1046,9 +1046,9 @@ HTMLCanvasElement::GetOpaqueAttr()
return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
}
already_AddRefed<CanvasLayer>
already_AddRefed<Layer>
HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
Layer *aOldLayer,
LayerManager *aManager)
{
// The address of sOffscreenCanvasLayerUserDataDummy is used as the user
@ -1064,7 +1064,7 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
if (mOffscreenCanvas) {
if (!mResetLayer &&
aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
RefPtr<CanvasLayer> ret = aOldLayer;
RefPtr<Layer> ret = aOldLayer;
return ret.forget();
}

View File

@ -31,6 +31,7 @@ namespace layers {
class AsyncCanvasRenderer;
class CanvasLayer;
class Image;
class Layer;
class LayerManager;
} // namespace layers
namespace gfx {
@ -120,6 +121,7 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
typedef layers::CanvasLayer CanvasLayer;
typedef layers::Layer Layer;
typedef layers::LayerManager LayerManager;
public:
@ -308,9 +310,9 @@ public:
* Helpers called by various users of Canvas
*/
already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
LayerManager *aManager);
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager);
// Should return true if the canvas layer should always be marked inactive.
// We should return true here if we can't do accelerated compositing with
// a non-BasicCanvasLayer.

View File

@ -0,0 +1,36 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://wiki.whatwg.org/wiki/OffscreenCanvas
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
// The new ImageBitmapRenderingContext is a canvas rendering context
// which only provides the functionality to replace the canvas's
// contents with the given ImageBitmap. Its context id (the first argument
// to getContext) is "bitmaprenderer".
[Exposed=(Window,Worker)]
interface ImageBitmapRenderingContext {
// Displays the given ImageBitmap in the canvas associated with this
// rendering context. Ownership of the ImageBitmap is transferred to
// the canvas. The caller may not use its reference to the ImageBitmap
// after making this call. (This semantic is crucial to enable prompt
// reclamation of expensive graphics resources, rather than relying on
// garbage collection to do so.)
//
// The ImageBitmap conceptually replaces the canvas's bitmap, but
// it does not change the canvas's intrinsic width or height.
//
// The ImageBitmap, when displayed, is clipped to the rectangle
// defined by the canvas's instrinsic width and height. Pixels that
// would be covered by the canvas's bitmap which are not covered by
// the supplied ImageBitmap are rendered transparent black. Any CSS
// styles affecting the display of the canvas are applied as usual.
void transferImageBitmap(ImageBitmap bitmap);
};

View File

@ -265,6 +265,7 @@ WEBIDL_FILES = [
'IDBTransaction.webidl',
'IDBVersionChangeEvent.webidl',
'ImageBitmap.webidl',
'ImageBitmapRenderingContext.webidl',
'ImageCapture.webidl',
'ImageData.webidl',
'ImageDocument.webidl',

View File

@ -13,6 +13,7 @@
#include "nsDisplayList.h"
#include "nsLayoutUtils.h"
#include "nsStyleUtil.h"
#include "ImageLayers.h"
#include "Layers.h"
#include "ActiveLayerTracker.h"
@ -338,7 +339,7 @@ nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer* oldLayer = static_cast<CanvasLayer*>
(aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
RefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
RefPtr<Layer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
if (!layer)
return nullptr;
@ -357,7 +358,13 @@ nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
transform.PreScale(destGFXRect.Width() / canvasSizeInPx.width,
destGFXRect.Height() / canvasSizeInPx.height);
layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
if (layer->GetType() == layers::Layer::TYPE_CANVAS) {
RefPtr<CanvasLayer> canvasLayer = static_cast<CanvasLayer*>(layer.get());
canvasLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
} else if (layer->GetType() == layers::Layer::TYPE_IMAGE) {
RefPtr<ImageLayer> imageLayer = static_cast<ImageLayer*>(layer.get());
imageLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
}
return layer.forget();
}