canvas 无法在 chrome 中生成 bmp 图像数据 url

canvas unable to generate bmp image dataurl in chrome

我有从视频中绘制图像的代码。这是代码

<script type="text/javascript">

function capture() {

    var video  = document.getElementById("videoId");
    var canvas = capture(video, 1);
    var dataURL = canvas.toDataURL("image/bmp", 1.0);
    console.log("dataurl: "+dataURL);

}

</script>


<body>
 <input type="button" value="Capture" onClick="capture()"/>
 <video id="videoId" width="640" height="480"/>
</body>

在控制台上数据url显示为"data:image/png;base64,..."

有问题吗?

为什么数据url生成的是png格式??

注意: 这发生在 chrome 浏览器 [41.0.2272.89] 中。 在 firefox 中 url 以 bmp 格式生成。

支持

并非所有浏览器都支持 BMP。没有要求支持other formats than PNG(我强调):

User agents must support PNG ("image/png"). User agents may support other types.

无法识别的任何格式will be saved as default PNG.

The first argument, if provided, controls the type of the image to be returned (e.g. PNG or JPEG). The default is image/png; that type is also used if the given type isn't supported.

要检查格式是否受支持,请检查返回的数据 uri 的第一部分:

 var wantType = "image/bmp";
 var dataUri = canvas.toDataURL(wantType);
 if (dataUri.indexOf(wantType) < 0) {  // or use substr etc. data: + mime
      // Format NOT supported - provide workaround/inform user
      // See update below for workaround (or replacement)
 }

解决方法:手动生成低级 BMP

要保存为 BMP,您必须从 canvas 中提取像素数据,格式化文件头,以正确的格式附加数据,创建一个 Blob 并通过 objectURL 将其提供给用户。

用法:

var bmpDataUri = CanvasToBMP.toDataURL(canvas);     // returns an data-URI

还可以选择将 BMP 图像作为原始图像 ArrayBuffer:

var bmpBuffer = CanvasToBMP.toArrayBuffer(canvas);

Blob:

var bmpBlob = CanvasToBMP.toBlob(canvas);
var url = URL.createObjectURL(bmpBlob);             // example objectURL

Blobs 当然可以与 createObjectURL() 一起使用,它可以用作图像源和下载目标,并且往往比使用数据 URI 更快,因为它们不需要到 encode/decode to/from Base-64.

它写入支持 alpha 的 32 位 BMP 文件(Firefox 目前忽略 BMP 文件中的 alpha 通道)。

无论如何,这里是-

演示和源码

/*! canvas-to-bmp version 1.0 ALPHA
    (c) 2015 Ken "Epistemex" Fyrstenberg
    MIT License (this header required)
*/

var CanvasToBMP = {

  /**
   * Convert a canvas element to ArrayBuffer containing a BMP file
   * with support for 32-bit (alpha).
   *
   * Note that CORS requirement must be fulfilled.
   *
   * @param {HTMLCanvasElement} canvas - the canvas element to convert
   * @return {ArrayBuffer}
   */
  toArrayBuffer: function(canvas) {

    var w = canvas.width,
        h = canvas.height,
        w4 = w * 4,
        idata = canvas.getContext("2d").getImageData(0, 0, w, h),
        data32 = new Uint32Array(idata.data.buffer), // 32-bit representation of canvas

        stride = Math.floor((32 * w + 31) / 32) * 4, // row length incl. padding
        pixelArraySize = stride * h,                 // total bitmap size
        fileLength = 122 + pixelArraySize,           // header size is known + bitmap

        file = new ArrayBuffer(fileLength),          // raw byte buffer (returned)
        view = new DataView(file),                   // handle endian, reg. width etc.
        pos = 0, x, y = 0, p, s = 0, a, v;

    // write file header
    setU16(0x4d42);          // BM
    setU32(fileLength);      // total length
    pos += 4;                // skip unused fields
    setU32(0x7a);            // offset to pixels

    // DIB header
    setU32(108);             // header size
    setU32(w);
    setU32(-h >>> 0);        // negative = top-to-bottom
    setU16(1);               // 1 plane
    setU16(32);              // 32-bits (RGBA)
    setU32(3);               // no compression (BI_BITFIELDS, 3)
    setU32(pixelArraySize);  // bitmap size incl. padding (stride x height)
    setU32(2835);            // pixels/meter h (~72 DPI x 39.3701 inch/m)
    setU32(2835);            // pixels/meter v
    pos += 8;                // skip color/important colors
    setU32(0xff0000);        // red channel mask
    setU32(0xff00);          // green channel mask
    setU32(0xff);            // blue channel mask
    setU32(0xff000000);      // alpha channel mask
    setU32(0x57696e20);      // " win" color space

    // bitmap data, change order of ABGR to BGRA
    while (y < h) {
      p = 0x7a + y * stride; // offset + stride x height
      x = 0;
      while (x < w4) {
        v = data32[s++];                     // get ABGR
        a = v >>> 24;                        // alpha channel
        view.setUint32(p + x, (v << 8) | a); // set BGRA
        x += 4;
      }
      y++
    }

    return file;

    // helper method to move current buffer position
    function setU16(data) {view.setUint16(pos, data, true); pos += 2}
    function setU32(data) {view.setUint32(pos, data, true); pos += 4}
  },

  /**
   * Converts a canvas to BMP file, returns a Blob representing the
   * file. This can be used with URL.createObjectURL().
   * Note that CORS requirement must be fulfilled.
   *
   * @param {HTMLCanvasElement} canvas - the canvas element to convert
   * @return {Blob}
   */
  toBlob: function(canvas) {
    return new Blob([this.toArrayBuffer(canvas)], {
      type: "image/bmp"
    });
  },

  /**
   * Converts the canvas to a data-URI representing a BMP file.
   * Note that CORS requirement must be fulfilled.
   *
   * @param canvas
   * @return {string}
   */
  toDataURL: function(canvas) {
    var buffer = new Uint8Array(this.toArrayBuffer(canvas)),
        bs = "", i = 0, l = buffer.length;
    while (i < l) bs += String.fromCharCode(buffer[i++]);
    return "data:image/bmp;base64," + btoa(bs);
  }
};


// -------- DEMO CODE -------------

var canvas = document.querySelector("canvas"),
  w = canvas.width,
  h = canvas.height,
  ctx = canvas.getContext("2d"),
  gr = ctx.createLinearGradient(0, 0, w, h),
  img = new Image();

gr.addColorStop(0, "hsl(" + (Math.random() * 360) + ", 90%, 70%)"); 
gr.addColorStop(1, "hsl(" + (Math.random() * 360) + ", 100%, 30%)"); 
ctx.fillStyle = gr;
ctx.fillRect(0, 0, w, h);

// append image from the data-uri returned by the CanvasToBMP code below:
img.src = CanvasToBMP.toDataURL(canvas);
document.body.appendChild(img);
<h2>Canvas left, BMP from canvas as image right</h2>
<canvas width="299" height="200"></canvas>