0

I want to merge n number of canvases horizontally and vertically. Since it's computational heavy, I want to use web worker to do this task so that it doesn't block the UI. I know that the canvases cannot be sent to web worker as they are part of the DOM. How do I achieve this, or how do I send canvases to web worker and get a newly combined canvas.

I am using the following code to stack canvases horizontally:

public getCombinedHorizontalCanvas(canvasArr: HTMLCanvasElement[]) {
    const newCanvas = document.createElement('canvas');
    const ctx = newCanvas.getContext('2d');
    const dimen = this.getDimensionsOfCanvas(canvasArr);
    newCanvas.width = dimen.combinedWidth;
    newCanvas.height = dimen.maxHeight;

    let x = 0;
    for (let id = 0; id < canvasArr.length; ++id) {
        ctx.beginPath();
        ctx.drawImage(canvasArr[id], x, 0, canvasArr[id].width, newCanvas.height);
        x += canvasArr[id].width;
    }

    return ;
}

// to get the dimensions for the canvas
private getDimensionsOfCanvas(canvas: HTMLCanvasElement[]) {
    let maxWidth = Number.NEGATIVE_INFINITY;
    let maxHeight = Number.NEGATIVE_INFINITY;
    let combinedWidth = 0;
    let combinedHeight = 0;
    for (let id = 0; id < canvas.length; ++id) {
      maxWidth = Math.max(maxWidth, canvas[id].width);
      maxHeight = Math.max(maxHeight, canvas[id].height);
      combinedHeight += canvas[id].height;
      combinedWidth += canvas[id].width;
    }

    return {
      maxHeight: maxHeight,
      maxWidth: maxWidth,
      combinedHeight: combinedHeight,
      combinedWidth: combinedWidth
    };
}
suraj bora
  • 61
  • 1
  • 11
  • 1
    There is [OffscreenCanvas](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/OffscreenCanvas), but currently only Chrome does support the 2d context there. But anyway, the simple fact of sending all these canvases to the worker thread would probably be just as slow as drawing them all on a single canvas. How many canvases are we talking of, and more importantly, what's the total area of the final canvas? Depending on the size of the source, you should be able to perform a few `drawImage()` in less than 16ms, drawImage is quite fast. – Kaiido Feb 20 '19 at 12:37
  • @Kaiido **OffscreenCanvas** is not supported by safari. I don't care about time to merge the canvases, but it also slows the UI for some time. If I move this logic of merging to web workers than at least the UI will not freeze. Number of canvases to be merged can be n, n being any natural number. – suraj bora Feb 20 '19 at 12:42
  • No, it can't. Canvas [has a maximum area size](https://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element). And for your UI blocking concerns, you can simply execute your code in timedout batches, e.g `drawNextHundreds() { for(let i=0;i<100; i++) {draw(canvases[i])} current+=100; if(current – Kaiido Feb 20 '19 at 12:47
  • @Kaiido thanks for informing about the canvas size. My concern is that I call the function to merge some canvases together and from the resulting canvas, export it as png to download in the client side. So if I execute the function in timeout batches it will take more time. By the way, I am showing a loader when the merging of canvases takes place. right now the loader is freezing when I am trying to merge two canvases of size `( 1300, 514 )`. – suraj bora Feb 20 '19 at 12:58
  • I've created image filters that run in background workers. The only way I could get it to work _back then_ was to get the pixel data from the canvas, send the byte data to the worker, do the processing and get the byte data back. It added some overhead but it worked. Now it looks like ImageBitmap objects are transferrable, so maybe you could use createImageBitmap on your canvas elements instead. – aptriangle Feb 20 '19 at 13:35
  • @aptriangle thanks, is there any simpler way? – suraj bora Feb 21 '19 at 04:42
  • 1
    Any time you make something mutlithreaded you add complexity. Javascript handles the memory problems by only allowing objects that implement the [Transferrable](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) interface to be sent. The APIs available to workers are [limited](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) and the only APIs that provide native image editing are not supported on all browers. ImageBitmap does not provide pixel access, so you are stuck with sending pixels in ArrayBuffer and putting them together through array manipulation. – aptriangle Feb 21 '19 at 16:49
  • 1
    It looks like firefox is adding offscreen canvas and Edge may support it when they switch to using the chrome engine, but for now it's not cross browser. – aptriangle Feb 21 '19 at 16:53
  • @aptriangle thanks for the information. `so you are stuck with sending pixels in ArrayBuffer and putting them together through array manipulation` , how do I try to implement this? – suraj bora Feb 25 '19 at 10:38

0 Answers0