@poe-platform/safe-bash 0.1.844 → 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 (43) 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-LORSYIAF.js → chunk-CSJHADNR.js} +31 -7
  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-3SWIXYXT.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 +535 -88
  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.d.ts +1 -0
  37. package/dist/safe-bash-command-imagemagick/index.js +895 -276
  38. package/dist/safe-bash-command-imagemagick/raw-output.d.ts +10 -0
  39. package/dist/safe-bash-command-python/executor.js +3 -3
  40. package/dist/safe-bash-command-python/index.js +3 -3
  41. package/dist/safe-bash-command-python/jspi.js +2 -2
  42. package/dist/safe-bash-command-python/worker.js +1 -1
  43. 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));
3420
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
+ }
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],
@@ -3947,6 +4098,13 @@ function parseMagickGeometry(raw) {
3947
4098
  break;
3948
4099
  }
3949
4100
  }
4101
+ if (s.includes(":")) {
4102
+ const parts = s.split(":");
4103
+ const ratio = Number(parts[0]) / Number(parts[1]);
4104
+ if (parts.length === 2 && Number(parts[0]) > 0 && Number(parts[1]) > 0 && Number.isFinite(ratio)) {
4105
+ return { aspectRatio: ratio, x, y, hasOffset, forceExact, shrinkOnly, enlargeOnly, fillArea, isPercent: false };
4106
+ }
4107
+ }
3950
4108
  if (isArea) {
3951
4109
  if (s.includes("x") || s.includes("X")) {
3952
4110
  const [wStr, hStr] = s.split(/[xX]/);
@@ -4050,6 +4208,7 @@ function createDefaultState() {
4050
4208
  tile: void 0,
4051
4209
  strip: false,
4052
4210
  adjoin: true,
4211
+ scene: 0,
4053
4212
  dither: true,
4054
4213
  formatStr: void 0,
4055
4214
  channels: { r: true, g: true, b: true, a: false },
@@ -4500,6 +4659,7 @@ function* formatMagickPropertyStringSteps(fmt, meta, getImage, context = {}, res
4500
4659
  Q: String(context.quality ?? meta.quality ?? 92)
4501
4660
  };
4502
4661
  const properties = {
4662
+ ...meta.properties,
4503
4663
  orientation: ["Undefined", "TopLeft", "TopRight", "BottomRight", "BottomLeft", "LeftTop", "RightTop", "RightBottom", "LeftBottom"][meta.orientation ?? 0] ?? "Undefined",
4504
4664
  ...meta.jpegSamplingFactor === void 0 ? {} : { "jpeg:sampling-factor": meta.jpegSamplingFactor },
4505
4665
  "exif:orientation": meta.orientation === void 0 ? "" : String(meta.orientation),
@@ -4803,14 +4963,16 @@ function* createCheckerboardImageSteps(width, height) {
4803
4963
  function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal) {
4804
4964
  let cooperativeWork = 0;
4805
4965
  const g = parseMagickGeometry(geomStr);
4806
- const sw = Math.max(0, Math.round(g.width ?? 0));
4807
- const sh = Math.max(0, Math.round(g.height ?? 0));
4966
+ const sw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? img.width / 100 : 1)));
4967
+ const sh = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? img.height / 100 : 1)));
4808
4968
  const box = gravityAdjustBox(img.width, img.height, sw, sh, g.x, g.y, gravity);
4809
4969
  const anchor = resolveGravityOffset(sw, sh, gravity);
4810
- const sx = Math.max(0, Math.min(img.width, box.x + anchor.left));
4811
- const sy = Math.max(0, Math.min(img.height, box.y + anchor.top));
4812
4970
  const outW = img.width + sw;
4813
4971
  const outH = img.height + sh;
4972
+ const xOffset = box.x + anchor.left;
4973
+ const sx = xOffset < 0 || xOffset > img.width ? outW : xOffset;
4974
+ if (box.y + anchor.top > outH) throw new Error("pixels are not authentic");
4975
+ const sy = Math.max(0, Math.min(img.height, box.y + anchor.top));
4814
4976
  const out = new Uint8Array(new ArrayBuffer(outW * outH * 4 + outH), 0, outW * outH * 4);
4815
4977
  for (let y = 0; y < outH; y++) {
4816
4978
  if (++cooperativeWork % 65536 === 0)
@@ -4825,7 +4987,7 @@ function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal
4825
4987
  out[dstIdx + 2] = bg.b;
4826
4988
  out[dstIdx + 3] = bg.a;
4827
4989
  } else {
4828
- const origX = x < sx ? x : x - sw;
4990
+ const origX = Math.min(img.width - 1, x < sx ? x : x - sw);
4829
4991
  const origY = y < sy ? y : y - sh;
4830
4992
  const srcIdx = (origY * img.width + origX) * 4;
4831
4993
  out[dstIdx] = img.data[srcIdx];
@@ -4944,19 +5106,32 @@ function hasSceneOutputPattern(path) {
4944
5106
  return false;
4945
5107
  }
4946
5108
  function formatSceneOutputPath(pattern, index) {
4947
- const padMatch = /%0(\d+)d/.exec(pattern);
4948
- if (padMatch) {
4949
- const width = parseInt(padMatch[1], 10);
4950
- return pattern.replace(/%0\d+d/, String(index).padStart(width, "0"));
4951
- }
4952
- if (pattern.includes("%d")) {
4953
- 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];
4954
5128
  }
4955
- const dot = pattern.lastIndexOf(".");
5129
+ if (hasScene) return path;
5130
+ const dot = path.lastIndexOf(".");
4956
5131
  if (dot > 0) {
4957
- return `${pattern.slice(0, dot)}-${index}${pattern.slice(dot)}`;
5132
+ return `${path.slice(0, dot)}-${index}${path.slice(dot)}`;
4958
5133
  }
4959
- return `${pattern}-${index}`;
5134
+ return `${path}-${index}`;
4960
5135
  }
