@poe-platform/safe-bash 0.1.835 → 0.1.837

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.
@@ -11,6 +11,8 @@ export declare class GifPalette {
11
11
  private readonly colors;
12
12
  private readonly used;
13
13
  private readonly missing;
14
+ private readonly histogram;
15
+ private quantized?;
14
16
  constructor();
15
17
  add(r: number, g: number, b: number, a: number): void;
16
18
  finish(): void;
@@ -1,4 +1,4 @@
1
1
  import type { ImageByteStorage, StoredRgbaImage } from "./png-storage.js";
2
2
  import { type GifOptions } from "./gif-output-parts.js";
3
- /** Multipass GIF encoding with a fixed palette and pull-driven owned subblocks. */
3
+ /** Multipass GIF encoding with a source-derived palette and pull-driven owned subblocks. */
4
4
  export declare function encodeGifFromStorage(image: StoredRgbaImage, storage: ImageByteStorage, signal: AbortSignal, options?: GifOptions): AsyncGenerator<Uint8Array<ArrayBufferLike>, void, unknown>;
@@ -11,6 +11,8 @@ export declare class GifPalette {
11
11
  private readonly colors;
12
12
  private readonly used;
13
13
  private readonly missing;
14
+ private readonly histogram;
15
+ private quantized?;
14
16
  constructor();
15
17
  add(r: number, g: number, b: number, a: number): void;
16
18
  finish(): void;
@@ -1,4 +1,4 @@
1
1
  import type { ImageByteStorage, StoredRgbaImage } from "./png-storage.js";
2
2
  import { type GifOptions } from "./gif-output-parts.js";
3
- /** Multipass GIF encoding with a fixed palette and pull-driven owned subblocks. */
3
+ /** Multipass GIF encoding with a source-derived palette and pull-driven owned subblocks. */
4
4
  export declare function encodeGifFromStorage(image: StoredRgbaImage, storage: ImageByteStorage, signal: AbortSignal, options?: GifOptions): AsyncGenerator<Uint8Array<ArrayBufferLike>, void, unknown>;
@@ -5394,12 +5394,66 @@ function decodeWebpImage(bytes) {
5394
5394
  };
5395
5395
  }
5396
5396
 
5397
+ // src/codecs/gif-quantization.ts
5398
+ function quantizeGif(histogram, palette) {
5399
+ let bins = [];
5400
+ for (let id = 0; id < 32768; id++) {
5401
+ let at = id * 4, count = histogram[at + 3];
5402
+ count && bins.push([
5403
+ histogram[at] / count,
5404
+ histogram[at + 1] / count,
5405
+ histogram[at + 2] / count,
5406
+ count,
5407
+ id
5408
+ ]);
5409
+ }
5410
+ let box2 = (colors) => {
5411
+ let sum = [0, 0, 0], squares = [0, 0, 0], count = 0;
5412
+ for (let color of colors) {
5413
+ count += color[3];
5414
+ for (let c = 0; c < 3; c++)
5415
+ sum[c] += color[c] * color[3], squares[c] += color[c] ** 2 * color[3];
5416
+ }
5417
+ let variance = squares.map((value, c) => Math.max(0, value - sum[c] ** 2 / count)), axis = variance.indexOf(Math.max(...variance));
5418
+ return { colors, count, sum, axis, error: colors.length > 1 ? variance[axis] : 0 };
5419
+ }, boxes = [box2(bins)];
5420
+ for (; boxes.length < 254; ) {
5421
+ let best = 0;
5422
+ for (let i = 1; i < boxes.length; i++) boxes[i].error > boxes[best].error && (best = i);
5423
+ let current = boxes[best];
5424
+ if (current.error <= 0) break;
5425
+ current.colors.sort((a, b) => a[current.axis] - b[current.axis]);
5426
+ let count = 0, split = 0;
5427
+ do
5428
+ count += current.colors[split++][3];
5429
+ while (count < current.count / 2 && split < current.colors.length - 1);
5430
+ boxes[best] = box2(current.colors.slice(0, split)), boxes.push(box2(current.colors.slice(split)));
5431
+ }
5432
+ palette.fill(0, 0, 762);
5433
+ for (let i = 0; i < boxes.length; i++)
5434
+ for (let c = 0; c < 3; c++)
5435
+ palette[i * 3 + c] = Math.round(boxes[i].sum[c] / boxes[i].count);
5436
+ let indices2 = new Uint8Array(32768);
5437
+ for (let color of bins) {
5438
+ let distance = 1 / 0, nearest = 0;
5439
+ for (let i = 0; i <= boxes.length; i++) {
5440
+ let index = i === boxes.length ? 254 : i, error = 0;
5441
+ for (let c = 0; c < 3; c++) error += (color[c] - palette[index * 3 + c]) ** 2;
5442
+ error < distance && (distance = error, nearest = index);
5443
+ }
5444
+ indices2[color[4]] = nearest;
5445
+ }
5446
+ return indices2;
5447
+ }
5448
+
5397
5449
  // src/codecs/gif-output-parts.ts
5398
5450
  var GifPalette = class {
5399
5451
  bytes = new Uint8Array(768);
5400
5452
  colors = /* @__PURE__ */ new Map();
5401
5453
  used = /* @__PURE__ */ new Set();
5402
5454
  missing = /* @__PURE__ */ new Set();
5455
+ histogram = new Float64Array(32768 * 4);
5456
+ quantized;
5403
5457
  constructor() {
5404
5458
  for (let i = 0; i < 254; i++) {
5405
5459
  let r = Math.round((i >>> 5 & 7) * 255 / 7), g = Math.round((i >>> 2 & 7) * 255 / 7), b = Math.round((i & 3) * 255 / 3);
@@ -5409,6 +5463,10 @@ var GifPalette = class {
5409
5463
  }
5410
5464
  add(r, g, b, a) {
5411
5465
  if (a < 128) return;
5466
+ if (r !== 255 || g !== 255 || b !== 255) {
5467
+ let at = (r >>> 3 << 10 | g >>> 3 << 5 | b >>> 3) * 4;
5468
+ this.histogram[at] += r, this.histogram[at + 1] += g, this.histogram[at + 2] += b, this.histogram[at + 3]++;
5469
+ }
5412
5470
  let key = r << 16 | g << 8 | b, existing = this.colors.get(key);
5413
5471
  existing !== void 0 ? this.used.add(existing) : this.missing.size < 255 && this.missing.add(key);
5414
5472
  }
@@ -5420,10 +5478,13 @@ var GifPalette = class {
5420
5478
  if (probe >= 254) break;
5421
5479
  this.used.add(probe), this.colors.set(key, probe), this.bytes.set([key >>> 16 & 255, key >>> 8 & 255, key & 255], probe * 3);
5422
5480
  }
5423
- }
5481
+ } else
5482
+ this.quantized = quantizeGif(this.histogram, this.bytes);
5424
5483
  }
5425
5484
  index(r, g, b, a) {
5426
5485
  if (a < 128) return 255;
5486
+ if (this.quantized)
5487
+ return r === 255 && g === 255 && b === 255 ? 254 : this.quantized[r >>> 3 << 10 | g >>> 3 << 5 | b >>> 3];
5427
5488
  let exact = this.colors.get(r << 16 | g << 8 | b);
5428
5489
  if (exact !== void 0) return exact;
5429
5490
  let value = r >>> 5 << 5 | g >>> 5 << 2 | b >>> 6;
@@ -5,7 +5,7 @@ import {
5
5
  PdfRetainedReader,
6
6
  renderPdfPageToPng,
7
7
  renderRetainedPagePixels
8
- } from "../safe-bash/command-chunks/chunk-YHRESVJ7.js";
8
+ } from "../safe-bash/command-chunks/chunk-4BSD2RGK.js";
9
9
  import "../safe-bash/command-chunks/chunk-E6SK3OU2.js";
10
10
  import "../safe-bash/command-chunks/chunk-NUGQQDGE.js";
11
11
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
@@ -257,7 +257,7 @@ import {
257
257
  transformNodeStringsAndStreamsSteps,
258
258
  transformPathSegment,
259
259
  transformPoint
260
- } from "../safe-bash/command-chunks/chunk-YHRESVJ7.js";
260
+ } from "../safe-bash/command-chunks/chunk-4BSD2RGK.js";
261
261
  import "../safe-bash/command-chunks/chunk-E6SK3OU2.js";
