Question

I have two canvases, and I want to pass the content of canvas1, serialize it to an ArrayBuffer, and then load it in canvas2. In the future I will send the canvas1 content to the server, process it, and return it to canvas2, but right now I just want to serialize and deserialize it.

I found this way of getting the canvas info in bytes:

var img1 = context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img1.data.length);
for (var i = 0; i < img1.data.length; i++) {
    binary[i] = img1.data[i];
}

And also found this way of set the information to a Image object:

var blob = new Blob( [binary], { type: "image/png" } );
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL( blob );
var img = new Image();
img.src = imageUrl;

But unfortunately it doesn't seem to work.

Which would be the right way of doing this?

Was it helpful?

Solution

The ImageData you get from getImageData() is already using an ArrayBuffer (used by the Uint8ClampedArray view). Just grab it and send it:

var imageData = context.getImageData(x, y, w, h);
var buffer = imageData.data.buffer;  // ArrayBuffer

To set it again:

var imageData = context.createImageData(w, h);
imageData.data.set(incomingBuffer);

You probably want to consider some form of byte encoding though (such as f.ex base-64) as any byte value above 127 (ASCII) is subject to character encoding used on a system. Or make sure all steps on the trip uses the same (f.ex. UTF8).

OTHER TIPS

Create an ArrayBuffer and send it into to the Uint8Array constructor, then send the buffer using websockets:

var img1 = context.getImageData(0, 0, 400, 320);
var data=img1.data;
var buffer = new ArrayBuffer(data.length);
var binary = new Uint8Array(buffer);
for (var i=0; i<binary.length; i++) {
    binary[i] = data[i];
}
websocket.send(buffer);

[ Previous answer using canvas.toDataURL removed ]

Consider using canvas.toBlob() instead of context.getImageData() if you want compact data rather than a raw ImageData object.

Example:

const imageIn = document.querySelector('#image-in');
const imageOut = document.querySelector('#image-out');
const canvas = document.querySelector('#canvas');
const imageDataByteLen = document.querySelector('#imagedata-byte-length');
const bufferByteLen = document.querySelector('#arraybuffer-byte-length');

const mimeType = 'image/png';

imageIn.addEventListener('load', () => {

  // Draw image to canvas.
  canvas.width = imageIn.width;
  canvas.height = imageIn.height;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(imageIn, 0, 0);

  // Convert canvas to ImageData.
  const imageData = ctx.getImageData(0, 0, imageIn.width, imageIn.height);
  imageDataByteLen.textContent = imageData.data.byteLength + ' bytes.';

  // Convert canvas to Blob, then Blob to ArrayBuffer.
  canvas.toBlob((blob) => {
    const reader = new FileReader();
    reader.addEventListener('loadend', () => {
      const arrayBuffer = reader.result;
      bufferByteLen.textContent = arrayBuffer.byteLength + ' bytes.';

      // Dispay Blob content in an Image.
      const blob = new Blob([arrayBuffer], {type: mimeType});
      imageOut.src = URL.createObjectURL(blob);
    });
    reader.readAsArrayBuffer(blob);
  }, mimeType);

});
<h1>Canvas ↔ ArrayBuffer</h1>

<h2>1. Source <code>&lt;img&gt;</code></h2>
<img id="image-in" src="https://ucarecdn.com/a0338bfa-9f88-4ce7-b53f-e6b61000df89/" crossorigin="">

<h2>2. Canvas</h2>
<canvas id="canvas"></canvas>

<h2>3. ImageData</h2>
<p id="imagedata-byte-length"></p>

<h2>4. ArrayBuffer</h2>
<p id="arraybuffer-byte-length"></p>

<h2>5. Final <code>&lt;img&gt;</code></h2>
<img id="image-out">

JSFiddle: https://jsfiddle.net/donmccurdy/jugzk15b/

Also note that images must be hosted on a service that provides CORS headers, or you'll see errors like "The canvas has been tainted by cross-origin data."

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top