4961
5136
  function parseChannelMask(spec) {
4962
5137
  const s = spec.toLowerCase().trim();
@@ -6898,13 +7073,12 @@ function magickCropArea(img, geomStr, gravity) {
6898
7073
  }
6899
7074
  function magickExtentLayout(img, geomStr, state) {
6900
7075
  const g = parseMagickGeometry(geomStr);
6901
- const targetW = Math.max(1, Math.round(g.width ?? img.width));
6902
- const targetH = Math.max(1, Math.round(g.height ?? img.height));
6903
- const offset = resolveGravityOffset(targetW - img.width, targetH - img.height, state.gravity);
6904
- const isEast = state.gravity === "east" || state.gravity === "northeast" || state.gravity === "southeast";
6905
- const isSouth = state.gravity === "south" || state.gravity === "southwest" || state.gravity === "southeast";
6906
- const left = Math.round(offset.left - (isEast ? -g.x : g.x));
6907
- const top = Math.round(offset.top - (isSouth ? -g.y : g.y));
7076
+ const targetW = Math.max(1, Math.round(g.aspectRatio !== void 0 ? Math.min(img.width, img.height * g.aspectRatio) : g.isPercent ? img.width * (g.percentX ?? 100) / 100 : g.width ?? img.width));
7077
+ const targetH = Math.max(1, Math.round(g.aspectRatio !== void 0 ? Math.min(img.height, img.width / g.aspectRatio) : g.isPercent ? img.height * (g.percentY ?? 100) / 100 : g.height ?? img.height));
7078
+ if (g.enlargeOnly && img.width > targetW && img.height > targetH || g.shrinkOnly && img.width < targetW && img.height < targetH) return void 0;
7079
+ const offset = gravityAdjustBox(img.width, img.height, targetW, targetH, g.x, g.y, state.gravity);
7080
+ const left = -offset.x;
7081
+ const top = -offset.y;
6908
7082
  const dstX0 = Math.max(0, left);
6909
7083
  const dstY0 = Math.max(0, top);
6910
7084
  const dstX1 = Math.min(targetW, left + img.width);
@@ -6914,6 +7088,7 @@ function magickExtentLayout(img, geomStr, state) {
6914
7088
  function* applyMagickExtentSteps(img, geomStr, state, signal) {
6915
7089
  yield;
6916
7090
  const layout = magickExtentLayout(img, geomStr, state);
7091
+ if (!layout) return img;
6917
7092
  const virtualCanvas = magickResizeCanvas(img, layout.width, layout.height);
6918
7093
  const source = img[magickInput];
6919
7094
  const canvas = {
@@ -6930,7 +7105,7 @@ async function decodeStoredMagickSvg(svg, density, backend, signal) {
6930
7105
  const encoder = new TextEncoder(), base = backend.storage.allocate(0);
6931
7106
  let size = 0;
6932
7107
  for (let offset = 0; offset < svg.length; ) {
6933
- await yieldTurn11(signal);
7108
+ await yieldTurn12(signal);
6934
7109
  const bytes = new Uint8Array(4096), { read, written } = encoder.encodeInto(svg.slice(offset, offset + 4097), bytes);
6935
7110
  await backend.storage.write(backend.storage.allocate(written), bytes.subarray(0, written));
6936
7111
  offset += read;
@@ -7297,12 +7472,32 @@ function* appendStackImagesSteps(stack, vertical, state) {
7297
7472
  }
7298
7473
  return { ...canvas, hasAlpha };
7299
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
+ }
7300
7496
  function inferOutputFormat(spec, fallback = "png") {
7301
7497
  const prefixMatch = /^([a-zA-Z0-9]+):(.*)$/.exec(spec);
7302
7498
  if (prefixMatch) {
7303
7499
  const prefix = prefixMatch[1].toLowerCase();
7304
7500
  const rest = prefixMatch[2];
7305
- if (prefix === "rgba") throw new Error("unsupported output coder 'rgba'");
7306
7501
  const fmt = extToImageFormat(prefix);
7307
7502
  if (fmt) {
7308
7503
  return { format: fmt, path: rest };
@@ -7317,7 +7512,11 @@ function inferOutputFormat(spec, fallback = "png") {
7317
7512
  return { format: fallback, path: spec };
7318
7513
  }
7319
7514
  function extToImageFormat(ext) {
7515
+ const rawCoder = ext.toLowerCase();
7516
+ if (isRawCoder(rawCoder)) return rawCoder;
7320
7517
  switch (ext.toLowerCase()) {
7518
+ case "miff":
7519
+ return "miff";
7321
7520
  case "png":
7322
7521
  return "png";
7323
7522
  case "jpg":
@@ -7332,6 +7531,8 @@ function extToImageFormat(ext) {
7332
7531
  case "tif":
7333
7532
  case "tiff":
7334
7533
  return "tiff";
7534
+ case "pam":
7535
+ return "pam";
7335
7536
  case "ppm":
7336
7537
  return "ppm";
7337
7538
  case "pgm":
@@ -7440,6 +7641,15 @@ function* parseInputOperandsSteps(token, files, state, stdinBytes, maxDecodeDime
7440
7641
  const { baseToken, pageSpec, inlineGeom } = parseInputToken(token);
7441
7642
  function* applyMod(imgs) {
7442
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
+ }
7443
7653
  if (state.delay !== void 0) im = { ...im, delay: Array(im.delay?.length ?? 1).fill(state.delay) };
7444
7654
  return inlineGeom ? yield* applyInlineReadModifierSteps(im, inlineGeom, state.kernel) : im;
7445
7655
  });
@@ -7486,14 +7696,28 @@ function* parseInputOperandsSteps(token, files, state, stdinBytes, maxDecodeDime
7486
7696
  if (lower === "null:") {
7487
7697
  return yield* applyMod([yield* createSolidRgbaImageSteps(1, 1, { r: 0, g: 0, b: 0, a: 0 })]);
7488
7698
  }
7489
- let cleanToken = baseToken;
7699
+ const rgb = lower.startsWith("rgb:");
7700
+ let cleanToken = rgb ? baseToken.slice(4) : baseToken;
7490
7701
  const prefixMatch = /^([a-zA-Z0-9]+):(.*)$/.exec(cleanToken);
7491
7702
  if (prefixMatch && extToImageFormat(prefixMatch[1])) {
7492
7703
  cleanToken = prefixMatch[2];
7493
7704
  }
7494
- const rawBytes = cleanToken === "-" ? stdinBytes : files.get(cleanToken) ?? files.get(token);
7705
+ let rawBytes = cleanToken === "-" ? stdinBytes : files.get(cleanToken) ?? files.get(token);
7495
7706
  if (!rawBytes)
7496
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
+ }
7497
7721
  let totalPages = 1;
7498
7722
  let inputMeta;
7499
7723
  try {
@@ -7644,7 +7868,7 @@ async function drainIdentifySteps(steps, signal) {
7644
7868
  while (!next.done) {
7645
7869
  try {
7646
7870
  if (next.value) await next.value.run();
7647
- else await yieldTurn11(signal);
7871
+ else await yieldTurn12(signal);
7648
7872
  next = steps.next();
7649
7873
  } catch (error) {
7650
7874
  next = steps.throw(error);
@@ -8505,7 +8729,7 @@ async function transformStoredMagickPixels(image, backend, operation, signal, hi
8505
8729
  let storedData16;
8506
8730
  let storedHueSamples;
8507
8731
  for (let offset = 0; offset < size; offset += 16384) {
8508
- if (offset % 1048576 === 0) await yieldTurn11(signal);
8732
+ if (offset % 1048576 === 0) await yieldTurn12(signal);
8509
8733
  const data = await backend.storage.read(image.position + offset, Math.min(16384, size - offset));
8510
8734
  const bytes16 = (highDepth || operation.highDepth) && image.storedData16 ? await backend.storage.read(image.storedData16.position + offset * 2, data.length * 2) : void 0;
8511
8735
  const data16 = bytes16 ? new Uint16Array(bytes16.buffer, bytes16.byteOffset, bytes16.byteLength / 2) : void 0;
@@ -8527,9 +8751,118 @@ async function transformStoredMagickPixels(image, backend, operation, signal, hi
8527
8751
  }
8528
8752
  return result;
8529
8753
  }
8530
- async function prepareStoredHueOutput(image, format, backend, signal) {
8531
- if (image.magickColorspace !== "HSL" && image.magickColorspace !== "HSB" && image.magickColorspace !== "HWB" && image.magickColorspace !== "XYZ" && image.magickColorspace !== "YCbCr" && image.magickColorspace !== "YUV") return image;
8532
- 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]);
8533
8866
  }
8534
8867
  function parseMagickDisposal(value) {
8535
8868
  const names = ["undefined", "none", "background", "previous"];
@@ -8538,6 +8871,49 @@ function parseMagickDisposal(value) {
8538
8871
  if (["0", "1", "2", "3"].includes(value)) return Number(value);
8539
8872
  return 0;
8540
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
+ }
8541
8917
  function applyMagickReadSetting(tokens, state, start) {
8542
8918
  let i = start;
8543
8919
  const t = tokens[i];
@@ -8554,6 +8930,23 @@ function applyMagickReadSetting(tokens, state, start) {
8554
8930
  } else if (t === "+channel") {
8555
8931
  state.channels = { r: true, g: true, b: true, a: false };
8556
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;
8557
8950
  } else if (t === "-size") {
8558
8951
  const g = parseMagickGeometry(tokens[++i] ?? "1x1");
8559
8952
  state.sizeWidth = Math.max(1, Math.round(g.width ?? 1));
@@ -8586,6 +8979,8 @@ function applyMagickReadSetting(tokens, state, start) {
8586
8979
  state.gravity = parseGravity(tokens[++i] ?? "center");
8587
8980
  } else if (t === "+gravity") {
8588
8981
  state.gravity = "northwest";
8982
+ } else if (t === "-comment" || t === "-label") {
8983
+ state[t === "-comment" ? "comment" : "label"] = tokens[++i] ?? "";
8589
8984
  } else if (t === "-sampling-factor") {
8590
8985
  state.samplingFactor = tokens[++i] ?? "";
8591
8986
  } else if (t === "+sampling-factor") {
@@ -8607,6 +9002,9 @@ function applyMagickReadSetting(tokens, state, start) {
8607
9002
  state.compose = parseCompose(cRaw);
8608
9003
  } else if (t === "-define") {
8609
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
+ }
8610
9008
  if (defStr.toLowerCase().startsWith("connected-components:")) {
8611
9009
  const [key, value, extra] = defStr.toLowerCase().split("=");
8612
9010
  if (extra !== void 0 || key !== "connected-components:verbose" || value !== "true" && value !== "false") throw new Error(`Unsupported connected-components define: ${defStr}`);
@@ -8620,12 +9018,13 @@ function applyMagickReadSetting(tokens, state, start) {
8620
9018
  const k = (tokens[++i] ?? "").toLowerCase();
8621
9019
  const v = tokens[++i] ?? "";
8622
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 };
8623
9022
  if (k === "option:compose:args")
8624
9023
  state.composeArgs = v;
8625
9024
  } else return void 0;
8626
9025
  return i;
8627
9026
  }
8628
- 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()) {
8629
9028
  let cooperativeWork = 63;
8630
9029
  let stack = [];
8631
9030
  let operandsOnly = false;
@@ -8638,9 +9037,20 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8638
9037
  i++;
8639
9038
  continue;
8640
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
+ }
8641
9050
  if (operandsOnly) {
8642
9051
  const loaded = yield* parseInputOperandsSteps(t, files, state, stdinBytes);
8643
- if (loaded) stack.push(...loaded);
9052
+ if (!loaded) throw new Error(`unable to open image '${t}'`);
9053
+ stack.push(...loaded);
8644
9054
  i++;
8645
9055
  continue;
8646
9056
  }
@@ -8659,11 +9069,39 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8659
9069
  const endIdx = depth === 0 ? j - 1 : j;
8660
9070
  const subTokens = tokens.slice(i + 1, endIdx);
8661
9071
  const subState = { ...state };
8662
- const subResult = yield* evaluatePipelineTokensSteps(subTokens, files, subState, stack, stdinBytes, signal, analysis);
9072
+ const subResult = yield* evaluatePipelineTokensSteps(subTokens, files, subState, stack, stdinBytes, signal, analysis, registers);
8663
9073
  stack.push(...subResult);
8664
9074
  i = j;
8665
9075
  continue;
8666
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
+ }
8667
9105
  if (t === "-set" && tokens[i + 1]?.toLowerCase() === "delay") {
8668
9106
  const delay = Number(tokens[i + 2] ?? 0) * 10;
8669
9107
  stack = yield* mapSteps(stack, function* (image) {
@@ -8682,6 +9120,26 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8682
9120
  i += 3;
8683
9121
  continue;
8684
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
+ }
8685
9143
  const setting = applyMagickReadSetting(tokens, state, i);
8686
9144
  const pixelOperation = setting === void 0 ? parseMagickPixelOperation(tokens, state, i, signal) : void 0;
8687
9145
  const warpOperation = setting === void 0 ? parseMagickWarpOperation(tokens, state, i) : void 0;
@@ -8723,35 +9181,59 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8723
9181
  return { ...output, ..."space" in plan && plan.space ? { space: plan.space, channels: plan.space === "srgb" ? 4 : output.channels } : {}, ...plan.virtualCanvas ? { virtualCanvas: plan.virtualCanvas } : {} };
8724
9182
  });
8725
9183
  } else if (t === "-write" || t === "+write") {
8726
- const writePath = tokens[++i] ?? "";
8727
- 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:") {
8728
9195
  const top = stack[stack.length - 1];
8729
9196
  const { format, path } = inferOutputFormat(writePath, top.format);
8730
- 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))) {
8731
9198
  for (let idx = 0; idx < stack.length; idx++) {
8732
9199
  if (++cooperativeWork % 65536 === 0)
8733
9200
  yield;
8734
- const framePath = formatSceneOutputPath(path, idx);
8735
- 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 });
8736
9203
  files.set(framePath, frameBytes);
8737
9204
  }
8738
9205
  } else {
8739
- 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 });
8740
- 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);
8741
9210
  }
8742
9211
  }
8743
9212
  } else if (t === "-geometry") {
8744
9213
  state.geometry = tokens[++i] ?? "+0+0";
8745
9214
  } else if (t === "-tile") {
8746
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));
8747
9222
  } else if (t === "-strip") {
8748
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
+ }
8749
9228
  } else if (t === "-format") {
8750
9229
  state.formatStr = tokens[++i] ?? "";
8751
9230
  } else if (t === "+adjoin") {
8752
9231
  state.adjoin = false;
8753
9232
  } else if (t === "-adjoin") {
8754
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");
8755
9237
  } else if (t === "-delay") {
8756
9238
  state.delay = Number(tokens[++i] ?? 0) * 10;
8757
9239
  } else if (t === "-loop") {
@@ -8920,6 +9402,12 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8920
9402
  const { virtualCanvas: ignoredCanvas, ...reset } = image;
8921
9403
  return reset;
8922
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
+ });
8923
9411
  } else if (t === "-resize" || t === "-scale" || t === "-sample" || t === "-thumbnail") {
8924
9412
  const geom = tokens[++i] ?? "100%";
8925
9413
  const k = t === "-sample" ? "nearest" : state.kernel;
@@ -8950,18 +9438,22 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
8950
9438
  return { ...canvas, virtualCanvas: magickBorderCanvas(im, bw, bh) };
8951
9439
  });
