【问题标题】:Serialize canvas content to ArrayBuffer and deserialize again将画布内容序列化为 ArrayBuffer 并再次反序列化
【发布时间】:2014-04-09 07:53:43
【问题描述】:

我有两个画布,我想把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;

但不幸的是,它似乎不起作用。

这样做的正确方法是什么?

【问题讨论】:

    标签: javascript html serialization html5-canvas arraybuffer


    【解决方案1】:

    如果您想要压缩数据而不是原始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 标头的服务上,否则您会看到诸如“画布已被跨域数据污染”之类的错误。

    【讨论】:

      【解决方案2】:

      您从 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);
      

      您可能想要考虑某种形式的字节编码(例如 f.ex base-64),因为任何高于 127 (ASCII) 的字节值都受系统上使用的字符编码的影响。或者确保旅途中的所有步骤都使用相同的(例如 UTF8)。

      【讨论】:

      • 在 IE 中不起作用,因为 IE 仍然使用 CanvasPixelArray 而不是 Uint8ClampedArray(CanvasPixelArray 的 imageData.data 没有 .buffer)。
      • @markE true,但在任何其他浏览器中......(一如既往):)
      • 这该死的 IE!不过最近情况好多了。 :)
      • @markE 当然,它会到达那里.. 最终(?) :-P 好的 ol' Mosaic :-]
      • 我成功了,我必须在“buffer”变量上使用 Uint8Array 才能使其在“imageData”中工作。谢谢!
      【解决方案3】:

      创建一个 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);
      

      [删除了使用 canvas.toDataURL 的先前答案]

      【讨论】:

      • 我需要用二进制文件。
      • 为什么?您始终可以在客户端或服务器端将图像转换为二进制文件。问题在于二进制传输——转换为二进制很简单。
      • 因为连接是一个接受二进制格式数据的websocket。这就是为什么我要求以二进制形式进行。
      • 很公平——你被限制在服务器端的特定类型的连接上。在您的顶级代码中,您需要创建一个 ArrayBuffer 并通过 websocket 发送它。请参阅我的更新答案。确保 websocket.binaryType="arrayBuffer" 因为默认类型是 blob。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 2015-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多