I post this as an alternative approach to check via the PNG file's header directly. This saves memory and doesn't have to iterate through pixels, and the same good performance will be the same regardless of image size.
You can do this by loading the file via HTTPRequest
or FileReader
as ArrayBuffer
, then simply check the file header structure using a DataView
.
A PNG file always starts with the IHDR chunk so we only need to check if it's actually a PNG file and then assume the offset for the information telling the depth and type.
The depth field can be of value 1, 2, 4, 8 and 16 (1, 2, 4 being indexed, 8 = 24-bit or 8-bits per channel etc.).
The type field can be 0 (grayscale), 2 (true-color or RGB), 3 (indexed), 4 (grayscale + alpha) and 6 (RGB + alpha).
For details on the PNG file format and the IHDR header, see this link.
loadXHR("//i.imgur.com/zpWwpEM.png", function(result) {
console.log(result); // result.buffer = original arraybuffer
});
function check(buffer, callback) {
var view = new DataView(buffer);
// is a PNG?
if (view.getUint32(0) === 0x89504E47 && view.getUint32(4) === 0x0D0A1A0A) {
// We know format field exists in the IHDR chunk. The chunk exists at
// offset 8 +8 bytes (size, name) +8 (depth) & +9 (type)
var depth = view.getUint8(8 + 8 + 8);
var type = view.getUint8(8 + 8 + 9);
callback({
depth: depth,
type: ["G", "", "RGB", "Indexed", "GA", "", "RGBA"][type],
buffer: view.buffer,
hasAlpha: type === 4 || type === 6 // grayscale + alpha or RGB + alpha
})
}
}
function loadXHR(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
if (xhr.status === 200) check(xhr.response, callback);
else consle.log("Loading error: " + xhr.statusText);
};
xhr.send();
}
Same example but inserting image that is being checked into DOM:
loadXHR("//i.imgur.com/zpWwpEM.png", function(result) {
console.log(result); // result.buffer = original arraybuffer
var img = new Image();
img.onload = function() {URL.revokeObjectURL(this.src)};
img.src = URL.createObjectURL(new Blob([result.buffer]));
document.body.appendChild(img);
});
function check(buffer, callback) {
var view = new DataView(buffer);
// is a PNG?
if (view.getUint32(0) === 0x89504E47 && view.getUint32(4) === 0x0D0A1A0A) {
// We know format field exists in the IHDR chunk. The chunk exists at
// offset 8 +8 bytes (size, name) +8 (depth) & +9 (type)
var depth = view.getUint8(8 + 8 + 8);
var type = view.getUint8(8 + 8 + 9);
callback({
depth: depth,
type: ["G", "", "RGB", "Indexed", "GA", "", "RGBA"][type],
buffer: view.buffer,
hasAlpha: type === 4 || type === 6 // grayscale + alpha or RGB + alpha
})
}
}
function loadXHR(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
if (xhr.status === 200) check(xhr.response, callback);
else consle.log("Loading error: " + xhr.statusText);
};
xhr.send();
}