// ==UserScript==
// @name bv7_jpeg2array_b
// @namespace bv7
// @version 0.6
// @description jpeg -> array
// @author bv7
// @include file:///D:/projects/JSProjects/bv7bbc/bv7_bbc_dark/bv_dev_canvas*.html
// @run-at document-idle
// @grant GM_xmlhttpRequest
// ==/UserScript==
class BaseImage {
constructor() {
this._src = null;
this.onload = (() => '');
}
load() {
GM_xmlhttpRequest({
method : 'GET',
url : this._src,
overrideMimeType: 'text/plain; charset=x-user-defined',
onload : (v) => {
let data = new Uint8Array(v.responseText.length);
data.forEach((val, i) => data[i] = v.responseText.charCodeAt(i));
this.parse(data);
//this.decode();
this.onload();
}
});
}
get src() {return this._src;}
set src(v) {
if (this._src !== v) {
this._src = v;
this.load();
}
}
drawCanvas(canvas, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) {
let context = canvas.getContext('2d');
let imageData = context.getImageData(0, 0, canvas.width, canvas.height);
this.copyToImageData(imageData);
//for (let x = dx; x < imageData; x++) for (let y = dy; y < dy + dHeight; y++) context.
//context.putImageData(imageData, dx, dy, sx, sy, sWidth, sHeight);
context.putImageData(imageData, 0, 0);
}
}
class JpegImage extends BaseImage {
parse(data) {
this.jfif = null;
this.adobe = null;
const dctZigZag = new Int32Array([
0,
1, 8,
16, 9, 2,
3, 10, 17, 24,
32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40,
48, 41, 34, 27, 20, 13, 6,
7, 14, 21, 28, 35, 42, 49, 56,
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58,
59, 52, 45, 38, 31,
39, 46, 53, 60,
61, 54, 47,
55, 62,
63
]);
const dctCos1 = 4017; // cos( pi/16)
const dctSin1 = 799; // sin( pi/16)
const dctCos3 = 3406; // cos(3*pi/16)
const dctSin3 = 2276; // sin(3*pi/16)
const dctCos6 = 1567; // cos(6*pi/16)
const dctSin6 = 3784; // sin(6*pi/16)
const dctSqrt2 = 5793; // sqrt(2)
const dctSqrt1d2 = 2896; // sqrt(2) / 2
let frame;
let resetInterval;
let quantizationTables = [];
let frames = [];
let huffmanTablesAC = [];
let huffmanTablesDC = [];
let offset = 0;
let readUint8 = () => data[offset++];
let readUint16 = () => ((readUint8() << 8) | readUint8());
let readDataBlock = () => {
let length = readUint16() - 2;
let value = data.slice(offset, offset + length - 2);
offset += length;
return value;
};
let buildHuffmanTable = (codeLengths, values) => {
let length = codeLengths.length;
while (length > 0 && !codeLengths[length - 1]) length--;
let p = {children: [], index: 0};
let code = [p];
for (let i = 0, k = 0, q; i < length; i++) {
for (let j = 0; j < codeLengths[i]; j++, k++) {
p = code.pop();
p.children[p.index] = values[k];
while (p.index > 0) p = code.pop();
p.index++;
code.push(p);
while (code.length <= i) {
code.push(q = {children: [], index: 0});
p.children[p.index] = q.children;
p = q;
}
}
if (i + 1 < length) { // p here points to last code
code.push(q = {children: [], index: 0});
p.children[p.index] = q.children;
p = q;
}
}
return code[0].children;
};
let buildComponentData = (component) => {
let lines = [];
let samplesPerLine = component.blocksPerLine << 3;
let R = new Int32Array(64);
let r = new Uint8Array(64);
// A port of poppler's IDCT method which in turn is taken from:
// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
// "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
// 988-991.
let quantizeAndInverse = (zz, dataOut, dataIn) => {
let t, v, u;
// dequant
dataIn.forEach((v, i) => dataIn[i] = zz[i] * component.quantizationTable[i]);
for (let i = 0; i < 8; ++i) {
let row = 8 * i;
v = dataIn.slice(row, 8 + row);
// check for all-zero AC coefficients
if (v.every((val, i) => (!i || !val))) dataIn.fill((dctSqrt2 * v[0] + 512) >> 10, row, 8 + row);
else {
// stage 4
u = [
(dctSqrt2 * v[0] + 128) >> 8,
(dctSqrt2 * v[4] + 128) >> 8,
v[2],
v[6],
(dctSqrt1d2 * (v[1] - v[7]) + 128) >> 8,
v[3] << 4,
v[5] << 4,
(dctSqrt1d2 * (v[1] + v[7]) + 128) >> 8
];
// stage 3
v = [
(u[0] + u[1] + 1) >> 1,
(u[0] - u[1] + 1) >> 1,
(u[2] * dctCos6 - u[3] * dctSin6 + 128) >> 8,
(u[2] * dctSin6 + u[3] * dctCos6 + 128) >> 8,
(u[4] + u[6] + 1) >> 1,
(u[7] - u[5] + 1) >> 1,
(u[4] - u[6] + 1) >> 1,
(u[7] + u[5] + 1) >> 1
];
// stage 2
u = [
(v[0] + v[3] + 1) >> 1,
(v[1] + v[2] + 1) >> 1,
(v[1] - v[2] + 1) >> 1,
(v[0] - v[3] + 1) >> 1,
(v[4] * dctCos3 - v[7] * dctSin3 + 2048) >> 12,
(v[5] * dctCos1 - v[6] * dctSin1 + 2048) >> 12,
(v[5] * dctSin1 + v[6] * dctCos1 + 2048) >> 12,
(v[4] * dctSin3 + v[7] * dctCos3 + 2048) >> 12
];
// stage 1
for(let j = 0; j < 4; j++) {
dataIn[0 + j + row] = u[j] + u[7 - j];
dataIn[7 - j + row] = u[j] - u[7 - j];
}
}
}
// inverse DCT on columns
for (let col = 0; col < 8; ++col) {
for (let i = 0; i < 8; i++) v[i] = dataIn[i * 8 + col];
// check for all-zero AC coefficients
if (v.every((val, i) => (!i || !val))) {
t = (dctSqrt2 * v[0] + 8192) >> 14;
v.forEach((val, i) => dataIn[i * 8 + col] = t);
} else {
// stage 4
u = [
(dctSqrt2 * v[0] + 2048) >> 12,
(dctSqrt2 * v[4] + 2048) >> 12,
v[2],
v[6],
(dctSqrt1d2 * (v[1] - v[7]) + 2048) >> 12,
v[3],
v[5],
(dctSqrt1d2 * (v[1] + v[7]) + 2048) >> 12
];
// stage 3
v = [
(u[0] + u[1] + 1) >> 1,
(u[0] - u[1] + 1) >> 1,
(u[2] * dctCos6 - u[3] * dctSin6 + 2048) >> 12,
(u[2] * dctSin6 + u[3] * dctCos6 + 2048) >> 12,
(u[4] + u[6] + 1) >> 1,
(u[7] - u[5] + 1) >> 1,
(u[4] - u[6] + 1) >> 1,
(u[7] + u[5] + 1) >> 1
];
// stage 2
u = [
(v[0] + v[3] + 1) >> 1,
(v[1] + v[2] + 1) >> 1,
(v[1] - v[2] + 1) >> 1,
(v[0] - v[3] + 1) >> 1,
(v[4] * dctCos3 - v[7] * dctSin3 + 2048) >> 12,
(v[5] * dctCos1 - v[6] * dctSin1 + 2048) >> 12,
(v[5] * dctSin1 + v[6] * dctCos1 + 2048) >> 12,
(v[4] * dctSin3 + v[7] * dctCos3 + 2048) >> 12
];
// stage 1
for (let i = 0; i < 4; i++) {
dataIn[(0 + i) * 8 + col] = u[i] + u[7 - i];
dataIn[(7 - i) * 8 + col] = u[i] - u[7 - i];
}
}
}
// convert to 8-bit integers
dataIn.forEach((val, i) => {
let sample = 128 + ((val + 8) >> 4);
dataOut[i] = sample < 0 ? 0 : sample > 0xFF ? 0xFF : sample;
});
};
for (let blockRow = 0; blockRow < component.blocksPerColumn; blockRow++) {
let scanLine = blockRow << 3;
for (let i = 0; i < 8; i++) lines.push(new Uint8Array(samplesPerLine));
for (let blockCol = 0; blockCol < component.blocksPerLine; blockCol++) {
quantizeAndInverse(component.blocks[blockRow][blockCol], r, R);
let sample = blockCol << 3;
for (let j = 0, offset = 0; j < 8; j++) {
let line = lines[scanLine + j];
for (let i = 0; i < 8; i++, offset++) line[sample + i] = r[offset];
}
}
}
return lines;
}
let prepareComponents = (frame) => {
frame.maxH = 0;
frame.maxV = 0;
frame.componentsOrder.forEach((v) => {
let component = frame.components[v];
if (frame.maxH < component.h) frame.maxH = component.h;
if (frame.maxV < component.v) frame.maxV = component.v;
});
frame.mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
frame.mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
frame.componentsOrder.forEach((v) => {
let component = frame.components[v];
component.blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
component.blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
component.blocks = [];
let blocksPerLineForMcu = frame.mcusPerLine * component.h;
let blocksPerColumnForMcu = frame.mcusPerColumn * component.v;
for (let i = 0; i < blocksPerColumnForMcu; i++) {
let row = [];
for (let j = 0; j < blocksPerLineForMcu; j++) row.push(new Int32Array(64));
component.blocks.push(row);
}
});
};
let decodeScan = (components, resetInterval, spectralStart, spectralEnd, successivePrev, successive) => {
let bitsData = 0;
let bitsCount = 0;
let readBit = () => {
if (bitsCount > 0) bitsCount--;
else {
bitsData = readUint8();
if (bitsData == 0xFF) {
let nextByte = readUint8();
if (nextByte) throw new Error('Unexpected marker: ' + ((bitsData << 8) | nextByte).toString(16));
// unstuff 0
}
bitsCount = 7;
}
return (bitsData >> bitsCount) & 1;
};
let decodeHuffman = (tree) => {
let node = tree;
let bit;
while ((bit = readBit()) !== null) if (typeof (node = node[bit]) === 'number') return node;
else if (typeof node !== 'object') throw new Error('Invalid huffman sequence');
return null;
};
let receive = (length) => {
let n = 0;
for (; length > 0; length--) {
let bit = readBit();
if (bit === null) return;
n = (n << 1) | bit;
}
return n;
};
let receiveAndExtend = (length) => {
let n = receive(length);
return (n >= 1 << (length - 1)) ? n : (n + (-1 << length) + 1);
};
let decodeBaseline = (component, zz) => {
let t = decodeHuffman(component.huffmanTableDC);
let diff = t === 0 ? 0 : receiveAndExtend(t);
zz[0] = (component.pred += diff);
let k = 1;
while (k < 64) {
let rs = decodeHuffman(component.huffmanTableAC);
let s = rs & 15;
let r = rs >> 4;
if (s === 0) {
if (r < 15) break;
else {
k += 16;
continue;
}
} else {
k += r;
zz[dctZigZag[k]] = receiveAndExtend(s);
k++;
}
}
}
let decodeDCFirst = (component, zz) => {
let t = decodeHuffman(component.huffmanTableDC);
let diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
zz[0] = (component.pred += diff);
}
let decodeDCSuccessive = (component, zz) => zz[0] |= readBit() << successive;
let eobrun = 0;
let decodeACFirst = (component, zz) => {
if (eobrun > 0) eobrun--;
else {
let k = spectralStart;
while (k <= spectralEnd) {
let rs = decodeHuffman(component.huffmanTableAC);
let s = rs & 15;
let r = rs >> 4;
if (s === 0) {
if (r < 15) {
eobrun = receive(r) + (1 << r) - 1;
break;
} else k += 16;
} else {
k += r;
zz[dctZigZag[k]] = receiveAndExtend(s) * (1 << successive);
k++;
}
}
}
};
let successiveACState = 0;
let successiveACNextValue = 0;
let decodeACSuccessive = (component, zz) => {
let k = spectralStart;
let r = 0;
while(k <= spectralEnd) {
let z = dctZigZag[k];
let direction = zz[z] < 0 ? -1 : 1;
switch (successiveACState) {
case 0: // initial state
let rs = decodeHuffman(component.huffmanTableAC);
let s = rs & 15;
r = rs >> 4;
if (s === 0) {
if (r < 15) {
eobrun = receive(r) + (1 << r);
successiveACState = 4;
} else {
r = 16;
successiveACState = 1;
}
} else if (s !== 1) throw new Error('Invalid ACn encoding');
else {
successiveACNextValue = receiveAndExtend(s);
successiveACState = r ? 2 : 3;
}
continue;
case 1: // skipping r zero items
case 2:
if (zz[z]) zz[z] += (readBit() << successive) * direction;
else {
r--;
if (r === 0) successiveACState = successiveACState == 2 ? 3 : 0;
}
break;
case 3: // set value for a zero item
if (zz[z]) zz[z] += (readBit() << successive) * direction;
else {
zz[z] = successiveACNextValue << successive;
successiveACState = 0;
}
break;
case 4: // eob
if (zz[z]) zz[z] += (readBit() << successive) * direction;
break;
}
k++;
}
if (successiveACState === 4) {
eobrun--;
if (eobrun === 0) successiveACState = 0;
}
};
let decodeMcu = (component, decode, mcu, row, col) => decode(
component,
component.blocks[((mcu / frame.mcusPerLine) | 0) * component.v + row][(mcu % frame.mcusPerLine) * component.h + col]
);
let decodeBlock = (component, decode, mcu) => decode(
component,
component.blocks[(mcu / component.blocksPerLine) | 0][mcu % component.blocksPerLine]
);
let decodeFn = frame.progressive ? (
spectralStart === 0 ? (
successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive
) : (
successivePrev === 0 ? decodeACFirst : decodeACSuccessive
)
) : decodeBaseline;
let mcu = 0;
let mcuExpected = (components.length == 1) ? (
components[0].blocksPerLine * components[0].blocksPerColumn
) : (
frame.mcusPerLine * frame.mcusPerColumn
);
if (!resetInterval) resetInterval = mcuExpected;
while (mcu < mcuExpected) {
// reset interval stuff
components.forEach((v) => v.pred = 0);
eobrun = 0;
if (components.length == 1) {
let component = components[0];
for (let n = 0; n < resetInterval; n++, mcu++) decodeBlock(component, decodeFn, mcu);
} else for (let n = 0; n < resetInterval; n++) {
for (let i = 0; i < components.length; i++) {
let component = components[i];
for (let j = 0; j < component.v; j++) for (let k = 0; k < component.h; k++) decodeMcu(component, decodeFn, mcu, j, k);
}
mcu++;
// If we've reached our expected MCU's, stop decoding
if (mcu === mcuExpected) break;
}
// find marker
bitsCount = 0;
let marker = readUint16();
if (marker < 0xFFD0 || marker > 0xFFD7) { // !RSTx
offset -= 2;
if (marker < 0xFF00) throw new Error('Marker was not found');
break;
}
}
};
let fileMarker = readUint16();
if (fileMarker != 0xFFD8) // SOI (Start of Image)
throw new Error('SOI not found');
while ((fileMarker = readUint16()) != 0xFFD9) { // EOI (End of image)
let length;
switch(fileMarker) {
case 0xFF00: break;
case 0xFFE0: // APP0 (Application Specific)
case 0xFFE1: // APP1
case 0xFFE2: // APP2
case 0xFFE3: // APP3
case 0xFFE4: // APP4
case 0xFFE5: // APP5
case 0xFFE6: // APP6
case 0xFFE7: // APP7
case 0xFFE8: // APP8
case 0xFFE9: // APP9
case 0xFFEA: // APP10
case 0xFFEB: // APP11
case 0xFFEC: // APP12
case 0xFFED: // APP13
case 0xFFEE: // APP14
case 0xFFEF: // APP15
case 0xFFFE: // COM (Comment)
let appData = readDataBlock();
switch(fileMarker){
case 0xFFE0:
if (
appData[0] === 0x4A &&
appData[1] === 0x46 &&
appData[2] === 0x49 &&
appData[3] === 0x46 &&
appData[4] === 0
) this.jfif = { // 'JFIF\x00'
version : { major: appData[5], minor: appData[6] },
densityUnits: appData[7],
xDensity : (appData[8 ] << 8) | appData[9 ],
yDensity : (appData[10] << 8) | appData[11],
thumbWidth : appData[12],
thumbHeight : appData[13],
thumbData : appData.slice(14, 14 + 3 * appData[12] * appData[13] + 1)
};
break;
// TODO APP1 - Exif
case 0xFFEE:
if (
appData[0] === 0x41 &&
appData[1] === 0x64 &&
appData[2] === 0x6F &&
appData[3] === 0x62 &&
appData[4] === 0x65 &&
appData[5] === 0
) this.adobe = { // 'Adobe\x00'
version : appData[6],
flags0 : (appData[7] << 8) | appData[8],
flags1 : (appData[9] << 8) | appData[10],
transformCode: appData[11]
};
break;
}
break;
case 0xFFDB: // DQT (Define Quantization Tables)
let quantizationTablesLength = readUint16();
let quantizationTablesEnd = quantizationTablesLength + offset - 2;
while (offset < quantizationTablesEnd) {
let quantizationTableSpec = readUint8();
let tableData = new Int32Array(64);
switch(quantizationTableSpec >> 4){
case 0: // 8 bit values
tableData.forEach((v, i) => tableData[dctZigZag[i]] = readUint8());
break;
case 1: //16 bit
tableData.forEach((v, i) => tableData[dctZigZag[i]] = readUint16());
break;
default:
throw new Error('DQT: invalid table spec: ' + (quantizationTableSpec >> 4));
}
quantizationTables[quantizationTableSpec & 15] = tableData;
}
break;
case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT)
case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT)
readUint16(); // skip data length
frame = {
extended : fileMarker === 0xFFC1,
progressive : fileMarker === 0xFFC2,
precision : readUint8(),
scanLines : readUint16(),
samplesPerLine : readUint16(),
components : {},
componentsOrder: new Uint8Array(readUint8())
};
frame.componentsOrder.forEach((v, i) => {
let componentId = readUint8();
let b = readUint8();
frame.components[frame.componentsOrder[i] = componentId] = {
h : b >> 4,
v : b & 15,
quantizationIdx: readUint8()
};
});
prepareComponents(frame);
frames.push(frame);
break;
case 0xFFC4: // DHT (Define Huffman Tables)
let huffmanLength = readUint16() - 2;
while (huffmanLength > 0) {
let huffmanTableSpec = readUint8();
let codeLengths = new Uint8Array(16);
let codeLengthSum = 0;
codeLengths.forEach((v, i) => codeLengthSum += (codeLengths[i] = readUint8()));
let huffmanValues = new Uint8Array(codeLengthSum);
huffmanValues.forEach((v, i) => huffmanValues[i] = readUint8());
huffmanLength -= 1 + codeLengths.length + huffmanValues.length;
((huffmanTableSpec >> 4) === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
}
break;
case 0xFFDD: // DRI (Define Restart Interval)
readUint16(); // skip data length
resetInterval = readUint16();
break;
case 0xFFDA: // SOS (Start of Scan)
readUint16(); // scanLength
let components = [];
for (let selectorsCount = readUint8(); selectorsCount > 0; selectorsCount--) {
let component = frame.components[readUint8()];
let tableSpec = readUint8();
component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
components.push(component);
}
let spectralStart = readUint8();
let spectralEnd = readUint8();
let successiveApproximation = readUint8();
decodeScan(
components,
resetInterval,
spectralStart,
spectralEnd,
successiveApproximation >> 4,
successiveApproximation & 15
);
break;
case 0xFFFF: // Fill bytes
if (data[offset] !== 0xFF) offset--; // Avoid skipping a valid marker.
break;
default:
// could be incorrect encoding -- last 0xFF byte of the previous
// block was eaten by the encoder
if (data[offset - 3] == data[offset - 2] && data[offset - 2] >= 0xC0 && d1 <= 0xFE) {
offset -= 3;
break;
}
else throw new Error('Unknown JPEG marker ' + fileMarker.toString(16));
}
}
if (frames.length != 1) throw new Error("Only single frame JPEGs supported");
// set each frame's components quantization table
frames.forEach((v) => {
for (var j in v.components) {
v.components[j].quantizationTable = quantizationTables[v.components[j].quantizationIdx];
delete v.components[j].quantizationIdx;
}
});
this.width = frame.samplesPerLine;
this.height = frame.scanLines;
this.components = [];
frame.componentsOrder.forEach((v) => {
let component = frame.components[v];
this.components.push({
lines : buildComponentData(component),
scaleX: component.h / frame.maxH,
scaleY: component.v / frame.maxV
});
});
}
clampTo8bit(a) {
return a < 0 ? 0 : a > 255 ? 255 : a;
}
getData(width, height) {
let scaleX = this.width / width ;
let scaleY = this.height / height;
let data = new Uint8Array(width * height * this.components.length);
let offset = 0;
if (this.components.length > 0 && this.components.length < 5) {
let components4 = this.components.length == 4;
// PDF might compress two component data in custom colorspace
if (components4 && !this.adobe) throw new Error('Unsupported color mode (4 components)');
else {
// The default transform for three components is true
// The adobe transform marker overrides any previous setting
let colorTransform = this.components.length > 2 && (
(this.adobe && this.adobe.transformCode) ||
(typeof this.colorTransform === 'undefined') ||
!!this.colorTransform
);
let componentLines = [];
let d = new Uint8Array(this.components.length);
for (let y = 0; y < height; y++) {
this.components.forEach((v, i) => (componentLines[i] = v.lines[0 | (y * v.scaleY * scaleY)]));
for (let x = 0; x < width; x++) {
componentLines.forEach((v, i) => (d[i] = v[0 | (x * this.components[i].scaleX * scaleX)])); // Y
if (colorTransform) {
data[offset++] = this.clampTo8bit(d[0] + 1.402 * (d[2] - 128));
data[offset++] = this.clampTo8bit(d[0] - 0.3441363 * (d[1] - 128) - 0.71413636 * (d[2] - 128));
data[offset++] = this.clampTo8bit(d[0] + 1.772 * (d[1] - 128));
if (components4) data[offset++] = 255 - d[0];
} else if (components4) d.forEach((v) => data[offset++] = 255 - v);
else d.forEach((v) => data[offset++] = v);
}
}
}
} else throw new Error('Unsupported color mode');
return data;
}
copyToImageData(imageData) {
let data = this.getData(imageData.width, imageData.height);
let i = 0;
let j = 0;
switch (this.components.length) {
case 1:
for (let y = 0; y < imageData.height; y++) for (let x = 0; x < imageData.width; x++) {
imageData.data[j++] = imageData.data[j++] = imageData.data[j++] = data[i++]; // Y
imageData.data[j++] = 255;
}
break;
case 3:
for (let y = 0; y < imageData.height; y++) for (let x = 0; x < imageData.width; x++) {
imageData.data[j++] = data[i++]; // R
imageData.data[j++] = data[i++]; // G
imageData.data[j++] = data[i++]; // B
imageData.data[j++] = 255;
}
break;
case 4:
for (let y = 0; y < imageData.height; y++) for (let x = 0; x < imageData.width; x++) {
let C = data[i++];
let M = data[i++];
let Y = data[i++];
let K = data[i++];
imageData.data[j++] = 255 - this.clampTo8bit(C * (1 - K / 255) + K); // R
imageData.data[j++] = 255 - this.clampTo8bit(M * (1 - K / 255) + K); // G
imageData.data[j++] = 255 - this.clampTo8bit(Y * (1 - K / 255) + K); // B
imageData.data[j++] = 255;
}
break;
default:
throw new Error('Unsupported color mode');
}
}
}
/*
let jpeg = new JpegImage();
jpeg.onload = function() {
this.drawCanvas(document.getElementById('canvas'), 10, 10, 20, 20, 50, 50);
console.log('jpeg =', this);
};
jpeg.src = document.getElementById('img1').src;
*/