roxify 1.1.0 → 1.1.1

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 CHANGED
@@ -173,7 +173,7 @@ async function decodeCommand(args) {
173
173
  catch (e) {
174
174
  console.log('Could not generate reconst PNG:', e.message);
175
175
  }
176
- console.log(`Reading: ${resolvedInputPath}`);
176
+ console.log(`Reading: ${resolvedInputPath.replace('_reconst.png', '.png')}`);
177
177
  const options = {};
178
178
  if (parsed.passphrase) {
179
179
  options.passphrase = parsed.passphrase;
package/dist/index.js CHANGED
@@ -108,6 +108,19 @@ async function loadRaw(imgInput) {
108
108
  return { data, info };
109
109
  }
110
110
  export async function cropAndReconstitute(input) {
111
+ async function loadRaw(imgInput) {
112
+ const { data, info } = await sharp(imgInput)
113
+ .ensureAlpha()
114
+ .raw()
115
+ .toBuffer({ resolveWithObject: true });
116
+ return { data, info };
117
+ }
118
+ function idxFor(x, y, width) {
119
+ return (y * width + x) * 4;
120
+ }
121
+ function eqRGB(a, b) {
122
+ return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
123
+ }
111
124
  const { data, info } = await loadRaw(input);
112
125
  const w = info.width;
113
126
  const h = info.height;
@@ -183,8 +196,8 @@ export async function cropAndReconstitute(input) {
183
196
  const sy1 = Math.min(startPoint.y, endPoint.y);
184
197
  const sx2 = Math.max(startPoint.x, endPoint.x);
185
198
  const sy2 = Math.max(startPoint.y, endPoint.y);
186
- const cropW = sx2 - sx1;
187
- const cropH = sy2 - sy1;
199
+ const cropW = sx2 - sx1 + 1;
200
+ const cropH = sy2 - sy1 + 1;
188
201
  if (cropW <= 0 || cropH <= 0)
189
202
  throw new Error('Invalid crop dimensions');
190
203
  const cropped = await sharp(input)
@@ -212,19 +225,84 @@ export async function cropAndReconstitute(input) {
212
225
  return false;
213
226
  return true;
214
227
  }
215
- const compressedLines = [];
228
+ const newWidth = cw;
229
+ const newHeight = ch + 1;
230
+ const out = Buffer.alloc(newWidth * newHeight * 4, 0);
231
+ for (let i = 0; i < out.length; i += 4)
232
+ out[i + 3] = 255;
216
233
  for (let y = 0; y < ch; y++) {
234
+ for (let x = 0; x < cw; x++) {
235
+ const srcI = ((y * cw + x) * 4) | 0;
236
+ const dstI = ((y * newWidth + x) * 4) | 0;
237
+ out[dstI] = cdata[srcI];
238
+ out[dstI + 1] = cdata[srcI + 1];
239
+ out[dstI + 2] = cdata[srcI + 2];
240
+ out[dstI + 3] = cdata[srcI + 3];
241
+ }
242
+ }
243
+ if (cw >= 3) {
244
+ const targetY = ch - 1;
245
+ for (let x = cw - 3; x < cw; x++) {
246
+ const i = ((targetY * newWidth + x) * 4) | 0;
247
+ out[i] = 0;
248
+ out[i + 1] = 0;
249
+ out[i + 2] = 0;
250
+ out[i + 3] = 255;
251
+ }
252
+ }
253
+ else {
254
+ const targetY = ch - 1;
255
+ for (let x = 0; x < cw; x++) {
256
+ const i = ((targetY * newWidth + x) * 4) | 0;
257
+ out[i] = 0;
258
+ out[i + 1] = 0;
259
+ out[i + 2] = 0;
260
+ out[i + 3] = 255;
261
+ }
262
+ }
263
+ const lastY = ch;
264
+ for (let x = 0; x < newWidth; x++) {
265
+ const i = ((lastY * newWidth + x) * 4) | 0;
266
+ out[i] = 0;
267
+ out[i + 1] = 0;
268
+ out[i + 2] = 0;
269
+ out[i + 3] = 255;
270
+ }
271
+ if (newWidth >= 3) {
272
+ const bgrStart = newWidth - 3;
273
+ let i = ((lastY * newWidth + bgrStart) * 4) | 0;
274
+ out[i] = 0;
275
+ out[i + 1] = 0;
276
+ out[i + 2] = 255;
277
+ out[i + 3] = 255;
278
+ i = ((lastY * newWidth + bgrStart + 1) * 4) | 0;
279
+ out[i] = 0;
280
+ out[i + 1] = 255;
281
+ out[i + 2] = 0;
282
+ out[i + 3] = 255;
283
+ i = ((lastY * newWidth + bgrStart + 2) * 4) | 0;
284
+ out[i] = 255;
285
+ out[i + 1] = 0;
286
+ out[i + 2] = 0;
287
+ out[i + 3] = 255;
288
+ }
289
+ function getPixel(x, y) {
290
+ const i = ((y * newWidth + x) * 4) | 0;
291
+ return [out[i], out[i + 1], out[i + 2], out[i + 3]];
292
+ }
293
+ const compressedLines = [];
294
+ for (let y = 0; y < newHeight; y++) {
217
295
  const line = [];
218
296
  let x = 0;
219
- while (x < cw) {
220
- const current = cat(x, y);
297
+ while (x < newWidth) {
298
+ const current = getPixel(x, y);
221
299
  if (current[0] === 0 && current[1] === 0 && current[2] === 0) {
222
300
  x++;
223
301
  continue;
224
302
  }
225
303
  line.push(current);
226
304
  let nx = x + 1;
227
- while (nx < cw && eq(cat(nx, y), current))
305
+ while (nx < newWidth && eq(getPixel(nx, y), current))
228
306
  nx++;
229
307
  x = nx;
230
308
  }
@@ -246,90 +324,25 @@ export async function cropAndReconstitute(input) {
246
324
  .png()
247
325
  .toBuffer();
248
326
  }
249
- const newWidth = Math.max(...compressedLines.map((l) => l.length));
250
- const newHeight = compressedLines.length + 1;
251
- const out = Buffer.alloc(newWidth * newHeight * 4, 0);
252
- for (let i = 0; i < out.length; i += 4)
253
- out[i + 3] = 255;
327
+ const finalWidth = Math.max(...compressedLines.map((l) => l.length));
328
+ const finalHeight = compressedLines.length;
329
+ const finalOut = Buffer.alloc(finalWidth * finalHeight * 4, 0);
330
+ for (let i = 0; i < finalOut.length; i += 4)
331
+ finalOut[i + 3] = 255;
254
332
  for (let y = 0; y < compressedLines.length; y++) {
255
333
  const line = compressedLines[y];
256
- const isSecondToLast = y === compressedLines.length - 1;
257
- const startX = 0;
258
- const effectiveLength = isSecondToLast
259
- ? Math.max(0, line.length - 3)
260
- : line.length;
261
- for (let x = 0; x < effectiveLength; x++) {
262
- const i = ((y * newWidth + startX + x) * 4) | 0;
263
- out[i] = line[x][0];
264
- out[i + 1] = line[x][1];
265
- out[i + 2] = line[x][2];
266
- out[i + 3] = line[x][3] === 0 ? 255 : line[x][3];
267
- }
268
- if (isSecondToLast) {
269
- for (let x = effectiveLength; x < Math.min(effectiveLength + 3, newWidth); x++) {
270
- const i = ((y * newWidth + x) * 4) | 0;
271
- out[i] = 0;
272
- out[i + 1] = 0;
273
- out[i + 2] = 0;
274
- out[i + 3] = 255;
275
- }
276
- }
277
- }
278
- const secondToLastY = newHeight - 2;
279
- let secondToLastIsBlack = true;
280
- for (let x = 0; x < newWidth; x++) {
281
- const i = ((secondToLastY * newWidth + x) * 4) | 0;
282
- if (out[i] !== 0 || out[i + 1] !== 0 || out[i + 2] !== 0) {
283
- secondToLastIsBlack = false;
284
- break;
285
- }
286
- }
287
- let finalHeight = newHeight;
288
- let finalOut = out;
289
- if (secondToLastIsBlack) {
290
- finalHeight = newHeight - 1;
291
- finalOut = Buffer.alloc(newWidth * finalHeight * 4, 0);
292
- for (let i = 0; i < finalOut.length; i += 4)
293
- finalOut[i + 3] = 255;
294
- for (let y = 0; y < newHeight - 2; y++) {
295
- for (let x = 0; x < newWidth; x++) {
296
- const srcI = ((y * newWidth + x) * 4) | 0;
297
- const dstI = ((y * newWidth + x) * 4) | 0;
298
- finalOut[dstI] = out[srcI];
299
- finalOut[dstI + 1] = out[srcI + 1];
300
- finalOut[dstI + 2] = out[srcI + 2];
301
- finalOut[dstI + 3] = out[srcI + 3];
302
- }
334
+ const isLastLine = y === compressedLines.length - 1;
335
+ const startX = isLastLine ? finalWidth - line.length : 0;
336
+ for (let x = 0; x < line.length; x++) {
337
+ const i = ((y * finalWidth + startX + x) * 4) | 0;
338
+ finalOut[i] = line[x][0];
339
+ finalOut[i + 1] = line[x][1];
340
+ finalOut[i + 2] = line[x][2];
341
+ finalOut[i + 3] = line[x][3] === 0 ? 255 : line[x][3];
303
342
  }
304
343
  }
305
- const lastY = finalHeight - 1;
306
- for (let x = 0; x < newWidth; x++) {
307
- const i = ((lastY * newWidth + x) * 4) | 0;
308
- finalOut[i] = 0;
309
- finalOut[i + 1] = 0;
310
- finalOut[i + 2] = 0;
311
- finalOut[i + 3] = 255;
312
- }
313
- if (newWidth >= 3) {
314
- const bgrStart = newWidth - 3;
315
- let i = ((lastY * newWidth + bgrStart) * 4) | 0;
316
- finalOut[i] = 0;
317
- finalOut[i + 1] = 0;
318
- finalOut[i + 2] = 255;
319
- finalOut[i + 3] = 255;
320
- i = ((lastY * newWidth + bgrStart + 1) * 4) | 0;
321
- finalOut[i] = 0;
322
- finalOut[i + 1] = 255;
323
- finalOut[i + 2] = 0;
324
- finalOut[i + 3] = 255;
325
- i = ((lastY * newWidth + bgrStart + 2) * 4) | 0;
326
- finalOut[i] = 255;
327
- finalOut[i + 1] = 0;
328
- finalOut[i + 2] = 0;
329
- finalOut[i + 3] = 255;
330
- }
331
344
  return sharp(finalOut, {
332
- raw: { width: newWidth, height: finalHeight, channels: 4 },
345
+ raw: { width: finalWidth, height: finalHeight, channels: 4 },
333
346
  })
334
347
  .png()
335
348
  .toBuffer();
@@ -442,7 +455,7 @@ export async function encodeBinaryToPng(input, opts = {}) {
442
455
  const spaceInLastRow = pixelsInLastRow === 0 ? logicalWidth : logicalWidth - pixelsInLastRow;
443
456
  const needsExtraRow = spaceInLastRow < MARKER_END.length;
444
457
  const logicalHeight = needsExtraRow ? dataRows + 1 : dataRows;
445
- const scale = 1;
458
+ const scale = 2;
446
459
  const width = logicalWidth * scale;
447
460
  const height = logicalHeight * scale;
448
461
  const raw = Buffer.alloc(width * height * bytesPerPixel);
@@ -489,10 +502,10 @@ export async function encodeBinaryToPng(input, opts = {}) {
489
502
  raw: { width, height, channels: 3 },
490
503
  })
491
504
  .png({
492
- compressionLevel: 0,
505
+ compressionLevel: 9,
493
506
  palette: false,
494
- effort: 1,
495
- adaptiveFiltering: false,
507
+ effort: 10,
508
+ adaptiveFiltering: true,
496
509
  })
497
510
  .toBuffer();
498
511
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roxify",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Encode binary data into PNG images and decode them back. Supports CLI and programmatic API (Node.js ESM).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",