262
262
  import "../safe-bash/command-chunks/chunk-NUGQQDGE.js";
263
263
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
@@ -3,7 +3,7 @@ import { type PdfRetainedPixelOptions } from "./retained-page-pixels.js";
3
3
  import type { PdfIndexStorage } from "../cos/object-index.js";
4
4
  import type { PdfRetainedPngOptions } from "./retained-png.js";
5
5
  import { type ParsedCosDocument } from "../cos/parser.js";
6
- import type { PdfDisplayList, PdfPaintOperation, PdfRgbColor, PdfSoftMask } from "../ast.js";
6
+ import type { PdfStoredOperations, PdfDisplayList, PdfPaintOperation, PdfRgbColor, PdfSoftMask } from "../ast.js";
7
7
  export interface RgbaBitmap {
8
8
  readonly width: number;
9
9
  readonly height: number;
@@ -48,10 +48,11 @@ export type PdfRasterWindowOptions = Pick<RenderToPngOptions, "scale" | "dpi" |
48
48
  * by the caller. Crop, rotation and anisotropic resampling belong to the driver. */
49
49
  export declare function renderDisplayListWindowSteps(displayList: PdfDisplayList, window: PdfCropRect, options?: PdfRasterWindowOptions): Generator<void, RgbaBitmap, void>;
50
50
  /** Consume a replayable operation source without collecting a page display list.
51
- * Opaque output makes a compositing prepass to preserve background quantization.
51
+ * Opaque output uses stored effect flags or a compositing prepass to preserve
52
+ * background quantization.
52
53
  * Each operation is painted before advancing its source. Individual paths,
53
54
  * resources and nested captures still have the evaluator's memory ownership. */
54
- export declare function renderOperationStreamWindow(page: Pick<PdfDisplayList, "width" | "height" | "origin">, operations: () => AsyncIterable<PdfPaintOperation>, window: PdfCropRect, options?: PdfRasterWindowOptions & {
55
+ export declare function renderOperationStreamWindow(page: Pick<PdfDisplayList, "width" | "height" | "origin">, operations: (() => AsyncIterable<PdfPaintOperation>) | PdfStoredOperations, window: PdfCropRect, options?: PdfRasterWindowOptions & {
55
56
  readonly signal?: AbortSignal;
56
57
  }): Promise<RgbaBitmap>;
57
58
  export declare function renderDisplayListToBitmapSteps(displayList: PdfDisplayList, options?: RenderToPngOptions): Generator<void, RgbaBitmap, void>;
@@ -15,6 +15,7 @@ export interface PdfRetainedPixels {
15
15
  }
16
16
  /** Render one bounded window at a time into caller-backed tile storage, then
17
17
  * read transformed pixels without allocating the full page or collecting paints.
18
- * Replays page evaluation per tile; nested captures/resources retain their own
19
- * admission contracts. Storage is acquired lazily and owned by the iterator. */
18
+ * With caller-owned image backing, evaluate once and replay stored paints per
19
+ * tile. Otherwise replay evaluation. Nested captures/resources retain their own
20
+ * admission contracts. Tile storage is acquired lazily and owned by the iterator. */
20
21
  export declare function renderRetainedPagePixels(page: PdfRetainedPage, storage: PdfIndexStorage, options?: PdfRetainedPixelOptions): Promise<PdfRetainedPixels>;
@@ -26663,6 +26663,71 @@ var PdfStagingStorage = class {
26663
26663
  }
26664
26664
  };
26665
26665
 
26666
+ // packages/pdf-ast/src/content/stored-operations.ts
26667
+ function operationEffects(operation) {
26668
+ const value = operation.value;
26669
+ let effects = (value.softMask ? 2 : 0) | (value.blendMode && value.blendMode !== "Normal" && value.blendMode !== "Compatible" ? 1 : 0);
26670
+ if (operation.kind === "group") {
26671
+ let children = operation.value.storedOperations?.effects ?? 0;
26672
+ for (const child of operation.value.operations) children |= operationEffects(child);
26673
+ if (operation.value.isolated === false && children & 3) effects |= 4;
26674
+ effects |= children;
26675
+ }
26676
+ return effects;
26677
+ }
26678
+ var StoredOperationsWriter = class {
26679
+ constructor(storage, signal) {
26680
+ this.storage = storage;
26681
+ this.signal = signal;
26682
+ }
26683
+ storage;
26684
+ signal;
26685
+ first = -1;
26686
+ last = -1;
26687
+ count = 0;
26688
+ effects = 0;
26689
+ async append(operation) {
26690
+ const position = await writeStoredRecord(this.storage, operation, -1, this.signal);
26691
+ const header2 = new Uint8Array(8), view = new DataView(header2.buffer);
26692
+ if (this.last >= 0) {
26693
+ view.setFloat64(0, position, true);
26694
+ await this.storage.write(
26695
+ this.last,
26696
+ header2.subarray(0, 8),
26697
+ this.signal ? { signal: this.signal } : void 0
26698
+ );
26699
+ }
26700
+ this.signal?.throwIfAborted();
26701
+ if (this.first < 0) this.first = position;
26702
+ this.last = position;
26703
+ this.count++;
26704
+ this.effects |= operationEffects(operation);
26705
+ }
26706
+ snapshot() {
26707
+ return {
26708
+ kind: "stored-operations",
26709
+ storage: this.storage,
26710
+ position: this.first,
26711
+ count: this.count,
26712
+ effects: this.effects
26713
+ };
26714
+ }
26715
+ };
26716
+ async function* readStoredOperations(source, signal) {
26717
+ if (!Number.isSafeInteger(source.count) || source.count < 0)
26718
+ throw new RangeError("Invalid capture count");
26719
+ let position = source.position;
26720
+ for (let record = 0; record < source.count; record++) {
26721
+ const { value, next } = await readStoredRecord(
26722
+ source.storage,
26723
+ position,
26724
+ signal
26725
+ );
26726
+ yield value;
26727
+ position = next;
26728
+ }
26729
+ }
26730
+
26666
26731
  // packages/pdf-ast/src/render/raster.ts
26667
26732
  import { PagedStorage as PagedStorage3, IntegerTable as IntegerTable3 } from "@poe-platform/safe-fs/storage";
26668
26733
 
@@ -27077,71 +27142,6 @@ async function* encodeSvgPathDataChunks(input, height, options = {}) {
27077
27142
  if (used) yield buffer.slice(0, used);
27078
27143
  }
27079
27144
 
27080
- // packages/pdf-ast/src/content/stored-operations.ts
27081
- function operationEffects(operation) {
27082
- const value = operation.value;
27083
- let effects = (value.softMask ? 2 : 0) | (value.blendMode && value.blendMode !== "Normal" && value.blendMode !== "Compatible" ? 1 : 0);
27084
- if (operation.kind === "group") {
27085
- let children = operation.value.storedOperations?.effects ?? 0;
27086
- for (const child of operation.value.operations) children |= operationEffects(child);
27087
- if (operation.value.isolated === false && children & 3) effects |= 4;
27088
- effects |= children;
27089
- }
27090
- return effects;
27091
- }
27092
- var StoredOperationsWriter = class {
27093
- constructor(storage, signal) {
27094
- this.storage = storage;
27095
- this.signal = signal;
27096
- }
27097
- storage;
27098
- signal;
27099
- first = -1;
27100
- last = -1;
27101
- count = 0;
27102
- effects = 0;
27103
- async append(operation) {
27104
- const position = await writeStoredRecord(this.storage, operation, -1, this.signal);
27105
- const header2 = new Uint8Array(8), view = new DataView(header2.buffer);
27106
- if (this.last >= 0) {
27107
- view.setFloat64(0, position, true);
27108
- await this.storage.write(
27109
- this.last,
27110
- header2.subarray(0, 8),
27111
- this.signal ? { signal: this.signal } : void 0
27112
- );
27113
- }
27114
- this.signal?.throwIfAborted();
27115
- if (this.first < 0) this.first = position;
27116
- this.last = position;
27117
- this.count++;
27118
- this.effects |= operationEffects(operation);
27119
- }
27120
- snapshot() {
27121
- return {
27122
- kind: "stored-operations",
27123
- storage: this.storage,
27124
- position: this.first,
27125
- count: this.count,
27126
- effects: this.effects
27127
- };
27128
- }
27129
- };
27130
- async function* readStoredOperations(source, signal) {
27131
- if (!Number.isSafeInteger(source.count) || source.count < 0)
27132
- throw new RangeError("Invalid capture count");
27133
- let position = source.position;
27134
- for (let record = 0; record < source.count; record++) {
27135
- const { value, next } = await readStoredRecord(
27136
- source.storage,
27137
- position,
27138
- signal
27139
- );
27140
- yield value;
27141
- position = next;
27142
- }
27143
- }
27144
-
27145
27145
  // packages/pdf-ast/src/content/stored-clips.ts
27146
27146
  var PAGE_BYTES = 4096;
27147
27147
  var RECORD_BYTES2 = 64;
@@ -48242,8 +48242,46 @@ function strokeEdges(contours) {
48242
48242
  } };
48243
48243
  }
