Is Bitmaprenderer supposed to work on OffscreenCanvas in a webworker?

408 Views Asked by At

I'm experimenting with using OffscreenCanvas in webworkers and trying various strategies. In my original code I'm drawing on multiple canvas elements (stacked on top of each other) sequentially in a single requestAnimationFrame and was looking to see if I can do this in parallel using webworkers.

My current experiment involves calling canvas.transferControlToOffscreen() and passing that to a webworker. In that same webworker I use a separate OffscreenCanvas not attached to the DOM and would like to copy that content to the transferred canvas using a bitmaprenderer context, but I'm not succeeding in getting this copy to work without using a different context.

The following alternative code works:

const targetCanvasContext = targetCanvas.getContext("2d")!;
targetCanvasContext.clearRect(0, 0, 1920, 1080);
targetCanvasContext.drawImage(originCanvas, 0, 0);

but this is quite a lot of work compared to transferring the ImageBitmap directly using bitmaprenderer as I tried by doing the following instead:

const targetCanvasContext = targetCanvas.getContext("bitmaprenderer")!;
const bmp = originCanvas.transferToImageBitmap();
targetCanvasContext.transferFromImageBitmap(bmp);

The latter is however not producing any visual result nor is it throwing any errors.

What am I missing here?

There is this SO question that was asked 3 years ago (Is 'bitmaprenderer' not a valid value for off-screen canvas rendering context in Chrome?), which was resolved because at the time ImageBitmapRenderingContext was not supported in a webworker context, but as can be seen on mdn (https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapRenderingContext) this should be the case today. Also my code is not throwing an error on the getContext request and actually returning a proper ImageBitmapRenderingContext whereas the related SO question did have errors thrown.

1

There are 1 best solutions below

1
Kaiido On BEST ANSWER

Yes, it is supposed to work and it does in Firefox. So this is a Chrome bug, which can even be reproduced without a Worker:

const ctx = new OffscreenCanvas(300,150).getContext("2d");
ctx.fillRect(50, 50, 50, 50);
const bmp = ctx.canvas.transferToImageBitmap();
const placeholder = document.querySelector("canvas").transferControlToOffscreen();
placeholder.getContext("bitmaprenderer").transferFromImageBitmap(bmp);
// in Firefox you can see the black square, not in Chrome
<canvas></canvas>

Given it's already been reported, there isn't much more you can do.

You already found a workaround: use a 2D context and drawImage() instead. You could also transfer the ImageBitmap to the main thread and create the bitmap-renderer from the HTMLCanvasElement directly, but you would lose the ability to have your placeholder updated even when the main thread is locked.

However, I must note that I'm a bit skeptical about the usefulness of that intermediary bitmap-renderer context here. Since you need a final context from which you'll grab the ImageBitmap, you could directly invoke that context on the placeholder's OffscreenCanvas and just avoid these to-bitmap -> transfer-from-bitmap steps.