问题
can the JavaScript check the color mode of a image?
I did a lot of search about this but the only thing I saw was color mode convert (but the convert wants you to set the original color mode)
I add this: --allow-file-access-from-files
to have a full control of img in canvas beacause I am using GoogleChrome
Html
<canvas id="canvas" width=6000 height=7919></canvas>
Js
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.crossOrigin = "anonymous";
img.onload = start;
img.src = "file:///D:/vincent-van-gogh-the-starry-night-picture-157544-2.png";
回答1:
Yes - basically JavaScript is able to determine the color mode of a png, but therefore it would be required to 1. convert png to base64 2. convert base64 to byte array 3. reading / parsing the array regarding png specification
A possible approach could look like this:
var PNG = {
parse: function(imgTag) {
var base64 = PNG.asBase64(imgTag);
var byteData = PNG.utils.base64StringToByteArray(base64);
var parsedPngData = PNG.utils.parseBytes(byteData);
return PNG.utils.enrichParsedData(parsedPngData);
},
asBase64: function(imgTag) {
var canvas = document.createElement("canvas");
canvas.width = imgTag.width;
canvas.height = imgTag.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(imgTag, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.split('base64,')[1];
},
utils: {
base64StringToByteArray: function(base64String) {
//http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
var byteCharacters = atob(base64String);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
return new Uint8Array(byteNumbers);
},
parseBytes: function(bytes) {
var pngData = {};
//see https://en.wikipedia.org/wiki/Portable_Network_Graphics
//verify file header
pngData['headerIsValid'] = bytes[0] == 0x89
&& bytes[1] == 0x50
&& bytes[2] == 0x4E
&& bytes[3] == 0x47
&& bytes[4] == 0x0D
&& bytes[5] == 0x0A
&& bytes[6] == 0x1A
&& bytes[7] == 0x0A
if (!pngData.headerIsValid) {
console.warn('Provided data does not belong to a png');
return pngData;
}
//parsing chunks
var chunks = [];
var chunk = PNG.utils.parseChunk(bytes, 8);
chunks.push(chunk);
while (chunk.name !== 'IEND') {
chunk = PNG.utils.parseChunk(bytes, chunk.end);
chunks.push(chunk);
}
pngData['chunks'] = chunks;
return pngData;
},
parseChunk: function(bytes, start) {
var chunkLength = PNG.utils.bytes2Int(bytes.slice(start, start + 4));
var chunkName = '';
chunkName += String.fromCharCode(bytes[start + 4]);
chunkName += String.fromCharCode(bytes[start + 5]);
chunkName += String.fromCharCode(bytes[start + 6]);
chunkName += String.fromCharCode(bytes[start + 7]);
var chunkData = [];
for (var idx = start + 8; idx<chunkLength + start + 8; idx++) {
chunkData.push(bytes[idx]);
}
//TODO validate crc as required!
return {
start: start,
end: Number(start) + Number(chunkLength) + 12, //12 = 4 (length) + 4 (name) + 4 (crc)
length: chunkLength,
name: chunkName,
data: chunkData,
crc: [
bytes[chunkLength + start + 8],
bytes[chunkLength + start + 9],
bytes[chunkLength + start + 10],
bytes[chunkLength + start + 11]
],
crcChecked: false
};
},
enrichParsedData: function(pngData) {
var idhrChunk = PNG.utils.getChunk(pngData, 'IDHR');
//see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
pngData.width = PNG.utils.bytes2Int(idhrChunk.data.slice(0, 4));
pngData.height = PNG.utils.bytes2Int(idhrChunk.data.slice(4, 8));
pngData.bitDepth = PNG.utils.bytes2Int(idhrChunk.data.slice(8, 9));
pngData.colorType = PNG.utils.bytes2Int(idhrChunk.data.slice(9, 10));
pngData.compressionMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(10, 11));
pngData.filterMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(11, 12));
pngData.interlaceMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(12, 13));
pngData.isGreyScale = pngData.colorType == 0 || pngData.colorType == 4;
pngData.isRgb = pngData.colorType == 2 || pngData.colorType == 6;
pngData.hasAlpha = pngData.colorType == 4 || pngData.colorType == 6;
pngData.hasPaletteMode = pngData.colorType == 3 && PNG.utils.getChunk(pngData, 'PLTE') != null;
return pngData;
},
getChunks: function(pngData, chunkName) {
var chunksForName = [];
for (var idx = 0; idx<pngData.chunks.length; idx++) {
if (pngData.chunks[idx].name = chunkName) {
chunksForName.push(pngData.chunks[idx]);
}
}
return chunksForName;
},
getChunk: function(pngData, chunkName) {
for (var idx = 0; idx<pngData.chunks.length; idx++) {
if (pngData.chunks[idx].name = chunkName) {
return pngData.chunks[idx];
}
}
return null;
},
bytes2Int: function(bytes) {
var ret = 0;
for (var idx = 0; idx<bytes.length; idx++) {
ret += bytes[idx];
if (idx < bytes.length - 1) {
ret = ret << 8;
}
}
return ret;
}
}
}
Which could be used as follows:
var pngData = PNG.parse(document.getElementById('yourImageId'));
console.log(pngData);
It contains some information, like color mode, number of chunks, the chunks itself, bit depth, etc.
Hope it helps.
回答2:
Can the JavaScript check the color mode of a image?
Yes and no.
No if you use ordinary image loading. All images that are supported by the browser are converted to RGB(A) before they are handed to us through the Image
object (HTMLImageElement
).
At this point no information about the original format and color model is presented except from its size and origin.
Canvas only deals with RGBA as all other elements that are displayed and there exist no native method to address color models such as CMYK.
You could always read the RGBA values and convert to CMYK naively, however, without a ICC profile for the target printer you would run into all sort of problems due to the wider gamut of RGB, and printer as well as print medium characteristics which must be adjusted for through ICC profiles. Meaning the result will not produce correct colors in most cases.
You would have to have a server-side solution setup for this to work. Send image to server, convert to CMYK/apply ICC, then send to printer. ImageMagick could be one way to go about it. The browser is simply not cut (nor intended) for print oriented processing.
Yes if you're willing to write image parser manually as well as ICC parser. You would need to support a variety of formats and format combinations, and also be able to apply ICC/gamma on import as well as export to CMYK through the target ICC (meaning you need to support XYZ/LAB color spaces).
The export would require you to write a file writer supporting the CMYK color space and ICC embedding.
The maturing process, bugs, instabilities etc. would be a natural part of the process and likely not something you would want to use in production right away.
If you're just after the color mode of the file and can load the image like ordinary you can of course load the file first through XHR, then scan the file using typed array to locate the information describing the color format depending on format. Then pass the typed array as Blob -> object-URL to load as an image, go through canvas and convert each pixel to CMYK values.
But from this point you will have the same problems as described above and would not escape writing the file writer etc.
回答3:
You can use exifreader package to read image metadata.
import ExifReader from 'exifreader'
const reader = new FileReader()
reader.onload = (event) => {
const metadata = ExifReader.load(event.target.result, { expanded: true })
const { file: { 'Color Components': { value } } } = metadata
switch (value) {
case 3: console.log('RGB'); break
case 4: console.log('CMYK'); break
default: console.log('Unknown')
}
}
reader.readAsArrayBuffer(instaceOfFileObject)
It doesn't have to be File
object, but the ExifReader.load()
expects array buffer as its first arg.
Reference: https://www.get-metadata.com/file-info/color-components
来源:https://stackoverflow.com/questions/37992117/how-to-get-image-color-mode-cmyk-rgb-in-javascript