将画布内容序列化为ArrayBuffer并进行反序列化

16

我有两个画布,我想传递canvas1的内容,将其序列化为ArrayBuffer,然后加载到canvas2中。将来,我会将canvas1的内容发送到服务器进行处理,并将其返回到canvas2中,但现在我只想对其进行序列化和反序列化。

我找到了一种以字节形式获取画布信息的方法:

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];
}

我还发现一种将信息设置给Image对象的方法:

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;

但不幸的是,它似乎无法工作。

哪种方法是正确的?

3个回答

27

getImageData()获取的ImageData已经使用了ArrayBuffer(由Uint8ClampedArray视图使用)。只需获取它并发送即可:

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

重新设置它:
var imageData = context.createImageData(w, h);
imageData.data.set(incomingBuffer);

你可能需要考虑使用某种字节编码(例如 base-64)来处理字节值大于 127(ASCII)的数据,因为这些字节值可能会受到系统中使用的字符编码的影响。或者确保整个过程都使用相同的字符编码(例如 UTF8)。


在IE中无法工作,因为IE仍然使用CanvasPixelArray而不是Uint8ClampedArray(CanvasPixelArray的imageData.data没有.buffer)。 - markE
@markE 是的,但在其他任何浏览器中都是这样(一如既往) :) - user1693593
那该死的IE!不过最近它变得好多了。 :) - markE
1
@markE 当然,它正在逐渐变得更好了..最终会成功的吧?:-P 好老旧的Mosaic :-] - user1693593
3
我已经让它工作了,我不得不在“缓冲区”变量上使用Uint8Array才能使其在“imageData”中工作。谢谢! - vtortola

4
创建一个ArrayBuffer并将其发送到Uint8Array构造函数中,然后使用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);

我需要用二进制来做。 - vtortola
为什么?你总是可以在客户端或服务器端将图像转换为二进制。问题在于二进制传输 - 转换为二进制很简单。 - markE
因为连接是一个接受二进制数据的websocket,所以我要求以二进制格式进行操作。 - vtortola
1
很好 - 你在服务器端受到了特定类型连接的限制。在你的顶部代码中,你需要创建一个ArrayBuffer并通过websocket发送它。请查看我的更新答案。确保websocket.binaryType="arrayBuffer",因为默认类型是blob。 - markE

2

如果您需要紧凑的数据而不是原始的ImageData对象,考虑使用canvas.toBlob()而不是context.getImageData()

示例:

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/

还要注意,图像必须托管在提供CORS标头的服务上,否则您将看到类似“画布已被跨域数据污染”的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接