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>
我有从视频中绘制图像的代码。这是代码
<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>