-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimageWorker.js
141 lines (128 loc) · 4.6 KB
/
imageWorker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// workers/imageWorker.js
const { workerData, parentPort } = require("worker_threads");
const sharp = require("sharp");
const fs = require("fs");
const path = require("path");
const colorMap = {
"0000": { r: 255, g: 255, b: 255 },
"0001": { r: 0, g: 0, b: 0 },
"0010": { r: 255, g: 0, b: 0 },
"0011": { r: 0, g: 255, b: 0 },
"0100": { r: 0, g: 0, b: 255 },
"0101": { r: 255, g: 255, b: 0 },
"0110": { r: 0, g: 255, b: 255 },
"0111": { r: 255, g: 0, b: 255 },
"1000": { r: 128, g: 0, b: 0 },
"1001": { r: 0, g: 128, b: 0 },
"1010": { r: 0, g: 0, b: 128 },
"1011": { r: 255, g: 165, b: 0 },
"1100": { r: 75, g: 0, b: 130 },
"1101": { r: 173, g: 255, b: 47 },
"1110": { r: 255, g: 20, b: 147 },
"1111": { r: 192, g: 192, b: 192 },
"A": { r: 128, g: 128, b: 128 },
"B": { r: 128, g: 128, b: 0 },
"C": { r: 0, g: 128, b: 128 },
"D": { r: 128, g: 0, b: 128 }
};
const colors = Object.entries(colorMap).map(([nibble, { r, g, b }]) => ({
nibble,
r,
g,
b,
}));
// Returns the nibble (as a string) that closely matches the pixel's RGB.
function findClosestNibble(r, g, b) {
let closest = "0000";
let smallestDiff = Infinity;
for (const { nibble, r: cr, g: cg, b: cb } of colors) {
const diff = Math.abs(cr - r) + Math.abs(cg - g) + Math.abs(cb - b);
if (diff < smallestDiff) {
smallestDiff = diff;
closest = nibble;
}
}
return closest;
}
// Convert an array of nibble strings to a byte array.
function nibblesToBytes(nibbles) {
const bytes = [];
for (let i = 0; i < nibbles.length; i += 2) {
const byte = (parseInt(nibbles[i], 2) << 4) | parseInt(nibbles[i + 1], 2);
bytes.push(byte);
}
return bytes;
}
/*
Extraction logic (old‑style):
Walks through the nibble stream:
- When an "A" is encountered, it toggles name reading.
- On the closing "A", an "A" marker is recorded (with start and end offsets in bytes).
- When a "D" is seen, a "D" marker is recorded and processing stops.
*/
function extractFileData(nibbles) {
const dataNibbles = [];
const identify = [];
let isReadingName = false;
let nameStartIndex = 0;
for (let i = 0; i < nibbles.length; i++) {
const nib = nibbles[i];
if (nib === "A") {
if (!isReadingName) {
// Začínáme číst název souboru – nastavíme počáteční index (v bajtech)
nameStartIndex = Math.floor(dataNibbles.length / 2);
} else {
// Dokončili jsme čtení názvu – zaznamenáme A marker s počátečním a koncovým indexem
const nameEndIndex = Math.floor(dataNibbles.length / 2);
identify.push({ type: "A", start: nameStartIndex, end: nameEndIndex });
}
isReadingName = !isReadingName;
} else if (nib === "D") {
// Na detekci D markeru zaznamenáme ukončení aktuálního souboru
const dataEndIndex = Math.floor(dataNibbles.length / 2);
identify.push({ type: "D", start: dataEndIndex, end: dataEndIndex });
// Místo "break" už pokračujeme ve zpracování, abychom načetli i další soubory
} else {
dataNibbles.push(nib);
}
}
return { dataNibbles, identify };
}
async function decodeImage(imagePath) {
const image = sharp(imagePath);
const { width, height } = await image.metadata();
const buffer = await image.raw().toBuffer();
const nibbles = [];
const totalPixels = width * height;
let lastReportedProgress = 0;
// Process each pixel and convert its color to a nibble.
for (let i = 0; i < buffer.length; i += 3) {
const r = buffer[i],
g = buffer[i + 1],
b = buffer[i + 2];
nibbles.push(findClosestNibble(r, g, b));
const processedPixels = Math.floor(i / 3);
const progress = Math.floor((processedPixels / totalPixels) * 100);
if (progress >= lastReportedProgress + 5) {
lastReportedProgress = progress;
parentPort.postMessage(progress);
}
}
parentPort.postMessage(100);
const binaryFilePath = path.join("./temp", `${path.parse(imagePath).name}.bin`);
const identifyFilePath = path.join("./temp", `${path.parse(imagePath).name}.id`);
// Extract the file data and marker positions.
const { dataNibbles, identify } = extractFileData(nibbles);
const bytes = nibblesToBytes(dataNibbles);
fs.writeFileSync(binaryFilePath, Buffer.from(bytes));
if (identify.length > 0) {
const identifyContent = identify
.map(entry => `${entry.start}-${entry.end} ${entry.type}`)
.join("\n");
fs.writeFileSync(identifyFilePath, identifyContent);
}
return binaryFilePath;
}
decodeImage(workerData)
.then((binaryFilePath) => parentPort.postMessage(binaryFilePath))
.catch((err) => parentPort.postMessage({ error: err.message }));