8952
9440
  } else if (t === "-shave") {
8953
- const g = parseMagickGeometry(tokens[++i] ?? "0x0");
9441
+ const geometry = tokens[++i] ?? "0x0";
9442
+ const g = parseMagickGeometry(geometry);
8954
9443
  const sw = Math.max(0, Math.round(g.width ?? 0));
8955
9444
  const sh = Math.max(0, Math.round(g.height ?? sw));
8956
9445
  stack = yield* mapSteps(stack, function* (im) {
8957
- const clampedSw = Math.min(Math.floor((im.width - 1) / 2), sw);
8958
- const clampedSh = Math.min(Math.floor((im.height - 1) / 2), sh);
9446
+ if (sw * 2 >= im.width || sh * 2 >= im.height) {
9447
+ analysis.warnings += `magick: geometry does not contain image '${geometry}'
9448
+ `;
9449
+ return im;
9450
+ }
8959
9451
  return { ...yield* extractImageSteps2(im, {
8960
- left: clampedSw,
8961
- top: clampedSh,
8962
- width: Math.max(1, im.width - clampedSw * 2),
8963
- height: Math.max(1, im.height - clampedSh * 2)
8964
- }), virtualCanvas: magickBorderCanvas(im, -clampedSw, -clampedSh) };
9452
+ left: sw,
9453
+ top: sh,
9454
+ width: im.width - sw * 2,
9455
+ height: im.height - sh * 2
9456
+ }), virtualCanvas: magickBorderCanvas(im, -sw, -sh) };
8965
9457
  });
8966
9458
  } else if (t === "-trim") {
8967
9459
  stack = yield* mapSteps(stack, function* (im) {
@@ -9294,25 +9786,36 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack, stdinBy
9294
9786
  if (mergeOntoCanvas) {
9295
9787
  state.background = prevBg;
9296
9788
  }
9297
- if (loaded) {
9298
- if (mergeOntoCanvas && loaded[0]) {
9299
- yield* blitOverRgbaInPlaceSteps(stack[0], loaded[0], 0, 0);
9300
- detachRgbaBuffer(loaded[0].data);
9301
- } else {
9302
- stack.push(...loaded);
9303
- }
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);
9304
9795
  }
9305
9796
  }
9306
9797
  i++;
9307
9798
  }
9308
9799
  return stack;
9309
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
+ }
9310
9813
  function* runConvertCliSteps(argv, files, stdinBytes, signal) {
9311
- const analysis = { text: "", warnings: "" };
9814
+ const analysis = { text: "", warnings: "", bytes: [] };
9312
9815
  const result = yield* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis);
9313
- return { ...result, stdout: analysis.text + result.stdout, stderr: analysis.warnings + result.stderr };
9816
+ return combineConvertOutput(result, analysis);
9314
9817
  }
