Javascript:将存储为 Uint8Array 的 PNG 渲染到没有数据 URI 的 Canvas 元素上
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13950865/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript: Render PNG stored as Uint8Array onto Canvas element without Data URI
提问by zashu
I'm trying to render a PNG image that is stored in a javascript Uint8Array
. The code that I originally tried was the following:
我正在尝试呈现存储在 javascript 中的 PNG 图像Uint8Array
。我最初尝试的代码如下:
var imgByteStr = String.fromCharCode.apply(null, this.imgBytes);
var pageImg = new Image();
pageImg.onload = function(e) {
// Draw onto the canvas
canvasContext.drawImage(pageImg, 0, 0);
};
// Attempt to generate the Data URI from the binary
// 3rd-party library adds toBase64() as a string function
pageImg.src="data:image/png;base64,"+encodeURIComponent(imgByteStr.toBase64());
However, I found that for some reason the onload function was never running (I had breakpoints set up in chrome and it simply never hit). I found that if I set the src to a URL instead of Data URI, it worked correctly. However, due to some constraints, I cannot directly refer to the image's URL, i.e., it must be loaded from the UInt8Array
.
但是,我发现由于某种原因,onload 函数从未运行(我在 chrome 中设置了断点,但它根本没有命中)。我发现如果我将 src 设置为 URL 而不是数据 URI,它可以正常工作。但是,由于一些限制,我不能直接引用图像的 URL,即它必须从UInt8Array
.
Also, to complicate things slightly more, these images may be megabytes in size. From what I have read, there are compatibility issues with attempting to use Data URIs for very large images. Because of this, I am extremely hesitant to utilize them.
此外,让事情稍微复杂一点,这些图像的大小可能是兆字节。从我读到的内容来看,尝试将数据 URI 用于非常大的图像存在兼容性问题。因此,我非常犹豫要不要使用它们。
The question, therefore, is how do I render this byte array as a PNG onto a canvas context without using Data URIs or referring directly to the image URL?
因此,问题是如何在不使用数据 URI 或直接引用图像 URL 的情况下将此字节数组作为 PNG 呈现到画布上下文中?
I've also tried using something like the following to manipulate the image data directly using the putImageData
function. Keep in mind that I don't 100% understand how this function works
我还尝试使用类似以下内容的方法直接使用该putImageData
函数操作图像数据。请记住,我不是 100% 了解此功能的工作原理
var imgdata = canvasContext.createImageData(this.baseHeight, this.baseWidth);
var imgdatalen = imgdata.data.length;
for(var i=0; i<imgdatalen; i++) {
imgdata.data[i] = _this.imgBytes[i];
}
canvasContext.putImageData(imgdata, 0, 0);
It was lifted from a blog whose tab I have since closed, so apologies to the inspiration for not giving appropriate credit.
它来自一个博客,我已经关闭了该博客的标签,因此对没有给予适当信任的灵感表示歉意。
Also, while slowly hammering away at this thing, I ran into an error in Chrome SECURITY_ERR: DOM Exception 18
. Turns out that, as soon as an image is loaded inot a canvas using drawImage, it cannot be retrieved without some additional workarounds. A Chromium Blog poston the topic was especially useful
此外,在慢慢敲打这件事的同时,我在 Chrome 中遇到了错误SECURITY_ERR: DOM Exception 18
。事实证明,一旦使用 drawImage 将图像加载到画布中,如果没有一些额外的解决方法,就无法检索它。一个Chromium博客博文的主题是特别有用
回答by Drew Noakes
If you have the PNG data in memory (which I think is what you're describing) then you can create an image from it. In HTML it would look like:
如果您在内存中有 PNG 数据(我认为这就是您所描述的),那么您可以从中创建一个图像。在 HTML 中,它看起来像:
<img src="data:image/png;base64,KSjs9JdldhAlisflAkshf==" />
In JavaScript you can do the same:
在 JavaScript 中,您可以执行相同的操作:
var image = document.createElement('img');
image.src = 'data:image/png;base64,' + base64Data;
Note that you can change the MIME type if you don't have PNG data.
请注意,如果您没有 PNG 数据,您可以更改 MIME 类型。
You could then draw the image
onto your canvas using context.drawImage(image, 0, 0)
or similar.
然后,您可以image
使用context.drawImage(image, 0, 0)
或类似的方法将其绘制到画布上。
So the remaining part of the puzzle is to encode the contents of your Uint8Array
into Base 64 data.
所以谜题的剩余部分是将您的内容编码Uint8Array
为 Base 64 数据。
var array = new Uint8Array(),
base64Data = btoa(String.fromCharCode.apply(null, array));
The function btoa
isn't standard, so some browsers might not support it. However it seems that most do. Otherwise you might find some code I usehelpful.
该功能btoa
不是标准的,因此某些浏览器可能不支持它。然而,似乎大多数都这样做。否则你可能会发现我使用的一些代码很有帮助。
I haven't tested any of this myself!
我自己没有测试过任何这些!
回答by ellisbben
If you already have a UInt8Array, you should consider using Blob
and createObjectURL
; createObjectURL
allocates a special URL that allows the browser to access an internally created blob of binary data as though it were a file being loaded externally.
如果你已经有一个 UInt8Array,你应该考虑使用Blob
and createObjectURL
;createObjectURL
分配一个特殊的 URL,允许浏览器访问内部创建的二进制数据 blob,就好像它是一个从外部加载的文件。
Where it is supported, createObjectURL
is an excellent alternative to huge data URIs. You may still need to fall back to use of data URIs in Opera, IE<10, and all but the most recent versions of mobile browsers.
凡有支持,createObjectURL
是庞大的数据URI一个很好的替代。您可能仍需要回退到在 Opera、IE<10 以及除最新版本的移动浏览器之外的所有浏览器中使用数据 URI。
var myArray; //= your data in a UInt8Array
var blob = new Blob([myArray], {'type': 'image/png'});
var url = URL.createObjectURL(blob); //possibly `webkitURL` or another vendor prefix for old browsers.
回答by J.M.I. MADISON
Full Working Example:(save as .html file and open)
完整工作示例:(另存为 .html 文件并打开)
1: Creates Uint8Array.
1:创建 Uint8Array。
2: Fills it with debug pattern.
2:用调试模式填充它。
3: Pastes it to canvas element on page.
3:将其粘贴到页面上的画布元素。
Output should like like below:
输出应如下所示:
<!DOCTYPE HTML >
<html lang="en">
<head>
<meta charset="UTF-8">
<title> U8A_TO_CANVAS </title>
<meta name ="author"
content="John Mark Isaac Madison">
<!-- EMAIL: [email protected] -->
</head>
<body>
<canvas id="CANVAS_ID"
width ="512"
height="512">
</canvas>
<script>
var com = 4 ; //:4 components( RGBA )
var wid = 512;
var hig = 512;
var tot_com = wid*hig*com;//:total#components
var u8a = new Uint8Array( tot_com );
DrawDebugPattern ( u8a , wid, hig );
Uint8ArrayToCanvas( u8a , wid, hig, "CANVAS_ID" );
function Uint8ArrayToCanvas(
u8a, //:uint8Array
wid, //:width__of_u8a_data_in_pixels
hig, //:height_of_u8a_data_in_pixels
nam, //:name_id_of_canvas_on_dom
){
//:Get Canvas:
var can = document.getElementById( nam );
if(!can){ throw "[FailedToGetCanvas]"; }
//:Get Canvas's 2D Context:
var ctx = can.getContext("2d");
if(!ctx){ throw "[FailedToGetContext]"; }
//:Use U8A to create image data object:
var UAC = new Uint8ClampedArray( u8a,wid,hig);
var DAT = new ImageData(UAC, wid, hig);
//:Paste Data Into Canvas:
var ORG_X = 0;
var ORG_Y = 0;
ctx.putImageData( DAT, ORG_X, ORG_Y );
}
function DrawDebugPattern(u8a,wid,hig){
var com = 4 ; //:RGBA==4components.
var tot_pix = wid*hig;//:total#pixels
//:Calculate point in center of canvas:
var cen_x = wid/2;
var cen_y = hig/2;
//:Define a circle's radius:
var rad_c = Math.min(wid,hig) / 2;
//:Make a pattern on array:
var d = 0; //:d_is_for_distance
var ci = 0; //:Component_Index
var pi = 0; //:Pixel_Index
var px = 0; //:Pixel_X_Coord
var py = 0; //:Pixel_Y_Coord
for( pi = 0; pi < tot_pix; pi++ ){
//:Calculate index of first component
//:of current pixel:
ci = pi * com;
//:Index_To_XY_Formula:
px = pi % wid ;
py = (pi-px)/ wid ;
//:Decide if pixel is inside circle:
var dx = (cen_x-px); //:delta_x
var dy = (cen_y-py); //:delta_y
d=Math.sqrt( (dx*dx)+(dy*dy) );
if( d < rad_c ){
//:INSIDE_CIRCLE:
u8a[ ci + 0 ] = 0 ; //:Red
u8a[ ci + 1 ] = 255; //:Green
u8a[ ci + 2 ] = 0 ; //:Blue
u8a[ ci + 3 ] = 255; //:Alpha
}else{
//:OUTSIDE_CIRCLE:
u8a[ ci + 0 ] = 0 ; //:Red
u8a[ ci + 1 ] = 0 ; //:Green
u8a[ ci + 2 ] = 64 ; //:Blue
u8a[ ci + 3 ] = 255; //:Alpha
}
}
}
</script>
</body>
<!-- In IE: Script cannot be outside of body. -->
</html>