byte-codec 1.5.5 → 1.5.7

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 CHANGED
@@ -185,29 +185,54 @@ 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 (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
188
+ out.subarray(outRowStart, outRowStart + bytesPerRow).forEach((_value, x) => {
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];
192
192
  const c = x >= bpp && prevOutRowStart !== void 0 ? out[prevOutRowStart + x - bpp] : 0;
193
193
  out[outRowStart + x] = raw + predictorValue(filterByte, a, b, c) & 255;
194
- }
194
+ });
195
195
  }
196
196
  return out;
197
197
  }
198
- function sumOfAbsSigned(bytes) {
198
+ function filterRowInto(current, previous, bpp, filterType, target) {
199
199
  let sum = 0;
200
- for (const byte of bytes) sum += Math.min(byte, 256 - byte);
201
- return sum;
202
- }
203
- function filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, filterType, out, outOffset) {
204
- for (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
205
- const rawByte = raw[rowStart + x];
206
- const a = x >= bpp ? raw[rowStart + x - bpp] : 0;
207
- const b = prevRowStart === void 0 ? 0 : raw[prevRowStart + x];
208
- const c = x >= bpp && prevRowStart !== void 0 ? raw[prevRowStart + x - bpp] : 0;
209
- out[outOffset + x] = rawByte - predictorValue(filterType, a, b, c) & 255;
200
+ switch (filterType) {
201
+ case 1:
202
+ target.forEach((_byte, x) => {
203
+ const filtered = current[x + bpp] - current[x] & 255;
204
+ target[x] = filtered;
205
+ sum += Math.min(filtered, 256 - filtered);
206
+ });
207
+ break;
208
+ case 2:
209
+ target.forEach((_byte, x) => {
210
+ const filtered = current[x + bpp] - previous[x + bpp] & 255;
211
+ target[x] = filtered;
212
+ sum += Math.min(filtered, 256 - filtered);
213
+ });
214
+ break;
215
+ case 3:
216
+ target.forEach((_byte, x) => {
217
+ const filtered = current[x + bpp] - Math.floor((current[x] + previous[x + bpp]) / 2) & 255;
218
+ target[x] = filtered;
219
+ sum += Math.min(filtered, 256 - filtered);
220
+ });
221
+ break;
222
+ case 4:
223
+ target.forEach((_byte, x) => {
224
+ const filtered = current[x + bpp] - paethPredictor(current[x], previous[x + bpp], previous[x]) & 255;
225
+ target[x] = filtered;
226
+ sum += Math.min(filtered, 256 - filtered);
227
+ });
228
+ break;
229
+ case 0: target.forEach((_byte, x) => {
230
+ const filtered = current[x + bpp];
231
+ target[x] = filtered;
232
+ sum += Math.min(filtered, 256 - filtered);
233
+ });
210
234
  }
235
+ return sum;
211
236
  }
212
237
  const ALL_FILTER_TYPES = [
213
238
  0,
@@ -219,31 +244,27 @@ const ALL_FILTER_TYPES = [
219
244
  function filterScanlines(raw, height, bytesPerRow, bpp, strategy = "adaptive") {
220
245
  const stride = bytesPerRow + 1;
221
246
  const out = new Uint8Array(height * stride);
222
- const candidate = new Uint8Array(bytesPerRow);
223
- for (let y = 0; y < height; y++) {
224
- const rowStart = y * bytesPerRow;
225
- const prevRowStart = y > 0 ? rowStart - bytesPerRow : void 0;
226
- const outRowStart = y * stride;
227
- if (strategy === "none") {
228
- out[outRowStart] = 0;
229
- filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, 0, out, outRowStart + 1);
230
- continue;
231
- }
247
+ let current = new Uint8Array(bytesPerRow + bpp);
248
+ let previous = new Uint8Array(bytesPerRow + bpp);
249
+ let candidate = new Uint8Array(bytesPerRow);
250
+ let best = new Uint8Array(bytesPerRow);
251
+ const filterTypes = strategy === "none" ? [0] : ALL_FILTER_TYPES;
252
+ Array.from({ length: height }).forEach((_row, y) => {
253
+ current.set(raw.subarray(y * bytesPerRow, (y + 1) * bytesPerRow), bpp);
232
254
  let bestType = 0;
233
255
  let bestSum = Number.POSITIVE_INFINITY;
234
- let best;
235
- for (const filterType of ALL_FILTER_TYPES) {
236
- filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, filterType, candidate, 0);
237
- const sum = sumOfAbsSigned(candidate);
256
+ for (const filterType of filterTypes) {
257
+ const sum = filterRowInto(current, previous, bpp, filterType, candidate);
238
258
  if (sum < bestSum) {
239
259
  bestSum = sum;
240
260
  bestType = filterType;
241
- best = candidate.slice();
261
+ [best, candidate] = [candidate, best];
242
262
  }
243
263
  }
244
- out[outRowStart] = bestType;
245
- if (best !== void 0) out.set(best, outRowStart + 1);
246
- }
264
+ out[y * stride] = bestType;
265
+ out.set(best, y * stride + 1);
266
+ [previous, current] = [current, previous];
267
+ });
247
268
  return out;
248
269
  }
249
270
  //#endregion
@@ -348,12 +369,11 @@ function writeTruecolorPng(writer, image, options) {
348
369
  const bytesPerRow = width * outChannels;
349
370
  const pixelCount = width * height;
350
371
  const interleaved = new Uint8Array(pixelCount * outChannels);
351
- for (const i of Array.from({ length: pixelCount }, (_, index) => index)) {
352
- const srcBase = i * channels;
353
- const dstBase = i * outChannels;
354
- for (const c of Array.from({ length: channels }, (_, index) => index)) interleaved[dstBase + c] = data[srcBase + c];
355
- if (alpha !== void 0) interleaved[dstBase + channels] = alpha[i];
356
- }
372
+ interleaved.forEach((_byte, index) => {
373
+ const pixel = Math.floor(index / outChannels);
374
+ const channel = index % outChannels;
375
+ interleaved[index] = alpha !== void 0 && channel === channels ? alpha[pixel] : data[pixel * channels + channel];
376
+ });
357
377
  writeIhdr(writer, width, height, colorTypeFor(image));
358
378
  writeChunk(writer, "IDAT", deflate(filterScanlines(interleaved, height, bytesPerRow, outChannels, options.filter ?? "adaptive")));
359
379
  }
package/dist/index.js CHANGED
@@ -184,29 +184,54 @@ 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 (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
187
+ out.subarray(outRowStart, outRowStart + bytesPerRow).forEach((_value, x) => {
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];
191
191
  const c = x >= bpp && prevOutRowStart !== void 0 ? out[prevOutRowStart + x - bpp] : 0;
192
192
  out[outRowStart + x] = raw + predictorValue(filterByte, a, b, c) & 255;
193
- }
193
+ });
194
194
  }
