@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.
122 lines (121 loc) • 3.01 kB
JavaScript
//#region src/utils/bgcode/qoi-decoder.ts
const QOI_OP_INDEX = 0;
const QOI_OP_DIFF = 64;
const QOI_OP_LUMA = 128;
const QOI_OP_RUN = 192;
const QOI_OP_RGB = 254;
const QOI_OP_RGBA = 255;
const QOI_MAGIC = 1903126886;
function hashColor(r, g, b, a) {
return (r * 3 + g * 5 + b * 7 + a * 11) % 64;
}
function decodeQOI(buffer) {
let pos = 0;
if (buffer.length < 14) throw new Error("Invalid QOI file: too short");
const magic = buffer.readUInt32BE(pos);
pos += 4;
if (magic !== QOI_MAGIC) throw new Error(`Invalid QOI magic: expected ${QOI_MAGIC.toString(16)}, got ${magic.toString(16)}`);
const width = buffer.readUInt32BE(pos);
pos += 4;
const height = buffer.readUInt32BE(pos);
pos += 4;
const channels = buffer.readUInt8(pos);
pos += 1;
const colorspace = buffer.readUInt8(pos);
pos += 1;
if (channels !== 3 && channels !== 4) throw new Error(`Invalid QOI channels: ${channels}`);
const outputSize = width * height * 4;
const output = Buffer.alloc(outputSize);
const colorArray = new Array(64);
for (let i = 0; i < 64; i++) colorArray[i] = [
0,
0,
0,
0
];
let r = 0;
let g = 0;
let b = 0;
let a = 255;
let outPos = 0;
while (outPos < outputSize) {
if (pos + 8 <= buffer.length) {
let isEnd = true;
for (let i = 0; i < 7; i++) if (buffer[pos + i] !== 0) {
isEnd = false;
break;
}
if (isEnd && buffer[pos + 7] === 1) break;
}
if (pos >= buffer.length) throw new Error("Unexpected end of QOI data");
const byte1 = buffer[pos++];
if (byte1 === QOI_OP_RGB) {
r = buffer[pos++];
g = buffer[pos++];
b = buffer[pos++];
} else if (byte1 === QOI_OP_RGBA) {
r = buffer[pos++];
g = buffer[pos++];
b = buffer[pos++];
a = buffer[pos++];
} else {
const tag = byte1 & 192;
if (tag === QOI_OP_INDEX) {
const index = byte1 & 63;
[r, g, b, a] = colorArray[index];
} else if (tag === QOI_OP_DIFF) {
const dr = (byte1 >> 4 & 3) - 2;
const dg = (byte1 >> 2 & 3) - 2;
const db = (byte1 & 3) - 2;
r = r + dr & 255;
g = g + dg & 255;
b = b + db & 255;
} else if (tag === QOI_OP_LUMA) {
const byte2 = buffer[pos++];
const dg = (byte1 & 63) - 32;
const dr_dg = (byte2 >> 4 & 15) - 8;
const db_dg = (byte2 & 15) - 8;
g = g + dg & 255;
r = r + dg + dr_dg & 255;
b = b + dg + db_dg & 255;
} else if (tag === QOI_OP_RUN) {
const run = (byte1 & 63) + 1;
for (let i = 0; i < run; i++) {
output[outPos++] = r;
output[outPos++] = g;
output[outPos++] = b;
output[outPos++] = a;
}
const hash = hashColor(r, g, b, a);
colorArray[hash] = [
r,
g,
b,
a
];
continue;
}
}
output[outPos++] = r;
output[outPos++] = g;
output[outPos++] = b;
output[outPos++] = a;
const hash = hashColor(r, g, b, a);
colorArray[hash] = [
r,
g,
b,
a
];
}
return {
width,
height,
channels,
colorspace,
data: output
};
}
//#endregion
export { decodeQOI };
//# sourceMappingURL=qoi-decoder.js.map