9315
- function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = { text: "", warnings: "" }) {
9818
+ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = { text: "", warnings: "", bytes: [] }) {
9316
9819
  const listRes = tryHandleMagickListOption(argv);
9317
9820
  if (listRes) return listRes;
9318
9821
  let cooperativeWork = 63;
@@ -9331,7 +9834,7 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9331
9834
  stderr: ""
9332
9835
  };
9333
9836
  }
9334
- const outSpec = argv[argv.length - 1];
9837
+ let outSpec = argv[argv.length - 1];
9335
9838
  const pipelineTokens = argv.slice(0, -1);
9336
9839
  const state = createDefaultState();
9337
9840
  try {
@@ -9344,9 +9847,10 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9344
9847
  `
9345
9848
  };
9346
9849
  }
9850
+ outSpec = expandFilenameProperties(outSpec, state);
9347
9851
  const finalImg = stack[stack.length - 1];
9348
9852
  const outLower = outSpec.toLowerCase();
9349
- if (outLower === "null:") return { exitCode: 0, stdout: "", stderr: "" };
9853
+ if (outLower === "null:" || outLower.startsWith("mpr:")) return { exitCode: 0, stdout: "", stderr: "" };
9350
9854
  if (outLower === "info:" || outLower === "info:-") {
9351
9855
  const formatted = [];
9352
9856
  for (const [idx, im] of stack.entries()) {
@@ -9379,50 +9883,21 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9379
9883
  return { exitCode: 0, stdout: hist, stderr: "" };
9380
9884
  }
9381
9885
  const { format, path: outPath } = inferOutputFormat(outSpec, "png");
9382
- 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))) {
9383
9887
  for (let idx = 0; idx < stack.length; idx++) {
9384
9888
  if (++cooperativeWork % 64 === 0)
9385
9889
  yield;
9386
- const framePath = formatSceneOutputPath(outPath, idx);
9387
- 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 });
9388
9892
  files.set(framePath, frameBytes);
9389
9893
  }
9390
9894
  return { exitCode: 0, stdout: "", stderr: "" };
9391
9895
  }
9392
- 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);
9393
9897
  let encodePageHeight;
9394
9898
  if (stack.length > 1 && format === "gif") {
9395
- let fw = 0, fh = 0;
9396
- for (const frame of stack) {
9397
- fw = Math.max(fw, frame.width);
9398
- fh = Math.max(fh, frame.height);
9399
- }
9400
- const combined = new Uint8Array(fw * fh * stack.length * 4);
9401
- const delays = [];
9402
- const disposals = [];
9403
- for (let idx = 0; idx < stack.length; idx++) {
9404
- if (++cooperativeWork % 64 === 0)
9405
- yield;
9406
- const outputFrame = yield* prepareHueOutput(stack[idx], format);
9407
- disposals.push(typeof outputFrame.disposal === "number" ? outputFrame.disposal : 1);
9408
- delays.push(outputFrame.delay?.[stack[idx][magickInput]?.sceneIdx ?? 0] ?? 100);
9409
- for (let y = 0; y < outputFrame.height; y++) {
9410
- if (++cooperativeWork % 64 === 0) yield;
9411
- combined.set(outputFrame.data.subarray(y * outputFrame.width * 4, (y + 1) * outputFrame.width * 4), (idx * fw * fh + y * fw) * 4);
9412
- }
9413
- }
9414
- toEncode = {
9415
- ...yield* prepareHueOutput(stack[0], format),
9416
- width: fw,
9417
- height: fh * stack.length,
9418
- pages: stack.length,
9419
- pageHeight: fh,
9420
- delay: delays,
9421
- disposal: disposals,
9422
- gifFrames: stack.map((frame) => ({ width: frame.width, height: frame.height, x: frame.virtualCanvas?.x ?? 0, y: frame.virtualCanvas?.y ?? 0 })),
9423
- data: combined
9424
- };
9425
- encodePageHeight = fh;
9899
+ toEncode = yield* prepareGifFrames(stack);
9900
+ encodePageHeight = toEncode.pageHeight;
9426
9901
  }
9427
9902
  if (typeof globalThis.gc === "function") {
9428
9903
  try {
@@ -9434,6 +9909,10 @@ function* runConvertCliResultSteps(argv, files, stdinBytes, signal, analysis = {
9434
9909
  }
9435
9910
  const { data: encoded } = encodeImage(toEncode, {
9436
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,
9437
9916
  ...state.samplingFactor === void 0 ? {} : { samplingFactor: state.samplingFactor },
9438
9917
  chromaSubsampling: state.quality < 90 ? "4:2:0" : "4:4:4",
9439
9918
  quality: state.quality,
@@ -9470,7 +9949,7 @@ async function transformStoredMagickRaster(image, backend, steps, signal, dimens
9470
9949
  signal.throwIfAborted();
9471
9950
  const request = next.value;
9472
9951
  if (!request) {
9473
- await yieldTurn11(signal);
9952
+ await yieldTurn12(signal);
9474
9953
  await checkpoint?.();
9475
9954
  next = steps.next();
9476
9955
  } else if (request.kind === "write") {
@@ -9502,7 +9981,7 @@ async function morphStoredMagickImage(image, backend, plan, signal) {
9502
9981
  const position = backend.storage.allocate(size);
9503
9982
  const a = images[step.a], b = images[step.b];
9504
9983
  for (let offset = 0; offset < size; offset += 16384) {
9505
- await yieldTurn11(signal);
9984
+ await yieldTurn12(signal);
9506
9985
  const length = Math.min(16384, size - offset);
9507
9986
  const left = new Uint8Array(await backend.storage.read(a.position + offset, length));
9508
9987
  const bytes = await drainSteps(subtractMagickPixelSteps(left, await backend.storage.read(b.position + offset, length), image.hasAlpha), signal);
@@ -9525,14 +10004,17 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9525
10004
  xs = [{ source: w - dx, target: 0, length: dx }, { source: 0, target: dx, length: w - dx }];
9526
10005
  ys = [{ source: h - dy, target: 0, length: dy }, { source: 0, target: dy, length: h - dy }];
9527
10006
  } else if (token === "-splice") {
9528
- const sw = Math.max(0, Math.round(g.width ?? 0)), sh = Math.max(0, Math.round(g.height ?? 0));
10007
+ const sw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? w / 100 : 1)));
10008
+ const sh = Math.max(0, Math.round((g.height ?? 0) * (g.isPercent ? h / 100 : 1)));
9529
10009
  const box = gravityAdjustBox(w, h, sw, sh, g.x, g.y, state.gravity);
9530
10010
  const anchor = resolveGravityOffset(sw, sh, state.gravity);
9531
- const x = Math.max(0, Math.min(w, box.x + anchor.left));
9532
- const y = Math.max(0, Math.min(h, box.y + anchor.top));
9533
10011
  width += sw;
9534
10012
  height += sh;
9535
- xs = [{ source: 0, target: 0, length: x }, { source: x, target: x + sw, length: w - x }];
10013
+ const xOffset = box.x + anchor.left;
10014
+ const x = xOffset < 0 || xOffset > w ? width : xOffset;
10015
+ if (box.y + anchor.top > height) throw new Error("pixels are not authentic");
10016
+ const y = Math.max(0, Math.min(h, box.y + anchor.top));
10017
+ xs = [{ source: 0, target: 0, length: x }, { source: x, target: x + sw, length: Math.max(0, w - x) }];
9536
10018
  ys = [{ source: 0, target: 0, length: y }, { source: y, target: y + sh, length: h - y }];
9537
10019
  } else {
9538
10020
  const cw = Math.max(0, Math.round((g.width ?? 0) * (g.isPercent ? w / 100 : 1)));
@@ -9551,9 +10033,18 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9551
10033
  let work = 0;
9552
10034
  for (const y of ys) for (let row = 0; row < y.length; row++) for (const x of xs) {
9553
10035
  for (let offset = 0; offset < x.length; offset += 4096) {
9554
- if (work++ % 64 === 0) await yieldTurn11(signal);
10036
+ if (work++ % 64 === 0) await yieldTurn12(signal);
9555
10037
  const length = Math.min(4096, x.length - offset) * 4;
9556
- const bytes = await backend.storage.read(image.position + ((y.source + row) * w + x.source + offset) * 4, length);
10038
+ const sourceX = Math.min(w - 1, x.source + offset);
10039
+ const sourceLength = Math.min(length, (w - sourceX) * 4);
10040
+ let bytes = await backend.storage.read(image.position + ((y.source + row) * w + sourceX) * 4, sourceLength);
10041
+ if (sourceLength < length) {
10042
+ const extended = new Uint8Array(length);
10043
+ extended.set(bytes);
10044
+ const edge = bytes.subarray(sourceLength - 4);
10045
+ for (let i = sourceLength; i < length; i += 4) extended.set(edge, i);
10046
+ bytes = extended;
10047
+ }
9557
10048
  await backend.storage.write(position + ((y.target + row) * width + x.target + offset) * 4, bytes);
9558
10049
  }
9559
10050
  }
@@ -9572,7 +10063,7 @@ async function compositeStoredMagick(base, overlay, backend, signal, left, top,
9572
10063
  let hasAlpha = base.hasAlpha || !generic && !compositeMode.delegated && !compositeMode.preservesAlpha;
9573
10064
  const position = inPlace ? base.position : backend.storage.allocate(base.width * base.height * 4);
9574
10065
  for (let start = 0; !inPlace && start < base.width * base.height * 4; start += 16384) {
9575
- await yieldTurn11(signal);
10066
+ await yieldTurn12(signal);
9576
10067
  await backend.storage.write(position + start, await backend.storage.read(base.position + start, Math.min(16384, base.width * base.height * 4 - start)));
9577
10068
  }
9578
10069
  const maskDestination = !generic && ["dstin", "destin", "srcin", "in", "srcout", "out", "dstatop", "destatop"].includes(compositeMode.name);
@@ -9581,7 +10072,7 @@ async function compositeStoredMagick(base, overlay, backend, signal, left, top,
9581
10072
  const bottom = tile || extendSource ? base.height : Math.min(base.height, top + overlay.height);
9582
10073
  for (let y = extendSource ? 0 : Math.max(0, top); y < bottom; y++) {
9583
10074
  for (let x = extendSource ? 0 : Math.max(0, left); x < right; ) {
9584
- await yieldTurn11(signal);
10075
+ await yieldTurn12(signal);
9585
10076
  const sourceX = tile ? x % overlay.width : Math.max(0, Math.min(overlay.width - 1, x - left));
9586
10077
  const sourceY = tile ? y % overlay.height : Math.max(0, Math.min(overlay.height - 1, y - top));
9587
10078
  const repeatEdge = extendSource && !tile && (x < left || x >= left + overlay.width);
@@ -9620,24 +10111,37 @@ async function compositeStoredMagick(base, overlay, backend, signal, left, top,
9620
10111
  }
9621
10112
  return { ...base, position, hasAlpha, channels: hasAlpha ? base.channels : base.channels <= 2 ? 1 : 3 };
9622
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
+ }
9623
10126
  async function tryConvertFiles(argv, input, stdinBytes, signal) {
9624
- const analysis = { text: "", warnings: "" };
10127
+ const analysis = { text: "", warnings: "", bytes: [] };
9625
10128
  const result = await tryConvertFilesResult(argv, input, stdinBytes, signal, analysis);
9626
- return result ? { ...result, stdout: analysis.text + result.stdout, stderr: analysis.warnings + result.stderr } : void 0;
10129
+ return result ? combineConvertOutput(result, analysis) : void 0;
9627
10130
  }
9628
10131
  async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis) {
9629
10132
  if (!input.filesystem.capabilities || !input.filesystem.open || !input.filesystem.removeFileConditional) return;
9630
- const outSpec = argv.at(-1);
10133
+ let outSpec = argv.at(-1) ?? "";
9631
10134
  const optionArgs = argv.slice(0, argv.indexOf("--") < 0 ? argv.length : argv.indexOf("--"));
9632
10135
  if (!outSpec || tryHandleMagickListOption(argv) || optionArgs.some((token) => ["--help", "-help", "-h", "--version", "-version"].includes(token))) {
9633
10136
  await probeImageArguments(argv, input, stdinBytes, signal);
9634
10137
  return drainSteps(runConvertCliSteps(argv, /* @__PURE__ */ new Map(), stdinBytes, signal), signal);
9635
10138
  }
9636
10139
  let output;
9637
- await yieldTurn11(signal);
10140
+ await yieldTurn12(signal);
9638
10141
  let state = createDefaultState();
9639
10142
  const tokens = argv.slice(0, -1), scopes = [];
9640
10143
  const steps = [];
10144
+ const registers = /* @__PURE__ */ new Map();
9641
10145
  const stackStep = (runStack) => {
9642
10146
  steps.push({ runStack });
9643
10147
  };
@@ -9645,7 +10149,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9645
10149
  try {
9646
10150
  output = inferOutputFormat(outSpec, "png");
9647
10151
  for (let i = 0; i < tokens.length; i++) {
9648
- if (i && i % 64 === 0) await yieldTurn11(signal);
10152
+ if (i && i % 64 === 0) await yieldTurn12(signal);
9649
10153
  const token = tokens[i];
9650
10154
  if (token === "") continue;
9651
10155
  if (!operandsOnly && token === "-sampling-factor") {
@@ -9701,20 +10205,20 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9701
10205
  const length = source.length;
9702
10206
  if (token.endsWith("clone") && length === 0) throw new Error(`UnableToCloneImage ${token}`);
9703
10207
  for (let repeat = 0; repeat < count; repeat++) for (const index of parseMagickIndexSpec(spec, length, "clone")) {
9704
- await yieldTurn11(signal);
10208
+ await yieldTurn12(signal);
9705
10209
  await stack.push(await source.get(index));
9706
10210
  }
9707
10211
  } else if (token.endsWith("delete")) await stack.remove(parseMagickIndexSpec(spec, stack.length, "delete"));
9708
10212
  else if (token === "-reverse") {
9709
10213
  for (let n = 0; n < Math.floor(stack.length / 2); n++) {
9710
- await yieldTurn11(signal);
10214
+ await yieldTurn12(signal);
9711
10215
  await stack.swap(n, stack.length - 1 - n);
9712
10216
  }
9713
10217
  } else if (token.endsWith("insert")) {
9714
10218
  const raw = token === "+insert" ? 0 : Number(spec), target = raw < 0 ? stack.length - 1 + raw : raw;
9715
10219
  if (!Number.isInteger(target) || target < 0 || target >= stack.length || raw < 0 && target === 0) throw new Error(`invalid image index ${token}`);
9716
10220
  for (let n = stack.length - 1; n > target; n--) {
9717
- await yieldTurn11(signal);
10221
+ await yieldTurn12(signal);
9718
10222
  await stack.swap(n, n - 1);
9719
10223
  }
9720
10224
  } else {
@@ -9766,7 +10270,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9766
10270
  const a = await stack.get(index), b = await stack.get(index + 1);
9767
10271
  await result.push(a);
9768
10272
  for (let step = 1; step <= count; step++) {
9769
- await yieldTurn11(signal);
10273
+ await yieldTurn12(signal);
9770
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));
9771
10275
  await result.push(await transformStoredMagickRaster(a, backend, morphPixelSteps(a, b, width, height, fraction), signal, { ...a, width, height }, [a, b]));
9772
10276
  }
@@ -9794,6 +10298,22 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9794
10298
  });
9795
10299
  continue;
9796
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
+ }
9797
10317
  if (!operandsOnly && token === "-set" && tokens[i + 1]?.toLowerCase() === "delay") {
9798
10318
  const delay = Number(tokens[i + 2] ?? 0) * 10;
9799
10319
  steps.push(async (image) => image ? {
@@ -9811,6 +10331,25 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9811
10331
  i += 2;
9812
10332
  continue;
9813
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
+ }
9814
10353
  const setting = operandsOnly ? void 0 : applyMagickReadSetting(tokens, state, i);
9815
10354
  if (setting !== void 0) {
9816
10355
  i = setting;
@@ -9868,7 +10407,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9868
10407
  if (!image) return;
9869
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;
9870
10409
  for (let offset = 0; offset < size; offset += 16384) {
9871
- if (offset % 1048576 === 0) await yieldTurn11(signal);
10410
+ if (offset % 1048576 === 0) await yieldTurn12(signal);
9872
10411
  const original = analysisOperation.highDepth ? image.storedData16 : void 0;
9873
10412
  const bytes = await backend.storage.read((original?.position ?? image.position) + offset * (original ? 2 : 1), Math.min(16384, size - offset) * (original ? 2 : 1));
9874
10413
  await drainSteps(accumulateMagickHistogram(original ? new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2) : bytes, histogram), signal);
@@ -9886,7 +10425,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9886
10425
  const layout = smushLayoutSteps(images, token === "-smush", gap, settings.gravity);
9887
10426
  let next = layout.next();
9888
10427
  while (!next.done) {
9889
- await yieldTurn11(signal);
10428
+ await yieldTurn12(signal);
9890
10429
  const request = next.value;
9891
10430
  next = layout.next(await backend.storage.read(images[request.index].position + request.position, request.length));
9892
10431
  }
@@ -9932,7 +10471,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9932
10471
  if (append && stack.length === 1) return stack;
9933
10472
  let width = 0, height = 0;
9934
10473
  for (let n = 0; n < (flatten ? 1 : stack.length); n++) {
9935
- await yieldTurn11(signal);
10474
+ await yieldTurn12(signal);
9936
10475
  const frame = await stack.get(n);
9937
10476
  width = append && !vertical ? width + frame.width : Math.max(width, frame.width);
9938
10477
  height = append && vertical ? height + frame.height : Math.max(height, frame.height);
@@ -9946,7 +10485,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9946
10485
  const left = vertical ? offset.left : cursor, top = vertical ? cursor : offset.top;
9947
10486
  for (let y = 0; y < frame.height; y++) {
9948
10487
  for (let x = 0; x < frame.width; x += 4096) {
9949
- await yieldTurn11(signal);
10488
+ await yieldTurn12(signal);
9950
10489
  const pixels = await backend.storage.read(frame.position + (y * frame.width + x) * 4, Math.min(4096, frame.width - x) * 4);
9951
10490
  await backend.storage.write(result.position + ((top + y) * width + left + x) * 4, pixels);
9952
10491
  }
@@ -9994,7 +10533,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9994
10533
  let next = bounds.next();
9995
10534
  while (!next.done) {
9996
10535
  if (!next.value) {
9997
- await yieldTurn11(signal);
10536
+ await yieldTurn12(signal);
9998
10537
  next = bounds.next();
9999
10538
  } else {
10000
10539
  const { image, position, length } = next.value;
@@ -10029,6 +10568,11 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10029
10568
  });
10030
10569
  continue;
10031
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
+ }
10032
10576
  if (!operandsOnly && (token === "+adjoin" || token === "-adjoin")) {
10033
10577
  state.adjoin = token === "-adjoin";
10034
10578
  continue;
@@ -10041,8 +10585,31 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10041
10585
  state.gravity = "northwest";
10042
10586
  continue;
10043
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
+ }
10044
10606
  if (!operandsOnly && token === "-strip") {
10045
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
+ });
10046
10613
  continue;
10047
10614
  }
10048
10615
  if (!operandsOnly && token === "+repage") {
@@ -10059,17 +10626,35 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10059
10626
  continue;
10060
10627
  }
10061
10628
  if (!operandsOnly && (token === "-write" || token === "+write")) {
10062
- 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;
10063
10636
  stackStep(async (stack, backend) => {
10637
+ if (stack.length === 0) throw new Error(`no images for write '${token}'`);
10064
10638
  const image = await stack.get(stack.length - 1);
10065
- 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:") {
10066
10647
  const output2 = inferOutputFormat(path, image.format);
10067
- 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))) {
10068
10649
  for (let index = 0; index < stack.length; index++) {
10069
- await yieldTurn11(signal);
10070
- 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 });
10071
10652
  }
10072
- } 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
+ }
10073
10658
  }
10074
10659
  return stack;
10075
10660
  });
@@ -10112,7 +10697,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10112
10697
  if (!image) return;
10113
10698
  for (const step of magickDrawSteps(image, drawing, settings)) {
10114
10699
  if (!step) {
10115
- await yieldTurn11(signal);
10700
+ await yieldTurn12(signal);
10116
10701
  continue;
10117
10702
  }
10118
10703
  if ((image.space === "b-w" || image.space === "grey16") && !(step.kind === "floodfill" && step.alphaOnly))
@@ -10228,6 +10813,14 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10228
10813
  } else if (!operandsOnly && ["-roll", "-splice", "-chop"].includes(token)) {
10229
10814
  const geometry = tokens[++i] ?? (token === "-roll" ? "+0+0" : "0x0"), captured = { ...state };
10230
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
+ });
10231
10824
  } else if (!operandsOnly && ["-resize", "-scale", "-sample", "-thumbnail"].includes(token)) {
10232
10825
  const geometry = tokens[++i] ?? "100%", kernel = token === "-sample" ? "nearest" : state.kernel, thumbnail = token === "-thumbnail";
10233
10826
  steps.push(async (image, backend) => {
@@ -10271,7 +10864,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10271
10864
  for (let n = 0; n < stack.length; n++) {
10272
10865
  const image = await stack.get(n);
10273
10866
  for (const area of magickCropAreas(image, geometry, gravity)) {
10274
- await yieldTurn11(signal);
10867
+ await yieldTurn12(signal);
10275
10868
  if (area === null) {
10276
10869
  analysis.warnings += `magick: geometry does not contain image '${geometry}'
10277
10870
  `;
@@ -10290,13 +10883,18 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10290
10883
  return frames;
10291
10884
  });
10292
10885
  } else if (!operandsOnly && token === "-shave") {
10293
- const geometry = parseMagickGeometry(tokens[++i] ?? "0x0"), width = Math.max(0, Math.round(geometry.width ?? 0)), height = Math.max(0, Math.round(geometry.height ?? width));
10886
+ const geometry = tokens[++i] ?? "0x0", parsed = parseMagickGeometry(geometry);
10887
+ const width = Math.max(0, Math.round(parsed.width ?? 0)), height = Math.max(0, Math.round(parsed.height ?? width));
10294
10888
  steps.push(async (image, backend) => {
10295
10889
  if (!image) return;
10296
- const left = Math.min(Math.floor((image.width - 1) / 2), width), top = Math.min(Math.floor((image.height - 1) / 2), height);
10890
+ if (width * 2 >= image.width || height * 2 >= image.height) {
10891
+ analysis.warnings += `magick: geometry does not contain image '${geometry}'
10892
+ `;
10893
+ return image;
10894
+ }
10297
10895
  return {
10298
- ...await transformStoredImage2(image, backend.storage, { kind: "extract", left, top, width: Math.max(1, image.width - left * 2), height: Math.max(1, image.height - top * 2) }, signal),
10299
- virtualCanvas: magickBorderCanvas(image, -left, -top)
10896
+ ...await transformStoredImage2(image, backend.storage, { kind: "extract", left: width, top: height, width: image.width - width * 2, height: image.height - height * 2 }, signal),
10897
+ virtualCanvas: magickBorderCanvas(image, -width, -height)
10300
10898
  };
10301
10899
  });
10302
10900
  } else if (!operandsOnly && token === "-trim") {
@@ -10310,6 +10908,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10310
10908
  steps.push(async (image, backend) => {
10311
10909
  if (!image) return;
10312
10910
  const layout = magickExtentLayout(image, geometry, captured);
10911
+ if (!layout) return image;
10313
10912
  const virtualCanvas = magickResizeCanvas(image, layout.width, layout.height);
10314
10913
  const source = image[magickInput];
10315
10914
  const canvas = {
@@ -10350,19 +10949,34 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10350
10949
  };
10351
10950
  });
10352
10951
  } else {
10353
- 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
+ }
10354
10958
  if (!operandsOnly && (token === "(" || token === ")")) continue;
10355
10959
  const captured = { ...state }, literal = operandsOnly, maxDecodeDimension = inferMaxDecodeDimensionFromUpcomingTokens(tokens, i + 1);
10356
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
+ }
10357
10970
  const base = stack.length === 1 ? await stack.get(0) : void 0, lower = token.toLowerCase();
10358
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);
10359
10972
  const settings = merge ? { ...captured, background: { r: 0, g: 0, b: 0, a: 0 } } : captured;
10360
- await parseStoredCompareInput(token, settings, backend, signal, { visit: async (image) => {
10973
+ const loaded = await parseStoredCompareInput(token, settings, backend, signal, { visit: async (image) => {
10361
10974
  if (merge) {
10362
10975
  const composed = await compositeStoredMagick(base, image, backend, signal, 0, 0, "over");
10363
10976
  await stack.set(0, { ...composed, hasAlpha: base.hasAlpha, channels: base.channels });
10364
10977
  } else await stack.push(image);
10365
10978
  }, ...maxDecodeDimension === void 0 ? {} : { maxDecodeDimension } });
10979
+ if (!loaded) throw new Error(`unable to open image '${token}'`);
10366
10980
  return stack;
10367
10981
  });
10368
10982
  }
@@ -10403,8 +11017,10 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10403
11017
  return { exitCode: 1, stdout: "", stderr: `magick: no images defined '${outSpec}'
10404
11018
  ` };
10405
11019
  }
10406
- await yieldTurn11(signal);
10407
- 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:")) {
10408
11024
  await backend.publishPending();
10409
11025
  return { exitCode: 0, stdout: "", stderr: "" };
10410
11026
  }
@@ -10413,7 +11029,7 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10413
11029
  async function* information() {
10414
11030
  let last = "";
10415
11031
  for (let index = 0; index < stack.length; index++) {
10416
- await yieldTurn11(signal);
11032
+ await yieldTurn12(signal);
10417
11033
  const frame = await stack.get(index);
10418
11034
  if (index && !state.formatStr) yield "\n";
10419
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`;
@@ -10437,60 +11053,24 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
10437
11053
  await backend.publishPending();
10438
11054
  return { exitCode: 0, stdout, stderr: "" };
10439
11055
  }
