diff --git a/dom/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp index f960776eb56ae..8abe32150b3f7 100644 --- a/dom/canvas/ImageBitmapRenderingContext.cpp +++ b/dom/canvas/ImageBitmapRenderingContext.cpp @@ -80,6 +80,18 @@ void ImageBitmapRenderingContext::TransferFromImageBitmap( return; } + // Note that this is reentrant and will call back into SetDimensions. + if (mCanvasElement) { + mCanvasElement->SetSize(mImage->GetSize(), aRv); + } else if (mOffscreenCanvas) { + mOffscreenCanvas->SetSize(mImage->GetSize(), aRv); + } + + if (NS_WARN_IF(aRv.Failed())) { + mImage = nullptr; + return; + } + if (aImageBitmap->IsWriteOnly()) { if (mCanvasElement) { mCanvasElement->SetWriteOnly(); @@ -96,6 +108,16 @@ NS_IMETHODIMP ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight) { mWidth = aWidth; mHeight = aHeight; + + if (mOffscreenCanvas) { + OffscreenCanvasDisplayData data; + data.mSize = {mWidth, mHeight}; + data.mIsOpaque = GetIsOpaque(); + data.mIsAlphaPremult = true; + data.mDoPaintCallbacks = false; + mOffscreenCanvas->UpdateDisplayData(data); + } + return NS_OK; } diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index 09e58fedff8ca..01ad5f112eb60 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -147,6 +147,23 @@ void OffscreenCanvas::SetHeight(uint32_t aHeight, ErrorResult& aRv) { CanvasAttrChanged(); } +void OffscreenCanvas::SetSize(const nsIntSize& aSize, ErrorResult& aRv) { + if (mNeutered) { + aRv.ThrowInvalidStateError( + "Cannot set dimensions of placeholder canvas transferred to worker."); + return; + } + + if (NS_WARN_IF(aSize.IsEmpty())) { + aRv.ThrowRangeError("OffscreenCanvas size is empty, must be non-empty."); + return; + } + + mWidth = aSize.width; + mHeight = aSize.height; + CanvasAttrChanged(); +} + void OffscreenCanvas::GetContext( JSContext* aCx, const OffscreenRenderingContextId& aContextId, JS::Handle aContextOptions, diff --git a/dom/canvas/OffscreenCanvas.h b/dom/canvas/OffscreenCanvas.h index 0e1fc682379c0..ffe899385c548 100644 --- a/dom/canvas/OffscreenCanvas.h +++ b/dom/canvas/OffscreenCanvas.h @@ -150,6 +150,8 @@ class OffscreenCanvas final : public DOMEventTargetHelper, bool MayNeuter() const { return !mNeutered && !mCurrentContext; } + void SetSize(const nsIntSize& aSize, ErrorResult& aRv); + nsIPrincipal* GetExpandedReader() const { return mExpandedReader; } void SetWriteOnly(RefPtr&& aExpandedReader); diff --git a/dom/canvas/test/test_bitmaprenderer.html b/dom/canvas/test/test_bitmaprenderer.html index bd6d8ee846f9f..ee42d9d7f5935 100644 --- a/dom/canvas/test/test_bitmaprenderer.html +++ b/dom/canvas/test/test_bitmaprenderer.html @@ -43,11 +43,11 @@ ctx.fillStyle = "#00FF00"; ctx.fillRect(0, 0, canvasWidth, canvasHeight); - var canvasRef = createCanvas(90, 90); + var canvasRef = createCanvas(canvasWidth, canvasHeight); var ctx = canvasRef.getContext("2d"); // Clear with black transparent first ctx.fillStyle = "rgba(0, 0, 0, 0)"; - ctx.fillRect(0, 0, 90, 90); + ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.fillStyle = "#00FF00"; ctx.fillRect(0, 0, canvasWidth, canvasHeight); @@ -89,52 +89,58 @@ }); } -function scaleTest() { - var canvas1 = createCanvas(64, 64); - var ctx = canvas1.getContext("2d"); +function createSolidGreenBitmap(width, height) { + const canvas = createCanvas(width, height); + const ctx = canvas.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); + ctx.fillRect(0, 0, width, height); + const bitmap = createImageBitmap(canvas); + document.body.removeChild(canvas); + return bitmap; +} - var p1 = createImageBitmap(canvas1); - var p2 = createImageBitmap(canvas2); - Promise.all([p1, p2]).then(async function(bitmaps) { - document.body.removeChild(canvas1); - document.body.removeChild(canvas2); +async function scaleTestCase(name, refWidth, refHeight, testWidth, testHeight, canvasWidth, canvasHeight) { + const refBitmap = await createSolidGreenBitmap(refWidth, refHeight); + const refCanvas = createCanvas(refWidth, refHeight); + const refCtx = refCanvas.getContext("bitmaprenderer"); + refCtx.transferFromImageBitmap(refBitmap); + is(refCanvas.width, refWidth, name + ": refCanvas width " + refCanvas.width + " is ref " + refWidth); + is(refCanvas.height, refHeight, name + ": refCanvas height " + refCanvas.height + " is ref " + refHeight); + const refSnapshot = await snapshotWindow(window); + document.body.removeChild(refCanvas); + + const bitmap = await createSolidGreenBitmap(testWidth, testHeight); + const canvas = createCanvas(canvasWidth, canvasHeight); + const ctx = canvas.getContext("bitmaprenderer"); + ctx.transferFromImageBitmap(bitmap); + is(canvas.width, testWidth, name + ": canvas width " + canvas.width + " is bitmap " + testWidth); + is(canvas.height, testHeight, name + ": canvas height " + canvas.height + " is bitmap " + testHeight); + if (refWidth !== testWidth) { + canvas.width = refWidth; + is(canvas.width, refWidth, name + ": canvas width " + canvas.width + " is ref " + refWidth); + } + if (refHeight !== testHeight) { + canvas.height = refHeight; + is(canvas.height, refHeight, name + ": canvas height " + canvas.height + " is ref " + refHeight); + } + const snapshot = await snapshotWindow(window); + document.body.removeChild(canvas); + + const results = compareSnapshots(snapshot, refSnapshot, true); + ok(results[0], name + ": screenshots should be the same"); + return Promise.resolve(); +} - // Create a large canvas then shrink. - var canvas3 = createCanvas(128, 128); - var ctx3 = canvas3.getContext("bitmaprenderer"); - ctx3.transferFromImageBitmap(bitmaps[0]); - var snapshotLargeRef = await snapshotWindow(window); - - canvas3.width = 32; - canvas3.height = 32; - var snapshotSmall = await snapshotWindow(window); - document.body.removeChild(canvas3); - - // Create a small canvas then grow. - var canvas4 = createCanvas(32, 32); - var ctx4 = canvas4.getContext("bitmaprenderer"); - ctx4.transferFromImageBitmap(bitmaps[1]); - var snapshotSmallRef = await snapshotWindow(window); - - canvas4.width = 128; - canvas4.height = 128; - var snapshotLarge = await 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(); - }); +async function scaleTest() { + await scaleTestCase("grow_unscaled", 64, 64, 64, 64, 32, 32); // Canvas grows, no scaling. + await scaleTestCase("grow_downscaled", 64, 64, 128, 128, 32, 32); // Canvas grows, scales down. + await scaleTestCase("grow_upscaled", 64, 64, 32, 32, 16, 16); // Canvas grows, scales up. + await scaleTestCase("same_downscaled", 64, 64, 128, 128, 128, 128); // Canvas unchanged, scales down. + await scaleTestCase("same_upscaled", 64, 64, 32, 32, 32, 32); // Canvas unchanged, scales up. + await scaleTestCase("shrink_unscaled", 64, 64, 64, 64, 128, 128); // Canvas shrinks, no scaling. + await scaleTestCase("shrink_downscaled", 64, 64, 128, 128, 256, 256); // Canvas shrinks, scales down. + await scaleTestCase("shrink_upscaled", 64, 64, 32, 32, 256, 256); // Canvas shrinks, scales up. + runTestOnWorker(); } function runTestOnWorker() { diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 8e8538c1bd49f..b25edbe71c442 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -1104,6 +1104,26 @@ void HTMLCanvasElement::SetHeight(uint32_t aHeight, ErrorResult& aRv) { SetUnsignedIntAttr(nsGkAtoms::height, aHeight, DEFAULT_CANVAS_HEIGHT, aRv); } +void HTMLCanvasElement::SetSize(const nsIntSize& aSize, ErrorResult& aRv) { + if (mOffscreenCanvas) { + aRv.ThrowInvalidStateError( + "Cannot set width of placeholder canvas transferred to " + "OffscreenCanvas."); + return; + } + + if (NS_WARN_IF(aSize.IsEmpty())) { + aRv.ThrowRangeError("Canvas size is empty, must be non-empty."); + return; + } + + SetUnsignedIntAttr(nsGkAtoms::width, aSize.width, DEFAULT_CANVAS_WIDTH, aRv); + MOZ_ASSERT(!aRv.Failed()); + SetUnsignedIntAttr(nsGkAtoms::height, aSize.height, DEFAULT_CANVAS_HEIGHT, + aRv); + MOZ_ASSERT(!aRv.Failed()); +} + void HTMLCanvasElement::FlushOffscreenCanvas() { if (mOffscreenDisplay) { mOffscreenDisplay->FlushForDisplay(); diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index 28228f7abca50..586a43fedc9ff 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -175,6 +175,11 @@ class HTMLCanvasElement final : public nsGenericHTMLElement, */ nsIntSize GetSize(); + /** + * Set the size in pixels of this canvas element. + */ + void SetSize(const nsIntSize& aSize, ErrorResult& aRv); + /** * Determine whether the canvas is write-only. */ diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index 2ccb6f0b20c1a..87cb5ef1544df 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -284,14 +284,14 @@ class nsDisplayCanvas final : public nsPaintedDisplayItem { if (!surface || !surface->IsValid()) { return; } - gfx::IntSize size = surface->GetSize(); transform = gfxUtils::SnapTransform( - transform, gfxRect(0, 0, size.width, size.height), nullptr); + transform, gfxRect(0, 0, canvasSizeInPx.width, canvasSizeInPx.height), + nullptr); aCtx->Multiply(transform); aCtx->GetDrawTarget()->FillRect( - Rect(0, 0, size.width, size.height), + Rect(0, 0, canvasSizeInPx.width, canvasSizeInPx.height), SurfacePattern(surface, ExtendMode::CLAMP, Matrix(), nsLayoutUtils::GetSamplingFilterForFrame(f))); return; diff --git a/testing/web-platform/meta/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html.ini b/testing/web-platform/meta/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html.ini deleted file mode 100644 index 5463866bcf8a3..0000000000000 --- a/testing/web-platform/meta/imagebitmap-renderingcontext/bitmaprenderer-as-imagesource.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[bitmaprenderer-as-imagesource.html] - [Test that createImageBitmap from a bitmaprenderer canvas produces correct result] - expected: FAIL