@poe-platform/safe-bash 0.1.845 → 0.1.846

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.
Files changed (42) hide show
  1. package/dist/image-ast/ast.d.ts +12 -2
  2. package/dist/image-ast/browser/ast.d.ts +12 -2
  3. package/dist/image-ast/browser/codecs/exif-metadata.d.ts +2 -0
  4. package/dist/image-ast/browser/codecs/exif.d.ts +2 -0
  5. package/dist/image-ast/browser/codecs/gif-metadata-storage.d.ts +1 -0
  6. package/dist/image-ast/browser/codecs/gif-metadata.d.ts +1 -0
  7. package/dist/image-ast/browser/codecs/gif-output-parts.d.ts +1 -1
  8. package/dist/image-ast/browser/codecs/jpeg.d.ts +1 -1
  9. package/dist/image-ast/browser/codecs/netpbm-storage.d.ts +2 -2
  10. package/dist/image-ast/browser/codecs/netpbm.d.ts +4 -4
  11. package/dist/image-ast/browser/codecs/png-storage.d.ts +9 -1
  12. package/dist/image-ast/browser/codecs/png.d.ts +1 -0
  13. package/dist/image-ast/browser/codecs/tiff-layout.d.ts +3 -2
  14. package/dist/image-ast/codecs/exif-metadata.d.ts +2 -0
  15. package/dist/image-ast/codecs/exif.d.ts +2 -0
  16. package/dist/image-ast/codecs/gif-metadata-storage.d.ts +1 -0
  17. package/dist/image-ast/codecs/gif-metadata.d.ts +1 -0
  18. package/dist/image-ast/codecs/gif-output-parts.d.ts +1 -1
  19. package/dist/image-ast/codecs/jpeg.d.ts +1 -1
  20. package/dist/image-ast/codecs/netpbm-storage.d.ts +2 -2
  21. package/dist/image-ast/codecs/netpbm.d.ts +4 -4
  22. package/dist/image-ast/codecs/png-storage.d.ts +9 -1
  23. package/dist/image-ast/codecs/png.d.ts +1 -0
  24. package/dist/image-ast/codecs/tiff-layout.d.ts +3 -2
  25. package/dist/image-ast/index.js +554 -163
  26. package/dist/mp4-ast/types.d.ts +2 -0
  27. package/dist/safe-bash/command-chunks/{chunk-CMGVQM4Y.js → chunk-CSJHADNR.js} +17 -3
  28. package/dist/safe-bash/command-chunks/{chunk-KSCVKF7I.js → chunk-F42YLXD4.js} +3 -3
  29. package/dist/safe-bash/command-chunks/{chunk-2EEIB5O3.js → chunk-HUE2ATJY.js} +3 -3
  30. package/dist/safe-bash/command-chunks/{chunk-4KM2VJRB.js → chunk-NBQWCUOP.js} +1 -1
  31. package/dist/safe-bash/command-chunks/{chunk-U5TQVW64.js → chunk-ZTEK2JJD.js} +1 -1
  32. package/dist/safe-bash-command-ffmpeg/index.js +433 -74
  33. package/dist/safe-bash-command-ffprobe/index.js +2 -2
  34. package/dist/safe-bash-command-ffprobe/media.js +2 -2
  35. package/dist/safe-bash-command-imagemagick/compare-file.d.ts +3 -2
  36. package/dist/safe-bash-command-imagemagick/index.js +835 -247
  37. package/dist/safe-bash-command-imagemagick/raw-output.d.ts +10 -0
  38. package/dist/safe-bash-command-python/executor.js +3 -3
  39. package/dist/safe-bash-command-python/index.js +3 -3
  40. package/dist/safe-bash-command-python/jspi.js +2 -2
  41. package/dist/safe-bash-command-python/worker.js +1 -1
  42. package/package.json +2 -2
@@ -1,7 +1,90 @@
1
1
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
2
2
 
3
- // packages/safe-bash-command-imagemagick/src/deskew.ts
3
+ // packages/safe-bash-command-imagemagick/src/raw-rgb.ts
4
+ function rawRgbInput(width, height, depth, endian, byteLength) {
5
+ if (depth !== 8 && depth !== 16) throw new Error(`unsupported RGB input depth '${depth}'`);
6
+ const length = width * height * 3 * (depth / 8);
7
+ if (!Number.isSafeInteger(length) || length > byteLength) throw new Error("unexpected end-of-file in RGB input");
8
+ const header = new TextEncoder().encode(`P6
9
+ ${width} ${height}
10
+ ${depth === 8 ? 255 : 65535}
11
+ `);
12
+ const swap = depth === 16 && endian !== "msb";
13
+ return {
14
+ size: header.length + length,
15
+ *read(position, size) {
16
+ const bytes = new Uint8Array(size);
17
+ const headerLength = Math.max(0, Math.min(size, header.length - position));
18
+ bytes.set(header.subarray(position, position + headerLength));
19
+ if (headerLength < size) {
20
+ const start = Math.max(0, position - header.length), end = start + size - headerLength;
21
+ const alignedStart = swap ? start - start % 2 : start;
22
+ const alignedEnd = swap ? end + end % 2 : end;
23
+ for (let offset = alignedStart; offset < alignedEnd; offset += 8192) {
24
+ const length2 = Math.min(8192, alignedEnd - offset);
25
+ const input = yield { position: offset, length: length2 };
26
+ if (input.length !== length2) throw new Error("unexpected end-of-file in RGB input");
27
+ for (let i = Math.max(start, offset); i < Math.min(end, offset + length2); i++) {
28
+ bytes[headerLength + i - start] = input[(swap ? i ^ 1 : i) - offset];
29
+ }
30
+ }
31
+ }
32
+ return bytes;
33
+ }
34
+ };
35
+ }
36
+
37
+ // packages/safe-bash-command-imagemagick/src/raw-output.ts
38
+ import { encodeImage as encodeRaster, encodeStoredImage as encodeStoredRaster } from "../image-ast/portable.js";
4
39
  import { yieldTurn } from "../safe-bash-contracts/yield.js";