10440
- 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))) {
10441
11057
  for (let index = 0; index < stack.length; index++) {
10442
- await yieldTurn11(signal);
10443
- 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 });
10444
11060
  }
10445
11061
  await backend.publishPending();
10446
11062
  return { exitCode: 0, stdout: "", stderr: "" };
10447
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);
10448
11066
  let pageHeight;
10449
11067
  if (stack.length > 1 && output.format === "gif") {
10450
- const first = await prepareStoredHueOutput(await stack.get(0), output.format, backend, signal);
10451
- let width = first.width, height = first.height;
10452
- for (let index = 1; index < stack.length; index++) {
10453
- const frame = await stack.get(index);
10454
- width = Math.max(width, frame.width);
10455
- height = Math.max(height, frame.height);
10456
- }
10457
- const size = width * height * 4, position = backend.storage.allocate(size * stack.length);
10458
- for (let index = 0; index < stack.length; index++) {
10459
- const frame = await prepareStoredHueOutput(await stack.get(index), output.format, backend, signal);
10460
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x += 4096) {
10461
- await yieldTurn11(signal);
10462
- const count = Math.min(4096, width - x), bytes = new Uint8Array(count * 4);
10463
- 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));
10464
- await backend.storage.write(position + index * size + (y * width + x) * 4, bytes);
10465
- }
10466
- }
10467
- pageHeight = height;
10468
- const { storedData16: ignored, ...metadata } = first;
10469
- image = {
10470
- ...metadata,
10471
- position,
10472
- width,
10473
- height: height * stack.length,
10474
- pages: stack.length,
10475
- pageHeight,
10476
- storedGifFrame: { async at(index) {
10477
- const frame = await stack.get(index);
10478
- return { width: frame.width, height: frame.height, x: frame.virtualCanvas?.x ?? 0, y: frame.virtualCanvas?.y ?? 0 };
10479
- } },
10480
- storedDisposal: { length: stack.length, async at(index) {
10481
- const frame = await stack.get(index);
10482
- return typeof frame.disposal === "number" ? frame.disposal : 1;
10483
- } },
10484
- storedDelay: { length: stack.length, async at(index) {
10485
- const frame = await stack.get(index);
10486
- const scene2 = frame[magickInput]?.sceneIdx ?? 0;
10487
- return await frame.storedDelay?.at(scene2, { signal }) ?? frame.delay?.[scene2] ?? 100;
10488
- } }
10489
- };
11068
+ image = await prepareStoredGifFrames(stack, backend, signal);
11069
+ pageHeight = image.pageHeight;
10490
11070
  }