195
195
  return out;
196
196
  }
197
- function sumOfAbsSigned(bytes) {
197
+ function filterRowInto(current, previous, bpp, filterType, target) {
198
198
  let sum = 0;
199
- for (const byte of bytes) sum += Math.min(byte, 256 - byte);
200
- return sum;
201
- }
202
- function filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, filterType, out, outOffset) {
203
- for (const x of Array.from({ length: bytesPerRow }, (_, i) => i)) {
204
- const rawByte = raw[rowStart + x];
205
- const a = x >= bpp ? raw[rowStart + x - bpp] : 0;
206
- const b = prevRowStart === void 0 ? 0 : raw[prevRowStart + x];
207
- const c = x >= bpp && prevRowStart !== void 0 ? raw[prevRowStart + x - bpp] : 0;
208
- out[outOffset + x] = rawByte - predictorValue(filterType, a, b, c) & 255;
199
+ switch (filterType) {
200
+ case 1:
201
+ target.forEach((_byte, x) => {
202
+ const filtered = current[x + bpp] - current[x] & 255;
203
+ target[x] = filtered;
204
+ sum += Math.min(filtered, 256 - filtered);
205
+ });
206
+ break;
207
+ case 2:
208
+ target.forEach((_byte, x) => {
209
+ const filtered = current[x + bpp] - previous[x + bpp] & 255;
210
+ target[x] = filtered;
211
+ sum += Math.min(filtered, 256 - filtered);
212
+ });
213
+ break;
214
+ case 3:
215
+ target.forEach((_byte, x) => {
216
+ const filtered = current[x + bpp] - Math.floor((current[x] + previous[x + bpp]) / 2) & 255;
217
+ target[x] = filtered;
218
+ sum += Math.min(filtered, 256 - filtered);
219
+ });
220
+ break;
221
+ case 4:
222
+ target.forEach((_byte, x) => {
223
+ const filtered = current[x + bpp] - paethPredictor(current[x], previous[x + bpp], previous[x]) & 255;
224
+ target[x] = filtered;
225
+ sum += Math.min(filtered, 256 - filtered);
226
+ });
227
+ break;
228
+ case 0: target.forEach((_byte, x) => {
229
+ const filtered = current[x + bpp];
230
+ target[x] = filtered;
231
+ sum += Math.min(filtered, 256 - filtered);
232
+ });
209
233
  }
234
+ return sum;
210
235
  }
211
236
  const ALL_FILTER_TYPES = [
212
237
  0,
@@ -218,31 +243,27 @@ const ALL_FILTER_TYPES = [
218
243
  function filterScanlines(raw, height, bytesPerRow, bpp, strategy = "adaptive") {
219
244
  const stride = bytesPerRow + 1;
220
245
  const out = new Uint8Array(height * stride);
221
- const candidate = new Uint8Array(bytesPerRow);
222
- for (let y = 0; y < height; y++) {
223
- const rowStart = y * bytesPerRow;
224
- const prevRowStart = y > 0 ? rowStart - bytesPerRow : void 0;
225
- const outRowStart = y * stride;
226
- if (strategy === "none") {
227
- out[outRowStart] = 0;
228
- filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, 0, out, outRowStart + 1);
229
- continue;
230
- }
246
+ let current = new Uint8Array(bytesPerRow + bpp);
247
+ let previous = new Uint8Array(bytesPerRow + bpp);
248
+ let candidate = new Uint8Array(bytesPerRow);
249
+ let best = new Uint8Array(bytesPerRow);
250
+ const filterTypes = strategy === "none" ? [0] : ALL_FILTER_TYPES;
251
+ Array.from({ length: height }).forEach((_row, y) => {
252
+ current.set(raw.subarray(y * bytesPerRow, (y + 1) * bytesPerRow), bpp);
231
253
  let bestType = 0;
232
254
  let bestSum = Number.POSITIVE_INFINITY;
233
- let best;
234
- for (const filterType of ALL_FILTER_TYPES) {
235
- filterRowInto(raw, rowStart, prevRowStart, bytesPerRow, bpp, filterType, candidate, 0);
236
- const sum = sumOfAbsSigned(candidate);
255
+ for (const filterType of filterTypes) {
256
+ const sum = filterRowInto(current, previous, bpp, filterType, candidate);
237
257
  if (sum < bestSum) {
238
258
  bestSum = sum;
239
259
  bestType = filterType;
240
- best = candidate.slice();
260
+ [best, candidate] = [candidate, best];
241
261
  }
242
262
  }
243
- out[outRowStart] = bestType;
244
- if (best !== void 0) out.set(best, outRowStart + 1);
245
- }
263
+ out[y * stride] = bestType;
264
+ out.set(best, y * stride + 1);
265
+ [previous, current] = [current, previous];
266
+ });
246
267
  return out;
247
268
  }
248
269
  //#endregion
@@ -347,12 +368,11 @@ function writeTruecolorPng(writer, image, options) {
347
368
  const bytesPerRow = width * outChannels;
348
369
  const pixelCount = width * height;
349
370
  const interleaved = new Uint8Array(pixelCount * outChannels);
350
- for (const i of Array.from({ length: pixelCount }, (_, index) => index)) {
351
- const srcBase = i * channels;
352
- const dstBase = i * outChannels;
353
- for (const c of Array.from({ length: channels }, (_, index) => index)) interleaved[dstBase + c] = data[srcBase + c];
354
- if (alpha !== void 0) interleaved[dstBase + channels] = alpha[i];
355
- }
371
+ interleaved.forEach((_byte, index) => {
372
+ const pixel = Math.floor(index / outChannels);
373
+ const channel = index % outChannels;
374
+ interleaved[index] = alpha !== void 0 && channel === channels ? alpha[pixel] : data[pixel * channels + channel];
375
+ });
356
376
  writeIhdr(writer, width, height, colorTypeFor(image));
357
377
  writeChunk(writer, "IDAT", deflate(filterScanlines(interleaved, height, bytesPerRow, outChannels, options.filter ?? "adaptive")));
358
378
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byte-codec",
3
- "version": "1.5.5",
3
+ "version": "1.5.7",
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": {