40
+ function isRawCoder(format) {
41
+ return format === "rgb" || format === "rgba" || format === "bgr" || format === "bgra" || format === "gray";
42
+ }
43
+ function encodeImage(image, options) {
44
+ const format = options.format;
45
+ if (!isRawCoder(format)) return encodeRaster(image, { ...options, format });
46
+ const channels = format === "gray" ? 1 : format.endsWith("a") ? 4 : 3;
47
+ const depth = (image.bitsPerSample ?? 8) > 8 ? 16 : 8;
48
+ const data = new Uint8Array(image.width * image.height * channels * depth / 8);
49
+ const view = new DataView(data.buffer), samples = image.data16 ?? image.data;
50
+ const scale = image.data16 ? 65535 : 255, maximum = depth === 16 ? 65535 : 255;
51
+ let offset = 0;
52
+ for (let pixel = 0; pixel < image.width * image.height; pixel++) {
53
+ for (let channel = 0; channel < channels; channel++) {
54
+ const index = format.startsWith("bgr") && channel < 3 ? 2 - channel : channel;
55
+ const value = format === "gray" ? 0.212656 * samples[pixel * 4] + 0.715158 * samples[pixel * 4 + 1] + 0.072186 * samples[pixel * 4 + 2] : channel === 3 && !image.hasAlpha ? scale : samples[pixel * 4 + index];
56
+ const quantum = Math.round(value * maximum / scale);
57
+ if (depth === 16) view.setUint16(offset, quantum, options.endian === void 0 || options.endian === "lsb");
58
+ else data[offset] = quantum;
59
+ offset += depth / 8;
60
+ }
61
+ }
62
+ return { data, format: "raw", channels };
63
+ }
64
+ async function* encodeStoredImage(image, storage, signal, options) {
65
+ if (!isRawCoder(options.format)) return yield* encodeStoredRaster(image, storage, signal, { ...options, format: options.format });
66
+ let size = 0, channels = 0;
67
+ for (let pixel = 0; pixel < image.width * image.height; pixel += 4096) {
68
+ await yieldTurn(signal);
69
+ const count = Math.min(4096, image.width * image.height - pixel);
70
+ const data = new Uint8Array(await storage.read(image.position + pixel * 4, count * 4, { signal }));
71
+ if (data.length !== count * 4) throw new Error("Truncated raw pixel storage");
72
+ let data16;
73
+ if (image.storedData16) {
74
+ const bytes = await storage.read(image.storedData16.position + pixel * 8, count * 8, { signal });
75
+ if (bytes.length !== count * 8) throw new Error("Truncated raw sample storage");
76
+ data16 = new Uint16Array(new Uint8Array(bytes).buffer);
77
+ }
78
+ const encoded = encodeImage({ ...image, width: count, height: 1, data, ...data16 ? { data16 } : {} }, options);
79
+ size += encoded.data.length;
80
+ channels = encoded.channels;
81
+ yield encoded.data;
82
+ }
83
+ return { format: "raw", width: image.width, height: image.height, channels, premultiplied: false, size };
84
+ }
85
+
86
+ // packages/safe-bash-command-imagemagick/src/deskew.ts
87
+ import { yieldTurn as yieldTurn2 } from "../safe-bash-contracts/yield.js";
5
88
  function* deskewAngle(width, height, spec) {
6
89
  const threshold = Number(spec.endsWith("%") ? spec.slice(0, -1) : spec) * (spec.endsWith("%") ? 255 / 100 : 255 / 65535);
7
90
  if (!spec || !Number.isFinite(threshold)) throw new Error("Invalid deskew threshold");
@@ -80,7 +163,7 @@ async function storedDeskewAngle(image, storage, spec, signal) {
80
163
  let next = steps.next();
81
164
  while (!next.done) {
82
165
  if (!next.value) {
83
- await yieldTurn(signal);
166
+ await yieldTurn2(signal);
84
167
  next = steps.next();
85
168
  } else {
86
169
  const { position, length } = next.value;
@@ -105,7 +188,7 @@ function deskewPlan(image, radians) {
105
188
  }
106
189
 
107
190
  // packages/safe-bash-command-imagemagick/src/raster-backing.ts
108
- import { yieldTurn as yieldTurn2 } from "../safe-bash-contracts/yield.js";
191
+ import { yieldTurn as yieldTurn3 } from "../safe-bash-contracts/yield.js";
109
192
  var RasterBackingCache = class {
110
193
  constructor(storage, signal) {
111
194
  this.storage = storage;
@@ -118,14 +201,14 @@ var RasterBackingCache = class {
118
201
  async allocate(length) {
119
202
  const region = { position: this.storage.allocate(length), length }, zero = new Uint8Array(Math.min(4096, length));
120
203
  for (let offset = 0; offset < length; offset += zero.length) {
121
- if (offset % 65536 === 0) await yieldTurn2(this.signal);
204
+ if (offset % 65536 === 0) await yieldTurn3(this.signal);
122
205
  await this.storage.write(region.position + offset, zero.subarray(0, Math.min(zero.length, length - offset)), { signal: this.signal });
123
206
  }
124
207
  return region;
125
208
  }
126
209
  async page(region, offset) {
127
210
  this.signal.throwIfAborted();
128
- if (++this.work % 16384 === 0) await yieldTurn2(this.signal);
211
+ if (++this.work % 16384 === 0) await yieldTurn3(this.signal);
129
212
  const start = Math.floor(offset / 4096) * 4096, key = region.position + ":" + start;
130
213
  let page = this.pages.get(key);
131
214
  if (page) {
@@ -181,7 +264,7 @@ var RasterBackingCache = class {
181
264
  };
182
265
 
183
266
  // packages/safe-bash-command-imagemagick/src/floodfill.ts
184
- import { yieldTurn as yieldTurn3 } from "../safe-bash-contracts/yield.js";
267
+ import { yieldTurn as yieldTurn4 } from "../safe-bash-contracts/yield.js";
185
268
  function matchesAlphaFloodfill(r, g, b, a, refR, refG, refB, refA, fuzz) {
186
269
  return (a - refA) ** 2 + ((r - refR) ** 2 + (g - refG) ** 2 + (b - refB) ** 2) * (a / 255) * (refA / 255) / 3 <= 2 * fuzz ** 2;
187
270
  }
@@ -206,7 +289,7 @@ async function floodfillStoredImage(image, storage, seedX, seedY, target, replac
206
289
  await cache.flush();
207
290
  const position = storage.allocate(count * 4);
208
291
  for (let start2 = 0; start2 < count; start2 += 1024) {
209
- await yieldTurn3(signal);
292
+ await yieldTurn4(signal);
210
293
  const length = Math.min(1024, count - start2), pixels = new Uint8Array(await storage.read(image.position + start2 * 4, length * 4, { signal }));
211
294
  const flags = await storage.read(visited.position + start2, length, { signal });
212
295
  if (pixels.length !== length * 4 || flags.length !== length) throw new Error("Truncated floodfill pixels");
@@ -342,6 +425,45 @@ function* extendEdgePixelSteps(source, width, height) {
342
425
  }
343
426
  }
344
427
 
428
+ // packages/safe-bash-command-imagemagick/src/bilevel-kernel.ts
429
+ function* bilevelPixelSteps(count, highDepth) {
430
+ const histogram = new Float64Array(65536), sampleBytes = highDepth ? 2 : 1;
431
+ let low = 0, high = 65535;
432
+ for (let pass = 0; pass < 2; pass++) {
433
+ for (let first = 0; first < count; first += 4096) {
434
+ yield;
435
+ const size = Math.min(4096, count - first);
436
+ const bytes = yield { kind: "read", position: first * 4 * sampleBytes, length: size * 4 * sampleBytes };
437
+ if (!bytes || bytes.length !== size * 4 * sampleBytes) throw new Error("Truncated bilevel pixels");
438
+ const samples = highDepth ? new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2) : bytes;
439
+ const data = pass ? new Uint8Array(size * 4) : void 0;
440
+ for (let pixel = 0; pixel < size; pixel++) {
441
+ const offset = pixel * 4;
442
+ const gray = Math.round((highDepth ? 1 : 257) * (0.212656 * samples[offset] + 0.715158 * samples[offset + 1] + 0.072186 * samples[offset + 2]));
443
+ if (!data) histogram[gray]++;
444
+ else {
445
+ const value = gray > (high > low ? (low + high) / 2 : 65535 / 2) ? 255 : 0;
446
+ data.fill(value, offset, offset + 3);
447
+ data[offset + 3] = 255;
448
+ }
449
+ }
450
+ if (data) yield { kind: "write", position: first * 4, data };
451
+ }
452
+ if (pass === 0) {
453
+ let sum = 0;
454
+ for (; low < 65535; low++) {
455
+ sum += histogram[low];
456
+ if (sum > 0.02 * count) break;
457
+ }
458
+ sum = 0;
459
+ for (; high > 0; high--) {
460
+ sum += histogram[high];
461
+ if (sum > 0.01 * count) break;
462
+ }
463
+ }
464
+ }
465
+ }
466
+
345
467
  // packages/safe-bash-command-imagemagick/src/compare-phash.ts
346
468
  function* perceptualHashSteps(info, image) {
347
469
  const weights = Array.from({ length: 9 }, (_, i) => {
@@ -1510,7 +1632,7 @@ function* pointResizePixelSteps(image, width, height, sample = false) {
1510
1632
 
1511
1633
  // packages/safe-bash-command-imagemagick/src/trim.ts
1512
1634
  import { extractImageSteps, transformStoredImage } from "../image-ast/portable.js";
1513
- import { yieldTurn as yieldTurn4 } from "../safe-bash-contracts/yield.js";
1635
+ import { yieldTurn as yieldTurn5 } from "../safe-bash-contracts/yield.js";
1514
1636
  function* trimBounds(width, height, fuzz) {
1515
1637
  const corners = [];
1516
1638
  for (const pixel of [0, width - 1, (height - 1) * width, width * height - 1]) {
@@ -1574,7 +1696,7 @@ async function trimStoredMagick(image, storage, fuzz, signal) {
1574
1696
  let next = steps.next();
1575
1697
  while (!next.done) {
1576
1698
  if (!next.value) {
1577
- await yieldTurn4(signal);
1699
+ await yieldTurn5(signal);
1578
1700
  next = steps.next();
1579
1701
  } else {
1580
1702
  const { position: position2, length } = next.value;
@@ -1881,7 +2003,7 @@ function* quantizePixelSteps(length, requested, grayscale = false) {
1881
2003
  }
1882
2004
 
1883
2005
  // packages/safe-bash-command-imagemagick/src/connected-components.ts
1884
- import { yieldTurn as yieldTurn5 } from "../safe-bash-contracts/yield.js";
2006
+ import { yieldTurn as yieldTurn6 } from "../safe-bash-contracts/yield.js";
1885
2007
  function* labelSteps(width, height, connectivity) {
1886
2008
  const components = [];
1887
2009
  for (let seed = 0; seed < width * height; seed++) {
@@ -1978,7 +2100,7 @@ async function connectedComponentsStored(image, storage, connectivity, verbose,
1978
2100
  while (!next.done) {
1979
2101
  const request = next.value;
1980
2102
  if (!request) {
1981
- await yieldTurn5(signal);
2103
+ await yieldTurn6(signal);
1982
2104
  next = steps.next(0);
1983
2105
  continue;
1984
2106
  }
@@ -1993,7 +2115,7 @@ async function connectedComponentsStored(image, storage, connectivity, verbose,
1993
2115
  const report = reportSteps(next.value);
1994
2116
  let part = report.next();
1995
2117
  while (!part.done) {
1996
- await yieldTurn5(signal);
2118
+ await yieldTurn6(signal);
1997
2119
  part = report.next();
1998
2120
  }
1999
2121
  text = part.value;
@@ -2001,7 +2123,7 @@ async function connectedComponentsStored(image, storage, connectivity, verbose,
2001
2123
  await cache.flush();
2002
2124
  const position = storage.allocate(size);
2003
2125
  for (let offset = 0; offset < size; offset += 4096) {
2004
- await yieldTurn5(signal);
2126
+ await yieldTurn6(signal);
2005
2127
  const bytes = new Uint8Array(await storage.read(regions.labels.position + offset, Math.min(4096, size - offset), { signal })), view = new DataView(bytes.buffer);
2006
2128
  for (let i = 0; i < bytes.length; i += 4) {
2007
2129
  const value = Math.round((view.getUint32(i, true) - 1) / 257);
@@ -2158,7 +2280,7 @@ async function withImageInputs(input, stdinBytes, signal, run) {
2158
2280
 
2159
2281
  // packages/safe-bash-command-imagemagick/src/text-output.ts
2160
2282
  import { IntegerTable } from "@poe-platform/safe-fs/storage";
2161
- import { yieldTurn as yieldTurn6 } from "../safe-bash-contracts/yield.js";
2283
+ import { yieldTurn as yieldTurn7 } from "../safe-bash-contracts/yield.js";
2162
2284
  var hex = (value) => value.toString(16).toUpperCase().padStart(2, "0");
2163
2285
  function pixelEnumerationLine(x, y, r, g, b, a) {
2164
2286
  return `${x},${y}: (${r},${g},${b},${a}) #${hex(r)}${hex(g)}${hex(b)}${a < 255 ? hex(a) : ""} srgba(${r},${g},${b},${(a / 255).toFixed(3)})`;
@@ -2173,7 +2295,7 @@ async function* storedPixelEnumeration(image, storage, signal) {
2173
2295
  `;
2174
2296
  const size = image.width * image.height * 4;
2175
2297
  for (let start = 0; start < size; start += 16384) {
2176
- await yieldTurn6(signal);
2298
+ await yieldTurn7(signal);
2177
2299
  const bytes = await storage.read(image.position + start, Math.min(16384, size - start));
2178
2300
  const original = (image.magickColorspace === "HSL" || image.magickColorspace === "HSB" || image.magickColorspace === "HWB" || image.magickColorspace === "XYZ" || image.magickColorspace === "YCbCr" || image.magickColorspace === "YUV") && image.storedData16 ? await storage.read(image.storedData16.position + start * 2, bytes.length * 2) : void 0;
2179
2301
  const data16 = original ? new Uint16Array(original.buffer.slice(original.byteOffset, original.byteOffset + original.byteLength)) : void 0;
@@ -2195,7 +2317,7 @@ async function* storedHistogram(image, storage, signal) {
2195
2317
  const sampleOffsets = (image.magickColorspace === "HWB" || image.magickColorspace === "XYZ" || image.magickColorspace === "YCbCr" || image.magickColorspace === "YUV") && image.storedHueSamples !== void 0 ? new IntegerTable(storage, 128) : void 0;
2196
2318
  let unique = 0;
2197
2319
  for (let start = 0; start < size; start += 16384) {
2198
- await yieldTurn6(signal);
2320
+ await yieldTurn7(signal);
2199
2321
  const bytes = await storage.read(image.position + start, Math.min(16384, size - start));
2200
2322
  const original = (image.magickColorspace === "HSL" || image.magickColorspace === "HSB" || image.magickColorspace === "HWB" || image.magickColorspace === "XYZ" || image.magickColorspace === "YCbCr" || image.magickColorspace === "YUV") && image.storedData16 ? await storage.read(image.storedData16.position + start * 2, bytes.length * 2) : void 0;
2201
2323
  const data16 = original ? new Uint16Array(original.buffer.slice(original.byteOffset, original.byteOffset + original.byteLength)) : void 0;
@@ -2209,7 +2331,7 @@ async function* storedHistogram(image, storage, signal) {
2209
2331
  }
2210
2332
  }
2211
2333
  for (let index = 0; index < unique; index++) {
2212
- if (index % 128 === 0) await yieldTurn6(signal);
2334
+ if (index % 128 === 0) await yieldTurn7(signal);
2213
2335
  const key = await order.get(BigInt(index));
2214
2336
  const samples = sampleOffsets ? await storage.read(image.storedHueSamples + Number(await sampleOffsets.get(BigInt(index))) * 8, 32) : void 0;
2215
2337
  const hueSamples = samples ? Array.from(new Float64Array(samples.buffer.slice(samples.byteOffset, samples.byteOffset + samples.byteLength))) : void 0;
@@ -2254,7 +2376,7 @@ async function* encodeText(parts, signal) {
2254
2376
 
2255
2377
  // packages/safe-bash-command-imagemagick/src/stored-stack.ts
2256
2378
  import { IntegerTable as IntegerTable2 } from "@poe-platform/safe-fs/storage";
2257
- import { yieldTurn as yieldTurn7 } from "../safe-bash-contracts/yield.js";
2379
+ import { yieldTurn as yieldTurn8 } from "../safe-bash-contracts/yield.js";
2258
2380
  var magickInput = /* @__PURE__ */ Symbol("magickInput");
2259
2381
  var StoredImageStack = class {
2260
2382
  constructor(storage, signal, parent) {
@@ -2263,6 +2385,7 @@ var StoredImageStack = class {
2263
2385
  this.frames = parent?.frames ?? /* @__PURE__ */ new Map();
2264
2386
  this.records = parent?.records ?? /* @__PURE__ */ new WeakMap();
2265
2387
  this.delayRegions = parent?.delayRegions ?? /* @__PURE__ */ new WeakMap();
2388
+ this.iccRegions = parent?.iccRegions ?? /* @__PURE__ */ new WeakMap();
2266
2389
  this.indices = new IntegerTable2(storage, 128);
2267
2390
  }
2268
2391
  storage;
@@ -2271,6 +2394,7 @@ var StoredImageStack = class {
2271
2394
  frames;
2272
2395
  records;
2273
2396
  delayRegions;
2397
+ iccRegions;
2274
2398
  length = 0;
2275
2399
  cache(position, image) {
2276
2400
  this.frames.delete(position);
@@ -2281,12 +2405,12 @@ var StoredImageStack = class {
2281
2405
  async retain(image) {
2282
2406
  const known = this.records.get(image);
2283
2407
  if (known !== void 0) return known;
2284
- const { delay, storedDelay, ...metadata } = image, source = storedDelay ?? delay;
2408
+ const { delay, storedDelay, icc, ...metadata } = image, source = storedDelay ?? delay;
2285
2409
  let delays = source ? this.delayRegions.get(source) : void 0;
2286
2410
  if (source && !delays) {
2287
2411
  delays = { position: this.storage.allocate(source.length * 9), length: source.length };
2288
2412
  for (let start = 0; start < source.length; start += 512) {
2289
- await yieldTurn7(this.signal);
2413
+ await yieldTurn8(this.signal);
2290
2414
  const count = Math.min(512, source.length - start), bytes = new Uint8Array(count * 9), view = new DataView(bytes.buffer);
2291
2415
  for (let i = 0; i < count; i++) {
2292
2416
  const value = storedDelay ? await storedDelay.at(start + i, { signal: this.signal }) : delay[start + i];
@@ -2299,11 +2423,20 @@ var StoredImageStack = class {
2299
2423
  }
2300
2424
  this.delayRegions.set(source, delays);
2301
2425
  }
2302
- const record = { image: metadata, ...image[magickInput] ? { source: image[magickInput] } : {}, ...delays ? { delays } : {} };
2426
+ let profile = icc ? this.iccRegions.get(icc) : void 0;
2427
+ if (icc && !profile) {
2428
+ profile = { position: this.storage.allocate(icc.length), length: icc.length };
2429
+ for (let offset = 0; offset < icc.length; offset += 4096) {
2430
+ await yieldTurn8(this.signal);
2431
+ await this.storage.write(profile.position + offset, icc.subarray(offset, offset + 4096));
2432
+ }
2433
+ this.iccRegions.set(icc, profile);
2434
+ }
2435
+ const record = { image: metadata, ...profile ? { icc: profile } : {}, ...image[magickInput] ? { source: image[magickInput] } : {}, ...delays ? { delays } : {} };
2303
2436
  const text = JSON.stringify(record), encoder = new TextEncoder(), position = this.storage.allocate(8);
2304
2437
  let size = 0;
2305
2438
  for (let offset = 0; offset < text.length; ) {
2306
- await yieldTurn7(this.signal);
2439
+ await yieldTurn8(this.signal);
2307
2440
  const bytes = new Uint8Array(4096), { read, written } = encoder.encodeInto(text.slice(offset, offset + 4097), bytes);
2308
2441
  await this.storage.write(this.storage.allocate(written), bytes.subarray(0, written));
2309
2442
  offset += read;
@@ -2326,11 +2459,20 @@ var StoredImageStack = class {
2326
2459
  const decoder = new TextDecoder();
2327
2460
  let text = "";
2328
2461
  for (let offset = 0; offset < size; offset += 4096) {
2329
- await yieldTurn7(this.signal);
2462
+ await yieldTurn8(this.signal);
2330
2463
  text += decoder.decode(await this.storage.read(position + 8 + offset, Math.min(4096, size - offset)), { stream: true });
2331
2464
  }
2332
2465
  const record = JSON.parse(text + decoder.decode());
2333
2466
  const image = { ...record.image, ...record.source ? { [magickInput]: record.source } : {} };
2467
+ if (record.icc) {
2468
+ const icc = new Uint8Array(record.icc.length);
2469
+ for (let offset = 0; offset < icc.length; offset += 4096) {
2470
+ await yieldTurn8(this.signal);
2471
+ icc.set(await this.storage.read(record.icc.position + offset, Math.min(4096, icc.length - offset)), offset);
2472
+ }
2473
+ Object.assign(image, { icc });
2474
+ this.iccRegions.set(icc, record.icc);
2475
+ }
2334
2476
  if (record.delays) {
2335
2477
  const region = record.delays, storage = this.storage, signal = this.signal;
2336
2478
  const reader = { length: region.length, async at(index, options) {
@@ -2375,12 +2517,12 @@ var StoredImageStack = class {
2375
2517
  const removed = new IntegerTable2(this.storage, 128);
2376
2518
  let work = 0;
2377
2519
  for (const index of indices) if (index >= 0 && index < this.length) {
2378
- if (++work % 128 === 0) await yieldTurn7(this.signal);
2520
+ if (++work % 128 === 0) await yieldTurn8(this.signal);
2379
2521
  await removed.set(BigInt(index), 1n);
2380
2522
  }
2381
2523
  let next = 0;
2382
2524
  for (let index = 0; index < this.length; index++) {
2383
- if (index % 128 === 0) await yieldTurn7(this.signal);
2525
+ if (index % 128 === 0) await yieldTurn8(this.signal);
2384
2526
  if (await removed.get(BigInt(index))) continue;
2385
2527
  if (next !== index) await this.indices.set(BigInt(next), await this.indices.get(BigInt(index)));
2386
2528
  next++;
@@ -2390,11 +2532,11 @@ var StoredImageStack = class {
2390
2532
  };
2391
2533
 
2392
2534
  // packages/safe-bash-command-imagemagick/src/remap.ts
2393
- import { yieldTurn as yieldTurn8 } from "../safe-bash-contracts/yield.js";
2535
+ import { yieldTurn as yieldTurn9 } from "../safe-bash-contracts/yield.js";
2394
2536
  async function remapStoredImage(image, paletteImage, storage, dither, signal) {
2395
2537
  const palette = [], seen = /* @__PURE__ */ new Set();
2396
2538
  for (let offset = 0; offset < paletteImage.width * paletteImage.height * 4 && palette.length < 256; offset += 4096) {
2397
- await yieldTurn8(signal);
2539
+ await yieldTurn9(signal);
2398
2540
  const length = Math.min(4096, paletteImage.width * paletteImage.height * 4 - offset), bytes = await storage.read(paletteImage.position + offset, length, { signal });
2399
2541
  if (bytes.length !== length) throw new Error("Truncated remap palette");
2400
2542
  for (let i = 0; i < length && palette.length < 256; i += 4) {
@@ -2409,7 +2551,7 @@ async function remapStoredImage(image, paletteImage, storage, dither, signal) {
2409
2551
  const count = image.width * image.height, cache = new RasterBackingCache(storage, signal);
2410
2552
  const errors = dither ? { position: storage.allocate(count * 16), length: count * 16 } : void 0;
2411
2553
  if (errors) for (let start = 0; start < count; start += 1024) {
2412
- await yieldTurn8(signal);
2554
+ await yieldTurn9(signal);
2413
2555
  const length = Math.min(1024, count - start), pixels = await storage.read(image.position + start * 4, length * 4, { signal });
2414
2556
  if (pixels.length !== length * 4) throw new Error("Truncated remap pixels");
2415
2557
  const bytes = new Uint8Array(length * 16), view = new DataView(bytes.buffer);
@@ -2418,7 +2560,7 @@ async function remapStoredImage(image, paletteImage, storage, dither, signal) {
2418
2560
  }
2419
2561
  const position = storage.allocate(count * 4);
2420
2562
  for (let start = 0; start < count; start += 1024) {
2421
- await yieldTurn8(signal);
2563
+ await yieldTurn9(signal);
2422
2564
  const length = Math.min(1024, count - start), pixels = new Uint8Array(await storage.read(image.position + start * 4, length * 4, { signal }));
2423
2565
  if (pixels.length !== length * 4) throw new Error("Truncated remap pixels");
2424
2566
  for (let i = 0; i < length; i++) {
@@ -3182,9 +3324,8 @@ function* embossPixelSteps(image, geometry) {
3182
3324
  }
3183
3325
 
3184
3326
  // packages/safe-bash-command-imagemagick/src/encoded-snapshots.ts
3185
- import { encodeStoredImage } from "../image-ast/portable.js";
3186
3327
  import { IntegerTable as IntegerTable3 } from "@poe-platform/safe-fs/storage";
3187
- import { yieldTurn as yieldTurn9 } from "../safe-bash-contracts/yield.js";
3328
+ import { yieldTurn as yieldTurn10 } from "../safe-bash-contracts/yield.js";
3188
3329
  var EncodedSnapshots = class {
3189
3330
  constructor(storage, signal) {
3190
3331
  this.storage = storage;
@@ -3214,7 +3355,7 @@ var EncodedSnapshots = class {
3214
3355
  };
3215
3356
  try {
3216
3357
  while (true) {
3217
- await yieldTurn9(this.signal);
3358
+ await yieldTurn10(this.signal);
3218
3359
  const next = await encoder.next();
3219
3360
  if (next.done) {
3220
3361
  if (!next.value) throw new Error("Image encoding did not finish");
@@ -3226,7 +3367,7 @@ var EncodedSnapshots = class {
3226
3367
  new DataView(header.buffer).setFloat64(0, bytes.length, true);
3227
3368
  await this.storage.write(position, header);
3228
3369
  for (let start = 0; start < bytes.length; start += 4096) {
3229
- await yieldTurn9(this.signal);
3370
+ await yieldTurn10(this.signal);
3230
3371
  await this.storage.write(position + 8 + start, bytes.subarray(start, start + 4096));
3231
3372
  }
3232
3373
  await this.buckets.set(hash, BigInt(position));
@@ -3265,7 +3406,7 @@ var EncodedSnapshots = class {
3265
3406
  const decoder = new TextDecoder();
3266
3407
  let text = "";
3267
3408
  for (let start = 0; start < size; start += 4096) {
3268
- await yieldTurn9(this.signal);
3409
+ await yieldTurn10(this.signal);
3269
3410
  text += decoder.decode(await this.storage.read(position + 8 + start, Math.min(4096, size - start)), { stream: true });
3270
3411
  }
3271
3412
  const record = JSON.parse(text + decoder.decode());
@@ -3275,7 +3416,7 @@ var EncodedSnapshots = class {
3275
3416
  async find(path, hash) {
3276
3417
  let position = Number(await this.buckets.get(hash) ?? -1n);
3277
3418
  while (position >= 0) {
3278
- await yieldTurn9(this.signal);
3419
+ await yieldTurn10(this.signal);
3279
3420
  const record = await this.record(position);
3280
3421
  if (record.path === path) return record;
3281
3422
  position = record.next ?? -1;
@@ -3333,13 +3474,13 @@ async function decodeFileImage(source, storage, filesystem, cwd, signal, options
3333
3474
  }
3334
3475
 
3335
3476
  // packages/safe-bash-command-imagemagick/src/compare-file.ts
3336
- import { readImageMetadataFromSource, tryPdfMetadata, readImageMetadata, decodeImage, encodeStoredImage as encodeStoredImage2, tryImageFile, UnsupportedStoredResource as UnsupportedStoredResource2, UnsupportedImageFormat as UnsupportedImageFormat2 } from "../image-ast/portable.js";
3477
+ import { readImageMetadataFromSource, tryPdfMetadata, readImageMetadata, decodeImage, tryImageFile, UnsupportedStoredResource as UnsupportedStoredResource2, UnsupportedImageFormat as UnsupportedImageFormat2 } from "../image-ast/portable.js";
3337
3478
  import { PagedStorage as PagedStorage2, PagedStorageCache } from "@poe-platform/safe-fs/storage";
3338
3479
  import { FsError as FsError2 } from "@poe-platform/safe-fs/contracts";
3339
3480
  import { resolvePath as resolvePath2 } from "../safe-bash-contracts/path.js";
3340
3481
  import { writeBytes } from "../safe-bash-contracts/io.js";
3341
3482
  import { writeFileOutput } from "../safe-bash-contracts/filesystem-output-budget.js";
3342
- import { yieldTurn as yieldTurn10 } from "../safe-bash-contracts/yield.js";
3483
+ import { yieldTurn as yieldTurn11 } from "../safe-bash-contracts/yield.js";
3343
3484
  var CompareInputFailure = class extends Error {
3344
3485
  constructor(reason) {
3345
3486
  super("Comparison input failed");
@@ -3364,7 +3505,7 @@ async function withCompareFiles(input, stdinBytes, signal, run) {
3364
3505
  const { data, data16: ignored, ...metadata } = image, position = storage.allocate(data.length);
3365
3506
  for (let offset = 0; offset < data.length; offset += 16384) {
3366
3507
  if (offset % 1048576 === 0)
3367
- await yieldTurn10(signal);
3508
+ await yieldTurn11(signal);
3368
3509
  await storage.write(position + offset, data.subarray(offset, offset + 16384));
3369
3510
  }
3370
3511
  return { ...metadata, position };
@@ -3393,46 +3534,53 @@ async function withCompareFiles(input, stdinBytes, signal, run) {
3393
3534
  try {
3394
3535
  const result = await withImageInputs(input, stdinBytes, signal, async (read) => {
3395
3536
  const publishImage = async (image, path, encoding, snapshot, retire = true) => {
3396
- if (path !== "-" || snapshot) {
3397
- const output = resolvePath2(cwd, path), info = await tryImageFile({ image, storage, ...snapshot ? { encoded: snapshot } : {}, async close() {
3398
- if (retire) {
3399
- await storage.close();
3400
- await read.close();
3401
- }
3402
- } }, output, { filesystem: budgetedFs, workingDirectory: cwd, signal }, encoding);
3403
- if (info)
3404
- return;
3405
- }
3406
- const encoded = snapshot ? (async function* () {
3407
- for (let position = 0; position < snapshot.source.size; position += 16384) {
3408
- await yieldTurn10(signal);
3409
- yield new Uint8Array(await snapshot.source.read(position, Math.min(16384, snapshot.source.size - position), io));
3410
- }
3411
- })() : encodeStoredImage2(image, storage, signal, encoding), chunks = [];
3412
- let size = 0;
3413
3537
  try {
3414
- for await (const chunk of encoded) {
3415
- if (path === "-" && !snapshot && stdout)
3416
- await writeBytes(stdout, chunk, signal);
3417
- else {
3418
- chunks.push(chunk);
3419
- size += chunk.length;
3538
+ if (path !== "-" || snapshot) {
3539
+ const output = resolvePath2(cwd, path), info = await tryImageFile({ image, storage, ...snapshot ? { encoded: snapshot } : {}, async close() {
3540
+ if (retire) {
3541
+ await storage.close();
3542
+ await read.close();
3543
+ }
3544
+ } }, output, { filesystem: budgetedFs, workingDirectory: cwd, signal }, { ...encoding, format: isRawCoder(encoding.format) ? "raw" : encoding.format });
3545
+ if (info)
3546
+ return;
3547
+ }
3548
+ const encoded = snapshot ? (async function* () {
3549
+ for (let position = 0; position < snapshot.source.size; position += 16384) {
3550
+ await yieldTurn11(signal);
3551
+ yield new Uint8Array(await snapshot.source.read(position, Math.min(16384, snapshot.source.size - position), io));
3552
+ }
3553
+ })() : encodeStoredImage(image, storage, signal, encoding), chunks = [];
3554
+ let size = 0;
3555
+ try {
3556
+ for await (const chunk of encoded) {
3557
+ if (path === "-" && !snapshot && stdout)
3558
+ await writeBytes(stdout, chunk, signal);
3559
+ else {
3560
+ chunks.push(chunk);
3561
+ size += chunk.length;
3562
+ }
3420
3563
  }
3564
+ } finally {
3565
+ await encoded.return(void 0);
3421
3566
  }
3422
- } finally {
3423
- await encoded.return(void 0);
3424
- }
3425
- if (path === "-" && !snapshot && stdout)
3426
- return;
3427
- const bytes = new Uint8Array(size);
3428
- let offset = 0;
3429
- for (const chunk of chunks) {
3430
- bytes.set(chunk, offset);
3431
- offset += chunk.length;
3567
+ if (path === "-" && !snapshot && stdout)
3568
+ return;
3569
+ const bytes = new Uint8Array(size);
3570
+ let offset = 0;
3571
+ for (const chunk of chunks) {
3572
+ bytes.set(chunk, offset);
3573
+ offset += chunk.length;
3574
+ }
3575
+ if (path === "-" && !snapshot)
3576
+ return bytes;
3577
+ await writeFileOutput(context, bytes, (admitted) => fs.writeFile(resolvePath2(cwd, path), admitted, io));
3578
+ } catch (error) {
3579
+ signal.throwIfAborted();
3580
+ if (error instanceof FsError2 && ["ENOENT", "ENOTDIR"].includes(error.code))
3581
+ throw new FsError2(error.code, { syscall: "write", path, cause: error });
3582
+ throw error;
3432
3583
  }
3433
- if (path === "-" && !snapshot)
3434
- return bytes;
3435
- await writeFileOutput(context, bytes, (admitted) => fs.writeFile(resolvePath2(cwd, path), admitted, io));
3436
3584
  };
3437
3585
  const publishPending = async () => {
3438
3586
  let index = 0;
@@ -3474,7 +3622,7 @@ async function withCompareFiles(input, stdinBytes, signal, run) {
3474
3622
  try {
3475
3623
  let next = iterator.next();
3476
3624
  while (!next.done) {
3477
- await yieldTurn10(signal);
3625
+ await yieldTurn11(signal);
3478
3626
  let configured = next.value, image;
3479
3627
  try {
3480
3628
  image = bytes ? await retain(decodeImage(bytes, configured)) : await decodeFileImage(checked, storage, fs, cwd, signal, configured);
@@ -3520,7 +3668,10 @@ async function withCompareFiles(input, stdinBytes, signal, run) {
3520
3668
  throw error;
3521
3669
  }
3522
3670
  },
3523
- stage: snapshots.stage.bind(snapshots),
3671
+ async stage(image, path, encoding) {
3672
+ if (path === "-") return publishImage(image, path, encoding, void 0, false);
3673
+ await snapshots.stage(image, path, encoding);
3674
+ },
3524
3675
  publishPending,
3525
3676
  async publishText(image, path, chunks) {
3526
3677
  const encoded = (async function* () {
@@ -3535,7 +3686,7 @@ async function withCompareFiles(input, stdinBytes, signal, run) {
3535
3686
  await publishPending();
3536
3687
  },
3537
3688
  async publish(image, path, encoding) {
3538
- if (!snapshots.size) return publishImage(image, path, encoding);
3689
+ if (!snapshots.size && (!isRawCoder(encoding.format) || path === "-")) return publishImage(image, path, encoding);
3539
3690
  let bytes;
3540
3691
  if (path === "-") bytes = await publishImage(image, path, encoding);
3541
3692
  else await snapshots.stage(image, path, encoding);
@@ -3642,7 +3793,7 @@ async function withIdentifyFiles(input, stdinBytes, signal, run) {
3642
3793
  // packages/safe-bash-command-imagemagick/src/index.ts
3643
3794
  import { FsError as FsError4 } from "../safe-bash-contracts/errors.js";
3644
3795
  import { resolvePath as resolvePath3 } from "../safe-bash-contracts/path.js";
3645
- import { yieldTurn as yieldTurn11, drainCooperativeSteps as drainSteps } from "../safe-bash-contracts/yield.js";
3796
+ import { yieldTurn as yieldTurn12, drainCooperativeSteps as drainSteps } from "../safe-bash-contracts/yield.js";
3646
3797
  import { InputByteBudget } from "../safe-bash-contracts/io.js";
3647
3798
  import { writeFileOutput as writeFileOutput2 } from "../safe-bash-contracts/filesystem-output-budget.js";
3648
3799
  import {
@@ -3651,7 +3802,7 @@ import {
3651
3802
  } from "../safe-bash-contracts/command.js";
3652
3803
  import { readBytes as readBytes2, writeBytes as writeBytes2 } from "../safe-bash-contracts/io.js";
3653
3804
  import { createOutputOperation } from "../safe-bash-contracts/output.js";
3654
- import { readImageMetadataFromSource as readImageMetadataFromSource3, decodeImageToStorage as decodeImageToStorage2, transformStoredImage as transformStoredImage2, UnsupportedStoredResource as UnsupportedStoredResource4, decodeImage as decodeImage3, decodePngToCanvas, FONT_5X7, encodeImage, readImageMetadata as readImageMetadata3, parseColor as baseParseColor, applyExifOrientationSteps, compositeImageSteps, computeImageStatsSteps as computeImageStatsSteps2, ensureAlphaImageSteps, extendImageSteps, extractChannelImageSteps, extractImageSteps as extractImageSteps2, flipImageSteps, flopImageSteps, grayscaleImageSteps, resizeImageSteps, rotateImageSteps, thresholdImageSteps } from "../image-ast/portable.js";
3805
+ import { readImageMetadataFromSource as readImageMetadataFromSource3, decodeImageToStorage as decodeImageToStorage2, transformStoredImage as transformStoredImage2, UnsupportedStoredResource as UnsupportedStoredResource4, decodeImage as decodeImage3, decodePngToCanvas, FONT_5X7, readImageMetadata as readImageMetadata3, parseColor as baseParseColor, applyExifOrientationSteps, compositeImageSteps, computeImageStatsSteps as computeImageStatsSteps2, ensureAlphaImageSteps, extendImageSteps, extractChannelImageSteps, extractImageSteps as extractImageSteps2, flipImageSteps, flopImageSteps, grayscaleImageSteps, resizeImageSteps, rotateImageSteps, thresholdImageSteps } from "../image-ast/portable.js";
3655
3806
  var X11_NAMED_COLORS = {
3656
3807
  aliceblue: [240, 248, 255, 255],
3657
3808
  antiquewhite: [250, 235, 215, 255],
@@ -4057,6 +4208,7 @@ function createDefaultState() {
4057
4208
  tile: void 0,
4058
4209
  strip: false,
4059
4210
  adjoin: true,
4211
+ scene: 0,
4060
4212
  dither: true,
4061
4213
  formatStr: void 0,
4062
4214
  channels: { r: true, g: true, b: true, a: false },
@@ -4507,6 +4659,7 @@ function* formatMagickPropertyStringSteps(fmt, meta, getImage, context = {}, res
4507
4659
  Q: String(context.quality ?? meta.quality ?? 92)
4508
4660
  };
4509
4661
  const properties = {
4662
+ ...meta.properties,
4510
4663
  orientation: ["Undefined", "TopLeft", "TopRight", "BottomRight", "BottomLeft", "LeftTop", "RightTop", "RightBottom", "LeftBottom"][meta.orientation ?? 0] ?? "Undefined",
4511
4664
  ...meta.jpegSamplingFactor === void 0 ? {} : { "jpeg:sampling-factor": meta.jpegSamplingFactor },
4512
4665
  "exif:orientation": meta.orientation === void 0 ? "" : String(meta.orientation),
@@ -4953,19 +5106,32 @@ function hasSceneOutputPattern(path) {
4953
5106
  return false;
4954
5107
  }
4955
5108
  function formatSceneOutputPath(pattern, index) {
4956
- const padMatch = /%0(\d+)d/.exec(pattern);
4957
- if (padMatch) {
4958
- const width = parseInt(padMatch[1], 10);
4959
- return pattern.replace(/%0\d+d/, String(index).padStart(width, "0"));
4960
- }
4961
- if (pattern.includes("%d")) {
4962
- return pattern.replace(/%d/, String(index));
5109
+ let path = "", hasScene = false;
5110
+ for (let cursor = 0; cursor < pattern.length; cursor++) {
5111
+ if (pattern[cursor] === "%") {
5112
+ if (pattern[cursor + 1] === "%") {
5113
+ path += "%";
5114
+ cursor++;
5115
+ continue;
5116
+ }
5117
+ let end = cursor + 1;
5118
+ while (pattern[end] !== void 0 && pattern[end] >= "0" && pattern[end] <= "9") end++;
5119
+ if (pattern[end] === "d") {
5120
+ const width = Number(pattern.slice(cursor + 1, end));
5121
+ path += String(index).padStart(width, pattern[cursor + 1] === "0" ? "0" : " ");
5122
+ hasScene = true;
5123
+ cursor = end;
5124
+ continue;
5125
+ }
5126
+ }
5127
+ path += pattern[cursor];
4963
5128
  }
4964
- const dot = pattern.lastIndexOf(".");
5129
+ if (hasScene) return path;
5130
+ const dot = path.lastIndexOf(".");
4965
5131
  if (dot > 0) {
4966
- return `${pattern.slice(0, dot)}-${index}${pattern.slice(dot)}`;
5132
+ return `${path.slice(0, dot)}-${index}${path.slice(dot)}`;
4967
5133
  }
4968
- return `${pattern}-${index}`;
5134
+ return `${path}-${index}`;
4969
5135
  }
4970
5136
  function parseChannelMask(spec) {
4971
5137
  const s = spec.toLowerCase().trim();
@@ -6939,7 +7105,7 @@ async function decodeStoredMagickSvg(svg, density, backend, signal) {
6939
7105
  const encoder = new TextEncoder(), base = backend.storage.allocate(0);
6940
7106
  let size = 0;
6941
7107
  for (let offset = 0; offset < svg.length; ) {
6942
- await yieldTurn11(signal);
7108
+ await yieldTurn12(signal);
6943
7109
  const bytes = new Uint8Array(4096), { read, written } = encoder.encodeInto(svg.slice(offset, offset + 4097), bytes);
6944
7110
  await backend.storage.write(backend.storage.allocate(written), bytes.subarray(0, written));
6945
7111
  offset += read;
@@ -7306,12 +7472,32 @@ function* appendStackImagesSteps(stack, vertical, state) {
7306
7472
  }
7307
7473
  return { ...canvas, hasAlpha };
7308
7474
  }
7475
+ function expandFilenameProperties(spec, state) {
7476
+ let output = "";
7477
+ for (let i = 0; i < spec.length; ) {
7478
+ if (spec.startsWith("%%", i)) {
7479
+ output += "%%";
7480
+ i += 2;
7481
+ continue;
7482
+ }
7483
+ if (spec.startsWith("%[filename:", i)) {
7484
+ const end = spec.indexOf("]", i + 11);
7485
+ if (end >= 0) {
7486
+ const key = spec.slice(i + 2, end).toLowerCase();
7487
+ output += state.filenameProperties?.[key] ?? spec.slice(i, end + 1);
7488
+ i = end + 1;
7489
+ continue;
7490
+ }
7491
+ }
7492
+ output += spec[i++];
7493
+ }
7494
+ return output;
7495
+ }
7309
7496
  function inferOutputFormat(spec, fallback = "png") {
7310
7497
  const prefixMatch = /^([a-zA-Z0-9]+):(.*)$/.exec(spec);
7311
7498
  if (prefixMatch) {
7312
7499
  const prefix = prefixMatch[1].toLowerCase();
7313
7500
  const rest = prefixMatch[2];
7314
- if (prefix === "rgba") throw new Error("unsupported output coder 'rgba'");
7315
7501
  const fmt = extToImageFormat(prefix);
7316
7502
  if (fmt) {
7317
7503
  return { format: fmt, path: rest };
@@ -7326,7 +7512,11 @@ function inferOutputFormat(spec, fallback = "png") {
7326
7512
  return { format: fallback, path: spec };
7327
7513
  }
7328
7514
  function extToImageFormat(ext) {
7515
+ const rawCoder = ext.toLowerCase();
7516
+ if (isRawCoder(rawCoder)) return rawCoder;
7329
7517
  switch (ext.toLowerCase()) {
7518
+ case "miff":
7519
+ return "miff";
7330
7520
  case "png":
7331
7521
  return "png";
7332
7522
  case "jpg":
@@ -7341,6 +7531,8 @@ function extToImageFormat(ext) {
7341
7531
  case "tif":
7342
7532
  case "tiff":
7343
7533
  return "tiff";
7534
+ case "pam":
7535
+ return "pam";
7344
7536
  case "ppm":
7345
7537
  return "ppm";
7346
7538
  case "pgm":
@@ -7449,6 +7641,15 @@ function* parseInputOperandsSteps(token, files, state, stdinBytes, maxDecodeDime
7449
7641
  const { baseToken, pageSpec, inlineGeom } = parseInputToken(token);
7450
7642
  function* applyMod(imgs) {
7451
7643
  return yield* mapSteps(imgs, function* (im) {
7644
+ if (state.orientation !== void 0) im = { ...im, orientation: state.orientation };
7645
+ if (state.units !== void 0) im = applyMagickUnits(im, state.units);
7646
+ for (const key of ["comment", "label"]) {
7647
+ const value = state[key];
7648
+ if (value !== void 0) {
7649
+ const expanded = yield* formatMagickPropertyStringSteps(value, im, () => im, { filePath: baseToken });
7650
+ im = { ...im, properties: { ...im.properties, [key]: expanded } };
7651
+ }
7652
+ }
7452
7653
  if (state.delay !== void 0) im = { ...im, delay: Array(im.delay?.length ?? 1).fill(state.delay) };
7453
7654
  return inlineGeom ? yield* applyInlineReadModifierSteps(im, inlineGeom, state.kernel) : im;
7454
7655
  });
@@ -7495,14 +7696,28 @@ function* parseInputOperandsSteps(token, files, state, stdinBytes, maxDecodeDime
7495
7696
  if (lower === "null:") {
7496
7697
  return yield* applyMod([yield* createSolidRgbaImageSteps(1, 1, { r: 0, g: 0, b: 0, a: 0 })]);
7497
7698
  }
7498
- let cleanToken = baseToken;
7699
+ const rgb = lower.startsWith("rgb:");
7700
+ let cleanToken = rgb ? baseToken.slice(4) : baseToken;
7499
7701
  const prefixMatch = /^([a-zA-Z0-9]+):(.*)$/.exec(cleanToken);
7500
7702
  if (prefixMatch && extToImageFormat(prefixMatch[1])) {
7501
7703
  cleanToken = prefixMatch[2];
7502
7704
  }
7503
- const rawBytes = cleanToken === "-" ? stdinBytes : files.get(cleanToken) ?? files.get(token);
7705
+ let rawBytes = cleanToken === "-" ? stdinBytes : files.get(cleanToken) ?? files.get(token);
7504
7706
  if (!rawBytes)
7505
7707
  return void 0;
7708
+ if (rgb) {
7709
+ if (!state.hasSize) throw new Error("must specify image size for RGB input");
7710
+ const raw = rawRgbInput(state.sizeWidth, state.sizeHeight, state.inputDepth ?? 16, state.inputEndian ?? "lsb", rawBytes.length);
7711
+ const bytes = new Uint8Array(raw.size);
7712
+ for (let position = 0; position < raw.size; position += 8192) {
7713
+ yield;
7714
+ const steps = raw.read(position, Math.min(8192, raw.size - position));
7715
+ let next = steps.next();
7716
+ while (!next.done) next = steps.next(rawBytes.subarray(next.value.position, next.value.position + next.value.length));
7717
+ bytes.set(next.value, position);
7718
+ }
7719
+ rawBytes = bytes;
7720
+ }
7506
7721
  let totalPages = 1;
7507
7722
  let inputMeta;
7508
7723
  try {
@@ -7653,7 +7868,7 @@ async function drainIdentifySteps(steps, signal) {
7653
7868
  while (!next.done) {
7654
7869
  try {
7655
7870
  if (next.value) await next.value.run();
7656
- else await yieldTurn11(signal);
7871
+ else await yieldTurn12(signal);
7657
7872
  next = steps.next();
7658
7873
  } catch (error) {
7659
7874
  next = steps.throw(error);
@@ -8514,7 +8729,7 @@ async function transformStoredMagickPixels(image, backend, operation, signal, hi
8514
8729
  let storedData16;
8515
8730
  let storedHueSamples;
8516
8731
  for (let offset = 0; offset < size; offset += 16384) {
8517
- if (offset % 1048576 === 0) await yieldTurn11(signal);
8732
+ if (offset % 1048576 === 0) await yieldTurn12(signal);
8518
8733
  const data = await backend.storage.read(image.position + offset, Math.min(16384, size - offset));
8519
8734
  const bytes16 = (highDepth || operation.highDepth) && image.storedData16 ? await backend.storage.read(image.storedData16.position + offset * 2, data.length * 2) : void 0;
8520
8735
  const data16 = bytes16 ? new Uint16Array(bytes16.buffer, bytes16.byteOffset, bytes16.byteLength / 2) : void 0;
@@ -8536,9 +8751,118 @@ async function transformStoredMagickPixels(image, backend, operation, signal, hi
8536
8751
  }
8537
8752
  return result;
8538
8753
  }
8539
- async function prepareStoredHueOutput(image, format, backend, signal) {
8540
- if (image.magickColorspace !== "HSL" && image.magickColorspace !== "HSB" && image.magickColorspace !== "HWB" && image.magickColorspace !== "XYZ" && image.magickColorspace !== "YCbCr" && image.magickColorspace !== "YUV") return image;
8541
- return transformStoredMagickPixels(image, backend, { end: 0, highDepth: true, apply: (frame) => prepareHueOutput(frame, format) }, signal);
8754
+ function* prepareMiffPages(stack) {
8755
+ const miffPages = [];
8756
+ for (const frame of stack) {
8757
+ yield;
8758
+ miffPages.push(yield* prepareMagickOutput(frame, "miff"));
8759
+ }
8760
+ return { ...miffPages[0], miffPages };
8761
+ }
8762
+ function prepareStoredMiffPages(stack, image, backend, signal) {
8763
+ return { ...image, storedMiffPages: { length: stack.length, async at(index) {
8764
+ return prepareStoredMagickOutput(await stack.get(index), "miff", backend, signal);
8765
+ } } };
8766
+ }
8767
+ function* prepareTiffPages(stack) {
8768
+ const tiffPages = [];
8769
+ for (const frame of stack) {
8770
+ yield;
8771
+ tiffPages.push(yield* prepareMagickOutput(frame, "tiff"));
8772
+ }
8773
+ return { ...tiffPages[0], tiffPages };
8774
+ }
8775
+ function prepareStoredTiffPages(stack, image, backend, signal) {
8776
+ return { ...image, storedTiffPages: { length: stack.length, async at(index) {
8777
+ return prepareStoredMagickOutput(await stack.get(index), "tiff", backend, signal);
8778
+ } } };
8779
+ }
8780
+ function* prepareGifFrames(stack) {
8781
+ let cooperativeWork = 0;
8782
+ let fw = 0, fh = 0;
8783
+ for (const frame of stack) {
8784
+ fw = Math.max(fw, frame.width);
8785
+ fh = Math.max(fh, frame.height);
8786
+ }
8787
+ const combined = new Uint8Array(fw * fh * stack.length * 4);
8788
+ const delays = [];
8789
+ const disposals = [];
8790
+ for (let idx = 0; idx < stack.length; idx++) {
8791
+ if (++cooperativeWork % 64 === 0) yield;
8792
+ const outputFrame = yield* prepareMagickOutput(stack[idx], "gif");
8793
+ disposals.push(typeof outputFrame.disposal === "number" ? outputFrame.disposal : 1);
8794
+ delays.push(outputFrame.delay?.[stack[idx][magickInput]?.sceneIdx ?? 0] ?? 100);
8795
+ for (let y = 0; y < outputFrame.height; y++) {
8796
+ if (++cooperativeWork % 64 === 0) yield;
8797
+ combined.set(outputFrame.data.subarray(y * outputFrame.width * 4, (y + 1) * outputFrame.width * 4), (idx * fw * fh + y * fw) * 4);
8798
+ }
8799
+ }
8800
+ return {
8801
+ ...yield* prepareMagickOutput(stack[0], "gif"),
8802
+ width: fw,
8803
+ height: fh * stack.length,
8804
+ pages: stack.length,
8805
+ pageHeight: fh,
8806
+ delay: delays,
8807
+ disposal: disposals,
8808
+ gifFrames: stack.map((frame) => ({ width: frame.width, height: frame.height, x: frame.virtualCanvas?.x ?? 0, y: frame.virtualCanvas?.y ?? 0 })),
8809
+ data: combined
8810
+ };
8811
+ }
8812
+ async function prepareStoredGifFrames(stack, backend, signal) {
8813
+ const first = await prepareStoredMagickOutput(await stack.get(0), "gif", backend, signal);
8814
+ let width = first.width, height = first.height;
8815
+ for (let index = 1; index < stack.length; index++) {
8816
+ const frame = await stack.get(index);
8817
+ width = Math.max(width, frame.width);
8818
+ height = Math.max(height, frame.height);
8819
+ }
8820
+ const size = width * height * 4, position = backend.storage.allocate(size * stack.length);
8821
+ for (let index = 0; index < stack.length; index++) {
8822
+ const frame = await prepareStoredMagickOutput(await stack.get(index), "gif", backend, signal);
8823
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x += 4096) {
8824
+ await yieldTurn12(signal);
8825
+ const count = Math.min(4096, width - x), bytes = new Uint8Array(count * 4);
8826
+ if (y < frame.height && x < frame.width) bytes.set(await backend.storage.read(frame.position + (y * frame.width + x) * 4, Math.min(count, frame.width - x) * 4));
8827
+ await backend.storage.write(position + index * size + (y * width + x) * 4, bytes);
8828
+ }
8829
+ }
8830
+ const { storedData16: ignored, ...metadata } = first;
8831
+ return {
8832
+ ...metadata,
8833
+ position,
8834
+ width,
8835
+ height: height * stack.length,
8836
+ pages: stack.length,
8837
+ pageHeight: height,
8838
+ storedGifFrame: { async at(index) {
8839
+ const frame = await stack.get(index);
8840
+ return { width: frame.width, height: frame.height, x: frame.virtualCanvas?.x ?? 0, y: frame.virtualCanvas?.y ?? 0 };
8841
+ } },
8842
+ storedDisposal: { length: stack.length, async at(index) {
8843
+ const frame = await stack.get(index);
8844
+ return typeof frame.disposal === "number" ? frame.disposal : 1;
8845
+ } },
8846
+ storedDelay: { length: stack.length, async at(index) {
8847
+ const frame = await stack.get(index);
8848
+ const scene = frame[magickInput]?.sceneIdx ?? 0;
8849
+ return await frame.storedDelay?.at(scene, { signal }) ?? frame.delay?.[scene] ?? 100;
8850
+ } }
8851
+ };
8852
+ }
8853
+ function* prepareMagickOutput(image, format) {
8854
+ image = yield* prepareHueOutput(image, format);
8855
+ if (format !== "pbm") return image;
8856
+ const source = image.data16 ? { ...image, data: new Uint8Array(image.data16.buffer, image.data16.byteOffset, image.data16.byteLength) } : image;
8857
+ return yield* applyMagickRasterSteps({ ...image, space: "b-w", channels: 1, hasAlpha: false }, bilevelPixelSteps(image.width * image.height, !!image.data16), void 0, { ...image, hasAlpha: false }, [source]);
8858
+ }
8859
+ async function prepareStoredMagickOutput(image, format, backend, signal) {
8860
+ if (image.magickColorspace === "HSL" || image.magickColorspace === "HSB" || image.magickColorspace === "HWB" || image.magickColorspace === "XYZ" || image.magickColorspace === "YCbCr" || image.magickColorspace === "YUV") {
8861
+ image = await transformStoredMagickPixels(image, backend, { end: 0, highDepth: true, apply: (frame) => prepareHueOutput(frame, format) }, signal);
8862
+ }
8863
+ if (format !== "pbm") return image;
8864
+ const source = image.storedData16 ? { ...image, position: image.storedData16.position } : image;
8865
+ return transformStoredMagickRaster({ ...image, space: "b-w", channels: 1, hasAlpha: false }, backend, bilevelPixelSteps(image.width * image.height, !!image.storedData16), signal, { ...image, hasAlpha: false }, [source]);
8542
8866
  }
8543
8867
  function parseMagickDisposal(value) {
8544
8868
  const names = ["undefined", "none", "background", "previous"];
@@ -8547,6 +8871,49 @@ function parseMagickDisposal(value) {
8547
8871
  if (["0", "1", "2", "3"].includes(value)) return Number(value);
8548
8872
  return 0;
8549
8873
  }
8874
+ function parseMagickOrientation(value) {
8875
+ const orientation = ["undefined", "topleft", "topright", "bottomright", "bottomleft", "lefttop", "righttop", "rightbottom", "leftbottom"].indexOf(value.toLowerCase());
8876
+ if (orientation < 0) throw new Error(`unrecognized image orientation '-orient' '${value}'`);
8877
+ return orientation;
8878
+ }
8879
+ function parseMagickUnits(value) {
8880
+ switch (value.toLowerCase()) {
8881
+ case "pixelsperinch":
8882
+ return "inch";
8883
+ case "pixelspercentimeter":
8884
+ return "cm";
8885
+ case "undefined":
8886
+ return "unknown";
8887
+ default:
8888
+ throw new Error(`unrecognized units type '-units' '${value}'`);
8889
+ }
8890
+ }
8891
+ function applyMagickUnits(image, unit) {
8892
+ const scale = image.resolution?.unit === "meter" ? 100 : 1;
8893
+ const x = image.resolution ? image.resolution.x / scale : image.density;
8894
+ const y = image.resolution ? image.resolution.y / scale : image.density;
8895
+ return { ...image, density: x * (unit === "cm" ? 2.54 : 1), resolution: { x, y, unit } };
8896
+ }
8897
+ function magickResampleGeometry(image, value) {
8898
+ const geometry = parseMagickGeometry(value);
8899
+ const x = geometry.width ?? 72, y = geometry.height ?? x;
8900
+ if (!(x > 0) || !(y > 0)) throw new Error(`invalid argument '-resample' '${value}'`);
8901
+ const scale = image.resolution?.unit === "meter" ? 100 : 1;
8902
+ const sourceX = (image.resolution ? image.resolution.x / scale : image.density) || 72;
8903
+ const sourceY = (image.resolution ? image.resolution.y / scale : image.density) || 72;
8904
+ return `${Math.max(1, Math.round(image.width * x / sourceX))}x${Math.max(1, Math.round(image.height * y / sourceY))}!`;
8905
+ }
8906
+ function applyMagickDensity(image, value) {
8907
+ const geometry = parseMagickGeometry(value);
8908
+ const x = geometry.width ?? 72, y = geometry.height ?? x;
8909
+ const unit = image.resolution?.unit ?? "unknown";
8910
+ const scale = unit === "meter" ? 100 : 1;
8911
+ return {
8912
+ ...image,
8913
+ density: x * (unit === "cm" || unit === "meter" ? 2.54 : 1),
8914
+ resolution: { x: x * scale, y: y * scale, unit }
8915
+ };
8916
+ }
8550
8917
  function applyMagickReadSetting(tokens, state, start) {
8551
8918
  let i = start;
8552
8919
  const t = tokens[i];
@@ -8563,6 +8930,23 @@ function applyMagickReadSetting(tokens, state, start) {
8563
8930
  } else if (t === "+channel") {
8564
8931
  state.channels = { r: true, g: true, b: true, a: false };
8565
8932
  state.channelExplicit = false;
8933
+ } else if (t === "-depth") {
8934
+ state.inputDepth = Number(tokens[++i]);
8935
+ if (!Number.isInteger(state.inputDepth) || state.inputDepth < 1) throw new Error("invalid input depth");
8936
+ } else if (t === "-compress") {
8937
+ const compression = (tokens[++i] ?? "").toLowerCase();
8938
+ if (compression !== "none" && compression !== "zip") throw new Error(`unsupported compression '${compression}'`);
8939
+ state.netpbmAscii = compression === "none";
8940
+ } else if (t === "+compress") {
8941
+ delete state.netpbmAscii;
8942
+ } else if (t === "+depth") {
8943
+ delete state.inputDepth;
8944
+ } else if (t === "-endian") {
8945
+ const endian = (tokens[++i] ?? "").toLowerCase();
8946
+ if (!["msb", "lsb", "undefined"].includes(endian)) throw new Error(`unrecognized endian type '${endian}'`);
8947
+ state.inputEndian = endian;
8948
+ } else if (t === "+endian") {
8949
+ delete state.inputEndian;
8566
8950
  } else if (t === "-size") {
8567
8951
  const g = parseMagickGeometry(tokens[++i] ?? "1x1");
8568
8952
  state.sizeWidth = Math.max(1, Math.round(g.width ?? 1));
@@ -8595,6 +8979,8 @@ function applyMagickReadSetting(tokens, state, start) {
8595
8979
  state.gravity = parseGravity(tokens[++i] ?? "center");
8596
8980
  } else if (t === "+gravity") {
8597
8981
  state.gravity = "northwest";
8982
+ } else if (t === "-comment" || t === "-label") {
8983
+ state[t === "-comment" ? "comment" : "label"] = tokens[++i] ?? "";
8598
8984
  } else if (t === "-sampling-factor") {
8599
8985
  state.samplingFactor = tokens[++i] ?? "";
8600
8986
  } else if (t === "+sampling-factor") {
@@ -8616,6 +9002,9 @@ function applyMagickReadSetting(tokens, state, start) {
8616
9002
  state.compose = parseCompose(cRaw);
8617
9003
  } else if (t === "-define") {
8618
9004
  const defStr = tokens[++i] ?? "";
9005
+ if (defStr.toLowerCase().startsWith("png:exclude-chunk=")) {
9006
+ state.omitPngResolution = defStr.slice("png:exclude-chunk=".length).split(",").some((chunk) => ["phys", "all"].includes(chunk.trim().toLowerCase()));
9007
+ }
8619
9008
  if (defStr.toLowerCase().startsWith("connected-components:")) {
8620
9009
  const [key, value, extra] = defStr.toLowerCase().split("=");
8621
9010
  if (extra !== void 0 || key !== "connected-components:verbose" || value !== "true" && value !== "false") throw new Error(`Unsupported connected-components define: ${defStr}`);
@@ -8629,12 +9018,13 @@ function applyMagickReadSetting(tokens, state, start) {
8629
9018
  const k = (tokens[++i] ?? "").toLowerCase();
8630
9019
  const v = tokens[++i] ?? "";
8631
9020
  if (k.startsWith("option:connected-components:") || k.startsWith("connected-components:")) throw new Error(`Unsupported connected-components setting: ${k}`);
9021
+ if (k.startsWith("filename:")) state.filenameProperties = { ...state.filenameProperties, [k]: v };
8632
9022
  if (k === "option:compose:args")
8633
9023
  state.composeArgs = v;
8634
9024
  } else return void 0;
8635
9025
  return i;
8636
9026
  }
8637
- function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBytes, signal, analysis = { text: "", warnings: "" }) {
9027
+ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBytes, signal, analysis = { text: "", warnings: "", bytes: [] }, registers = /* @__PURE__ */ new Map()) {
8638
9028
  let cooperativeWork = 63;
8639
9029
  let stack = [];
8640
9030
  let operandsOnly = false;
@@ -8647,9 +9037,20 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8647
9037
  i++;
8648
9038
  continue;
8649
9039
  }
9040
+ if (t.slice(0, 4).toLowerCase() === "mpr:") {
9041
+ const saved = registers.get(t.slice(4));
9042
+ if (!saved) throw new Error(`unable to open image '${t}'`);
9043
+ for (const image of saved) {
9044
+ yield;
9045
+ stack.push({ ...image, data: new Uint8Array(image.data) });
9046
+ }
9047
+ i++;
9048
+ continue;
9049
+ }
8650
9050
  if (operandsOnly) {
8651
9051
  const loaded = yield* parseInputOperandsSteps(t, files, state, stdinBytes);
8652
- if (loaded) stack.push(...loaded);
9052
+ if (!loaded) throw new Error(`unable to open image '${t}'`);
9053
+ stack.push(...loaded);
8653
9054
  i++;
8654
9055
  continue;
8655
9056
  }
@@ -8668,11 +9069,39 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8668
9069
  const endIdx = depth === 0 ? j - 1 : j;
8669
9070
  const subTokens = tokens.slice(i + 1, endIdx);
8670
9071
  const subState = { ...state };
8671
- const subResult = yield* evaluatePipelineTokensSteps(subTokens, files, subState, stack, stdinBytes, signal, analysis);
9072
+ const subResult = yield* evaluatePipelineTokensSteps(subTokens, files, subState, stack, stdinBytes, signal, analysis, registers);
8672
9073
  stack.push(...subResult);
8673
9074
  i = j;
8674
9075
  continue;
8675
9076
  }
9077
+ if (t === "-orient") {
9078
+ const orientation = state.orientation = parseMagickOrientation(tokens[++i] ?? "");
9079
+ stack = yield* mapSteps(stack, function* (image) {
9080
+ yield;
9081
+ return { ...image, orientation };
9082
+ });
9083
+ i++;
9084
+ continue;
9085
+ }
9086
+ if (t === "-units") {
9087
+ const units = state.units = parseMagickUnits(tokens[++i] ?? "");
9088
+ stack = yield* mapSteps(stack, function* (image) {
9089
+ yield;
9090
+ return applyMagickUnits(image, units);
9091
+ });
9092
+ i++;
9093
+ continue;
9094
+ }
9095
+ if (t === "-density") {
9096
+ const value = tokens[++i] ?? "72";
9097
+ state.density = Math.max(1, Math.round(parseMagickGeometry(value).width ?? 72));
9098
+ stack = yield* mapSteps(stack, function* (image) {
9099
+ yield;
9100
+ return applyMagickDensity(image, value);
9101
+ });
9102
+ i++;
9103
+ continue;
9104
+ }
8676
9105
  if (t === "-set" && tokens[i + 1]?.toLowerCase() === "delay") {
8677
9106
  const delay = Number(tokens[i + 2] ?? 0) * 10;
8678
9107
  stack = yield* mapSteps(stack, function* (image) {
@@ -8691,6 +9120,26 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8691
9120
  i += 3;
8692
9121
  continue;
8693
9122
  }
9123
+ if (t === "-set" && !tokens[i + 1]?.toLowerCase().startsWith("option:") && !tokens[i + 1]?.toLowerCase().startsWith("filename:") && !tokens[i + 1]?.toLowerCase().startsWith("connected-components:")) {
9124
+ const key = tokens[++i] ?? "", value = tokens[++i] ?? "";
9125
+ stack = yield* mapSteps(stack, function* (image) {
9126
+ const expanded = yield* formatMagickPropertyStringSteps(value, image, () => image, image[magickInput]);
9127
+ return key.toLowerCase() === "density" ? applyMagickDensity(image, expanded) : { ...image, properties: { ...image.properties, [key]: expanded } };
9128
+ });
9129
+ i++;
9130
+ continue;
9131
+ }
9132
+ if (t === "+set") {
9133
+ const key = tokens[++i] ?? "";
9134
+ stack = yield* mapSteps(stack, function* (image) {
9135
+ yield;
9136
+ const properties = { ...image.properties };
9137
+ delete properties[key];
9138
+ return { ...image, properties };
9139
+ });
9140
+ i++;
9141
+ continue;
9142
+ }
8694
9143
  const setting = applyMagickReadSetting(tokens, state, i);
8695
9144
  const pixelOperation = setting === void 0 ? parseMagickPixelOperation(tokens, state, i, signal) : void 0;
8696
9145
  const warpOperation = setting === void 0 ? parseMagickWarpOperation(tokens, state, i) : void 0;
@@ -8732,35 +9181,59 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8732
9181
  return { ...output, ..."space" in plan && plan.space ? { space: plan.space, channels: plan.space === "srgb" ? 4 : output.channels } : {}, ...plan.virtualCanvas ? { virtualCanvas: plan.virtualCanvas } : {} };
8733
9182
  });
8734
9183
  } else if (t === "-write" || t === "+write") {
8735
- const writePath = tokens[++i] ?? "";
8736
- if (stack.length > 0 && writePath && writePath.toLowerCase() !== "null:") {
9184
+ if (i + 1 >= tokens.length) throw new Error(`missing argument '${t}'`);
9185
+ if (stack.length === 0) throw new Error(`no images for write '${t}'`);
9186
+ const writePath = expandFilenameProperties(tokens[++i] ?? "", state);
9187
+ if (writePath.slice(0, 4).toLowerCase() === "mpr:") {
9188
+ const saved = [];
9189
+ for (const image of stack) {
9190
+ yield;
9191
+ saved.push({ ...image, data: new Uint8Array(image.data) });
9192
+ }
9193
+ registers.set(writePath.slice(4), saved);
9194
+ } else if (stack.length > 0 && writePath && writePath.toLowerCase() !== "null:") {
8737
9195
  const top = stack[stack.length - 1];
8738
9196
  const { format, path } = inferOutputFormat(writePath, top.format);
8739
- if (stack.length > 1 && (/%0?\d*d/.test(path) || !state.adjoin)) {
9197
+ if (hasSceneOutputPattern(path) || stack.length > 1 && (!state.adjoin || path !== "-" && ["png", "jpeg", "bmp"].includes(format))) {
8740
9198
  for (let idx = 0; idx < stack.length; idx++) {
8741
9199
  if (++cooperativeWork % 65536 === 0)
8742
9200
  yield;
8743
- const framePath = formatSceneOutputPath(path, idx);
8744
- const { data: frameBytes } = encodeImage(yield* prepareHueOutput(stack[idx], format), { format, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
9201
+ const framePath = formatSceneOutputPath(path, state.scene + idx);
9202
+ const { data: frameBytes } = encodeImage(yield* prepareMagickOutput(stack[idx], format), { format, netpbmAscii: state.netpbmAscii ?? false, ...state.inputDepth === void 0 ? {} : { netpbmDepth: Math.min(16, state.inputDepth) }, endian: state.inputEndian, omitPngResolution: state.strip || state.omitPngResolution === true, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
8745
9203
  files.set(framePath, frameBytes);
8746
9204
  }
8747
9205
  } else {
8748
- const { data: encoded } = encodeImage(yield* prepareHueOutput(top, format), { format, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
8749
- files.set(path, encoded);
9206
+ const image = stack.length > 1 && format === "miff" ? yield* prepareMiffPages(stack) : stack.length > 1 && format === "tiff" ? yield* prepareTiffPages(stack) : stack.length > 1 && format === "gif" ? yield* prepareGifFrames(stack) : yield* prepareMagickOutput(top, format);
9207
+ const { data: encoded } = encodeImage(image, { format, ...stack.length > 1 && format === "gif" ? { pageHeight: image.pageHeight } : {}, ...state.loop === void 0 ? {} : { loop: state.loop }, ...state.optimizeLayers ? { optimizeFrames: true } : {}, netpbmAscii: state.netpbmAscii ?? false, ...state.inputDepth === void 0 ? {} : { netpbmDepth: Math.min(16, state.inputDepth) }, endian: state.inputEndian, omitPngResolution: state.strip || state.omitPngResolution === true, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
9208
+ if (path === "-") analysis.bytes.push(encoded);
9209
+ else files.set(path, encoded);
8750
9210
  }
8751
9211
  }
8752
9212
  } else if (t === "-geometry") {
8753
9213
  state.geometry = tokens[++i] ?? "+0+0";
8754
9214
  } else if (t === "-tile") {
8755
9215
  state.tile = tokens[++i];
9216
+ } else if (t === "-profile" || t === "+profile") {
9217
+ const name = tokens[++i];
9218
+ if (!name) throw new Error(`missing argument '${t}'`);
9219
+ const profile = t === "-profile" ? files.get(name) : void 0;
9220
+ if (t === "-profile" && !profile) throw new Error(`unable to open profile '${name}'`);
9221
+ stack = stack.map((image) => applyMagickProfile(image, name, profile));
8756
9222
  } else if (t === "-strip") {
8757
9223
  state.strip = true;
9224
+ for (let index = 0; index < stack.length; index++) {
9225
+ const { icc: ignored, properties: ignoredProperties, ...image } = stack[index];
9226
+ stack[index] = image;
9227
+ }
8758
9228
  } else if (t === "-format") {
8759
9229
  state.formatStr = tokens[++i] ?? "";
8760
9230
  } else if (t === "+adjoin") {
8761
9231
  state.adjoin = false;
8762
9232
  } else if (t === "-adjoin") {
8763
9233
  state.adjoin = true;
9234
+ } else if (t === "-scene") {
9235
+ state.scene = Number(tokens[++i]);
9236
+ if (!Number.isSafeInteger(state.scene) || state.scene < 0) throw new Error("invalid scene number");
8764
9237
  } else if (t === "-delay") {
8765
9238
  state.delay = Number(tokens[++i] ?? 0) * 10;
8766
9239
  } else if (t === "-loop") {
@@ -8929,6 +9402,12 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8929
9402
  const { virtualCanvas: ignoredCanvas, ...reset } = image;
8930
9403
  return reset;
8931
9404
  });
9405
+ } else if (t === "-resample") {
9406
+ const value = tokens[++i] ?? "72";
9407
+ stack = yield* mapSteps(stack, function* (image) {
9408
+ const resized = yield* applyMagickResizeSteps(image, magickResampleGeometry(image, value), state.kernel);
9409
+ return applyMagickDensity(resized, value);
9410
+ });
8932
9411
  } else if (t === "-resize" || t === "-scale" || t === "-sample" || t === "-thumbnail") {
8933
9412
  const geom = tokens[++i] ?? "100%";
8934
9413
  const k = t === "-sample" ? "nearest" : state.kernel;
@@ -9307,25 +9786,36 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
9307
9786
  if (mergeOntoCanvas) {
9308
9787
  state.background = prevBg;
9309
9788
  }
9310
- if (loaded) {
9311
- if (mergeOntoCanvas && loaded[0]) {
9312
- yield* blitOverRgbaInPlaceSteps(stack[0], loaded[0], 0, 0);
9313
- detachRgbaBuffer(loaded[0].data);
9314
- } else {
9315
- stack.push(...loaded);
9316
- }
9789
+ if (!loaded) throw new Error(`unable to open image '${t}'`);
9790
+ if (mergeOntoCanvas && loaded[0]) {
9791
+ yield* blitOverRgbaInPlaceSteps(stack[0], loaded[0], 0, 0);
9792
+ detachRgbaBuffer(loaded[0].data);
9793
+ } else {
9794
+ stack.push(...loaded);
9317
9795
  }
9318
9796
  }
9319
9797
  i++;
9320
9798
  }
9321
9799
  return stack;
9322
9800
  }
9801
+ function combineConvertOutput(result, analysis) {
9802
+ const stdout = analysis.text + result.stdout, stderr = analysis.warnings + result.stderr;
9803
+ if (!analysis.bytes.length) return { ...result, stdout, stderr };
9804
+ const chunks = [...analysis.bytes, ...result.stdoutBytes ? [result.stdoutBytes] : stdout ? [new TextEncoder().encode(stdout)] : []];
9805
+ const stdoutBytes = new Uint8Array(chunks.reduce((size, chunk) => size + chunk.length, 0));
9806
+ let offset = 0;
9807
+ for (const chunk of chunks) {
9808
+ stdoutBytes.set(chunk, offset);
9809
+ offset += chunk.length;
9810
+ }
9811
+ return { ...result, stdout: "", stderr, stdoutBytes };
9812
+ }
9323
9813
  function* runConvertCliSteps(argv, files, stdinBytes, signal) {
9324
- const analysis = { text: "", warnings: "" };
9814
+ const analysis = { text: "", warnings: "", bytes: [] };
9325
9815
  const result = yield* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis);
9326
- return { ...result, stdout: analysis.text + result.stdout, stderr: analysis.warnings + result.stderr };
9816
+ return combineConvertOutput(result, analysis);
9327
9817
  }
9328
- function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = { text: "", warnings: "" }) {
9818
+ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = { text: "", warnings: "", bytes: [] }) {
9329
9819
  const listRes = tryHandleMagickListOption(argv);
9330
9820
  if (listRes) return listRes;
9331
9821
  let cooperativeWork = 63;
@@ -9344,7 +9834,7 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9344
9834
  stderr: ""
9345
9835
  };
9346
9836
  }
9347
- const outSpec = argv[argv.length - 1];
9837
+ let outSpec = argv[argv.length - 1];
9348
9838
  const pipelineTokens = argv.slice(0, -1);
9349
9839
  const state = createDefaultState();
9350
9840
  try {
@@ -9357,9 +9847,10 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9357
9847
  `
9358
9848
  };
9359
9849
  }
9850
+ outSpec = expandFilenameProperties(outSpec, state);
9360
9851
  const finalImg = stack[stack.length - 1];
9361
9852
  const outLower = outSpec.toLowerCase();
9362
- if (outLower === "null:") return { exitCode: 0, stdout: "", stderr: "" };
9853
+ if (outLower === "null:" || outLower.startsWith("mpr:")) return { exitCode: 0, stdout: "", stderr: "" };
9363
9854
  if (outLower === "info:" || outLower === "info:-") {
9364
9855
  const formatted = [];
9365
9856
  for (const [idx, im] of stack.entries()) {
@@ -9392,50 +9883,21 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9392
9883
  return { exitCode: 0, stdout: hist, stderr: "" };
9393
9884
  }
9394
9885
  const { format, path: outPath } = inferOutputFormat(outSpec, "png");
9395
- if (stack.length > 1 && (/%0?\d*d/.test(outPath) || !state.adjoin)) {
9886
+ if (hasSceneOutputPattern(outPath) || stack.length > 1 && (!state.adjoin || outPath !== "-" && ["png", "jpeg", "bmp"].includes(format))) {
9396
9887
  for (let idx = 0; idx < stack.length; idx++) {
9397
9888
  if (++cooperativeWork % 64 === 0)
9398
9889
  yield;
9399
- const framePath = formatSceneOutputPath(outPath, idx);
9400
- const { data: frameBytes } = encodeImage(yield* prepareHueOutput(stack[idx], format), { format, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
9890
+ const framePath = formatSceneOutputPath(outPath, state.scene + idx);
9891
+ const { data: frameBytes } = encodeImage(yield* prepareMagickOutput(stack[idx], format), { format, netpbmAscii: state.netpbmAscii ?? false, ...state.inputDepth === void 0 ? {} : { netpbmDepth: Math.min(16, state.inputDepth) }, endian: state.inputEndian, omitPngResolution: state.strip || state.omitPngResolution === true, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
9401
9892
  files.set(framePath, frameBytes);
9402
9893
  }
9403
9894
  return { exitCode: 0, stdout: "", stderr: "" };
9404
9895
  }
9405
- let toEncode = yield* prepareHueOutput(finalImg, format);
9896
+ let toEncode = stack.length > 1 && format === "miff" ? yield* prepareMiffPages(stack) : stack.length > 1 && format === "tiff" ? yield* prepareTiffPages(stack) : yield* prepareMagickOutput(finalImg, format);
9406
9897
  let encodePageHeight;
9407
9898
  if (stack.length > 1 && format === "gif") {
9408
- let fw = 0, fh = 0;
9409
- for (const frame of stack) {
9410
- fw = Math.max(fw, frame.width);
9411
- fh = Math.max(fh, frame.height);
9412
- }
9413
- const combined = new Uint8Array(fw * fh * stack.length * 4);
9414
- const delays = [];
9415
- const disposals = [];
9416
- for (let idx = 0; idx < stack.length; idx++) {
9417
- if (++cooperativeWork % 64 === 0)
9418
- yield;
9419
- const outputFrame = yield* prepareHueOutput(stack[idx], format);
9420
- disposals.push(typeof outputFrame.disposal === "number" ? outputFrame.disposal : 1);
9421
- delays.push(outputFrame.delay?.[stack[idx][magickInput]?.sceneIdx ?? 0] ?? 100);
9422
- for (let y = 0; y < outputFrame.height; y++) {
9423
- if (++cooperativeWork % 64 === 0) yield;
9424
- combined.set(outputFrame.data.subarray(y * outputFrame.width * 4, (y + 1) * outputFrame.width * 4), (idx * fw * fh + y * fw) * 4);
9425
- }
9426
- }
9427
- toEncode = {
9428
- ...yield* prepareHueOutput(stack[0], format),
9429
- width: fw,
9430
- height: fh * stack.length,
9431
- pages: stack.length,
9432
- pageHeight: fh,
9433
- delay: delays,
9434
- disposal: disposals,
9435
- gifFrames: stack.map((frame) => ({ width: frame.width, height: frame.height, x: frame.virtualCanvas?.x ?? 0, y: frame.virtualCanvas?.y ?? 0 })),
9436
- data: combined
9437
- };
9438
- encodePageHeight = fh;
9899
+ toEncode = yield* prepareGifFrames(stack);
9900
+ encodePageHeight = toEncode.pageHeight;
9439
9901
  }
9440
9902
  if (typeof globalThis.gc === "function") {
9441
9903
  try {
@@ -9447,6 +9909,10 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9447
9909
  }
9448
9910
  const { data: encoded } = encodeImage(toEncode, {
9449
9911
  format,
9912
+ netpbmAscii: state.netpbmAscii ?? false,
9913
+ ...state.inputDepth === void 0 ? {} : { netpbmDepth: Math.min(16, state.inputDepth) },
9914
+ endian: state.inputEndian,
9915
+ omitPngResolution: state.strip || state.omitPngResolution === true,
9450
9916
  ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor },
9451
9917
  chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4",
9452
9918
  quality: state.quality,
@@ -9483,7 +9949,7 @@ async function transformStoredMagickRaster(image, backend, steps, signal, dimens
9483
9949
  signal.throwIfAborted();
9484
9950
  const request = next.value;
9485
9951
  if (!request) {
9486
- await yieldTurn11(signal);
9952
+ await yieldTurn12(signal);
9487
9953
  await checkpoint?.();
9488
9954
  next = steps.next();
9489
9955
  } else if (request.kind === "write") {
@@ -9515,7 +9981,7 @@ async function morphStoredMagickImage(image, backend, plan, signal) {
9515
9981
  const position = backend.storage.allocate(size);
9516
9982
  const a = images[step.a], b = images[step.b];
9517
9983
  for (let offset = 0; offset < size; offset += 16384) {
9518
- await yieldTurn11(signal);
9984
+ await yieldTurn12(signal);
9519
9985
  const length = Math.min(16384, size - offset);
9520
9986
  const left = new Uint8Array(await backend.storage.read(a.position + offset, length));
9521
9987
  const bytes = await drainSteps(subtractMagickPixelSteps(left, await backend.storage.read(b.position + offset, length), image.hasAlpha), signal);
@@ -9567,7 +10033,7 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9567
10033
  let work = 0;
9568
10034
  for (const y of ys) for (let row = 0; row < y.length; row++) for (const x of xs) {
9569
10035
  for (let offset = 0; offset < x.length; offset += 4096) {
9570
- if (work++ % 64 === 0) await yieldTurn11(signal);
10036
+ if (work++ % 64 === 0) await yieldTurn12(signal);
9571
10037
  const length = Math.min(4096, x.length - offset) * 4;
9572
10038
  const sourceX = Math.min(w - 1, x.source + offset);
9573
10039
  const sourceLength = Math.min(length, (w - sourceX) * 4);
@@ -9597,7 +10063,7 @@ async function compositeStoredMagick(base, overlay, backend, signal, left, top,
9597
10063
  let hasAlpha = base.hasAlpha || !generic && !compositeMode.delegated && !compositeMode.preservesAlpha;
9598
10064
  const position = inPlace ? base.position : backend.storage.allocate(base.width * base.height * 4);
9599
10065
  for (let start = 0; !inPlace && start < base.width * base.height * 4; start += 16384) {
9600
- await yieldTurn11(signal);
10066
+ await yieldTurn12(signal);
9601
10067
  await backend.storage.write(position + start, await backend.storage.read(base.position + start, Math.min(16384, base.width * base.height * 4 - start)));
9602
10068
  }
9603
10069
  const maskDestination = !generic && ["dstin", "destin", "srcin", "in", "srcout", "out", "dstatop", "destatop"].includes(compositeMode.name);
@@ -9606,7 +10072,7 @@ async function compositeStoredMagick(base, overlay, backend, signal, left, top,
9606
10072
  const bottom = tile || extendSource ? base.height : Math.min(base.height, top + overlay.height);
9607
10073
  for (let y = extendSource ? 0 : Math.max(0, top); y < bottom; y++) {
9608
10074
  for (let x = extendSource ? 0 : Math.max(0, left); x < right; ) {
9609
- await yieldTurn11(signal);
10075
+ await yieldTurn12(signal);
9610
10076
  const sourceX = tile ? x % overlay.width : Math.max(0, Math.min(overlay.width - 1, x - left));
9611
10077
  const sourceY = tile ? y % overlay.height : Math.max(0, Math.min(overlay.height - 1, y - top));
9612
10078
  const repeatEdge = extendSource && !tile && (x < left || x >= left + overlay.width);
@@ -9645,24 +10111,37 @@ async function compositeStoredMagick(base, overlay, backend, signal, left, top,
9645
10111
  }
9646
10112
  return { ...base, position, hasAlpha, channels: hasAlpha ? base.channels : base.channels <= 2 ? 1 : 3 };
9647
10113
  }
10114
+ function applyMagickProfile(image, name, profile) {
10115
+ if (profile) {
10116
+ if (profile.length < 128 || String.fromCharCode(...profile.subarray(36, 40)) !== "acsp") throw new Error(`invalid ICC profile '${name}'`);
10117
+ if (image.icc && (image.icc.length !== profile.length || image.icc.some((byte2, index) => byte2 !== profile[index]))) throw new Error("ICC color conversion is unsupported");
10118
+ return { ...image, icc: profile };
10119
+ }
10120
+ if (["icc", "icm", "*"].includes(name.toLowerCase())) {
10121
+ const { icc: ignored, ...rest } = image;
10122
+ return rest;
10123
+ }
10124
+ return image;
10125
+ }
9648
10126
  async function tryConvertFiles(argv, input, stdinBytes, signal) {
9649
- const analysis = { text: "", warnings: "" };
10127
+ const analysis = { text: "", warnings: "", bytes: [] };
9650
10128
  const result = await tryConvertFilesResult(argv, input, stdinBytes, signal, analysis);
9651
- return result ? { ...result, stdout: analysis.text + result.stdout, stderr: analysis.warnings + result.stderr } : void 0;
10129
+ return result ? combineConvertOutput(result, analysis) : void 0;
9652
10130
  }
9653
10131
  async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis) {
9654
10132
  if (!input.filesystem.capabilities || !input.filesystem.open || !input.filesystem.removeFileConditional) return;
9655
- const outSpec = argv.at(-1);
10133
+ let outSpec = argv.at(-1) ?? "";
9656
10134
  const optionArgs = argv.slice(0, argv.indexOf("--") < 0 ? argv.length : argv.indexOf("--"));
9657
10135
  if (!outSpec || tryHandleMagickListOption(argv) || optionArgs.some((token) => ["--help", "-help", "-h", "--version", "-version"].includes(token))) {
9658
10136
  await probeImageArguments(argv, input, stdinBytes, signal);
9659
10137
  return drainSteps(runConvertCliSteps(argv, /* @__PURE__ */ new Map(), stdinBytes, signal), signal);
9660
10138
  }
9661
10139
  let output;
9662
- await yieldTurn11(signal);
10140
+ await yieldTurn12(signal);
9663
10141
  let state = createDefaultState();
9664
10142
  const tokens = argv.slice(0, -1), scopes = [];
9665
10143
  const steps = [];
10144
+ const registers = /* @__PURE__ */ new Map();
9666
10145
  const stackStep = (runStack) => {
9667
10146
  steps.push({ runStack });
9668
10147
  };
@@ -9670,7 +10149,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9670
10149
  try {
9671
10150
  output = inferOutputFormat(outSpec, "png");
9672
10151
  for (let i = 0; i < tokens.length; i++) {
9673
- if (i && i % 64 === 0) await yieldTurn11(signal);
10152
+ if (i && i % 64 === 0) await yieldTurn12(signal);
9674
10153
  const token = tokens[i];
9675
10154
  if (token === "") continue;
9676
10155
  if (!operandsOnly && token === "-sampling-factor") {
@@ -9726,20 +10205,20 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9726
10205
  const length = source.length;
9727
10206
  if (token.endsWith("clone") && length === 0) throw new Error(`UnableToCloneImage ${token}`);
9728
10207
  for (let repeat = 0; repeat < count; repeat++) for (const index of parseMagickIndexSpec(spec, length, "clone")) {
9729
- await yieldTurn11(signal);
10208
+ await yieldTurn12(signal);
9730
10209
  await stack.push(await source.get(index));
9731
10210
  }
9732
10211
  } else if (token.endsWith("delete")) await stack.remove(parseMagickIndexSpec(spec, stack.length, "delete"));
9733
10212
  else if (token === "-reverse") {
9734
10213
  for (let n = 0; n < Math.floor(stack.length / 2); n++) {
9735
- await yieldTurn11(signal);
10214
+ await yieldTurn12(signal);
9736
10215
  await stack.swap(n, stack.length - 1 - n);
9737
10216
  }
9738
10217
  } else if (token.endsWith("insert")) {
9739
10218
  const raw = token === "+insert" ? 0 : Number(spec), target = raw < 0 ? stack.length - 1 + raw : raw;
9740
10219
  if (!Number.isInteger(target) || target < 0 || target >= stack.length || raw < 0 && target === 0) throw new Error(`invalid image index ${token}`);
9741
10220
  for (let n = stack.length - 1; n > target; n--) {
9742
- await yieldTurn11(signal);
10221
+ await yieldTurn12(signal);
9743
10222
  await stack.swap(n, n - 1);
9744
10223
  }
9745
10224
  } else {
@@ -9791,7 +10270,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9791
10270
  const a = await stack.get(index), b = await stack.get(index + 1);
9792
10271
  await result.push(a);
9793
10272
  for (let step = 1; step <= count; step++) {
9794
- await yieldTurn11(signal);
10273
+ await yieldTurn12(signal);
9795
10274
  const fraction = step / (count + 1), width = Math.max(1, Math.round(a.width * (1 - fraction) + b.width * fraction)), height = Math.max(1, Math.round(a.height * (1 - fraction) + b.height * fraction));
9796
10275
  await result.push(await transformStoredMagickRaster(a, backend, morphPixelSteps(a, b, width, height, fraction), signal, { ...a, width, height }, [a, b]));
9797
10276
  }
@@ -9819,6 +10298,22 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9819
10298
  });
9820
10299
  continue;
9821
10300
  }
10301
+ if (!operandsOnly && token === "-orient") {
10302
+ const orientation = state.orientation = parseMagickOrientation(tokens[++i] ?? "");
10303
+ steps.push(async (image) => image ? { ...image, orientation } : void 0);
10304
+ continue;
10305
+ }
10306
+ if (!operandsOnly && token === "-units") {
10307
+ const units = state.units = parseMagickUnits(tokens[++i] ?? "");
10308
+ steps.push(async (image) => image ? applyMagickUnits(image, units) : void 0);
10309
+ continue;
10310
+ }
10311
+ if (!operandsOnly && token === "-density") {
10312
+ const value = tokens[++i] ?? "72";
10313
+ state.density = Math.max(1, Math.round(parseMagickGeometry(value).width ?? 72));
10314
+ steps.push(async (image) => image ? applyMagickDensity(image, value) : void 0);
10315
+ continue;
10316
+ }
9822
10317
  if (!operandsOnly && token === "-set" && tokens[i + 1]?.toLowerCase() === "delay") {
9823
10318
  const delay = Number(tokens[i + 2] ?? 0) * 10;
9824
10319
  steps.push(async (image) => image ? {
@@ -9836,6 +10331,25 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9836
10331
  i += 2;
9837
10332
  continue;
9838
10333
  }
10334
+ if (!operandsOnly && token === "-set" && !tokens[i + 1]?.toLowerCase().startsWith("option:") && !tokens[i + 1]?.toLowerCase().startsWith("filename:") && !tokens[i + 1]?.toLowerCase().startsWith("connected-components:")) {
10335
+ const key = tokens[++i] ?? "", value = tokens[++i] ?? "";
10336
+ steps.push(async (image, backend) => {
10337
+ if (!image) return;
10338
+ const expanded = await formatRetainedIdentify(value, image, image[magickInput] ?? {}, async () => ({ image, storage: backend.storage }), signal);
10339
+ return key.toLowerCase() === "density" ? applyMagickDensity(image, expanded) : { ...image, properties: { ...image.properties, [key]: expanded } };
10340
+ });
10341
+ continue;
10342
+ }
10343
+ if (!operandsOnly && token === "+set") {
10344
+ const key = tokens[++i] ?? "";
10345
+ steps.push(async (image) => {
10346
+ if (!image) return;
10347
+ const properties = { ...image.properties };
10348
+ delete properties[key];
10349
+ return { ...image, properties };
10350
+ });
10351
+ continue;
10352
+ }
9839
10353
  const setting = operandsOnly ? void 0 : applyMagickReadSetting(tokens, state, i);
9840
10354
  if (setting !== void 0) {
9841
10355
  i = setting;
@@ -9893,7 +10407,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9893
10407
  if (!image) return;
9894
10408
  const histogram = Array.from({ length: analysisOperation.luminance ? 5 : 4 }, (_, channel) => new Float64Array(channel === 4 || analysisOperation.highDepth && image.storedData16 ? 65536 : 256)), size = image.width * image.height * 4;
9895
10409
  for (let offset = 0; offset < size; offset += 16384) {
9896
- if (offset % 1048576 === 0) await yieldTurn11(signal);
10410
+ if (offset % 1048576 === 0) await yieldTurn12(signal);
9897
10411
  const original = analysisOperation.highDepth ? image.storedData16 : void 0;
9898
10412
  const bytes = await backend.storage.read((original?.position ?? image.position) + offset * (original ? 2 : 1), Math.min(16384, size - offset) * (original ? 2 : 1));
9899
10413
  await drainSteps(accumulateMagickHistogram(original ? new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2) : bytes, histogram), signal);
@@ -9911,7 +10425,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9911
10425
  const layout = smushLayoutSteps(images, token === "-smush", gap, settings.gravity);
9912
10426
  let next = layout.next();
9913
10427
  while (!next.done) {
9914
- await yieldTurn11(signal);
10428
+ await yieldTurn12(signal);
9915
10429
  const request = next.value;
9916
10430
  next = layout.next(await backend.storage.read(images[request.index].position + request.position, request.length));
9917
10431
  }
@@ -9957,7 +10471,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9957
10471
  if (append && stack.length === 1) return stack;
9958
10472
  let width = 0, height = 0;
9959
10473
  for (let n = 0; n < (flatten ? 1 : stack.length); n++) {
9960
- await yieldTurn11(signal);
10474
+ await yieldTurn12(signal);
9961
10475
  const frame = await stack.get(n);
9962
10476
  width = append && !vertical ? width + frame.width : Math.max(width, frame.width);
9963
10477
  height = append && vertical ? height + frame.height : Math.max(height, frame.height);
@@ -9971,7 +10485,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9971
10485
  const left = vertical ? offset.left : cursor, top = vertical ? cursor : offset.top;
9972
10486
  for (let y = 0; y < frame.height; y++) {
9973
10487
  for (let x = 0; x < frame.width; x += 4096) {
9974
- await yieldTurn11(signal);
10488
+ await yieldTurn12(signal);
9975
10489
  const pixels = await backend.storage.read(frame.position + (y * frame.width + x) * 4, Math.min(4096, frame.width - x) * 4);
9976
10490
  await backend.storage.write(result.position + ((top + y) * width + left + x) * 4, pixels);
9977
10491
  }
@@ -10019,7 +10533,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10019
10533
  let next = bounds.next();
10020
10534
  while (!next.done) {
10021
10535
  if (!next.value) {
10022
- await yieldTurn11(signal);
10536
+ await yieldTurn12(signal);
10023
10537
  next = bounds.next();
10024
10538
  } else {
10025
10539
  const { image, position, length } = next.value;
@@ -10054,6 +10568,11 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10054
10568
  });
10055
10569
  continue;
10056
10570
  }
10571
+ if (!operandsOnly && token === "-scene") {
10572
+ state.scene = Number(tokens[++i]);
10573
+ if (!Number.isSafeInteger(state.scene) || state.scene < 0) throw new Error("invalid scene number");
10574
+ continue;
10575
+ }
10057
10576
  if (!operandsOnly && (token === "+adjoin" || token === "-adjoin")) {
10058
10577
  state.adjoin = token === "-adjoin";
10059
10578
  continue;
@@ -10066,8 +10585,31 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10066
10585
  state.gravity = "northwest";
10067
10586
  continue;
10068
10587
  }
10588
+ if (!operandsOnly && (token === "-profile" || token === "+profile")) {
10589
+ const name = tokens[++i];
10590
+ if (!name) throw new Error(`missing argument '${token}'`);
10591
+ stackStep(async (stack, backend) => {
10592
+ const profile = token === "-profile" ? await backend.read(name, async (source) => {
10593
+ const bytes = new Uint8Array(source.size);
10594
+ for (let offset = 0; offset < source.size; offset += 16384) {
10595
+ await yieldTurn12(signal);
10596
+ bytes.set(await source.read(offset, Math.min(16384, source.size - offset), { signal }), offset);
10597
+ }
10598
+ return bytes;
10599
+ }) : void 0;
10600
+ if (token === "-profile" && !profile) throw new Error(`unable to open profile '${name}'`);
10601
+ for (let index = 0; index < stack.length; index++) await stack.set(index, applyMagickProfile(await stack.get(index), name, profile));
10602
+ return stack;
10603
+ });
10604
+ continue;
10605
+ }
10069
10606
  if (!operandsOnly && token === "-strip") {
10070
10607
  state.strip = true;
10608
+ steps.push(async (image) => {
10609
+ if (!image) return image;
10610
+ const { icc: ignored, properties: ignoredProperties, ...rest } = image;
10611
+ return rest;
10612
+ });
10071
10613
  continue;
10072
10614
  }
10073
10615
  if (!operandsOnly && token === "+repage") {
@@ -10084,17 +10626,35 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10084
10626
  continue;
10085
10627
  }
10086
10628
  if (!operandsOnly && (token === "-write" || token === "+write")) {
10087
- const path = tokens[++i] ?? "", quality = state.quality, samplingFactor = state.samplingFactor, adjoin = state.adjoin;
10629
+ if (i + 1 >= tokens.length) {
10630
+ stackStep(async () => {
10631
+ throw new Error(`missing argument '${token}'`);
10632
+ });
10633
+ break;
10634
+ }
10635
+ const path = expandFilenameProperties(tokens[++i] ?? "", state), endian = state.inputEndian, quality = state.quality, omitPngResolution = state.strip || state.omitPngResolution === true, samplingFactor = state.samplingFactor, netpbmAscii = state.netpbmAscii ?? false, netpbmDepth = state.inputDepth === void 0 ? void 0 : Math.min(16, state.inputDepth), adjoin = state.adjoin, scene = state.scene, loop = state.loop, optimizeLayers = state.optimizeLayers;
10088
10636
  stackStep(async (stack, backend) => {
10637
+ if (stack.length === 0) throw new Error(`no images for write '${token}'`);
10089
10638
  const image = await stack.get(stack.length - 1);
10090
- if (image && path && path.toLowerCase() !== "null:") {
10639
+ if (path.slice(0, 4).toLowerCase() === "mpr:") {
10640
+ const saved = new StoredImageStack(backend.storage, signal, stack);
10641
+ for (let index = 0; index < stack.length; index++) {
10642
+ await yieldTurn12(signal);
10643
+ await saved.push(await stack.get(index));
10644
+ }
10645
+ registers.set(path.slice(4), saved);
10646
+ } else if (image && path && path.toLowerCase() !== "null:") {
10091
10647
  const output2 = inferOutputFormat(path, image.format);
10092
- if (stack.length > 1 && (hasSceneOutputPattern(output2.path) || !adjoin)) {
10648
+ if (hasSceneOutputPattern(output2.path) || stack.length > 1 && (!adjoin || output2.path !== "-" && ["png", "jpeg", "bmp"].includes(output2.format))) {
10093
10649
  for (let index = 0; index < stack.length; index++) {
10094
- await yieldTurn11(signal);
10095
- await backend.stage(await prepareStoredHueOutput(await stack.get(index), output2.format, backend, signal), formatSceneOutputPath(output2.path, index), { format: output2.format, ...samplingFactor === void 0 ? {} : { samplingFactor }, chromaSubsampling: quality < 90 ? "4:2:0" : "4:4:4", quality });
10650
+ await yieldTurn12(signal);
10651
+ await backend.stage(await prepareStoredMagickOutput(await stack.get(index), output2.format, backend, signal), formatSceneOutputPath(output2.path, scene + index), { format: output2.format, endian, omitPngResolution, netpbmAscii, ...netpbmDepth === void 0 ? {} : { netpbmDepth }, ...samplingFactor === void 0 ? {} : { samplingFactor }, chromaSubsampling: quality < 90 ? "4:2:0" : "4:4:4", quality });
10096
10652
  }
10097
- } else await backend.stage(await prepareStoredHueOutput(image, output2.format, backend, signal), output2.path, { format: output2.format, ...samplingFactor === void 0 ? {} : { samplingFactor }, chromaSubsampling: quality < 90 ? "4:2:0" : "4:4:4", quality });
10653
+ } else {
10654
+ const frames = stack.length > 1 && output2.format === "miff" ? prepareStoredMiffPages(stack, image, backend, signal) : stack.length > 1 && output2.format === "tiff" ? prepareStoredTiffPages(stack, image, backend, signal) : stack.length > 1 && output2.format === "gif" ? await prepareStoredGifFrames(stack, backend, signal) : await prepareStoredMagickOutput(image, output2.format, backend, signal);
10655
+ const bytes = await backend.stage(frames, output2.path, { format: output2.format, ...stack.length > 1 && output2.format === "gif" ? { pageHeight: frames.pageHeight } : {}, endian, omitPngResolution, netpbmAscii, ...netpbmDepth === void 0 ? {} : { netpbmDepth }, ...samplingFactor === void 0 ? {} : { samplingFactor }, chromaSubsampling: quality < 90 ? "4:2:0" : "4:4:4", quality, ...loop === void 0 ? {} : { loop }, ...optimizeLayers ? { optimizeFrames: true } : {} });
10656
+ if (bytes) analysis.bytes.push(bytes);
10657
+ }
10098
10658
  }
10099
10659
  return stack;
10100
10660
  });
@@ -10137,7 +10697,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10137
10697
  if (!image) return;
10138
10698
  for (const step of magickDrawSteps(image, drawing, settings)) {
10139
10699
  if (!step) {
10140
- await yieldTurn11(signal);
10700
+ await yieldTurn12(signal);
10141
10701
  continue;
10142
10702
  }
10143
10703
  if ((image.space === "b-w" || image.space === "grey16") && !(step.kind === "floodfill" && step.alphaOnly))
@@ -10253,6 +10813,14 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10253
10813
  } else if (!operandsOnly && ["-roll", "-splice", "-chop"].includes(token)) {
10254
10814
  const geometry = tokens[++i] ?? (token === "-roll" ? "+0+0" : "0x0"), captured = { ...state };
10255
10815
  steps.push(async (image, backend) => image ? transformStoredMagickCoordinates(image, backend, token, geometry, captured, signal) : void 0);
10816
+ } else if (!operandsOnly && token === "-resample") {
10817
+ const value = tokens[++i] ?? "72", kernel = state.kernel;
10818
+ steps.push(async (image, backend) => {
10819
+ if (!image) return;
10820
+ const options = magickResizeOptions(image, magickResampleGeometry(image, value), kernel);
10821
+ const resized = options ? await resizeStoredMagickImage(image, backend, options, signal) : image;
10822
+ return applyMagickDensity(resized, value);
10823
+ });
10256
10824
  } else if (!operandsOnly && ["-resize", "-scale", "-sample", "-thumbnail"].includes(token)) {
10257
10825
  const geometry = tokens[++i] ?? "100%", kernel = token === "-sample" ? "nearest" : state.kernel, thumbnail = token === "-thumbnail";
10258
10826
  steps.push(async (image, backend) => {
@@ -10296,7 +10864,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10296
10864
  for (let n = 0; n < stack.length; n++) {
10297
10865
  const image = await stack.get(n);
10298
10866
  for (const area of magickCropAreas(image, geometry, gravity)) {
10299
- await yieldTurn11(signal);
10867
+ await yieldTurn12(signal);
10300
10868
  if (area === null) {
10301
10869
  analysis.warnings += `magick: geometry does not contain image '${geometry}'
10302
10870
  `;
@@ -10381,19 +10949,34 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10381
10949
  };
10382
10950
  });
10383
10951
  } else {
10384
- if (!operandsOnly && (token.startsWith("-") && token !== "-" || token.startsWith("+"))) throw new Error(`unrecognized option '${token}'`);
10952
+ if (!operandsOnly && (token.startsWith("-") && token !== "-" || token.startsWith("+"))) {
10953
+ stackStep(async () => {
10954
+ throw new Error(`unrecognized option '${token}'`);
10955
+ });
10956
+ break;
10957
+ }
10385
10958
  if (!operandsOnly && (token === "(" || token === ")")) continue;
10386
10959
  const captured = { ...state }, literal = operandsOnly, maxDecodeDimension = inferMaxDecodeDimensionFromUpcomingTokens(tokens, i + 1);
10387
10960
  stackStep(async (stack, backend) => {
10961
+ if (token.slice(0, 4).toLowerCase() === "mpr:") {
10962
+ const saved = registers.get(token.slice(4));
10963
+ if (!saved) throw new Error(`unable to open image '${token}'`);
10964
+ for (let index = 0; index < saved.length; index++) {
10965
+ await yieldTurn12(signal);
10966
+ await stack.push(await saved.get(index));
10967
+ }
10968
+ return stack;
10969
+ }
10388
10970
  const base = stack.length === 1 ? await stack.get(0) : void 0, lower = token.toLowerCase();
10389
10971
  const merge = !literal && (lower.startsWith("label:") || lower.startsWith("caption:")) && base && captured.hasSize && base.width === captured.sizeWidth && base.height === captured.sizeHeight && Object.values(captured.background).every((value) => value === 255);
10390
10972
  const settings = merge ? { ...captured, background: { r: 0, g: 0, b: 0, a: 0 } } : captured;
10391
- await parseStoredCompareInput(token, settings, backend, signal, { visit: async (image) => {
10973
+ const loaded = await parseStoredCompareInput(token, settings, backend, signal, { visit: async (image) => {
10392
10974
  if (merge) {
10393
10975
  const composed = await compositeStoredMagick(base, image, backend, signal, 0, 0, "over");
10394
10976
  await stack.set(0, { ...composed, hasAlpha: base.hasAlpha, channels: base.channels });
10395
10977
  } else await stack.push(image);
10396
10978
  }, ...maxDecodeDimension === void 0 ? {} : { maxDecodeDimension } });
10979
+ if (!loaded) throw new Error(`unable to open image '${token}'`);
10397
10980
  return stack;
10398
10981
  });
10399
10982
  }
@@ -10434,8 +11017,10 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10434
11017
  return { exitCode: 1, stdout: "", stderr: `magick: no images defined '${outSpec}'
10435
11018
  ` };
10436
11019
  }
10437
- await yieldTurn11(signal);
10438
- if (outSpec.toLowerCase() === "null:") {
11020
+ await yieldTurn12(signal);
11021
+ outSpec = expandFilenameProperties(outSpec, state);
11022
+ output = inferOutputFormat(outSpec, "png");
11023
+ if (outSpec.toLowerCase() === "null:" || outSpec.toLowerCase().startsWith("mpr:")) {
10439
11024
  await backend.publishPending();
10440
11025
  return { exitCode: 0, stdout: "", stderr: "" };
10441
11026
  }
@@ -10444,7 +11029,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10444
11029
  async function* information() {
10445
11030
  let last = "";
10446
11031
  for (let index = 0; index < stack.length; index++) {
10447
- await yieldTurn11(signal);
11032
+ await yieldTurn12(signal);
10448
11033
  const frame = await stack.get(index);
10449
11034
  if (index && !state.formatStr) yield "\n";
10450
11035
  const text = state.formatStr ? await formatRetainedIdentify(state.formatStr, frame, { ...frame[magickInput], sceneIdx: index, sceneCount: stack.length, quality: state.quality }, async () => ({ image: frame, storage: backend.storage }), signal) : `${frame.width}x${frame.height} ${frame.magickColorspace ?? "sRGB"} 8-bit`;
@@ -10468,60 +11053,24 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10468
11053
  await backend.publishPending();
10469
11054
  return { exitCode: 0, stdout, stderr: "" };
10470
11055
  }
10471
- if (stack.length > 1 && (hasSceneOutputPattern(output.path) || !state.adjoin)) {
11056
+ if (hasSceneOutputPattern(output.path) || stack.length > 1 && (!state.adjoin || output.path !== "-" && ["png", "jpeg", "bmp"].includes(output.format))) {
10472
11057
  for (let index = 0; index < stack.length; index++) {
10473
- await yieldTurn11(signal);
10474
- await backend.stage(await prepareStoredHueOutput(await stack.get(index), output.format, backend, signal), formatSceneOutputPath(output.path, index), { format: output.format, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
11058
+ await yieldTurn12(signal);
11059
+ await backend.stage(await prepareStoredMagickOutput(await stack.get(index), output.format, backend, signal), formatSceneOutputPath(output.path, state.scene + index), { format: output.format, netpbmAscii: state.netpbmAscii ?? false, ...state.inputDepth === void 0 ? {} : { netpbmDepth: Math.min(16, state.inputDepth) }, endian: state.inputEndian, omitPngResolution: state.strip || state.omitPngResolution === true, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality });
10475
11060
  }
10476
11061
  await backend.publishPending();
10477
11062
  return { exitCode: 0, stdout: "", stderr: "" };
10478
11063
  }
11064
+ if (stack.length > 1 && output.format === "miff") image = prepareStoredMiffPages(stack, image, backend, signal);
11065
+ if (stack.length > 1 && output.format === "tiff") image = prepareStoredTiffPages(stack, image, backend, signal);
10479
11066
  let pageHeight;
10480
11067
  if (stack.length > 1 && output.format === "gif") {
10481
- const first = await prepareStoredHueOutput(await stack.get(0), output.format, backend, signal);
10482
- let width = first.width, height = first.height;
10483
- for (let index = 1; index < stack.length; index++) {
10484
- const frame = await stack.get(index);
10485
- width = Math.max(width, frame.width);
10486
- height = Math.max(height, frame.height);
10487
- }
10488
- const size = width * height * 4, position = backend.storage.allocate(size * stack.length);
10489
- for (let index = 0; index < stack.length; index++) {
10490
- const frame = await prepareStoredHueOutput(await stack.get(index), output.format, backend, signal);
10491
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x += 4096) {
10492
- await yieldTurn11(signal);
10493
- const count = Math.min(4096, width - x), bytes = new Uint8Array(count * 4);
10494
- if (y < frame.height && x < frame.width) bytes.set(await backend.storage.read(frame.position + (y * frame.width + x) * 4, Math.min(count, frame.width - x) * 4));
10495
- await backend.storage.write(position + index * size + (y * width + x) * 4, bytes);
10496
- }
10497
- }
10498
- pageHeight = height;
10499
- const { storedData16: ignored, ...metadata } = first;
10500
- image = {
10501
- ...metadata,
10502
- position,
10503
- width,
10504
- height: height * stack.length,
10505
- pages: stack.length,
10506
- pageHeight,
10507
- storedGifFrame: { async at(index) {
10508
- const frame = await stack.get(index);
10509
- return { width: frame.width, height: frame.height, x: frame.virtualCanvas?.x ?? 0, y: frame.virtualCanvas?.y ?? 0 };
10510
- } },
10511
- storedDisposal: { length: stack.length, async at(index) {
10512
- const frame = await stack.get(index);
10513
- return typeof frame.disposal === "number" ? frame.disposal : 1;
10514
- } },
10515
- storedDelay: { length: stack.length, async at(index) {
10516
- const frame = await stack.get(index);
10517
- const scene2 = frame[magickInput]?.sceneIdx ?? 0;
10518
- return await frame.storedDelay?.at(scene2, { signal }) ?? frame.delay?.[scene2] ?? 100;
10519
- } }
10520
- };
11068
+ image = await prepareStoredGifFrames(stack, backend, signal);
11069
+ pageHeight = image.pageHeight;
10521
11070
  }
10522
11071
  const scene = pageHeight === void 0 ? (await stack.get(0))[magickInput]?.sceneIdx ?? 0 : 0;
10523
11072
  const delay = pageHeight === void 0 ? await image.storedDelay?.at(scene, { signal }) ?? image.delay?.[scene] : void 0;
10524
- const stdoutBytes = await backend.publish(await prepareStoredHueOutput(image, output.format, backend, signal), output.path, { format: output.format, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality, ...pageHeight === void 0 ? {} : { pageHeight }, ...delay === void 0 ? {} : { delay }, ...state.loop === void 0 ? {} : { loop: state.loop }, ...state.optimizeLayers ? { optimizeFrames: true } : {} });
11073
+ const stdoutBytes = await backend.publish(await prepareStoredMagickOutput(image, output.format, backend, signal), output.path, { format: output.format, netpbmAscii: state.netpbmAscii ?? false, ...state.inputDepth === void 0 ? {} : { netpbmDepth: Math.min(16, state.inputDepth) }, endian: state.inputEndian, omitPngResolution: state.strip || state.omitPngResolution === true, ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor }, chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4", quality: state.quality, ...pageHeight === void 0 ? {} : { pageHeight }, ...delay === void 0 ? {} : { delay }, ...state.loop === void 0 ? {} : { loop: state.loop }, ...state.optimizeLayers ? { optimizeFrames: true } : {} });
10525
11074
  return { exitCode: 0, stdout: "", stderr: "", ...stdoutBytes ? { stdoutBytes } : {} };
10526
11075
  });
10527
11076
  }
@@ -10553,6 +11102,7 @@ function* parseMogrifySteps(argv) {
10553
11102
  const readSettings = [];
10554
11103
  const targets = [];
10555
11104
  const flagsWithOneArg = /* @__PURE__ */ new Set([
11105
+ "-resample",
10556
11106
  "-resize",
10557
11107
  "-scale",
10558
11108
  "-sample",
@@ -10598,12 +11148,15 @@ function* parseMogrifySteps(argv) {
10598
11148
  "-liquid-rescale",
10599
11149
  "-adaptive-resize",
10600
11150
  "-define",
11151
+ "+set",
10601
11152
  "-write",
10602
11153
  "+write",
10603
11154
  "-compose",
10604
11155
  "-geometry",
10605
11156
  "-size",
10606
11157
  "-depth",
11158
+ "-compress",
11159
+ "-endian",
10607
11160
  "-units",
10608
11161
  "-interlace",
10609
11162
  "-sampling-factor",
@@ -10657,7 +11210,7 @@ function* parseMogrifySteps(argv) {
10657
11210
  }
10658
11211
  } else if (t === "-evaluate" || t === "-function" || t === "-morphology" || t === "-statistic" || t === "-set" || t === "-distort" || t === "+distort") {
10659
11212
  opTokens.push(t, argv[++i] ?? "", argv[++i] ?? "");
10660
- } else if (["-density", "-background", "-fuzz", "-filter", "-size"].includes(t)) {
11213
+ } else if (["-density", "-background", "-fuzz", "-filter", "-size", "-comment", "-label"].includes(t)) {
10661
11214
  readSettings.push(t, argv[++i] ?? "");
10662
11215
  } else if (flagsWithOneArg.has(t)) {
10663
11216
  opTokens.push(t, argv[++i] ?? "");
@@ -10770,13 +11323,13 @@ async function probeImageArguments(argv, files, stdinBytes, active) {
10770
11323
  await withImageInputs(files, stdinBytes, active, async (read) => {
10771
11324
  const inspect = async (source) => {
10772
11325
  for (let position = 0; position < source.size; position += 16384) {
10773
- await yieldTurn11(active);
11326
+ await yieldTurn12(active);
10774
11327
  await source.read(position, Math.min(16384, source.size - position), { signal: active });
10775
11328
  }
10776
11329
  };
10777
11330
  let operandsOnly = false;
10778
11331
  for (let index = 0; index < argv.length; index++) {
10779
- await yieldTurn11(active);
11332
+ await yieldTurn12(active);
10780
11333
  const token = argv[index];
10781
11334
  if (!operandsOnly && token === "--") {
10782
11335
  operandsOnly = true;
@@ -10827,6 +11380,19 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10827
11380
  const pattern = await parseStoredCompareInput(baseToken.slice(5), state, backend, signal, { source: readOptions.source, lastPage: readOptions.lastPage === true });
10828
11381
  if (!pattern) return;
10829
11382
  image = await tile(pattern);
11383
+ } else if (lower.startsWith("rgb:")) {
11384
+ if (!state.hasSize) throw new Error("must specify image size for RGB input");
11385
+ const path = baseToken.slice(4);
11386
+ const consume = async (source) => {
11387
+ const raw = rawRgbInput(state.sizeWidth, state.sizeHeight, state.inputDepth ?? 16, state.inputEndian ?? "lsb", source.size);
11388
+ return parseStoredCompareInput(`ppm:${token.slice(4)}`, state, backend, signal, { ...readOptions, source: { size: raw.size, async read(position, length) {
11389
+ const steps = raw.read(position, length);
11390
+ let next = steps.next();
11391
+ while (!next.done) next = steps.next(await source.read(next.value.position, next.value.length, { signal }));
11392
+ return next.value;
11393
+ } } });
11394
+ };
11395
+ return readOptions.source === null ? void 0 : readOptions.source ? consume(readOptions.source) : backend.read(path, consume);
10830
11396
  } else if (lower.startsWith("xc:") || lower.startsWith("canvas:") || lower === "null:") {
10831
11397
  const color = lower === "null:" ? { r: 0, g: 0, b: 0, a: 0 } : parseColor(baseToken.slice(baseToken.indexOf(":") + 1) || "white"), width = lower === "null:" ? 1 : state.sizeWidth, height = lower === "null:" ? 1 : state.sizeHeight;
10832
11398
  image = { ...await createStoredCanvas(width, height, color, backend, signal), hasAlpha: color.a < 255 };
@@ -10836,7 +11402,7 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10836
11402
  const [first, last] = baseToken.slice(baseToken.indexOf(":") + 1).split("-"), a = parseColor(checker ? "white" : first || "#ffffff"), b = parseColor(checker ? "black" : last || "#000000");
10837
11403
  const cx = (width - 1) / 2, cy = (height - 1) / 2, radius = Math.max(1, Math.hypot(cx, cy));
10838
11404
  for (let start = 0; start < width * height; start += 4096) {
10839
- if (start % 262144 === 0) await yieldTurn11(signal);
11405
+ if (start % 262144 === 0) await yieldTurn12(signal);
10840
11406
  const count = Math.min(4096, width * height - start), bytes = new Uint8Array(count * 4);
10841
11407
  for (let i = 0; i < count; i++) {
10842
11408
  const x = (start + i) % width, y = Math.floor((start + i) / width), offset = i * 4;
@@ -10898,7 +11464,7 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10898
11464
  const width = state.sizeWidth, height = state.sizeHeight, position = backend.storage.allocate(width * height * 4);
10899
11465
  let cachedPosition = -1, cached = new Uint8Array();
10900
11466
  for (let start = 0; start < width * height; start += 4096) {
10901
- if (start % 262144 === 0) await yieldTurn11(signal);
11467
+ if (start % 262144 === 0) await yieldTurn12(signal);
10902
11468
  const count = Math.min(4096, width * height - start), bytes = new Uint8Array(count * 4);
10903
11469
  for (let offset = 0; offset < count; ) {
10904
11470
  const x = (start + offset) % width, y = Math.floor((start + offset) / width), px = x % pattern.width, py = y % pattern.height, blockX = Math.floor(px / 4096) * 4096;
@@ -10916,6 +11482,15 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10916
11482
  return { position, width, height, format: "png", channels: 4, depth: "uchar", space: "srgb", density: 72, hasAlpha: pattern.hasAlpha };
10917
11483
  }
10918
11484
  async function finish(image2) {
11485
+ if (state.orientation !== void 0) image2 = { ...image2, orientation: state.orientation };
11486
+ if (state.units !== void 0) image2 = applyMagickUnits(image2, state.units);
11487
+ for (const key of ["comment", "label"]) {
11488
+ const value = state[key];
11489
+ if (value !== void 0) {
11490
+ const expanded = await formatRetainedIdentify(value, image2, { filePath: baseToken }, async () => ({ image: image2, storage: backend.storage }), signal);
11491
+ image2 = { ...image2, properties: { ...image2.properties, [key]: expanded } };
11492
+ }
11493
+ }
10919
11494
  if (state.delay !== void 0) {
10920
11495
  const delay = state.delay;
10921
11496
  image2 = { ...image2, storedDelay: { length: image2.storedDelay?.length ?? image2.delay?.length ?? 1, async at() {
@@ -11640,7 +12215,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11640
12215
  } };
11641
12216
  };
11642
12217
  for (let index = 0; index < paths.length; index++) {
11643
- await yieldTurn11(signal);
12218
+ await yieldTurn12(signal);
11644
12219
  const token = paths[index];
11645
12220
  let path2 = parseInputToken(token.toLowerCase().startsWith("tile:") ? token.slice(5) : token).baseToken;
11646
12221
  const colon = path2.indexOf(":");
@@ -11648,7 +12223,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11648
12223
  await backend.read(path2, async (source2) => {
11649
12224
  const position = backend.storage.allocate(source2.size);
11650
12225
  for (let start = 0; start < source2.size; start += 16384) {
11651
- await yieldTurn11(signal);
12226
+ await yieldTurn12(signal);
11652
12227
  await backend.storage.write(position + start, await source2.read(start, Math.min(16384, source2.size - start), { signal }));
11653
12228
  }
11654
12229
  await sources.set(BigInt(index * 5), BigInt(position));
@@ -11678,7 +12253,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11678
12253
  await sources.set(BigInt(index * 5 + 3), BigInt(height));
11679
12254
  let directPng = metadata.format === "png" && !metadata.isProgressive && prefix[24] === 8 && (prefix[25] === 2 || prefix[25] === 6), idat = false;
11680
12255
  if (directPng) for (let offset = 8; offset + 8 <= source.size; ) {
11681
- await yieldTurn11(signal);
12256
+ await yieldTurn12(signal);
11682
12257
  const header = await source.read(offset, 8), size = new DataView(header.buffer, header.byteOffset, 4).getUint32(0, false);
11683
12258
  if (offset + 8 + size > source.size) break;
11684
12259
  const name = String.fromCharCode(...header.subarray(4));
@@ -11742,7 +12317,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11742
12317
  canvasWidth = canvasHeight = 0;
11743
12318
  let rowWidth = 0, rowHeight = 0;
11744
12319
  for (let index = 0; index < Math.min(n, cols * rows); index++) {
11745
- await yieldTurn11(signal);
12320
+ await yieldTurn12(signal);
11746
12321
  const image = fast ? void 0 : await images.get(index);
11747
12322
  const width = image ? image.width : Number(await sources.get(BigInt(index * 5 + 2)));
11748
12323
  const height = image ? image.height : Number(await sources.get(BigInt(index * 5 + 3)));
@@ -11760,7 +12335,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11760
12335
  let canvas = await createStoredCanvas(Math.max(1, canvasWidth), Math.max(1, canvasHeight), state.background, backend, signal);
11761
12336
  let anyTransparent = state.background.a < 255 || borderW > 0 && state.borderColor.a < 255;
11762
12337
  for (let index = 0; index < n; index++) {
11763
- await yieldTurn11(signal);
12338
+ await yieldTurn12(signal);
11764
12339
  const col = index % cols, row = Math.floor(index / cols);
11765
12340
  if (row >= rows) break;
11766
12341
  let image, directPng = false;
@@ -11795,7 +12370,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11795
12370
  const x = (variableCells ? tileX : col * slotW) + (concatenate ? borderW : 0) + padX + offset.left, y = (variableCells ? tileY : row * slotH) + (concatenate ? borderW : 0) + padY + offset.top;
11796
12371
  tileX += width + padX * 2;
11797
12372
  if (!anyTransparent) for (let start = 0; start < image.width * image.height * 4; start += 16384) {
11798
- await yieldTurn11(signal);
12373
+ await yieldTurn12(signal);
11799
12374
  const bytes = await backend.storage.read(image.position + start, Math.min(16384, image.width * image.height * 4 - start));
11800
12375
  for (let k = 3; k < bytes.length; k += 4) if (bytes[k] < 255) {
11801
12376
  anyTransparent = true;
@@ -11887,7 +12462,7 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11887
12462
  const hasOutputOperand = runner !== runIdentifyCli && !(runner === runMagickCli && argv[0] === "identify");
11888
12463
  let operandsOnly = false;
11889
12464
  for (const [index, token] of argv.entries()) {
11890
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12465
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11891
12466
  if (!operandsOnly && token === "--") {
11892
12467
  operandsOnly = true;
11893
12468
  continue;
@@ -11922,7 +12497,7 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11922
12497
  const chunks = [];
11923
12498
  let total = 0;
11924
12499
  for await (const chunk of readBytes2(context.stdin, signal)) {
11925
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12500
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11926
12501
  chargeInput(chunk.byteLength);
11927
12502
  chunks.push(chunk);
11928
12503
  total += chunk.byteLength;
@@ -11930,7 +12505,7 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11930
12505
  stdinBytes = new Uint8Array(total);
11931
12506
  let off = 0;
11932
12507
  for (const chunk of chunks) {
11933
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12508
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11934
12509
  stdinBytes.set(chunk, off);
11935
12510
  off += chunk.byteLength;
11936
12511
  }
@@ -11950,10 +12525,17 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11950
12525
  delivered = { ...result, stdout: "" };
11951
12526
  }
11952
12527
  for (const [key, val] of vfsFiles.entries()) {
11953
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12528
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11954
12529
  if (existingSnap.get(key) !== val) {
11955
12530
  const abs = resolvePath3(context.cwd, key);
11956
- await writeFileOutput2(context, val, (data) => context.fs.writeFile(abs, data, { signal }));
12531
+ try {
12532
+ await writeFileOutput2(context, val, (data) => context.fs.writeFile(abs, data, { signal }));
12533
+ } catch (error) {
12534
+ signal.throwIfAborted();
12535
+ if (error instanceof FsError4 && ["ENOENT", "ENOTDIR"].includes(error.code))
12536
+ throw new FsError4(error.code, { syscall: "write", path: key, cause: error });
12537
+ throw error;
12538
+ }
11957
12539
  }
11958
12540
  }
11959
12541
  vfsFiles.clear();
@@ -12016,6 +12598,12 @@ async function executeVfsMagickTool(context, runner, maxInputBytes) {
12016
12598
  await writeBytes2(stdout.output, new TextEncoder().encode(res.stdout), invocation.signal);
12017
12599
  }
12018
12600
  return { exitCode: res.exitCode };
12601
+ } catch (error) {
12602
+ invocation.signal.throwIfAborted();
12603
+ if (!(error instanceof FsError4) || !["ENOENT", "ENOTDIR"].includes(error.code)) throw error;
12604
+ await writeBytes2(context.stderr, new TextEncoder().encode(`magick: ${error.message}
12605
+ `), invocation.signal);
12606
+ return { exitCode: 1 };
12019
12607
  } finally {
12020
12608
  await invocation.close();
12021
12609
  }