10491
11071
  const scene = pageHeight === void 0 ? (await stack.get(0))[magickInput]?.sceneIdx ?? 0 : 0;
10492
11072
  const delay = pageHeight === void 0 ? await image.storedDelay?.at(scene, { signal }) ?? image.delay?.[scene] : void 0;
10493
- 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 } : {} });
10494
11074
  return { exitCode: 0, stdout: "", stderr: "", ...stdoutBytes ? { stdoutBytes } : {} };
10495
11075
  });
10496
11076
  }
@@ -10522,6 +11102,7 @@ function* parseMogrifySteps(argv) {
10522
11102
  const readSettings = [];
10523
11103
  const targets = [];
10524
11104
  const flagsWithOneArg = /* @__PURE__ */ new Set([
11105
+ "-resample",
10525
11106
  "-resize",
10526
11107
  "-scale",
10527
11108
  "-sample",
@@ -10567,12 +11148,15 @@ function* parseMogrifySteps(argv) {
10567
11148
  "-liquid-rescale",
10568
11149
  "-adaptive-resize",
10569
11150
  "-define",
11151
+ "+set",
10570
11152
  "-write",
10571
11153
  "+write",
10572
11154
  "-compose",
10573
11155
  "-geometry",
10574
11156
  "-size",
10575
11157
  "-depth",
11158
+ "-compress",
11159
+ "-endian",
10576
11160
  "-units",
10577
11161
  "-interlace",
10578
11162
  "-sampling-factor",
@@ -10626,7 +11210,7 @@ function* parseMogrifySteps(argv) {
10626
11210
  }
10627
11211
  } else if (t === "-evaluate" || t === "-function" || t === "-morphology" || t === "-statistic" || t === "-set" || t === "-distort" || t === "+distort") {
10628
11212
  opTokens.push(t, argv[++i] ?? "", argv[++i] ?? "");
10629
- } else if (["-density", "-background", "-fuzz", "-filter", "-size"].includes(t)) {
11213
+ } else if (["-density", "-background", "-fuzz", "-filter", "-size", "-comment", "-label"].includes(t)) {
10630
11214
  readSettings.push(t, argv[++i] ?? "");
10631
11215
  } else if (flagsWithOneArg.has(t)) {
10632
11216
  opTokens.push(t, argv[++i] ?? "");
@@ -10739,13 +11323,13 @@ async function probeImageArguments(argv, files, stdinBytes, active) {
10739
11323
  await withImageInputs(files, stdinBytes, active, async (read) => {
10740
11324
  const inspect = async (source) => {
10741
11325
  for (let position = 0; position < source.size; position += 16384) {
10742
- await yieldTurn11(active);
11326
+ await yieldTurn12(active);
10743
11327
  await source.read(position, Math.min(16384, source.size - position), { signal: active });
10744
11328
  }
10745
11329
  };
10746
11330
  let operandsOnly = false;
10747
11331
  for (let index = 0; index < argv.length; index++) {
10748
- await yieldTurn11(active);
11332
+ await yieldTurn12(active);
10749
11333
  const token = argv[index];
10750
11334
  if (!operandsOnly && token === "--") {
10751
11335
  operandsOnly = true;
@@ -10796,6 +11380,19 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10796
11380
  const pattern = await parseStoredCompareInput(baseToken.slice(5), state, backend, signal, { source: readOptions.source, lastPage: readOptions.lastPage === true });
10797
11381
  if (!pattern) return;
10798
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);
10799
11396
  } else if (lower.startsWith("xc:") || lower.startsWith("canvas:") || lower === "null:") {
10800
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;
10801
11398
  image = { ...await createStoredCanvas(width, height, color, backend, signal), hasAlpha: color.a < 255 };
@@ -10805,7 +11402,7 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10805
11402
  const [first, last] = baseToken.slice(baseToken.indexOf(":") + 1).split("-"), a = parseColor(checker ? "white" : first || "#ffffff"), b = parseColor(checker ? "black" : last || "#000000");
10806
11403
  const cx = (width - 1) / 2, cy = (height - 1) / 2, radius = Math.max(1, Math.hypot(cx, cy));
10807
11404
  for (let start = 0; start < width * height; start += 4096) {
10808
- if (start % 262144 === 0) await yieldTurn11(signal);
11405
+ if (start % 262144 === 0) await yieldTurn12(signal);
10809
11406
  const count = Math.min(4096, width * height - start), bytes = new Uint8Array(count * 4);
10810
11407
  for (let i = 0; i < count; i++) {
10811
11408
  const x = (start + i) % width, y = Math.floor((start + i) / width), offset = i * 4;
@@ -10867,7 +11464,7 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10867
11464
  const width = state.sizeWidth, height = state.sizeHeight, position = backend.storage.allocate(width * height * 4);
10868
11465
  let cachedPosition = -1, cached = new Uint8Array();
10869
11466
  for (let start = 0; start < width * height; start += 4096) {
10870
- if (start % 262144 === 0) await yieldTurn11(signal);
11467
+ if (start % 262144 === 0) await yieldTurn12(signal);
10871
11468
  const count = Math.min(4096, width * height - start), bytes = new Uint8Array(count * 4);
10872
11469
  for (let offset = 0; offset < count; ) {
10873
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;
@@ -10885,6 +11482,15 @@ async function parseStoredCompareInput(token, state, backend, signal, readOption
10885
11482
  return { position, width, height, format: "png", channels: 4, depth: "uchar", space: "srgb", density: 72, hasAlpha: pattern.hasAlpha };
10886
11483
  }
10887
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
+ }
10888
11494
  if (state.delay !== void 0) {
10889
11495
  const delay = state.delay;
10890
11496
  image2 = { ...image2, storedDelay: { length: image2.storedDelay?.length ?? image2.delay?.length ?? 1, async at() {
@@ -11609,7 +12215,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11609
12215
  } };
11610
12216
  };
11611
12217
  for (let index = 0; index < paths.length; index++) {
11612
- await yieldTurn11(signal);
12218
+ await yieldTurn12(signal);
11613
12219
  const token = paths[index];
11614
12220
  let path2 = parseInputToken(token.toLowerCase().startsWith("tile:") ? token.slice(5) : token).baseToken;
11615
12221
  const colon = path2.indexOf(":");
@@ -11617,7 +12223,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11617
12223
  await backend.read(path2, async (source2) => {
11618
12224
  const position = backend.storage.allocate(source2.size);
11619
12225
  for (let start = 0; start < source2.size; start += 16384) {
11620
- await yieldTurn11(signal);
12226
+ await yieldTurn12(signal);
11621
12227
  await backend.storage.write(position + start, await source2.read(start, Math.min(16384, source2.size - start), { signal }));
11622
12228
  }
11623
12229
  await sources.set(BigInt(index * 5), BigInt(position));
@@ -11647,7 +12253,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11647
12253
  await sources.set(BigInt(index * 5 + 3), BigInt(height));
11648
12254
  let directPng = metadata.format === "png" && !metadata.isProgressive && prefix[24] === 8 && (prefix[25] === 2 || prefix[25] === 6), idat = false;
11649
12255
  if (directPng) for (let offset = 8; offset + 8 <= source.size; ) {
11650
- await yieldTurn11(signal);
12256
+ await yieldTurn12(signal);
11651
12257
  const header = await source.read(offset, 8), size = new DataView(header.buffer, header.byteOffset, 4).getUint32(0, false);
11652
12258
  if (offset + 8 + size > source.size) break;
11653
12259
  const name = String.fromCharCode(...header.subarray(4));
@@ -11711,7 +12317,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11711
12317
  canvasWidth = canvasHeight = 0;
11712
12318
  let rowWidth = 0, rowHeight = 0;
11713
12319
  for (let index = 0; index < Math.min(n, cols * rows); index++) {
11714
- await yieldTurn11(signal);
12320
+ await yieldTurn12(signal);
11715
12321
  const image = fast ? void 0 : await images.get(index);
11716
12322
  const width = image ? image.width : Number(await sources.get(BigInt(index * 5 + 2)));
11717
12323
  const height = image ? image.height : Number(await sources.get(BigInt(index * 5 + 3)));
@@ -11729,7 +12335,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11729
12335
  let canvas = await createStoredCanvas(Math.max(1, canvasWidth), Math.max(1, canvasHeight), state.background, backend, signal);
11730
12336
  let anyTransparent = state.background.a < 255 || borderW > 0 && state.borderColor.a < 255;
11731
12337
  for (let index = 0; index < n; index++) {
11732
- await yieldTurn11(signal);
12338
+ await yieldTurn12(signal);
11733
12339
  const col = index % cols, row = Math.floor(index / cols);
11734
12340
  if (row >= rows) break;
11735
12341
  let image, directPng = false;
@@ -11764,7 +12370,7 @@ async function runMontageFiles(argv, input, stdinBytes, signal) {
11764
12370
  const x = (variableCells ? tileX : col * slotW) + (concatenate ? borderW : 0) + padX + offset.left, y = (variableCells ? tileY : row * slotH) + (concatenate ? borderW : 0) + padY + offset.top;
11765
12371
  tileX += width + padX * 2;
11766
12372
  if (!anyTransparent) for (let start = 0; start < image.width * image.height * 4; start += 16384) {
11767
- await yieldTurn11(signal);
12373
+ await yieldTurn12(signal);
11768
12374
  const bytes = await backend.storage.read(image.position + start, Math.min(16384, image.width * image.height * 4 - start));
11769
12375
  for (let k = 3; k < bytes.length; k += 4) if (bytes[k] < 255) {
11770
12376
  anyTransparent = true;
@@ -11856,7 +12462,7 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11856
12462
  const hasOutputOperand = runner !== runIdentifyCli && !(runner === runMagickCli && argv[0] === "identify");
11857
12463
  let operandsOnly = false;
11858
12464
  for (const [index, token] of argv.entries()) {
11859
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12465
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11860
12466
  if (!operandsOnly && token === "--") {
11861
12467
  operandsOnly = true;
11862
12468
  continue;
@@ -11891,7 +12497,7 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11891
12497
  const chunks = [];
11892
12498
  let total = 0;
11893
12499
  for await (const chunk of readBytes2(context.stdin, signal)) {
11894
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12500
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11895
12501
  chargeInput(chunk.byteLength);
11896
12502
  chunks.push(chunk);
11897
12503
  total += chunk.byteLength;
@@ -11899,7 +12505,7 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11899
12505
  stdinBytes = new Uint8Array(total);
11900
12506
  let off = 0;
11901
12507
  for (const chunk of chunks) {
11902
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12508
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11903
12509
  stdinBytes.set(chunk, off);
11904
12510
  off += chunk.byteLength;
11905
12511
  }
@@ -11919,10 +12525,17 @@ async function runBufferedImageFiles(argv, input, runner, stdinBytes, signal) {
11919
12525
  delivered = { ...result, stdout: "" };
11920
12526
  }
11921
12527
  for (const [key, val] of vfsFiles.entries()) {
11922
- if (++cooperativeWork % 64 === 0) await yieldTurn11(context.signal);
12528
+ if (++cooperativeWork % 64 === 0) await yieldTurn12(context.signal);
11923
12529
  if (existingSnap.get(key) !== val) {
11924
12530
  const abs = resolvePath3(context.cwd, key);
11925
- 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
+ }
11926
12539
  }
11927
12540
  }
11928
12541
  vfsFiles.clear();
@@ -11985,6 +12598,12 @@ async function executeVfsMagickTool(context, runner, maxInputBytes) {
11985
12598
  await writeBytes2(stdout.output, new TextEncoder().encode(res.stdout), invocation.signal);
11986
12599
  }
11987
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 };
11988
12607
  } finally {
11989
12608
  await invocation.close();
11990
12609
  }