Replace CDN React/ReactDOM/Babel with local libs; remove Babel and inline scripts Build Tailwind locally, add safelist; switch to assets/tailwind.css Self-host Font Awesome and Inter (CSS + woff2); remove external font CDNs Implement strict CSP (no unsafe-inline/eval; scripts/styles/fonts from self) Extract inline handlers; move PWA scripts to external files Add local QR code generation (qrcode lib) and remove api.qrserver.com Improve SessionTypeSelector visual selection (highlighted background and ring) Keep PWA working with service worker and offline assets Refs: CSP hardening, offline-first, no external dependencies
268 lines
6.3 KiB
JavaScript
268 lines
6.3 KiB
JavaScript
"use strict";
|
|
|
|
let interlaceUtils = require("./interlace");
|
|
|
|
let pixelBppMapper = [
|
|
// 0 - dummy entry
|
|
function () {},
|
|
|
|
// 1 - L
|
|
// 0: 0, 1: 0, 2: 0, 3: 0xff
|
|
function (pxData, data, pxPos, rawPos) {
|
|
if (rawPos === data.length) {
|
|
throw new Error("Ran out of data");
|
|
}
|
|
|
|
let pixel = data[rawPos];
|
|
pxData[pxPos] = pixel;
|
|
pxData[pxPos + 1] = pixel;
|
|
pxData[pxPos + 2] = pixel;
|
|
pxData[pxPos + 3] = 0xff;
|
|
},
|
|
|
|
// 2 - LA
|
|
// 0: 0, 1: 0, 2: 0, 3: 1
|
|
function (pxData, data, pxPos, rawPos) {
|
|
if (rawPos + 1 >= data.length) {
|
|
throw new Error("Ran out of data");
|
|
}
|
|
|
|
let pixel = data[rawPos];
|
|
pxData[pxPos] = pixel;
|
|
pxData[pxPos + 1] = pixel;
|
|
pxData[pxPos + 2] = pixel;
|
|
pxData[pxPos + 3] = data[rawPos + 1];
|
|
},
|
|
|
|
// 3 - RGB
|
|
// 0: 0, 1: 1, 2: 2, 3: 0xff
|
|
function (pxData, data, pxPos, rawPos) {
|
|
if (rawPos + 2 >= data.length) {
|
|
throw new Error("Ran out of data");
|
|
}
|
|
|
|
pxData[pxPos] = data[rawPos];
|
|
pxData[pxPos + 1] = data[rawPos + 1];
|
|
pxData[pxPos + 2] = data[rawPos + 2];
|
|
pxData[pxPos + 3] = 0xff;
|
|
},
|
|
|
|
// 4 - RGBA
|
|
// 0: 0, 1: 1, 2: 2, 3: 3
|
|
function (pxData, data, pxPos, rawPos) {
|
|
if (rawPos + 3 >= data.length) {
|
|
throw new Error("Ran out of data");
|
|
}
|
|
|
|
pxData[pxPos] = data[rawPos];
|
|
pxData[pxPos + 1] = data[rawPos + 1];
|
|
pxData[pxPos + 2] = data[rawPos + 2];
|
|
pxData[pxPos + 3] = data[rawPos + 3];
|
|
},
|
|
];
|
|
|
|
let pixelBppCustomMapper = [
|
|
// 0 - dummy entry
|
|
function () {},
|
|
|
|
// 1 - L
|
|
// 0: 0, 1: 0, 2: 0, 3: 0xff
|
|
function (pxData, pixelData, pxPos, maxBit) {
|
|
let pixel = pixelData[0];
|
|
pxData[pxPos] = pixel;
|
|
pxData[pxPos + 1] = pixel;
|
|
pxData[pxPos + 2] = pixel;
|
|
pxData[pxPos + 3] = maxBit;
|
|
},
|
|
|
|
// 2 - LA
|
|
// 0: 0, 1: 0, 2: 0, 3: 1
|
|
function (pxData, pixelData, pxPos) {
|
|
let pixel = pixelData[0];
|
|
pxData[pxPos] = pixel;
|
|
pxData[pxPos + 1] = pixel;
|
|
pxData[pxPos + 2] = pixel;
|
|
pxData[pxPos + 3] = pixelData[1];
|
|
},
|
|
|
|
// 3 - RGB
|
|
// 0: 0, 1: 1, 2: 2, 3: 0xff
|
|
function (pxData, pixelData, pxPos, maxBit) {
|
|
pxData[pxPos] = pixelData[0];
|
|
pxData[pxPos + 1] = pixelData[1];
|
|
pxData[pxPos + 2] = pixelData[2];
|
|
pxData[pxPos + 3] = maxBit;
|
|
},
|
|
|
|
// 4 - RGBA
|
|
// 0: 0, 1: 1, 2: 2, 3: 3
|
|
function (pxData, pixelData, pxPos) {
|
|
pxData[pxPos] = pixelData[0];
|
|
pxData[pxPos + 1] = pixelData[1];
|
|
pxData[pxPos + 2] = pixelData[2];
|
|
pxData[pxPos + 3] = pixelData[3];
|
|
},
|
|
];
|
|
|
|
function bitRetriever(data, depth) {
|
|
let leftOver = [];
|
|
let i = 0;
|
|
|
|
function split() {
|
|
if (i === data.length) {
|
|
throw new Error("Ran out of data");
|
|
}
|
|
let byte = data[i];
|
|
i++;
|
|
let byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1;
|
|
switch (depth) {
|
|
default:
|
|
throw new Error("unrecognised depth");
|
|
case 16:
|
|
byte2 = data[i];
|
|
i++;
|
|
leftOver.push((byte << 8) + byte2);
|
|
break;
|
|
case 4:
|
|
byte2 = byte & 0x0f;
|
|
byte1 = byte >> 4;
|
|
leftOver.push(byte1, byte2);
|
|
break;
|
|
case 2:
|
|
byte4 = byte & 3;
|
|
byte3 = (byte >> 2) & 3;
|
|
byte2 = (byte >> 4) & 3;
|
|
byte1 = (byte >> 6) & 3;
|
|
leftOver.push(byte1, byte2, byte3, byte4);
|
|
break;
|
|
case 1:
|
|
byte8 = byte & 1;
|
|
byte7 = (byte >> 1) & 1;
|
|
byte6 = (byte >> 2) & 1;
|
|
byte5 = (byte >> 3) & 1;
|
|
byte4 = (byte >> 4) & 1;
|
|
byte3 = (byte >> 5) & 1;
|
|
byte2 = (byte >> 6) & 1;
|
|
byte1 = (byte >> 7) & 1;
|
|
leftOver.push(byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
get: function (count) {
|
|
while (leftOver.length < count) {
|
|
split();
|
|
}
|
|
let returner = leftOver.slice(0, count);
|
|
leftOver = leftOver.slice(count);
|
|
return returner;
|
|
},
|
|
resetAfterLine: function () {
|
|
leftOver.length = 0;
|
|
},
|
|
end: function () {
|
|
if (i !== data.length) {
|
|
throw new Error("extra data found");
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) {
|
|
// eslint-disable-line max-params
|
|
let imageWidth = image.width;
|
|
let imageHeight = image.height;
|
|
let imagePass = image.index;
|
|
for (let y = 0; y < imageHeight; y++) {
|
|
for (let x = 0; x < imageWidth; x++) {
|
|
let pxPos = getPxPos(x, y, imagePass);
|
|
pixelBppMapper[bpp](pxData, data, pxPos, rawPos);
|
|
rawPos += bpp; //eslint-disable-line no-param-reassign
|
|
}
|
|
}
|
|
return rawPos;
|
|
}
|
|
|
|
function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) {
|
|
// eslint-disable-line max-params
|
|
let imageWidth = image.width;
|
|
let imageHeight = image.height;
|
|
let imagePass = image.index;
|
|
for (let y = 0; y < imageHeight; y++) {
|
|
for (let x = 0; x < imageWidth; x++) {
|
|
let pixelData = bits.get(bpp);
|
|
let pxPos = getPxPos(x, y, imagePass);
|
|
pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit);
|
|
}
|
|
bits.resetAfterLine();
|
|
}
|
|
}
|
|
|
|
exports.dataToBitMap = function (data, bitmapInfo) {
|
|
let width = bitmapInfo.width;
|
|
let height = bitmapInfo.height;
|
|
let depth = bitmapInfo.depth;
|
|
let bpp = bitmapInfo.bpp;
|
|
let interlace = bitmapInfo.interlace;
|
|
let bits;
|
|
|
|
if (depth !== 8) {
|
|
bits = bitRetriever(data, depth);
|
|
}
|
|
let pxData;
|
|
if (depth <= 8) {
|
|
pxData = Buffer.alloc(width * height * 4);
|
|
} else {
|
|
pxData = new Uint16Array(width * height * 4);
|
|
}
|
|
let maxBit = Math.pow(2, depth) - 1;
|
|
let rawPos = 0;
|
|
let images;
|
|
let getPxPos;
|
|
|
|
if (interlace) {
|
|
images = interlaceUtils.getImagePasses(width, height);
|
|
getPxPos = interlaceUtils.getInterlaceIterator(width, height);
|
|
} else {
|
|
let nonInterlacedPxPos = 0;
|
|
getPxPos = function () {
|
|
let returner = nonInterlacedPxPos;
|
|
nonInterlacedPxPos += 4;
|
|
return returner;
|
|
};
|
|
images = [{ width: width, height: height }];
|
|
}
|
|
|
|
for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
|
|
if (depth === 8) {
|
|
rawPos = mapImage8Bit(
|
|
images[imageIndex],
|
|
pxData,
|
|
getPxPos,
|
|
bpp,
|
|
data,
|
|
rawPos
|
|
);
|
|
} else {
|
|
mapImageCustomBit(
|
|
images[imageIndex],
|
|
pxData,
|
|
getPxPos,
|
|
bpp,
|
|
bits,
|
|
maxBit
|
|
);
|
|
}
|
|
}
|
|
if (depth === 8) {
|
|
if (rawPos !== data.length) {
|
|
throw new Error("extra data found");
|
|
}
|
|
} else {
|
|
bits.end();
|
|
}
|
|
|
|
return pxData;
|
|
};
|