roxify 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -4
- package/dist/index.js +173 -175
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync, writeFileSync } from 'fs';
|
|
3
|
-
import { basename, dirname,
|
|
3
|
+
import { basename, dirname, resolve } from 'path';
|
|
4
4
|
import sharp from 'sharp';
|
|
5
5
|
import { cropAndReconstitute, DataFormatError, decodePngToBinary, encodeBinaryToPng, IncorrectPassphraseError, PassphraseRequiredError, } from './index.js';
|
|
6
6
|
const VERSION = '1.0.4';
|
|
@@ -187,9 +187,6 @@ async function decodeCommand(args) {
|
|
|
187
187
|
})
|
|
188
188
|
.png()
|
|
189
189
|
.toBuffer();
|
|
190
|
-
if (options.debugDir) {
|
|
191
|
-
writeFileSync(join(options.debugDir, 'doubled.png'), doubledBuffer);
|
|
192
|
-
}
|
|
193
190
|
const reconstructedBuffer = await cropAndReconstitute(doubledBuffer, options.debugDir);
|
|
194
191
|
const result = await decodePngToBinary(reconstructedBuffer, options);
|
|
195
192
|
const decodeTime = Date.now() - startDecode;
|
package/dist/index.js
CHANGED
|
@@ -148,9 +148,8 @@ export async function cropAndReconstitute(input, debugDir) {
|
|
|
148
148
|
await sharp(doubledBuffer).toFile(join(debugDir, 'doubled.png'));
|
|
149
149
|
}
|
|
150
150
|
const { data: doubledData, info: doubledInfo } = await loadRaw(doubledBuffer);
|
|
151
|
-
const w = doubledInfo.width;
|
|
152
|
-
const
|
|
153
|
-
function at(x, y) {
|
|
151
|
+
const w = doubledInfo.width, h = doubledInfo.height;
|
|
152
|
+
const at = (x, y) => {
|
|
154
153
|
const i = idxFor(x, y, w);
|
|
155
154
|
return [
|
|
156
155
|
doubledData[i],
|
|
@@ -158,178 +157,110 @@ export async function cropAndReconstitute(input, debugDir) {
|
|
|
158
157
|
doubledData[i + 2],
|
|
159
158
|
doubledData[i + 3],
|
|
160
159
|
];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
let endPoint = null;
|
|
193
|
-
for (let y = h - 1; y >= 0 && !endPoint; y--) {
|
|
194
|
-
for (let x = w - 1; x >= 0; x--) {
|
|
195
|
-
const p = at(x, y);
|
|
196
|
-
if (p[0] !== 255 || p[1] !== 0 || p[2] !== 0)
|
|
197
|
-
continue;
|
|
198
|
-
let nx = x - 1;
|
|
199
|
-
while (nx >= 0 && eqRGB(at(nx, y), p))
|
|
200
|
-
nx--;
|
|
201
|
-
if (nx < 0)
|
|
202
|
-
continue;
|
|
203
|
-
const a = at(nx, y);
|
|
204
|
-
let nx2 = nx - 1;
|
|
205
|
-
while (nx2 >= 0 && eqRGB(at(nx2, y), a))
|
|
206
|
-
nx2--;
|
|
207
|
-
if (nx2 < 0)
|
|
208
|
-
continue;
|
|
209
|
-
const b = at(nx2, y);
|
|
210
|
-
const isRgbReverse = a[0] === 0 &&
|
|
211
|
-
a[1] === 255 &&
|
|
212
|
-
a[2] === 0 &&
|
|
213
|
-
b[0] === 0 &&
|
|
214
|
-
b[1] === 0 &&
|
|
215
|
-
b[2] === 255;
|
|
216
|
-
if (isRgbReverse) {
|
|
217
|
-
endPoint = { x, y, type: 'bgr' };
|
|
218
|
-
break;
|
|
160
|
+
};
|
|
161
|
+
const findPattern = (startX, startY, dirX, dirY, pattern) => {
|
|
162
|
+
for (let y = startY; y >= 0 && y < h; y += dirY) {
|
|
163
|
+
for (let x = startX; x >= 0 && x < w; x += dirX) {
|
|
164
|
+
const p = at(x, y);
|
|
165
|
+
if (p[0] !== 255 || p[1] !== 0 || p[2] !== 0)
|
|
166
|
+
continue;
|
|
167
|
+
let nx = x + dirX;
|
|
168
|
+
while (nx >= 0 && nx < w && eqRGB(at(nx, y), p))
|
|
169
|
+
nx += dirX;
|
|
170
|
+
if (nx < 0 || nx >= w)
|
|
171
|
+
continue;
|
|
172
|
+
const a = at(nx, y);
|
|
173
|
+
let nx2 = nx + dirX;
|
|
174
|
+
while (nx2 >= 0 && nx2 < w && eqRGB(at(nx2, y), a))
|
|
175
|
+
nx2 += dirX;
|
|
176
|
+
if (nx2 < 0 || nx2 >= w)
|
|
177
|
+
continue;
|
|
178
|
+
const b = at(nx2, y);
|
|
179
|
+
if (a[0] === pattern[0][0] &&
|
|
180
|
+
a[1] === pattern[0][1] &&
|
|
181
|
+
a[2] === pattern[0][2] &&
|
|
182
|
+
b[0] === pattern[1][0] &&
|
|
183
|
+
b[1] === pattern[1][1] &&
|
|
184
|
+
b[2] === pattern[1][2]) {
|
|
185
|
+
return { x, y };
|
|
186
|
+
}
|
|
219
187
|
}
|
|
220
188
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
189
|
+
return null;
|
|
190
|
+
};
|
|
191
|
+
const startPoint = findPattern(0, 0, 1, 1, [
|
|
192
|
+
[0, 255, 0],
|
|
193
|
+
[0, 0, 255],
|
|
194
|
+
]);
|
|
195
|
+
const endPoint = findPattern(w - 1, h - 1, -1, -1, [
|
|
196
|
+
[0, 255, 0],
|
|
197
|
+
[0, 0, 255],
|
|
198
|
+
]);
|
|
199
|
+
if (!startPoint || !endPoint)
|
|
200
|
+
throw new Error('Patterns not found');
|
|
201
|
+
const sx1 = Math.min(startPoint.x, endPoint.x), sy1 = Math.min(startPoint.y, endPoint.y);
|
|
202
|
+
const sx2 = Math.max(startPoint.x, endPoint.x), sy2 = Math.max(startPoint.y, endPoint.y);
|
|
203
|
+
const cropW = sx2 - sx1 + 1, cropH = sy2 - sy1 + 1;
|
|
232
204
|
if (cropW <= 0 || cropH <= 0)
|
|
233
205
|
throw new Error('Invalid crop dimensions');
|
|
234
206
|
const cropped = await sharp(doubledBuffer)
|
|
235
207
|
.extract({ left: sx1, top: sy1, width: cropW, height: cropH })
|
|
236
208
|
.png()
|
|
237
209
|
.toBuffer();
|
|
238
|
-
const { data: cdata, info: cinfo } = await
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.toBuffer({ resolveWithObject: true });
|
|
242
|
-
const cw = cinfo.width;
|
|
243
|
-
const ch = cinfo.height;
|
|
244
|
-
function cat(x, y) {
|
|
245
|
-
const i = idxFor(x, y, cw);
|
|
246
|
-
return [cdata[i], cdata[i + 1], cdata[i + 2], cdata[i + 3]];
|
|
247
|
-
}
|
|
248
|
-
function eq(a, b) {
|
|
249
|
-
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
|
|
250
|
-
}
|
|
251
|
-
function lineEq(l1, l2) {
|
|
252
|
-
if (l1.length !== l2.length)
|
|
253
|
-
return false;
|
|
254
|
-
for (let i = 0; i < l1.length; i++)
|
|
255
|
-
if (!eq(l1[i], l2[i]))
|
|
256
|
-
return false;
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
const newWidth = cw;
|
|
260
|
-
const newHeight = ch + 1;
|
|
210
|
+
const { data: cdata, info: cinfo } = await loadRaw(cropped);
|
|
211
|
+
const cw = cinfo.width, ch = cinfo.height;
|
|
212
|
+
const newWidth = cw, newHeight = ch + 1;
|
|
261
213
|
const out = Buffer.alloc(newWidth * newHeight * 4, 0);
|
|
262
214
|
for (let i = 0; i < out.length; i += 4)
|
|
263
215
|
out[i + 3] = 255;
|
|
264
216
|
for (let y = 0; y < ch; y++) {
|
|
265
217
|
for (let x = 0; x < cw; x++) {
|
|
266
|
-
const srcI = (
|
|
267
|
-
const dstI = (
|
|
218
|
+
const srcI = (y * cw + x) * 4;
|
|
219
|
+
const dstI = (y * newWidth + x) * 4;
|
|
268
220
|
out[dstI] = cdata[srcI];
|
|
269
221
|
out[dstI + 1] = cdata[srcI + 1];
|
|
270
222
|
out[dstI + 2] = cdata[srcI + 2];
|
|
271
223
|
out[dstI + 3] = cdata[srcI + 3];
|
|
272
224
|
}
|
|
273
225
|
}
|
|
274
|
-
const targetY = ch - 1;
|
|
275
|
-
for (let x = 0; x < cw; x++) {
|
|
276
|
-
const i = ((targetY * newWidth + x) * 4) | 0;
|
|
277
|
-
out[i] = 0;
|
|
278
|
-
out[i + 1] = 0;
|
|
279
|
-
out[i + 2] = 0;
|
|
280
|
-
out[i + 3] = 255;
|
|
281
|
-
}
|
|
282
|
-
const lastY = ch;
|
|
283
226
|
for (let x = 0; x < newWidth; x++) {
|
|
284
|
-
const i = ((
|
|
285
|
-
out[i] = 0;
|
|
286
|
-
out[i + 1] = 0;
|
|
287
|
-
out[i + 2] = 0;
|
|
227
|
+
const i = ((ch - 1) * newWidth + x) * 4;
|
|
228
|
+
out[i] = out[i + 1] = out[i + 2] = 0;
|
|
288
229
|
out[i + 3] = 255;
|
|
230
|
+
const j = (ch * newWidth + x) * 4;
|
|
231
|
+
out[j] = out[j + 1] = out[j + 2] = 0;
|
|
232
|
+
out[j + 3] = 255;
|
|
289
233
|
}
|
|
290
234
|
if (newWidth >= 3) {
|
|
291
235
|
const bgrStart = newWidth - 3;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
out[i + 1] = 0;
|
|
305
|
-
out[i + 2] = 0;
|
|
306
|
-
out[i + 3] = 255;
|
|
236
|
+
const bgr = [
|
|
237
|
+
[0, 0, 255],
|
|
238
|
+
[0, 255, 0],
|
|
239
|
+
[255, 0, 0],
|
|
240
|
+
];
|
|
241
|
+
for (let k = 0; k < 3; k++) {
|
|
242
|
+
const i = (ch * newWidth + bgrStart + k) * 4;
|
|
243
|
+
out[i] = bgr[k][0];
|
|
244
|
+
out[i + 1] = bgr[k][1];
|
|
245
|
+
out[i + 2] = bgr[k][2];
|
|
246
|
+
out[i + 3] = 255;
|
|
247
|
+
}
|
|
307
248
|
}
|
|
308
|
-
|
|
309
|
-
const i = (
|
|
249
|
+
const getPixel = (x, y) => {
|
|
250
|
+
const i = (y * newWidth + x) * 4;
|
|
310
251
|
return [out[i], out[i + 1], out[i + 2], out[i + 3]];
|
|
311
|
-
}
|
|
252
|
+
};
|
|
312
253
|
const compressedLines = [];
|
|
313
254
|
for (let y = 0; y < newHeight; y++) {
|
|
314
255
|
const line = [];
|
|
315
|
-
let x = 0;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
line.push(current);
|
|
323
|
-
let nx = x + 1;
|
|
324
|
-
while (nx < newWidth && eq(getPixel(nx, y), current))
|
|
325
|
-
nx++;
|
|
326
|
-
x = nx;
|
|
327
|
-
}
|
|
328
|
-
if (line.length === 0)
|
|
329
|
-
continue;
|
|
330
|
-
if (compressedLines.length === 0 ||
|
|
331
|
-
!lineEq(compressedLines[compressedLines.length - 1], line))
|
|
256
|
+
for (let x = 0; x < newWidth; x++)
|
|
257
|
+
line.push(getPixel(x, y));
|
|
258
|
+
const isAllBlack = line.every((p) => p[0] === 0 && p[1] === 0 && p[2] === 0 && p[3] === 255);
|
|
259
|
+
if (!isAllBlack &&
|
|
260
|
+
(compressedLines.length === 0 ||
|
|
261
|
+
!line.every((p, i) => p.every((v, j) => v === compressedLines[compressedLines.length - 1][i][j])))) {
|
|
332
262
|
compressedLines.push(line);
|
|
263
|
+
}
|
|
333
264
|
}
|
|
334
265
|
if (compressedLines.length === 0) {
|
|
335
266
|
return sharp({
|
|
@@ -343,49 +274,116 @@ export async function cropAndReconstitute(input, debugDir) {
|
|
|
343
274
|
.png()
|
|
344
275
|
.toBuffer();
|
|
345
276
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const finalOut = Buffer.alloc(finalWidth * finalHeight * 4, 0);
|
|
277
|
+
let finalWidth = newWidth, finalHeight = compressedLines.length;
|
|
278
|
+
let finalOut = Buffer.alloc(finalWidth * finalHeight * 4, 0);
|
|
349
279
|
for (let i = 0; i < finalOut.length; i += 4)
|
|
350
280
|
finalOut[i + 3] = 255;
|
|
351
|
-
for (let y = 0; y <
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
finalOut[i] =
|
|
358
|
-
finalOut[i + 1] = line[x][1];
|
|
359
|
-
finalOut[i + 2] = line[x][2];
|
|
360
|
-
finalOut[i + 3] = line[x][3] === 0 ? 255 : line[x][3];
|
|
281
|
+
for (let y = 0; y < finalHeight; y++) {
|
|
282
|
+
for (let x = 0; x < finalWidth; x++) {
|
|
283
|
+
const i = (y * finalWidth + x) * 4;
|
|
284
|
+
finalOut[i] = compressedLines[y][x][0];
|
|
285
|
+
finalOut[i + 1] = compressedLines[y][x][1];
|
|
286
|
+
finalOut[i + 2] = compressedLines[y][x][2];
|
|
287
|
+
finalOut[i + 3] = compressedLines[y][x][3] || 255;
|
|
361
288
|
}
|
|
362
289
|
}
|
|
363
|
-
if (finalHeight >=
|
|
364
|
-
const
|
|
290
|
+
if (finalHeight >= 1 && finalWidth >= 3) {
|
|
291
|
+
const lastY = finalHeight - 1;
|
|
292
|
+
for (let k = 0; k < 3; k++) {
|
|
293
|
+
const i = (lastY * finalWidth + finalWidth - 3 + k) * 4;
|
|
294
|
+
finalOut[i] = finalOut[i + 1] = finalOut[i + 2] = 0;
|
|
295
|
+
finalOut[i + 3] = 255;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (finalWidth >= 2) {
|
|
299
|
+
const kept = [];
|
|
365
300
|
for (let x = 0; x < finalWidth; x++) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
finalOut[
|
|
375
|
-
|
|
301
|
+
if (kept.length === 0) {
|
|
302
|
+
kept.push(x);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const prevX = kept[kept.length - 1];
|
|
306
|
+
let same = true;
|
|
307
|
+
for (let y = 0; y < finalHeight; y++) {
|
|
308
|
+
const ia = (y * finalWidth + prevX) * 4, ib = (y * finalWidth + x) * 4;
|
|
309
|
+
if (finalOut[ia] !== finalOut[ib] ||
|
|
310
|
+
finalOut[ia + 1] !== finalOut[ib + 1] ||
|
|
311
|
+
finalOut[ia + 2] !== finalOut[ib + 2] ||
|
|
312
|
+
finalOut[ia + 3] !== finalOut[ib + 3]) {
|
|
313
|
+
same = false;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (!same)
|
|
318
|
+
kept.push(x);
|
|
319
|
+
}
|
|
320
|
+
if (kept.length !== finalWidth) {
|
|
321
|
+
const newFinalWidth = kept.length;
|
|
322
|
+
const newOut = Buffer.alloc(newFinalWidth * finalHeight * 4, 0);
|
|
323
|
+
for (let i = 0; i < newOut.length; i += 4)
|
|
324
|
+
newOut[i + 3] = 255;
|
|
325
|
+
for (let nx = 0; nx < kept.length; nx++) {
|
|
326
|
+
const sx = kept[nx];
|
|
327
|
+
for (let y = 0; y < finalHeight; y++) {
|
|
328
|
+
const srcI = (y * finalWidth + sx) * 4, dstI = (y * newFinalWidth + nx) * 4;
|
|
329
|
+
newOut[dstI] = finalOut[srcI];
|
|
330
|
+
newOut[dstI + 1] = finalOut[srcI + 1];
|
|
331
|
+
newOut[dstI + 2] = finalOut[srcI + 2];
|
|
332
|
+
newOut[dstI + 3] = finalOut[srcI + 3];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
finalOut = newOut;
|
|
336
|
+
finalWidth = newFinalWidth;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (finalHeight >= 2 && finalWidth >= 3) {
|
|
340
|
+
const secondLastY = finalHeight - 2;
|
|
341
|
+
const bgrSeq = [
|
|
342
|
+
[0, 0, 255],
|
|
343
|
+
[0, 255, 0],
|
|
344
|
+
[255, 0, 0],
|
|
345
|
+
];
|
|
346
|
+
let hasBGR = true;
|
|
347
|
+
for (let k = 0; k < 3; k++) {
|
|
348
|
+
const i = (secondLastY * finalWidth + finalWidth - 3 + k) * 4;
|
|
349
|
+
if (finalOut[i] !== bgrSeq[k][0] ||
|
|
350
|
+
finalOut[i + 1] !== bgrSeq[k][1] ||
|
|
351
|
+
finalOut[i + 2] !== bgrSeq[k][2]) {
|
|
352
|
+
hasBGR = false;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (hasBGR) {
|
|
357
|
+
for (let k = 0; k < 3; k++) {
|
|
358
|
+
const i = (secondLastY * finalWidth + finalWidth - 3 + k) * 4;
|
|
359
|
+
finalOut[i] = finalOut[i + 1] = finalOut[i + 2] = 0;
|
|
376
360
|
finalOut[i + 3] = 255;
|
|
377
361
|
}
|
|
378
362
|
}
|
|
379
363
|
}
|
|
380
|
-
|
|
364
|
+
if (finalHeight >= 1 && finalWidth >= 1) {
|
|
365
|
+
const lastYFinal = finalHeight - 1;
|
|
366
|
+
const bgrSeq = [
|
|
367
|
+
[0, 0, 255],
|
|
368
|
+
[0, 255, 0],
|
|
369
|
+
[255, 0, 0],
|
|
370
|
+
];
|
|
371
|
+
for (let k = 0; k < 3; k++) {
|
|
372
|
+
const sx = finalWidth - 3 + k;
|
|
373
|
+
if (sx >= 0) {
|
|
374
|
+
const i = (lastYFinal * finalWidth + sx) * 4;
|
|
375
|
+
finalOut[i] = bgrSeq[k][0];
|
|
376
|
+
finalOut[i + 1] = bgrSeq[k][1];
|
|
377
|
+
finalOut[i + 2] = bgrSeq[k][2];
|
|
378
|
+
finalOut[i + 3] = 255;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return sharp(finalOut, {
|
|
381
383
|
raw: { width: finalWidth, height: finalHeight, channels: 4 },
|
|
382
384
|
})
|
|
383
385
|
.png()
|
|
384
386
|
.toBuffer();
|
|
385
|
-
if (debugDir) {
|
|
386
|
-
await sharp(resultBuffer).toFile(join(debugDir, 'reconstructed.png'));
|
|
387
|
-
}
|
|
388
|
-
return resultBuffer;
|
|
389
387
|
}
|
|
390
388
|
/**
|
|
391
389
|
* Encode a Buffer into a PNG wrapper. Supports optional compression and
|
package/package.json
CHANGED