byte-codec 1.5.1 → 1.5.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/index.cjs +29 -31
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +29 -32
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,6 @@ var ByteWriter = class {
|
|
|
8
8
|
return this.byteLength;
|
|
9
9
|
}
|
|
10
10
|
writeBytes(bytes) {
|
|
11
|
-
if (bytes.length === 0) return;
|
|
12
11
|
this.chunks.push(bytes);
|
|
13
12
|
this.byteLength += bytes.length;
|
|
14
13
|
}
|
|
@@ -128,8 +127,8 @@ function inflateTolerant(data) {
|
|
|
128
127
|
};
|
|
129
128
|
} catch {}
|
|
130
129
|
let offset = 0;
|
|
131
|
-
while (
|
|
132
|
-
|
|
130
|
+
while (isAsciiWhitespace(data[offset])) offset++;
|
|
131
|
+
try {
|
|
133
132
|
return {
|
|
134
133
|
bytes: inflate(data.subarray(offset)),
|
|
135
134
|
recovered: true
|
|
@@ -161,8 +160,9 @@ function paethPredictor(a, b, c) {
|
|
|
161
160
|
const pa = Math.abs(p - a);
|
|
162
161
|
const pb = Math.abs(p - b);
|
|
163
162
|
const pc = Math.abs(p - c);
|
|
164
|
-
|
|
165
|
-
if (
|
|
163
|
+
const smallest = Math.min(pa, pb, pc);
|
|
164
|
+
if (smallest === pa) return a;
|
|
165
|
+
if (smallest === pb) return b;
|
|
166
166
|
return c;
|
|
167
167
|
}
|
|
168
168
|
function isPngFilterType(value) {
|
|
@@ -185,7 +185,7 @@ function unfilterScanlines(data, height, bytesPerRow, bpp) {
|
|
|
185
185
|
const rowStart = y * stride + 1;
|
|
186
186
|
const outRowStart = y * bytesPerRow;
|
|
187
187
|
const prevOutRowStart = y > 0 ? outRowStart - bytesPerRow : void 0;
|
|
188
|
-
for (
|
|
188
|
+
for (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
|
|
189
189
|
const raw = data[rowStart + x];
|
|
190
190
|
const a = x >= bpp ? out[outRowStart + x - bpp] : 0;
|
|
191
191
|
const b = prevOutRowStart === void 0 ? 0 : out[prevOutRowStart + x];
|
|
@@ -197,11 +197,11 @@ function unfilterScanlines(data, height, bytesPerRow, bpp) {
|
|
|
197
197
|
}
|
|
198
198
|
function sumOfAbsSigned(bytes) {
|
|
199
199
|
let sum = 0;
|
|
200
|
-
for (const byte of bytes) sum += byte
|
|
200
|
+
for (const byte of bytes) sum += Math.min(byte, 256 - byte);
|
|
201
201
|
return sum;
|
|
202
202
|
}
|
|
203
203
|
function filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, filterType, out, outOffset) {
|
|
204
|
-
for (
|
|
204
|
+
for (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
|
|
205
205
|
const rawByte = raw[rowStart + x];
|
|
206
206
|
const a = x >= bpp ? raw[rowStart + x - bpp] : 0;
|
|
207
207
|
const b = prevRowStart === void 0 ? 0 : raw[prevRowStart + x];
|
|
@@ -261,6 +261,9 @@ const PNG_SIGNATURE$1 = new Uint8Array([
|
|
|
261
261
|
const MAX_PALETTE_ENTRIES = 256;
|
|
262
262
|
const PNG_MAX_DIMENSION = 2147483647;
|
|
263
263
|
const PNG_MAX_PIXELS = 1e8;
|
|
264
|
+
function exceedsMaxPixelCount(pixelCount) {
|
|
265
|
+
return pixelCount > PNG_MAX_PIXELS;
|
|
266
|
+
}
|
|
264
267
|
function u32be(value) {
|
|
265
268
|
const bytes = /* @__PURE__ */ new Uint8Array(4);
|
|
266
269
|
new DataView(bytes.buffer).setUint32(0, value);
|
|
@@ -302,7 +305,7 @@ function detectPalette(image) {
|
|
|
302
305
|
const g = data[base + 1] ?? 0;
|
|
303
306
|
const b = data[base + 2] ?? 0;
|
|
304
307
|
const a = alpha === void 0 ? 255 : alpha[i] ?? 0;
|
|
305
|
-
const key = r
|
|
308
|
+
const key = r | g << 8 | b << 16 | a << 24;
|
|
306
309
|
let index = colorToIndex.get(key);
|
|
307
310
|
if (index === void 0) {
|
|
308
311
|
if (colorToIndex.size >= MAX_PALETTE_ENTRIES) return;
|
|
@@ -345,10 +348,10 @@ function writeTruecolorPng(writer, image, options) {
|
|
|
345
348
|
const bytesPerRow = width * outChannels;
|
|
346
349
|
const pixelCount = width * height;
|
|
347
350
|
const interleaved = new Uint8Array(pixelCount * outChannels);
|
|
348
|
-
for (
|
|
351
|
+
for (const i of Array.from({ length: pixelCount }, (_, index) => index)) {
|
|
349
352
|
const srcBase = i * channels;
|
|
350
353
|
const dstBase = i * outChannels;
|
|
351
|
-
for (
|
|
354
|
+
for (const c of Array.from({ length: channels }, (_, index) => index)) interleaved[dstBase + c] = data[srcBase + c];
|
|
352
355
|
if (alpha !== void 0) interleaved[dstBase + channels] = alpha[i];
|
|
353
356
|
}
|
|
354
357
|
writeIhdr(writer, width, height, colorTypeFor(image));
|
|
@@ -357,7 +360,7 @@ function writeTruecolorPng(writer, image, options) {
|
|
|
357
360
|
function encodePng(image, options = {}) {
|
|
358
361
|
if (!Number.isInteger(image.width) || image.width <= 0 || image.width > 2147483647 || !Number.isInteger(image.height) || image.height <= 0 || image.height > 2147483647) throw new Error(`cannot encode a PNG with an invalid dimension (width=${image.width}, height=${image.height}); the PNG spec's IHDR section requires both width and height to be positive integers no greater than ${PNG_MAX_DIMENSION}`);
|
|
359
362
|
const pixelCount = image.width * image.height;
|
|
360
|
-
if (pixelCount
|
|
363
|
+
if (exceedsMaxPixelCount(pixelCount)) throw new Error(`cannot encode a PNG with ${pixelCount} pixels (width=${image.width}, height=${image.height}); each dimension is individually within the PNG spec's own limit, but this encoder bounds their product to ${PNG_MAX_PIXELS} to avoid an unbounded per-pixel scan and allocation`);
|
|
361
364
|
const paletteEncoding = image.channels === 3 ? detectPalette(image) : void 0;
|
|
362
365
|
if (paletteEncoding === void 0) return buildPng((writer) => {
|
|
363
366
|
writeTruecolorPng(writer, image, options);
|
|
@@ -432,19 +435,14 @@ function filterBpp(bitDepth, channels) {
|
|
|
432
435
|
}
|
|
433
436
|
function unpackRow(rowBytes, width, channels, bitDepth) {
|
|
434
437
|
const sampleCount = width * channels;
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
444
|
-
samples[i] = rowBytes[byteIndex] >> shift & mask;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
return samples;
|
|
438
|
+
if (bitDepth === 16) return Array.from({ length: sampleCount }, (_, i) => rowBytes[i * 2]);
|
|
439
|
+
const mask = (1 << bitDepth) - 1;
|
|
440
|
+
return Array.from({ length: sampleCount }, (_, i) => {
|
|
441
|
+
const bitOffset = i * bitDepth;
|
|
442
|
+
const byteIndex = bitOffset >> 3;
|
|
443
|
+
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
444
|
+
return rowBytes[byteIndex] >> shift & mask;
|
|
445
|
+
});
|
|
448
446
|
}
|
|
449
447
|
function readTrnsGrayValue(trns) {
|
|
450
448
|
return requireDataView(trns).getUint16(0);
|
|
@@ -477,7 +475,7 @@ function decodePng(bytes, options = {}) {
|
|
|
477
475
|
const { bytes: inflated, recovered } = inflateTolerant(concatBytes(idatChunks));
|
|
478
476
|
if (recovered && options.onWarning !== void 0) options.onWarning("PNG IDAT stream required tolerant recovery (truncated or malformed)");
|
|
479
477
|
const unfiltered = unfilterScanlines(inflated, ihdr.height, bytesPerRow, bpp);
|
|
480
|
-
const palette =
|
|
478
|
+
const palette = chunks.find((c) => c.type === "PLTE")?.data;
|
|
481
479
|
if (ihdr.colorType === 3 && palette === void 0) throw new Error("indexed-colour PNG has no PLTE chunk");
|
|
482
480
|
const trns = chunks.find((c) => c.type === "tRNS")?.data;
|
|
483
481
|
return buildRawImage(ihdr, channels, bytesPerRow, unfiltered, palette, trns);
|
|
@@ -489,17 +487,17 @@ function buildRawImage(ihdr, channels, bytesPerRow, unfiltered, palette, trns) {
|
|
|
489
487
|
const alpha = colorType === 4 || colorType === 6 || trns !== void 0 ? new Uint8Array(width * height).fill(255) : void 0;
|
|
490
488
|
const trnsGray = colorType === 0 && trns !== void 0 ? readTrnsGrayValue(trns) : void 0;
|
|
491
489
|
const trnsRgb = colorType === 2 && trns !== void 0 ? readTrnsRgbKey(trns) : void 0;
|
|
492
|
-
for (
|
|
490
|
+
for (const y of Array.from({ length: height }, (_, i) => i)) {
|
|
493
491
|
const rowStart = y * bytesPerRow;
|
|
494
492
|
const samples = unpackRow(unfiltered.subarray(rowStart, rowStart + bytesPerRow), width, channels, bitDepth);
|
|
495
|
-
for (
|
|
493
|
+
for (const x of Array.from({ length: width }, (_, i) => i)) {
|
|
496
494
|
const pixelBase = x * channels;
|
|
497
495
|
const outBase = (y * width + x) * outChannels;
|
|
498
496
|
const alphaIndex = y * width + x;
|
|
499
497
|
if (colorType === 0) {
|
|
500
498
|
const g = samples[pixelBase];
|
|
501
499
|
data[outBase] = scaleToByte(g, bitDepth);
|
|
502
|
-
if (
|
|
500
|
+
if (trnsGray !== void 0) alpha[alphaIndex] = g === trnsGray ? 0 : 255;
|
|
503
501
|
} else if (colorType === 2) {
|
|
504
502
|
const r = samples[pixelBase];
|
|
505
503
|
const g = samples[pixelBase + 1];
|
|
@@ -513,7 +511,6 @@ function buildRawImage(ihdr, channels, bytesPerRow, unfiltered, palette, trns) {
|
|
|
513
511
|
}
|
|
514
512
|
} else if (colorType === 3) {
|
|
515
513
|
const index = samples[pixelBase];
|
|
516
|
-
if (palette === void 0) throw new Error("indexed-colour PNG has no PLTE chunk");
|
|
517
514
|
data[outBase] = palette[index * 3];
|
|
518
515
|
data[outBase + 1] = palette[index * 3 + 1];
|
|
519
516
|
data[outBase + 2] = palette[index * 3 + 2];
|
|
@@ -582,7 +579,7 @@ function readJpegInfo(bytes) {
|
|
|
582
579
|
if (requireByte(bytes, 0) !== 255 || requireByte(bytes, 1) !== SOI) throw new Error("not a valid JPEG file: missing SOI marker");
|
|
583
580
|
let offset = 2;
|
|
584
581
|
let adobeTransform;
|
|
585
|
-
while (offset
|
|
582
|
+
while (bytes[offset] !== void 0) {
|
|
586
583
|
if (bytes[offset] !== 255) {
|
|
587
584
|
offset++;
|
|
588
585
|
continue;
|
|
@@ -624,6 +621,7 @@ exports.crc32 = crc32;
|
|
|
624
621
|
exports.decodePng = decodePng;
|
|
625
622
|
exports.deflate = deflate;
|
|
626
623
|
exports.encodePng = encodePng;
|
|
624
|
+
exports.exceedsMaxPixelCount = exceedsMaxPixelCount;
|
|
627
625
|
exports.filterScanlines = filterScanlines;
|
|
628
626
|
exports.inflate = inflate;
|
|
629
627
|
exports.inflateTolerant = inflateTolerant;
|
package/dist/index.d.cts
CHANGED
|
@@ -59,6 +59,7 @@ declare function decodePng(bytes: Uint8Array<ArrayBuffer>, options?: PngDecodeOp
|
|
|
59
59
|
//#region src/image/png-encode.d.ts
|
|
60
60
|
declare const PNG_MAX_DIMENSION = 2147483647;
|
|
61
61
|
declare const PNG_MAX_PIXELS = 100000000;
|
|
62
|
+
declare function exceedsMaxPixelCount(pixelCount: number): boolean;
|
|
62
63
|
interface PngEncodeOptions {
|
|
63
64
|
readonly filter?: "none" | "adaptive";
|
|
64
65
|
}
|
|
@@ -80,4 +81,4 @@ interface JpegInfo {
|
|
|
80
81
|
}
|
|
81
82
|
declare function readJpegInfo(bytes: Uint8Array<ArrayBuffer>): JpegInfo;
|
|
82
83
|
//#endregion
|
|
83
|
-
export { ByteReader, ByteWriter, DeflateLevel, InflateResult, JpegInfo, MAX_INFLATE_OUTPUT_BYTES, PNG_MAX_DIMENSION, PNG_MAX_PIXELS, PngDecodeOptions, PngEncodeOptions, PngFilterType, RawImage, concatBytes, crc32, decodePng, deflate, encodePng, filterScanlines, inflate, inflateTolerant, isAsciiWhitespace, readJpegInfo, unfilterScanlines };
|
|
84
|
+
export { ByteReader, ByteWriter, DeflateLevel, InflateResult, JpegInfo, MAX_INFLATE_OUTPUT_BYTES, PNG_MAX_DIMENSION, PNG_MAX_PIXELS, PngDecodeOptions, PngEncodeOptions, PngFilterType, RawImage, concatBytes, crc32, decodePng, deflate, encodePng, exceedsMaxPixelCount, filterScanlines, inflate, inflateTolerant, isAsciiWhitespace, readJpegInfo, unfilterScanlines };
|
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ declare function decodePng(bytes: Uint8Array<ArrayBuffer>, options?: PngDecodeOp
|
|
|
59
59
|
//#region src/image/png-encode.d.ts
|
|
60
60
|
declare const PNG_MAX_DIMENSION = 2147483647;
|
|
61
61
|
declare const PNG_MAX_PIXELS = 100000000;
|
|
62
|
+
declare function exceedsMaxPixelCount(pixelCount: number): boolean;
|
|
62
63
|
interface PngEncodeOptions {
|
|
63
64
|
readonly filter?: "none" | "adaptive";
|
|
64
65
|
}
|
|
@@ -80,4 +81,4 @@ interface JpegInfo {
|
|
|
80
81
|
}
|
|
81
82
|
declare function readJpegInfo(bytes: Uint8Array<ArrayBuffer>): JpegInfo;
|
|
82
83
|
//#endregion
|
|
83
|
-
export { ByteReader, ByteWriter, DeflateLevel, InflateResult, JpegInfo, MAX_INFLATE_OUTPUT_BYTES, PNG_MAX_DIMENSION, PNG_MAX_PIXELS, PngDecodeOptions, PngEncodeOptions, PngFilterType, RawImage, concatBytes, crc32, decodePng, deflate, encodePng, filterScanlines, inflate, inflateTolerant, isAsciiWhitespace, readJpegInfo, unfilterScanlines };
|
|
84
|
+
export { ByteReader, ByteWriter, DeflateLevel, InflateResult, JpegInfo, MAX_INFLATE_OUTPUT_BYTES, PNG_MAX_DIMENSION, PNG_MAX_PIXELS, PngDecodeOptions, PngEncodeOptions, PngFilterType, RawImage, concatBytes, crc32, decodePng, deflate, encodePng, exceedsMaxPixelCount, filterScanlines, inflate, inflateTolerant, isAsciiWhitespace, readJpegInfo, unfilterScanlines };
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,6 @@ var ByteWriter = class {
|
|
|
7
7
|
return this.byteLength;
|
|
8
8
|
}
|
|
9
9
|
writeBytes(bytes) {
|
|
10
|
-
if (bytes.length === 0) return;
|
|
11
10
|
this.chunks.push(bytes);
|
|
12
11
|
this.byteLength += bytes.length;
|
|
13
12
|
}
|
|
@@ -127,8 +126,8 @@ function inflateTolerant(data) {
|
|
|
127
126
|
};
|
|
128
127
|
} catch {}
|
|
129
128
|
let offset = 0;
|
|
130
|
-
while (
|
|
131
|
-
|
|
129
|
+
while (isAsciiWhitespace(data[offset])) offset++;
|
|
130
|
+
try {
|
|
132
131
|
return {
|
|
133
132
|
bytes: inflate(data.subarray(offset)),
|
|
134
133
|
recovered: true
|
|
@@ -160,8 +159,9 @@ function paethPredictor(a, b, c) {
|
|
|
160
159
|
const pa = Math.abs(p - a);
|
|
161
160
|
const pb = Math.abs(p - b);
|
|
162
161
|
const pc = Math.abs(p - c);
|
|
163
|
-
|
|
164
|
-
if (
|
|
162
|
+
const smallest = Math.min(pa, pb, pc);
|
|
163
|
+
if (smallest === pa) return a;
|
|
164
|
+
if (smallest === pb) return b;
|
|
165
165
|
return c;
|
|
166
166
|
}
|
|
167
167
|
function isPngFilterType(value) {
|
|
@@ -184,7 +184,7 @@ function unfilterScanlines(data, height, bytesPerRow, bpp) {
|
|
|
184
184
|
const rowStart = y * stride + 1;
|
|
185
185
|
const outRowStart = y * bytesPerRow;
|
|
186
186
|
const prevOutRowStart = y > 0 ? outRowStart - bytesPerRow : void 0;
|
|
187
|
-
for (
|
|
187
|
+
for (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
|
|
188
188
|
const raw = data[rowStart + x];
|
|
189
189
|
const a = x >= bpp ? out[outRowStart + x - bpp] : 0;
|
|
190
190
|
const b = prevOutRowStart === void 0 ? 0 : out[prevOutRowStart + x];
|
|
@@ -196,11 +196,11 @@ function unfilterScanlines(data, height, bytesPerRow, bpp) {
|
|
|
196
196
|
}
|
|
197
197
|
function sumOfAbsSigned(bytes) {
|
|
198
198
|
let sum = 0;
|
|
199
|
-
for (const byte of bytes) sum += byte
|
|
199
|
+
for (const byte of bytes) sum += Math.min(byte, 256 - byte);
|
|
200
200
|
return sum;
|
|
201
201
|
}
|
|
202
202
|
function filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, filterType, out, outOffset) {
|
|
203
|
-
for (
|
|
203
|
+
for (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
|
|
204
204
|
const rawByte = raw[rowStart + x];
|
|
205
205
|
const a = x >= bpp ? raw[rowStart + x - bpp] : 0;
|
|
206
206
|
const b = prevRowStart === void 0 ? 0 : raw[prevRowStart + x];
|
|
@@ -260,6 +260,9 @@ const PNG_SIGNATURE$1 = new Uint8Array([
|
|
|
260
260
|
const MAX_PALETTE_ENTRIES = 256;
|
|
261
261
|
const PNG_MAX_DIMENSION = 2147483647;
|
|
262
262
|
const PNG_MAX_PIXELS = 1e8;
|
|
263
|
+
function exceedsMaxPixelCount(pixelCount) {
|
|
264
|
+
return pixelCount > PNG_MAX_PIXELS;
|
|
265
|
+
}
|
|
263
266
|
function u32be(value) {
|
|
264
267
|
const bytes = /* @__PURE__ */ new Uint8Array(4);
|
|
265
268
|
new DataView(bytes.buffer).setUint32(0, value);
|
|
@@ -301,7 +304,7 @@ function detectPalette(image) {
|
|
|
301
304
|
const g = data[base + 1] ?? 0;
|
|
302
305
|
const b = data[base + 2] ?? 0;
|
|
303
306
|
const a = alpha === void 0 ? 255 : alpha[i] ?? 0;
|
|
304
|
-
const key = r
|
|
307
|
+
const key = r | g << 8 | b << 16 | a << 24;
|
|
305
308
|
let index = colorToIndex.get(key);
|
|
306
309
|
if (index === void 0) {
|
|
307
310
|
if (colorToIndex.size >= MAX_PALETTE_ENTRIES) return;
|
|
@@ -344,10 +347,10 @@ function writeTruecolorPng(writer, image, options) {
|
|
|
344
347
|
const bytesPerRow = width * outChannels;
|
|
345
348
|
const pixelCount = width * height;
|
|
346
349
|
const interleaved = new Uint8Array(pixelCount * outChannels);
|
|
347
|
-
for (
|
|
350
|
+
for (const i of Array.from({ length: pixelCount }, (_, index) => index)) {
|
|
348
351
|
const srcBase = i * channels;
|
|
349
352
|
const dstBase = i * outChannels;
|
|
350
|
-
for (
|
|
353
|
+
for (const c of Array.from({ length: channels }, (_, index) => index)) interleaved[dstBase + c] = data[srcBase + c];
|
|
351
354
|
if (alpha !== void 0) interleaved[dstBase + channels] = alpha[i];
|
|
352
355
|
}
|
|
353
356
|
writeIhdr(writer, width, height, colorTypeFor(image));
|
|
@@ -356,7 +359,7 @@ function writeTruecolorPng(writer, image, options) {
|
|
|
356
359
|
function encodePng(image, options = {}) {
|
|
357
360
|
if (!Number.isInteger(image.width) || image.width <= 0 || image.width > 2147483647 || !Number.isInteger(image.height) || image.height <= 0 || image.height > 2147483647) throw new Error(`cannot encode a PNG with an invalid dimension (width=${image.width}, height=${image.height}); the PNG spec's IHDR section requires both width and height to be positive integers no greater than ${PNG_MAX_DIMENSION}`);
|
|
358
361
|
const pixelCount = image.width * image.height;
|
|
359
|
-
if (pixelCount
|
|
362
|
+
if (exceedsMaxPixelCount(pixelCount)) throw new Error(`cannot encode a PNG with ${pixelCount} pixels (width=${image.width}, height=${image.height}); each dimension is individually within the PNG spec's own limit, but this encoder bounds their product to ${PNG_MAX_PIXELS} to avoid an unbounded per-pixel scan and allocation`);
|
|
360
363
|
const paletteEncoding = image.channels === 3 ? detectPalette(image) : void 0;
|
|
361
364
|
if (paletteEncoding === void 0) return buildPng((writer) => {
|
|
362
365
|
writeTruecolorPng(writer, image, options);
|
|
@@ -431,19 +434,14 @@ function filterBpp(bitDepth, channels) {
|
|
|
431
434
|
}
|
|
432
435
|
function unpackRow(rowBytes, width, channels, bitDepth) {
|
|
433
436
|
const sampleCount = width * channels;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
443
|
-
samples[i] = rowBytes[byteIndex] >> shift & mask;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
return samples;
|
|
437
|
+
if (bitDepth === 16) return Array.from({ length: sampleCount }, (_, i) => rowBytes[i * 2]);
|
|
438
|
+
const mask = (1 << bitDepth) - 1;
|
|
439
|
+
return Array.from({ length: sampleCount }, (_, i) => {
|
|
440
|
+
const bitOffset = i * bitDepth;
|
|
441
|
+
const byteIndex = bitOffset >> 3;
|
|
442
|
+
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
443
|
+
return rowBytes[byteIndex] >> shift & mask;
|
|
444
|
+
});
|
|
447
445
|
}
|
|
448
446
|
function readTrnsGrayValue(trns) {
|
|
449
447
|
return requireDataView(trns).getUint16(0);
|
|
@@ -476,7 +474,7 @@ function decodePng(bytes, options = {}) {
|
|
|
476
474
|
const { bytes: inflated, recovered } = inflateTolerant(concatBytes(idatChunks));
|
|
477
475
|
if (recovered && options.onWarning !== void 0) options.onWarning("PNG IDAT stream required tolerant recovery (truncated or malformed)");
|
|
478
476
|
const unfiltered = unfilterScanlines(inflated, ihdr.height, bytesPerRow, bpp);
|
|
479
|
-
const palette =
|
|
477
|
+
const palette = chunks.find((c) => c.type === "PLTE")?.data;
|
|
480
478
|
if (ihdr.colorType === 3 && palette === void 0) throw new Error("indexed-colour PNG has no PLTE chunk");
|
|
481
479
|
const trns = chunks.find((c) => c.type === "tRNS")?.data;
|
|
482
480
|
return buildRawImage(ihdr, channels, bytesPerRow, unfiltered, palette, trns);
|
|
@@ -488,17 +486,17 @@ function buildRawImage(ihdr, channels, bytesPerRow, unfiltered, palette, trns) {
|
|
|
488
486
|
const alpha = colorType === 4 || colorType === 6 || trns !== void 0 ? new Uint8Array(width * height).fill(255) : void 0;
|
|
489
487
|
const trnsGray = colorType === 0 && trns !== void 0 ? readTrnsGrayValue(trns) : void 0;
|
|
490
488
|
const trnsRgb = colorType === 2 && trns !== void 0 ? readTrnsRgbKey(trns) : void 0;
|
|
491
|
-
for (
|
|
489
|
+
for (const y of Array.from({ length: height }, (_, i) => i)) {
|
|
492
490
|
const rowStart = y * bytesPerRow;
|
|
493
491
|
const samples = unpackRow(unfiltered.subarray(rowStart, rowStart + bytesPerRow), width, channels, bitDepth);
|
|
494
|
-
for (
|
|
492
|
+
for (const x of Array.from({ length: width }, (_, i) => i)) {
|
|
495
493
|
const pixelBase = x * channels;
|
|
496
494
|
const outBase = (y * width + x) * outChannels;
|
|
497
495
|
const alphaIndex = y * width + x;
|
|
498
496
|
if (colorType === 0) {
|
|
499
497
|
const g = samples[pixelBase];
|
|
500
498
|
data[outBase] = scaleToByte(g, bitDepth);
|
|
501
|
-
if (
|
|
499
|
+
if (trnsGray !== void 0) alpha[alphaIndex] = g === trnsGray ? 0 : 255;
|
|
502
500
|
} else if (colorType === 2) {
|
|
503
501
|
const r = samples[pixelBase];
|
|
504
502
|
const g = samples[pixelBase + 1];
|
|
@@ -512,7 +510,6 @@ function buildRawImage(ihdr, channels, bytesPerRow, unfiltered, palette, trns) {
|
|
|
512
510
|
}
|
|
513
511
|
} else if (colorType === 3) {
|
|
514
512
|
const index = samples[pixelBase];
|
|
515
|
-
if (palette === void 0) throw new Error("indexed-colour PNG has no PLTE chunk");
|
|
516
513
|
data[outBase] = palette[index * 3];
|
|
517
514
|
data[outBase + 1] = palette[index * 3 + 1];
|
|
518
515
|
data[outBase + 2] = palette[index * 3 + 2];
|
|
@@ -581,7 +578,7 @@ function readJpegInfo(bytes) {
|
|
|
581
578
|
if (requireByte(bytes, 0) !== 255 || requireByte(bytes, 1) !== SOI) throw new Error("not a valid JPEG file: missing SOI marker");
|
|
582
579
|
let offset = 2;
|
|
583
580
|
let adobeTransform;
|
|
584
|
-
while (offset
|
|
581
|
+
while (bytes[offset] !== void 0) {
|
|
585
582
|
if (bytes[offset] !== 255) {
|
|
586
583
|
offset++;
|
|
587
584
|
continue;
|
|
@@ -613,4 +610,4 @@ function readJpegInfo(bytes) {
|
|
|
613
610
|
throw new Error("no SOF marker found in JPEG file");
|
|
614
611
|
}
|
|
615
612
|
//#endregion
|
|
616
|
-
export { ByteReader, ByteWriter, MAX_INFLATE_OUTPUT_BYTES, PNG_MAX_DIMENSION, PNG_MAX_PIXELS, concatBytes, crc32, decodePng, deflate, encodePng, filterScanlines, inflate, inflateTolerant, isAsciiWhitespace, readJpegInfo, unfilterScanlines };
|
|
613
|
+
export { ByteReader, ByteWriter, MAX_INFLATE_OUTPUT_BYTES, PNG_MAX_DIMENSION, PNG_MAX_PIXELS, concatBytes, crc32, decodePng, deflate, encodePng, exceedsMaxPixelCount, filterScanlines, inflate, inflateTolerant, isAsciiWhitespace, readJpegInfo, unfilterScanlines };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "byte-codec",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Generic byte-level primitives (ByteWriter, ByteReader, CRC-32, deflate/inflate) and PNG/JPEG image encoding/decoding with zero PDF knowledge — the shared utility package for the documents.js family.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|