48244
48244
  var SUB_OFFSETS_4X4 = [0.125, 0.375, 0.625, 0.875];
48245
+ function* cacheSmallEdgesSteps(edges) {
48246
+ if (Array.isArray(edges)) return edges;
48247
+ const cached = [];
48248
+ for (const edge of edges) {
48249
+ if (!edge) {
48250
+ yield;
48251
+ continue;
48252
+ }
48253
+ if (cached.length === 128) return edges;
48254
+ cached.push(edge);
48255
+ }
48256
+ return cached;
48257
+ }
48245
48258
  function* computeSubScanlineCountsSteps(edges, scanY, fillRule, deltas, winding, rowCounts) {
48246
48259
  const samples = winding.length;
48260
+ if (Array.isArray(edges)) {
48261
+ const crossings = [];
48262
+ for (const edge of edges) {
48263
+ if (edge.y0 <= scanY && edge.y1 > scanY || edge.y1 <= scanY && edge.y0 > scanY) {
48264
+ crossings.push({ x: edge.x0 + (scanY - edge.y0) / (edge.y1 - edge.y0) * (edge.x1 - edge.x0), direction: edge.y0 < edge.y1 ? 1 : -1 });
48265
+ }
48266
+ }
48267
+ crossings.sort((a, b) => a.x - b.x);
48268
+ let value2 = 0, start = 0;
48269
+ for (const crossing of crossings) {
48270
+ const before = value2;
48271
+ value2 = fillRule === "evenodd" ? 1 - value2 : value2 + crossing.direction;
48272
+ if (before === 0 && value2 !== 0) start = crossing.x;
48273
+ else if (before !== 0 && value2 === 0) {
48274
+ const first = Math.max(0, Math.ceil((start - 0.125) * 4));
48275
+ const end = Math.min(samples, Math.floor((crossing.x - 0.125) * 4) + 1);
48276
+ for (let index2 = first; index2 < end; ) {
48277
+ const count = Math.min(end - index2, 4 - index2 % 4);
48278
+ rowCounts[index2 >>> 2] = rowCounts[index2 >>> 2] + count;
48279
+ index2 += count;
48280
+ }
48281
+ }
48282
+ }
48283
+ return;
48284
+ }
48247
48285
  deltas.fill(0);
48248
48286
  let work = 0;
48249
48287
  for (const edge of edges) {
@@ -48279,6 +48317,7 @@ function* computeSubScanlineCountsSteps(edges, scanY, fillRule, deltas, winding,
48279
48317
  }
48280
48318
  }
48281
48319
  function* fillEdgesScanline4x4Steps(rgba, width, height, edges, color, alpha = 1, fillRule = "nonzero", clipScreen, antialias = true, blendMode, clipMask, groupAlpha) {
48320
+ edges = yield* cacheSmallEdgesSteps(edges);
48282
48321
  let work = 0;
48283
48322
  let minY = Infinity;
48284
48323
  let maxY = -Infinity;
@@ -48318,6 +48357,7 @@ function* fillEdgesScanline4x4Steps(rgba, width, height, edges, color, alpha = 1
48318
48357
  }
48319
48358
  }
48320
48359
  function* fillMaskScanline4x4Steps(mask, width, height, edges, fillRule = "nonzero", intersect = false) {
48360
+ edges = yield* cacheSmallEdgesSteps(edges);
48321
48361
  let work = 0;
48322
48362
  let minY = Infinity;
48323
48363
  let maxY = -Infinity;
@@ -48536,6 +48576,21 @@ function* renderDisplayListLayerSteps(displayList, options, scale, backdrop, win
48536
48576
  if (++work % 16384 === 0) yield;
48537
48577
  if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
48538
48578
  let operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
48579
+ let fillEdges;
48580
+ if (operation.kind === "path" && operation.value.fillColor && !operation.value.strokeColor) {
48581
+ const path = operation.value;
48582
+ fillEdges = yield* cacheSmallEdgesSteps(screenFillEdges(path.segments, toScreen, path.storedSegments, images));
48583
+ if (Array.isArray(fillEdges)) {
48584
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
48585
+ for (const edge of fillEdges) {
48586
+ minX = Math.min(minX, edge.x0, edge.x1);
48587
+ minY = Math.min(minY, edge.y0, edge.y1);
48588
+ maxX = Math.max(maxX, edge.x0, edge.x1);
48589
+ maxY = Math.max(maxY, edge.y0, edge.y1);
48590
+ }
48591
+ if (maxX < 0 || maxY < 0 || minX > width || minY > height) continue;
48592
+ }
48593
+ }
48539
48594
  if (operation.kind === "group") {
48540
48595
  const group = operation.value;
48541
48596
  const bitmap = yield* renderDisplayListLayerSteps(
@@ -48637,7 +48692,7 @@ function* renderDisplayListLayerSteps(displayList, options, scale, backdrop, win
48637
48692
  if (operation.kind === "path") {
48638
48693
  const path = operation.value;
48639
48694
  if (path.fillColor) {
48640
- const edges = screenFillEdges(path.segments, toScreen, path.storedSegments, images);
48695
+ const edges = fillEdges ?? screenFillEdges(path.segments, toScreen, path.storedSegments, images);
48641
48696
  const clipScreen = path.clipRect ? [(path.clipRect[0] - originX) * scale - offsetX, (pageTop - path.clipRect[3]) * scale - offsetY, (path.clipRect[2] - originX) * scale - offsetX, (pageTop - path.clipRect[1]) * scale - offsetY] : void 0;
48642
48697
  yield* fillEdgesScanline4x4Steps(rgba, width, height, edges, path.fillColor, path.fillAlpha ?? 1, path.fillRule ?? "nonzero", clipScreen, original.kind === "glyph" ? aaTxt : aaVec, path.blendMode, clipMask, groupAlpha);
48643
48698
  }
@@ -48797,7 +48852,7 @@ async function renderOperationStreamWindow(page2, operations, window, options =
48797
48852
  const { signal } = options;
48798
48853
  signal?.throwIfAborted();
48799
48854
  async function* cursor() {
48800
- const source2 = operations()[Symbol.asyncIterator]();
48855
+ const source2 = (typeof operations === "function" ? operations() : readStoredOperations(operations, signal))[Symbol.asyncIterator]();
48801
48856
  let failed2 = false, pulls = 0;
48802
48857
  try {
48803
48858
  while (true) {
@@ -48819,8 +48874,8 @@ async function renderOperationStreamWindow(page2, operations, window, options =
48819
48874
  }
48820
48875
  }
48821
48876
  }
48822
- const input = { requested: false, next: void 0, compositing: false };
48823
- if (!options.transparent) for await (const operation of cursor()) {
48877
+ const input = { requested: false, next: void 0, compositing: typeof operations !== "function" && !!(operations.effects & 1) };
48878
+ if (!options.transparent && typeof operations === "function") for await (const operation of cursor()) {
48824
48879
  if (hasCompositingEffects([operation])) {
48825
48880
  input.compositing = true;
48826
48881
  break;
@@ -49671,10 +49726,17 @@ async function renderRetainedPagePixels(page2, storage, options = {}) {
49671
49726
  let source, failed = false;
49672
49727
  async function* tiles() {
49673
49728
  let work = 0;
49729
+ const paints = options.imageStorage ? new StoredOperationsWriter(options.imageStorage, signal) : void 0;
49730
+ if (paints) for await (const event of page2.evaluateSteps(shared, options)) {
49731
+ if (!event.captured) await paints.append(event.operation);
49732
+ if (++work % 256 === 0) await new Promise((resolve2) => setTimeout(resolve2, 0));
49733
+ signal?.throwIfAborted();
49734
+ }
49735
+ const stored = paints?.snapshot();
49674
49736
  for (let y = 0; y < height; y += tileSize) for (let x = 0; x < width; x += tileSize) {
49675
49737
  if (++work % 32 === 0) await new Promise((resolve2) => setTimeout(resolve2, 0));
49676
49738
  signal?.throwIfAborted();
49677
- const tile = await renderOperationStreamWindow(geometry, async function* () {
49739
+ const tile = await renderOperationStreamWindow(geometry, stored ?? async function* () {
49678
49740
  for await (const event of page2.evaluateSteps(shared, options)) if (!event.captured) yield event.operation;
49679
49741
  }, { x, y, width: Math.min(tileSize, width - x), height: Math.min(tileSize, height - y) }, { ...options, scale: scaleX });
49680
49742
  yield tile.data;