I have Uint8Array named frameBytes. I have created RGBA values from this byte array with this code.
for (var i = 0; i < frameBytes.length; i++) {
imgData.data[4 * i] = frameBytes[i];// red
imgData.data[4 * i + 1] = frameBytes[i]; // green
imgData.data[4 * i + 2] = frameBytes[i];// blue
imgData.data[4 * i + 3] = 255; // alpha
}
Then I have shown this GRBA values to canvas using the following code .
var ctx = fingerFrame.getContext('2d');
var imgData = ctx.createImageData(fingerFrame.width, fingerFrame.height);
ctx.putImageData(imgData, 0, 0, 0, 0, fingerFrame.width, fingerFrame.height);
After that from canvas I used to image in image tag using the following code:
const img = document.getElementById('i');
img.src = fingerFrame.toDataURL();
But I do not want to use canvas. I want to show image in image tag from Uint8Array directly. How can I do that? Any help will be highly appreciated.
I want to show image in image tag from Uint8Array directly
This is very simple using a Blob:
// Small red dot image
const content = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 5, 0, 0, 0, 5, 8, 6, 0, 0, 0, 141, 111, 38, 229, 0, 0, 0, 28, 73, 68, 65, 84, 8, 215, 99, 248, 255, 255, 63, 195, 127, 6, 32, 5, 195, 32, 18, 132, 208, 49, 241, 130, 88, 205, 4, 0, 14, 245, 53, 203, 209, 142, 14, 31, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]);
document.getElementById('my-img').src = URL.createObjectURL(
new Blob([content.buffer], { type: 'image/png' } /* (1) */)
);
Should display a small red dot: <img id="my-img">
(1) It also works without specifying the Blob MIME type.
I think you can make an ImageData
directly, from the example on that page:
const arr = new Uint8ClampedArray(40000);
// Iterate through every pixel
for (let i = 0; i < arr.length; i += 4) {
arr[i + 0] = 0; // R value
arr[i + 1] = 190; // G value
arr[i + 2] = 0; // B value
arr[i + 3] = 255; // A value
}
// Initialize a new ImageData object
let imageData = new ImageData(arr, 200);
Unfortunately there does not seem to be any way to display an ImageData
in an <img>
element, only in a <canvas>
. <img>
requires an actual image file.
Fortunately the BMP format is widely supported and supports raw RGBA data. You just need to prepend an appropriate BMP header. Once that is done you can pass your data to the <img>
using the technique outlined by Ben Fortune. I would NOT use a data:
URL even though you find people all over the web using it. It is needlessly inefficient.
Here's some example code. It appends the pixel data to the bitmap header in a single buffer since that will be more efficient. If you already have the data you could create a separate Uint8Array
just for the header and concatenate them in the Blob
constructor, i.e. new Blob([header, pixels])
. I haven't tried that.
const header_size = 70;
const width = 255;
const height = 255;
const image_size = width * height * 4;
const arr = new Uint8Array(header_size + image_size);
const view = new DataView(arr.buffer);
// File Header
// BM magic number.
view.setUint16(0, 0x424D, false);
// File size.
view.setUint32(2, arr.length, true);
// Offset to image data.
view.setUint32(10, header_size, true);
// BITMAPINFOHEADER
// Size of BITMAPINFOHEADER
view.setUint32(14, 40, true);
// Width
view.setInt32(18, width, true);
// Height (signed because negative values flip
// the image vertically).
view.setInt32(22, height, true);
// Number of colour planes (colours stored as
// separate images; must be 1).
view.setUint16(26, 1, true);
// Bits per pixel.
view.setUint16(28, 32, true);
// Compression method, 6 = BI_ALPHABITFIELDS
view.setUint32(30, 6, true);
// Image size in bytes.
view.setUint32(34, image_size, true);
// Horizontal resolution, pixels per metre.
// This will be unused in this situation.
view.setInt32(38, 10000, true);
// Vertical resolution, pixels per metre.
view.setInt32(42, 10000, true);
// Number of colours. 0 = all
view.setUint32(46, 0, true);
// Number of important colours. 0 = all
view.setUint32(50, 0, true);
// Colour table. Because we used BI_ALPHABITFIELDS
// this specifies the R, G, B and A bitmasks.
// Red
view.setUint32(54, 0x000000FF, true);
// Green
view.setUint32(58, 0x0000FF00, true);
// Blue
view.setUint32(62, 0x00FF0000, true);
// Alpha
view.setUint32(66, 0xFF000000, true);
// Pixel data.
for (let w = 0; w < width; ++w) {
for (let h = 0; h < height; ++h) {
const offset = header_size + (h * width + w) * 4;
arr[offset + 0] = w; // R value
arr[offset + 1] = h; // G value
arr[offset + 2] = 255-w; // B value
arr[offset + 3] = 255-h; // A value
}
}
const blob = new Blob([arr], { type: "image/bmp" });
const url = window.URL.createObjectURL(blob);
const img = document.getElementById('i');
img.src = url;
<img id="i">
A big caveat is that this RGBA variant of BMP is not widely supported at all. Chrome seems to support it. Firefox doesn't, nor does Apple's finder. If you're writing an Electron app it should be fine but I wouldn't use it on the web.
However, since you have set alpha to 255 I'm guessing you don't even need the alpha channel. In that case you can use BI_RGB
instead:
const header_size = 54;
const width = 255;
const height = 255;
const image_size = width * height * 4;
const arr = new Uint8Array(header_size + image_size);
const view = new DataView(arr.buffer);
// File Header
// BM magic number.
view.setUint16(0, 0x424D, false);
// File size.
view.setUint32(2, arr.length, true);
// Offset to image data.
view.setUint32(10, header_size, true);
// BITMAPINFOHEADER
// Size of BITMAPINFOHEADER
view.setUint32(14, 40, true);
// Width
view.setInt32(18, width, true);
// Height (signed because negative values flip
// the image vertically).
view.setInt32(22, height, true);
// Number of colour planes (colours stored as
// separate images; must be 1).
view.setUint16(26, 1, true);
// Bits per pixel.
view.setUint16(28, 32, true);
// Compression method, 0 = BI_RGB
view.setUint32(30, 0, true);
// Image size in bytes.
view.setUint32(34, image_size, true);
// Horizontal resolution, pixels per metre.
// This will be unused in this situation.
view.setInt32(38, 10000, true);
// Vertical resolution, pixels per metre.
view.setInt32(42, 10000, true);
// Number of colours. 0 = all
view.setUint32(46, 0, true);
// Number of important colours. 0 = all
view.setUint32(50, 0, true);
// Pixel data.
for (let w = 0; w < width; ++w) {
for (let h = 0; h < height; ++h) {
const offset = header_size + (h * width + w) * 4;
arr[offset + 0] = w; // R value
arr[offset + 1] = h; // G value
arr[offset + 2] = 255-w; // B value
// arr[offset + 3] is ignored but must still be present because we specified 32 BPP
}
}
const blob = new Blob([arr], { type: "image/bmp" });
const url = window.URL.createObjectURL(blob);
const img = document.getElementById('i');
img.src = url;
<img id="i">
In the above example I still use 32 BPP, but because I set the compression to BI_RGB
the alpha channel is ignored. This is a bit wasteful of memory. You can set it to 24 BPP
instead and then only use 3 bytes per pixel, but the caveat is each row has to be padded to up to a multiple of 4 bytes, which I couldn't be bothered to do here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With