mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 709490 - Part 7: If layer is not available, fallback to BasicCanvasLayer. r=roc
--HG-- extra : rebase_source : cc7761567d60d652b8d0bc9cab04cf310ef100e3
This commit is contained in:
parent
83252e218f
commit
a8eab58fba
@ -8,6 +8,7 @@
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/unused.h"
|
||||
@ -165,6 +166,7 @@ public:
|
||||
mSize,
|
||||
mImage,
|
||||
nullptr,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
|
||||
@ -178,6 +180,7 @@ public:
|
||||
mSize,
|
||||
mImage,
|
||||
nullptr,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
}
|
||||
@ -234,6 +237,7 @@ ImageEncoder::ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
@ -242,10 +246,9 @@ ImageEncoder::ExtractData(nsAString& aType,
|
||||
}
|
||||
|
||||
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, nullptr,
|
||||
aContext, aStream, encoder);
|
||||
aContext, aRenderer, aStream, encoder);
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
|
||||
@ -341,6 +344,7 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
||||
const nsIntSize aSize,
|
||||
layers::Image* aImage,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder)
|
||||
{
|
||||
@ -366,6 +370,11 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
||||
rv = aContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aRenderer) {
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
rv = aRenderer->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aImage) {
|
||||
// It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread.
|
||||
// Other image formats could have problem to convert format off-main-thread.
|
||||
|
@ -19,6 +19,7 @@ class nsICanvasRenderingContextInternal;
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class Image;
|
||||
} // namespace layers
|
||||
|
||||
@ -40,6 +41,7 @@ public:
|
||||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
// Extracts data asynchronously. aType may change to "image/png" if we had to
|
||||
@ -84,7 +86,7 @@ public:
|
||||
nsIInputStream** aStream);
|
||||
|
||||
private:
|
||||
// When called asynchronously, aContext is null.
|
||||
// When called asynchronously, aContext and aRenderer are null.
|
||||
static nsresult
|
||||
ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
@ -93,6 +95,7 @@ private:
|
||||
const nsIntSize aSize,
|
||||
layers::Image* aImage,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder);
|
||||
|
||||
|
@ -23,10 +23,12 @@ namespace dom {
|
||||
|
||||
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
bool aNeutered)
|
||||
: mRenderer(aRenderer)
|
||||
, mWidth(aWidth)
|
||||
, mHeight(aHeight)
|
||||
, mCompositorBackendType(aCompositorBackend)
|
||||
, mNeutered(aNeutered)
|
||||
{
|
||||
}
|
||||
@ -37,26 +39,20 @@ OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
|
||||
|
||||
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer)
|
||||
: mAttrDirty(false)
|
||||
, mNeutered(false)
|
||||
, mWidth(aWidth)
|
||||
, mHeight(aHeight)
|
||||
, mCompositorBackendType(aCompositorBackend)
|
||||
, mCanvasClient(nullptr)
|
||||
, mCanvasRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
OffscreenCanvas::~OffscreenCanvas()
|
||||
{
|
||||
if (mCanvasRenderer) {
|
||||
mCanvasRenderer->SetCanvasClient(nullptr);
|
||||
mCanvasRenderer->mContext = nullptr;
|
||||
mCanvasRenderer->mActiveThread = nullptr;
|
||||
}
|
||||
|
||||
if (mCanvasClient) {
|
||||
ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
|
||||
}
|
||||
ClearResources();
|
||||
}
|
||||
|
||||
OffscreenCanvas*
|
||||
@ -72,6 +68,26 @@ OffscreenCanvas::WrapObject(JSContext* aCx,
|
||||
return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
OffscreenCanvas::ClearResources()
|
||||
{
|
||||
if (mCanvasClient) {
|
||||
mCanvasClient->Clear();
|
||||
ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
|
||||
mCanvasClient = nullptr;
|
||||
|
||||
if (mCanvasRenderer) {
|
||||
nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
|
||||
MOZ_RELEASE_ASSERT(activeThread);
|
||||
MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
|
||||
mCanvasRenderer->SetCanvasClient(nullptr);
|
||||
mCanvasRenderer->mContext = nullptr;
|
||||
mCanvasRenderer->mGLContext = nullptr;
|
||||
mCanvasRenderer->ResetActiveThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
OffscreenCanvas::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
@ -103,26 +119,34 @@ OffscreenCanvas::GetContext(JSContext* aCx,
|
||||
aContextOptions,
|
||||
aRv);
|
||||
|
||||
if (mCanvasRenderer && mCurrentContext && ImageBridgeChild::IsCreated()) {
|
||||
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
if (!mCurrentContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mCanvasClient = ImageBridgeChild::GetSingleton()->
|
||||
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
|
||||
mCanvasRenderer->SetCanvasClient(mCanvasClient);
|
||||
gl::GLContext* gl = static_cast<WebGLContext*>(mCurrentContext.get())->GL();
|
||||
if (mCanvasRenderer) {
|
||||
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
|
||||
gl::GLContext* gl = webGL->GL();
|
||||
mCanvasRenderer->mContext = mCurrentContext;
|
||||
mCanvasRenderer->mActiveThread = NS_GetCurrentThread();
|
||||
mCanvasRenderer->SetActiveThread();
|
||||
mCanvasRenderer->mGLContext = gl;
|
||||
mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
|
||||
|
||||
gl::GLScreenBuffer* screen = gl->Screen();
|
||||
gl::SurfaceCaps caps = screen->mCaps;
|
||||
auto forwarder = mCanvasClient->GetForwarder();
|
||||
if (ImageBridgeChild::IsCreated()) {
|
||||
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
mCanvasClient = ImageBridgeChild::GetSingleton()->
|
||||
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
|
||||
mCanvasRenderer->SetCanvasClient(mCanvasClient);
|
||||
|
||||
UniquePtr<gl::SurfaceFactory> factory =
|
||||
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
|
||||
gl::GLScreenBuffer* screen = gl->Screen();
|
||||
gl::SurfaceCaps caps = screen->mCaps;
|
||||
auto forwarder = mCanvasClient->GetForwarder();
|
||||
|
||||
if (factory)
|
||||
screen->Morph(Move(factory));
|
||||
UniquePtr<gl::SurfaceFactory> factory =
|
||||
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
|
||||
|
||||
if (factory)
|
||||
screen->Morph(Move(factory));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -157,6 +181,7 @@ OffscreenCanvas::CommitFrameToCompositor()
|
||||
}
|
||||
|
||||
if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
|
||||
mCanvasRenderer->NotifyElementAboutInvalidation();
|
||||
ImageBridgeChild::GetSingleton()->
|
||||
UpdateAsyncCanvasRenderer(mCanvasRenderer);
|
||||
}
|
||||
@ -165,8 +190,8 @@ OffscreenCanvas::CommitFrameToCompositor()
|
||||
OffscreenCanvasCloneData*
|
||||
OffscreenCanvas::ToCloneData()
|
||||
{
|
||||
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth,
|
||||
mHeight, mNeutered);
|
||||
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
|
||||
mCompositorBackendType, mNeutered);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<OffscreenCanvas>
|
||||
@ -174,7 +199,8 @@ OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
nsRefPtr<OffscreenCanvas> wc =
|
||||
new OffscreenCanvas(aData->mWidth, aData->mHeight, aData->mRenderer);
|
||||
new OffscreenCanvas(aData->mWidth, aData->mHeight,
|
||||
aData->mCompositorBackendType, aData->mRenderer);
|
||||
if (aData->mNeutered) {
|
||||
wc->SetNeutered();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define MOZILLA_DOM_OFFSCREENCANVAS_H_
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "CanvasRenderingContextHelper.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
@ -33,12 +34,14 @@ struct OffscreenCanvasCloneData final
|
||||
{
|
||||
OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
bool aNeutered);
|
||||
~OffscreenCanvasCloneData();
|
||||
|
||||
RefPtr<layers::AsyncCanvasRenderer> mRenderer;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
layers::LayersBackend mCompositorBackendType;
|
||||
bool mNeutered;
|
||||
};
|
||||
|
||||
@ -51,6 +54,7 @@ public:
|
||||
|
||||
OffscreenCanvas(uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer);
|
||||
|
||||
OffscreenCanvas* GetParentObject() const;
|
||||
@ -58,6 +62,8 @@ public:
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void ClearResources();
|
||||
|
||||
uint32_t Width() const
|
||||
{
|
||||
return mWidth;
|
||||
@ -141,6 +147,11 @@ public:
|
||||
return mNeutered;
|
||||
}
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const
|
||||
{
|
||||
return mCompositorBackendType;
|
||||
}
|
||||
|
||||
private:
|
||||
~OffscreenCanvas();
|
||||
|
||||
@ -156,6 +167,8 @@ private:
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
|
||||
layers::LayersBackend mCompositorBackendType;
|
||||
|
||||
layers::CanvasClient* mCanvasClient;
|
||||
RefPtr<layers::AsyncCanvasRenderer> mCanvasRenderer;
|
||||
};
|
||||
|
@ -1064,32 +1064,8 @@ WebGLContext::GetImageBuffer(uint8_t** out_imageBuffer, int32_t* out_format)
|
||||
|
||||
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map))
|
||||
return;
|
||||
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
if (!imageBuffer) {
|
||||
dataSurface->Unmap();
|
||||
return;
|
||||
}
|
||||
memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
|
||||
|
||||
dataSurface->Unmap();
|
||||
|
||||
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
*out_imageBuffer = imageBuffer;
|
||||
*out_format = format;
|
||||
return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
|
||||
out_imageBuffer, out_format);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1101,20 +1077,18 @@ WebGLContext::GetInputStream(const char* mimeType,
|
||||
if (!gl)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += mimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder)
|
||||
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
|
||||
bool premult;
|
||||
RefPtr<SourceSurface> snapshot =
|
||||
GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
|
||||
if (!snapshot)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||
if (!imageBuffer)
|
||||
return NS_ERROR_FAILURE;
|
||||
MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
|
||||
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, encoderOptions, out_stream);
|
||||
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha, mimeType,
|
||||
encoderOptions, out_stream);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1234,11 +1208,12 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
|
||||
layers::LayersBackend
|
||||
WebGLContext::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
if (mCanvasElement) {
|
||||
return mCanvasElement->GetCompositorBackendType();
|
||||
} else if (mOffscreenCanvas) {
|
||||
return mOffscreenCanvas->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ support-files =
|
||||
imagebitmap_structuredclone.js
|
||||
imagebitmap_structuredclone_iframe.html
|
||||
offscreencanvas.js
|
||||
offscreencanvas_mask.svg
|
||||
offscreencanvas_neuter.js
|
||||
offscreencanvas_serviceworker_inner.html
|
||||
|
||||
@ -266,6 +267,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
|
||||
[test_filter.html]
|
||||
[test_offscreencanvas_basic_webgl.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_dynamic_fallback.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_sharedworker.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_serviceworker.html]
|
||||
|
@ -17,6 +17,14 @@ function finish() {
|
||||
}
|
||||
}
|
||||
|
||||
function drawCount(count) {
|
||||
if (port) {
|
||||
port.postMessage({type: "draw", count: count});
|
||||
} else {
|
||||
postMessage({type: "draw", count: count});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// WebGL Drawing Functions
|
||||
//--------------------------------------------------------------------
|
||||
@ -144,6 +152,7 @@ function createDrawFunc(canvas) {
|
||||
preDraw(prefix);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
postDraw(prefix);
|
||||
gl.commit();
|
||||
checkGLError(prefix);
|
||||
};
|
||||
}
|
||||
@ -185,6 +194,22 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Test dynamic fallback
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_fallback") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var iid = setInterval(function() {
|
||||
++count;
|
||||
draw("loop " + count);
|
||||
drawCount(count);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Canvas Size Change from Worker
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_changesize") {
|
||||
|
11
dom/canvas/test/offscreencanvas_mask.svg
Normal file
11
dom/canvas/test/offscreencanvas_mask.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
<mask id="fade_mask_both" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
|
||||
<linearGradient id="fade_gradient_both" gradientUnits="objectBoundingBox" x2="0" y2="1">
|
||||
<stop stop-color="white" stop-opacity="0" offset="0"></stop>
|
||||
<stop stop-color="white" stop-opacity="1" offset="0.2"></stop>
|
||||
<stop stop-color="white" stop-opacity="1" offset="0.8"></stop>
|
||||
<stop stop-color="white" stop-opacity="0" offset="1"></stop>
|
||||
</linearGradient>
|
||||
<rect x="0" y="0" width="1" height="1" fill="url(#fade_gradient_both)"></rect>
|
||||
</mask>
|
||||
</svg>
|
After Width: | Height: | Size: 638 B |
@ -7,10 +7,23 @@
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<canvas id="c-ref" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function testToDataURL() {
|
||||
// testing toDataURL
|
||||
// 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");
|
||||
ok(c.toDataURL() == htmlCanvas.toDataURL(), "toDataURL should return a 64x64 green square");
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
@ -25,6 +38,7 @@ function runTest() {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
testToDataURL();
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
80
dom/canvas/test/test_offscreencanvas_dynamic_fallback.html
Normal file
80
dom/canvas/test/test_offscreencanvas_dynamic_fallback.html
Normal file
@ -0,0 +1,80 @@
|
||||
<!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>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createCanvas(initWithMask) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
document.body.appendChild(canvas);
|
||||
if (initWithMask) {
|
||||
canvas.style.mask = "url('offscreencanvas_mask.svg#fade_mask_both')";
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getRefSnapshot(initWithMask) {
|
||||
var refCanvas = createCanvas(!initWithMask);
|
||||
var ctx = refCanvas.getContext("2d");
|
||||
ctx.rect(0, 0, 64, 64);
|
||||
ctx.fillStyle = "#00FF00";
|
||||
ctx.fill();
|
||||
var result = snapshotWindow(window);
|
||||
document.body.removeChild(refCanvas);
|
||||
return result;
|
||||
}
|
||||
|
||||
function runTest(initWithMask) {
|
||||
var htmlCanvas = createCanvas(initWithMask);
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "draw") {
|
||||
if (msg.count === 10) {
|
||||
// Change the fallback state dynamically when drawing count reaches 10.
|
||||
if (initWithMask) {
|
||||
htmlCanvas.style.mask = "";
|
||||
} else {
|
||||
htmlCanvas.style.mask = "url('offscreencanvas_mask.svg#fade_mask_both')";
|
||||
}
|
||||
} else if (msg.count === 20) {
|
||||
var snapshotFallback = snapshotWindow(window);
|
||||
worker.terminate();
|
||||
document.body.removeChild(htmlCanvas);
|
||||
|
||||
var results = compareSnapshots(snapshotFallback, getRefSnapshot(initWithMask), true);
|
||||
ok(results[0], "after dynamic fallback, screenshots should be the same");
|
||||
|
||||
if (initWithMask) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
runTest(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
|
||||
worker.postMessage({test: 'webgl_fallback', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest.bind(false));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -350,6 +350,7 @@ NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
|
||||
|
||||
HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mResetLayer(true) ,
|
||||
mWriteOnly(false)
|
||||
{
|
||||
}
|
||||
@ -690,6 +691,7 @@ HTMLCanvasElement::ExtractData(nsAString& aType,
|
||||
aOptions,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
mAsyncCanvasRenderer,
|
||||
aStream);
|
||||
}
|
||||
|
||||
@ -773,7 +775,10 @@ HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
|
||||
renderer->SetWidth(sz.width);
|
||||
renderer->SetHeight(sz.height);
|
||||
|
||||
mOffscreenCanvas = new OffscreenCanvas(sz.width, sz.height, renderer);
|
||||
mOffscreenCanvas = new OffscreenCanvas(sz.width,
|
||||
sz.height,
|
||||
GetCompositorBackendType(),
|
||||
renderer);
|
||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
@ -1042,7 +1047,8 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
if (aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
|
||||
if (!mResetLayer &&
|
||||
aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
|
||||
nsRefPtr<CanvasLayer> ret = aOldLayer;
|
||||
return ret.forget();
|
||||
}
|
||||
@ -1055,7 +1061,12 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
LayerUserData* userData = nullptr;
|
||||
layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
|
||||
layer->SetAsyncRenderer(GetAsyncCanvasRenderer());
|
||||
|
||||
CanvasLayer::Data data;
|
||||
data.mRenderer = GetAsyncCanvasRenderer();
|
||||
data.mSize = GetWidthHeight();
|
||||
layer->Initialize(data);
|
||||
|
||||
layer->Updated();
|
||||
return layer.forget();
|
||||
}
|
||||
@ -1201,6 +1212,18 @@ HTMLCanvasElement::GetAsyncCanvasRenderer()
|
||||
return mAsyncCanvasRenderer;
|
||||
}
|
||||
|
||||
layers::LayersBackend
|
||||
HTMLCanvasElement::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElement::OnVisibilityChange()
|
||||
{
|
||||
@ -1235,8 +1258,9 @@ HTMLCanvasElement::OnVisibilityChange()
|
||||
};
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
|
||||
if (mAsyncCanvasRenderer->mActiveThread) {
|
||||
mAsyncCanvasRenderer->mActiveThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
nsCOMPtr<nsIThread> activeThread = mAsyncCanvasRenderer->GetActiveThread();
|
||||
if (activeThread) {
|
||||
activeThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1276,8 +1300,9 @@ HTMLCanvasElement::OnMemoryPressure()
|
||||
};
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
|
||||
if (mAsyncCanvasRenderer->mActiveThread) {
|
||||
mAsyncCanvasRenderer->mActiveThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
nsCOMPtr<nsIThread> activeThread = mAsyncCanvasRenderer->GetActiveThread();
|
||||
if (activeThread) {
|
||||
activeThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -1295,6 +1320,10 @@ HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer
|
||||
return;
|
||||
}
|
||||
|
||||
if (element->GetWidthHeight() == aRenderer->GetSize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gfx::IntSize asyncCanvasSize = aRenderer->GetSize();
|
||||
|
||||
ErrorResult rv;
|
||||
@ -1307,6 +1336,19 @@ HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer
|
||||
if (rv.Failed()) {
|
||||
NS_WARNING("Failed to set height attribute to a canvas element asynchronously.");
|
||||
}
|
||||
|
||||
element->mResetLayer = true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
|
||||
{
|
||||
HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element->InvalidateCanvasContent(nullptr);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "mozilla/dom/CanvasRenderingContextHelper.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsITimerCallback;
|
||||
@ -330,11 +331,14 @@ public:
|
||||
|
||||
nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const;
|
||||
|
||||
void OnVisibilityChange();
|
||||
|
||||
void OnMemoryPressure();
|
||||
|
||||
static void SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
|
||||
static void InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
|
||||
|
||||
protected:
|
||||
virtual ~HTMLCanvasElement();
|
||||
@ -360,6 +364,7 @@ protected:
|
||||
|
||||
AsyncCanvasRenderer* GetAsyncCanvasRenderer();
|
||||
|
||||
bool mResetLayer;
|
||||
nsRefPtr<HTMLCanvasElement> mOriginalCanvas;
|
||||
nsRefPtr<PrintCallback> mPrintCallback;
|
||||
nsRefPtr<HTMLCanvasPrintState> mPrintState;
|
||||
|
@ -533,8 +533,8 @@ ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest)
|
||||
#endif
|
||||
}
|
||||
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
YInvertImageSurface(DataSourceSurface* aSurf)
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
YInvertImageSurface(gfx::DataSourceSurface* aSurf)
|
||||
{
|
||||
RefPtr<DataSourceSurface> temp =
|
||||
Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
|
||||
|
@ -34,6 +34,9 @@ void ReadPixelsIntoDataSurface(GLContext* aGL,
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat);
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
YInvertImageSurface(gfx::DataSourceSurface* aSurf);
|
||||
|
||||
class GLReadTexImageHelper final
|
||||
{
|
||||
// The GLContext is the sole owner of the GLBlitHelper.
|
||||
|
@ -8,8 +8,12 @@
|
||||
|
||||
#include "gfxUtils.h"
|
||||
#include "GLContext.h"
|
||||
#include "GLReadTexImageHelper.h"
|
||||
#include "GLScreenBuffer.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/layers/CanvasClient.h"
|
||||
#include "mozilla/layers/TextureClientSharedSurface.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
@ -17,10 +21,15 @@ namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
AsyncCanvasRenderer::AsyncCanvasRenderer()
|
||||
: mWidth(0)
|
||||
: mHTMLCanvasElement(nullptr)
|
||||
, mContext(nullptr)
|
||||
, mGLContext(nullptr)
|
||||
, mIsAlphaPremultiplied(true)
|
||||
, mWidth(0)
|
||||
, mHeight(0)
|
||||
, mCanvasClientAsyncID(0)
|
||||
, mCanvasClient(nullptr)
|
||||
, mMutex("AsyncCanvasRenderer::mMutex")
|
||||
{
|
||||
MOZ_COUNT_CTOR(AsyncCanvasRenderer);
|
||||
}
|
||||
@ -65,6 +74,41 @@ AsyncCanvasRenderer::NotifyElementAboutAttributesChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::NotifyElementAboutInvalidation()
|
||||
{
|
||||
class Runnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer) {
|
||||
dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsRunnable> runnable = new Runnable(this);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch a runnable to the main-thread.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
|
||||
{
|
||||
@ -76,5 +120,58 @@ AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::SetActiveThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mActiveThread = NS_GetCurrentThread();
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::ResetActiveThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mActiveThread = nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIThread>
|
||||
AsyncCanvasRenderer::GetActiveThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
nsCOMPtr<nsIThread> result = mActiveThread;
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
AsyncCanvasRenderer::UpdateTarget()
|
||||
{
|
||||
// This function will be implemented in a later patch.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
AsyncCanvasRenderer::GetSurface()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return UpdateTarget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncCanvasRenderer::GetInputStream(const char *aMimeType,
|
||||
const char16_t *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RefPtr<gfx::DataSourceSurface> surface = GetSurface();
|
||||
if (!surface) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Handle y flip.
|
||||
RefPtr<gfx::DataSourceSurface> dataSurf = gl::YInvertImageSurface(surface);
|
||||
|
||||
return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions, aStream);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -7,15 +7,22 @@
|
||||
#ifndef MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
|
||||
#define MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
|
||||
|
||||
#include "LayersTypes.h"
|
||||
#include "mozilla/gfx/Point.h" // for IntSize
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/RefPtr.h" // for nsAutoPtr, nsRefPtr, etc
|
||||
#include "nsCOMPtr.h" // for nsCOMPtr
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsIInputStream;
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
}
|
||||
|
||||
namespace gl {
|
||||
class GLContext;
|
||||
}
|
||||
@ -36,8 +43,13 @@ class CanvasClient;
|
||||
* Each HTMLCanvasElement object is responsible for creating
|
||||
* AsyncCanvasRenderer object. Once Canvas is transfered to worker,
|
||||
* OffscreenCanvas will keep reference pointer of this object.
|
||||
* This object will pass to ImageBridgeChild for submitting frames to
|
||||
* Compositor.
|
||||
*
|
||||
* Sometimes main thread needs AsyncCanvasRenderer's result, such as layers
|
||||
* fallback to BasicLayerManager or calling toDataURL in Javascript. Simply call
|
||||
* GetSurface() in main thread will readback the result to mSurface.
|
||||
*
|
||||
* If layers backend is LAYERS_CLIENT, this object will pass to ImageBridgeChild
|
||||
* for submitting frames to Compositor.
|
||||
*/
|
||||
class AsyncCanvasRenderer final
|
||||
{
|
||||
@ -47,6 +59,7 @@ public:
|
||||
AsyncCanvasRenderer();
|
||||
|
||||
void NotifyElementAboutAttributesChanged();
|
||||
void NotifyElementAboutInvalidation();
|
||||
|
||||
void SetCanvasClient(CanvasClient* aClient);
|
||||
|
||||
@ -60,6 +73,28 @@ public:
|
||||
mHeight = aHeight;
|
||||
}
|
||||
|
||||
void SetIsAlphaPremultiplied(bool aIsAlphaPremultiplied)
|
||||
{
|
||||
mIsAlphaPremultiplied = aIsAlphaPremultiplied;
|
||||
}
|
||||
|
||||
// Active thread means the thread which spawns GLContext.
|
||||
void SetActiveThread();
|
||||
void ResetActiveThread();
|
||||
|
||||
// This will readback surface and return the surface
|
||||
// in the DataSourceSurface.
|
||||
// Can be called in main thread only.
|
||||
already_AddRefed<gfx::DataSourceSurface> GetSurface();
|
||||
|
||||
// Readback current WebGL's content and convert it to InputStream. This
|
||||
// function called GetSurface implicitly and GetSurface handles only get
|
||||
// called in the main thread. So this function can be called in main thread.
|
||||
nsresult
|
||||
GetInputStream(const char *aMimeType,
|
||||
const char16_t *aEncoderOptions,
|
||||
nsIInputStream **aStream);
|
||||
|
||||
gfx::IntSize GetSize() const
|
||||
{
|
||||
return gfx::IntSize(mWidth, mHeight);
|
||||
@ -75,26 +110,44 @@ public:
|
||||
return mCanvasClient;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIThread> GetActiveThread();
|
||||
|
||||
// The lifetime is controllered by HTMLCanvasElement.
|
||||
// Only accessed in main thread.
|
||||
dom::HTMLCanvasElement* mHTMLCanvasElement;
|
||||
|
||||
// Only accessed in active thread.
|
||||
nsICanvasRenderingContextInternal* mContext;
|
||||
|
||||
// We need to keep a reference to the context around here, otherwise the
|
||||
// canvas' surface texture destructor will deref and destroy it too early
|
||||
// Only accessed in active thread.
|
||||
RefPtr<gl::GLContext> mGLContext;
|
||||
|
||||
nsCOMPtr<nsIThread> mActiveThread;
|
||||
private:
|
||||
|
||||
virtual ~AsyncCanvasRenderer();
|
||||
|
||||
// Readback current WebGL's content and return it as DataSourceSurface.
|
||||
already_AddRefed<gfx::DataSourceSurface> UpdateTarget();
|
||||
|
||||
bool mIsAlphaPremultiplied;
|
||||
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
uint64_t mCanvasClientAsyncID;
|
||||
|
||||
// The lifetime of this pointer is controlled by OffscreenCanvas
|
||||
// Can be accessed in active thread and ImageBridge thread.
|
||||
// But we never accessed it at the same time on both thread. So no
|
||||
// need to protect this member.
|
||||
CanvasClient* mCanvasClient;
|
||||
|
||||
|
||||
// Protect non thread-safe objects.
|
||||
Mutex mMutex;
|
||||
|
||||
// Can be accessed in any thread, need protect by mutex.
|
||||
nsCOMPtr<nsIThread> mActiveThread;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
@ -64,6 +64,9 @@ CopyableCanvasLayer::Initialize(const Data& aData)
|
||||
}
|
||||
} else if (aData.mBufferProvider) {
|
||||
mBufferProvider = aData.mBufferProvider;
|
||||
} else if (aData.mRenderer) {
|
||||
mAsyncRenderer = aData.mRenderer;
|
||||
mOriginPos = gl::OriginPos::BottomLeft;
|
||||
} else {
|
||||
MOZ_CRASH("CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
|
||||
}
|
||||
@ -80,11 +83,9 @@ CopyableCanvasLayer::IsDataValid(const Data& aData)
|
||||
void
|
||||
CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
|
||||
{
|
||||
if (!mBufferProvider && !mGLContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBufferProvider) {
|
||||
if (mAsyncRenderer) {
|
||||
mSurface = mAsyncRenderer->GetSurface();
|
||||
} else if (mBufferProvider) {
|
||||
mSurface = mBufferProvider->GetSnapshot();
|
||||
}
|
||||
|
||||
@ -99,10 +100,12 @@ CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBufferProvider) {
|
||||
if (mBufferProvider || mAsyncRenderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mGLContext);
|
||||
|
||||
SharedSurface* frontbuffer = nullptr;
|
||||
if (mGLFrontbuffer) {
|
||||
frontbuffer = mGLFrontbuffer.get();
|
||||
|
@ -2146,12 +2146,6 @@ CanvasLayer::CanvasLayer(LayerManager* aManager, void* aImplData)
|
||||
CanvasLayer::~CanvasLayer()
|
||||
{}
|
||||
|
||||
void
|
||||
CanvasLayer::SetAsyncRenderer(AsyncCanvasRenderer *aAsyncRenderer)
|
||||
{
|
||||
mAsyncRenderer = aAsyncRenderer;
|
||||
}
|
||||
|
||||
void
|
||||
CanvasLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||
{
|
||||
|
@ -2273,15 +2273,17 @@ public:
|
||||
Data()
|
||||
: mBufferProvider(nullptr)
|
||||
, mGLContext(nullptr)
|
||||
, mRenderer(nullptr)
|
||||
, mFrontbufferGLTex(0)
|
||||
, mSize(0,0)
|
||||
, mHasAlpha(false)
|
||||
, mIsGLAlphaPremult(true)
|
||||
{ }
|
||||
|
||||
// One of these two must be specified for Canvas2D, but never both
|
||||
// One of these three must be specified for Canvas2D, but never more than one
|
||||
PersistentBufferProvider* mBufferProvider; // A BufferProvider for the Canvas contents
|
||||
mozilla::gl::GLContext* mGLContext; // or this, for GL.
|
||||
AsyncCanvasRenderer* mRenderer; // or this, for OffscreenCanvas
|
||||
|
||||
// Frontbuffer override
|
||||
uint32_t mFrontbufferGLTex;
|
||||
@ -2402,8 +2404,6 @@ public:
|
||||
return !!mAsyncRenderer;
|
||||
}
|
||||
|
||||
void SetAsyncRenderer(AsyncCanvasRenderer *aAsyncRenderer);
|
||||
|
||||
protected:
|
||||
CanvasLayer(LayerManager* aManager, void* aImplData);
|
||||
virtual ~CanvasLayer();
|
||||
|
@ -4,6 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "BasicCanvasLayer.h"
|
||||
#include "AsyncCanvasRenderer.h"
|
||||
#include "basic/BasicLayers.h" // for BasicLayerManager
|
||||
#include "basic/BasicLayersImpl.h" // for GetEffectiveOperator
|
||||
#include "mozilla/mozalloc.h" // for operator new
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "gfxDrawable.h"
|
||||
#include "imgIEncoder.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/dom/ImageEncoder.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
@ -1546,6 +1547,69 @@ gfxUtils::CopyAsDataURI(DrawTarget* aDT)
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface,
|
||||
bool aIsAlphaPremultiplied,
|
||||
uint8_t** outImageBuffer,
|
||||
int32_t* outFormat)
|
||||
{
|
||||
*outImageBuffer = nullptr;
|
||||
*outFormat = 0;
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!aSurface->Map(DataSourceSurface::MapType::READ, &map))
|
||||
return;
|
||||
|
||||
uint32_t bufferSize = aSurface->GetSize().width * aSurface->GetSize().height * 4;
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[bufferSize];
|
||||
if (!imageBuffer) {
|
||||
aSurface->Unmap();
|
||||
return;
|
||||
}
|
||||
memcpy(imageBuffer, map.mData, bufferSize);
|
||||
|
||||
aSurface->Unmap();
|
||||
|
||||
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!aIsAlphaPremultiplied) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(imageBuffer, bufferSize);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
*outImageBuffer = imageBuffer;
|
||||
*outFormat = format;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface,
|
||||
bool aIsAlphaPremultiplied,
|
||||
const char* aMimeType,
|
||||
const char16_t* aEncoderOptions,
|
||||
nsIInputStream** outStream)
|
||||
{
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += aMimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(aSurface, aIsAlphaPremultiplied, getter_Transfers(imageBuffer), &format);
|
||||
if (!imageBuffer)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return dom::ImageEncoder::GetInputStream(aSurface->GetSize().width,
|
||||
aSurface->GetSize().height,
|
||||
imageBuffer, format,
|
||||
encoder, aEncoderOptions, outStream);
|
||||
}
|
||||
|
||||
class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
class gfxASurface;
|
||||
class gfxDrawable;
|
||||
class nsIInputStream;
|
||||
class nsIGfxInfo;
|
||||
class nsIntRegion;
|
||||
class nsIPresShell;
|
||||
@ -281,6 +282,17 @@ public:
|
||||
static nsCString GetAsDataURI(DrawTarget* aDT);
|
||||
static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
|
||||
|
||||
static void GetImageBuffer(DataSourceSurface* aSurface,
|
||||
bool aIsAlphaPremultiplied,
|
||||
uint8_t** outImageBuffer,
|
||||
int32_t* outFormat);
|
||||
|
||||
static nsresult GetInputStream(DataSourceSurface* aSurface,
|
||||
bool aIsAlphaPremultiplied,
|
||||
const char* aMimeType,
|
||||
const char16_t* aEncoderOptions,
|
||||
nsIInputStream** outStream);
|
||||
|
||||
static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
int32_t feature,
|
||||
int32_t* status);
|
||||
|
Loading…
Reference in New Issue
Block a user