@pixerate/editor 0.102.0 → 0.104.0

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 (59) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/dist/agent/index.cjs +7 -7
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.js +7 -7
  5. package/dist/canvas/index.cjs +3 -3
  6. package/dist/canvas/index.cjs.map +1 -1
  7. package/dist/canvas/index.js +4 -4
  8. package/dist/chunk-6BU5WU4R.cjs +1 -0
  9. package/dist/chunk-6BU5WU4R.cjs.map +1 -0
  10. package/dist/{chunk-IB53LVGV.js → chunk-DS7KDKPX.js} +1 -1
  11. package/dist/chunk-NQLQ2JOI.js +7176 -0
  12. package/dist/chunk-NQLQ2JOI.js.map +1 -0
  13. package/dist/{chunk-ZZOTHJ4K.cjs → chunk-PV42JITT.cjs} +1 -1
  14. package/dist/{chunk-ZZOTHJ4K.cjs.map → chunk-PV42JITT.cjs.map} +1 -1
  15. package/dist/chunk-VSMM7B3P.cjs +7176 -0
  16. package/dist/chunk-VSMM7B3P.cjs.map +1 -0
  17. package/dist/chunk-XYBJKUAU.js +1 -0
  18. package/dist/chunk-XYBJKUAU.js.map +1 -0
  19. package/dist/compositor-B1ZrKR89.d.ts +835 -0
  20. package/dist/compositor-CTMU-B1t.d.cts +835 -0
  21. package/dist/export-BLpMyCYg.d.ts +3139 -0
  22. package/dist/export-X9rY79p0.d.cts +3139 -0
  23. package/dist/homography-CZ-tFjay.d.cts +50 -0
  24. package/dist/homography-Ctfjoc1b.d.ts +50 -0
  25. package/dist/image-editor/index.cjs +2846 -9407
  26. package/dist/image-editor/index.cjs.map +1 -1
  27. package/dist/image-editor/index.d.cts +24 -3151
  28. package/dist/image-editor/index.d.ts +24 -3151
  29. package/dist/image-editor/index.js +2508 -9069
  30. package/dist/image-editor/index.js.map +1 -1
  31. package/dist/mixer-DG_9Yu2W.d.ts +282 -0
  32. package/dist/mixer-DGcH6HLu.d.cts +282 -0
  33. package/dist/paint/index.cjs +4 -3
  34. package/dist/paint/index.cjs.map +1 -1
  35. package/dist/paint/index.d.cts +3 -281
  36. package/dist/paint/index.d.ts +3 -281
  37. package/dist/paint/index.js +2 -1
  38. package/dist/paint-editor/index.cjs +4417 -0
  39. package/dist/paint-editor/index.cjs.map +1 -0
  40. package/dist/paint-editor/index.d.cts +1539 -0
  41. package/dist/paint-editor/index.d.ts +1539 -0
  42. package/dist/paint-editor/index.js +4417 -0
  43. package/dist/paint-editor/index.js.map +1 -0
  44. package/dist/raster/index.d.cts +6 -835
  45. package/dist/raster/index.d.ts +6 -835
  46. package/dist/{surface-DIA-XatS.d.cts → surface-AfHCH7BR.d.cts} +1 -1
  47. package/dist/{surface-DIA-XatS.d.ts → surface-AfHCH7BR.d.ts} +1 -1
  48. package/dist/tools/index.cjs +3 -3
  49. package/dist/tools/index.cjs.map +1 -1
  50. package/dist/tools/index.js +3 -3
  51. package/dist/vector/index.d.cts +2 -1
  52. package/dist/vector/index.d.ts +2 -1
  53. package/dist/{warp-B8rAguzs.d.ts → warp-CWHZzE3x.d.ts} +2 -49
  54. package/dist/{warp-nYs2DPrx.d.cts → warp-D5jz_tN7.d.cts} +2 -49
  55. package/package.json +6 -1
  56. package/skills/README.md +1 -0
  57. package/skills/pixerate-image-editor/SKILL.md +14 -5
  58. package/skills/pixerate-paint-editor/SKILL.md +203 -0
  59. /package/dist/{chunk-IB53LVGV.js.map → chunk-DS7KDKPX.js.map} +0 -0
@@ -0,0 +1,3139 @@
1
+ import { e as RasterLayerBase, f as RasterMask$1, b as RasterPixelLayer, G as GroupBlendMode, g as GlobalLight, E as EffectPattern, I as Interpolation, h as LayerLocation$1, L as LayerLocks, i as LayerEffect, j as LayerEffects, d as RasterSources, c as RasterCompositor, k as CompositorOptions, l as LayerEffectKind } from './compositor-B1ZrKR89.js';
2
+ import { P as PixelSurface, B as BlendMode, a as TiledSurface } from './surface-AfHCH7BR.js';
3
+ import { Q as Quad, a as WarpMesh, V as Vec2, A as AffineMatrix, H as Homography } from './types-CwMPoRMU.js';
4
+ import { B as BrushSettings, M as MixerSettings, a as MixerLoad, c as StrokeCoverage, d as MixerTarget, e as MixerStroke, f as StrokeApply, S as StrokePoint } from './mixer-DG_9Yu2W.js';
5
+ import { W as WarpPreset } from './warp-CWHZzE3x.js';
6
+
7
+ /**
8
+ * A selection: one 8-bit coverage value per image pixel (0 = not selected, 255 = fully
9
+ * selected), row-major over the whole source image in natural pixels — the same space as the
10
+ * crop box and annotations. Soft values are partial selection (feathered or anti-aliased edges).
11
+ *
12
+ * Treat a mask as immutable: every operation returns a new one, so history entries can share
13
+ * them without copying.
14
+ */
15
+ interface SelectionMask {
16
+ readonly width: number;
17
+ readonly height: number;
18
+ readonly data: Uint8Array;
19
+ /** Bounding box of the non-zero coverage, or `null` when nothing is selected. */
20
+ readonly bounds: CropBox | null;
21
+ }
22
+ /** How a new selection combines with the current one. */
23
+ type SelectionMode = 'replace' | 'add' | 'subtract' | 'intersect';
24
+ declare const SELECTION_MODES: readonly SelectionMode[];
25
+ declare function isSelectionMode(value: unknown): value is SelectionMode;
26
+ /** Parses `replace|add|subtract|intersect`; anything else is `replace`. */
27
+ declare function parseSelectionMode(value: unknown): SelectionMode;
28
+ /** RGBA pixels, 8 bits per channel, straight alpha (an `ImageData` qualifies). */
29
+ interface PixelBuffer {
30
+ width: number;
31
+ height: number;
32
+ data: ArrayLike<number>;
33
+ }
34
+ /** Serialized selection: a run-length encoding of the 8-bit coverage. */
35
+ interface SerializedSelection {
36
+ width: number;
37
+ height: number;
38
+ /** `r1:` + base64 of (value byte, LEB128 run length) pairs. */
39
+ rle: string;
40
+ }
41
+ /** Bounding box of non-zero bytes. */
42
+ declare function computeSelectionBounds(width: number, height: number, data: ArrayLike<number>): CropBox | null;
43
+ /** Wraps coverage bytes (taken over, not copied) as a selection, computing its bounds. */
44
+ declare function createSelectionMask(width: number, height: number, data?: Uint8Array): SelectionMask;
45
+ /** Select › All. */
46
+ declare function fullSelection(width: number, height: number): SelectionMask;
47
+ declare function isSelectionEmpty(mask: SelectionMask | null | undefined): boolean;
48
+ /** Coverage at a pixel, 0..1 (0 outside the image). */
49
+ declare function selectionValueAt(mask: SelectionMask, x: number, y: number): number;
50
+ /** 8-bit value of a coverage (clamped and rounded; NaN → 0). */
51
+ declare function coverageToByte(v: number): number;
52
+ /** A selection from 0..1 coverage. */
53
+ declare function selectionFromCoverage(width: number, height: number, coverage: ArrayLike<number>): SelectionMask;
54
+ /** The selection's coverage as 0..1 floats. */
55
+ declare function selectionToCoverage(mask: SelectionMask): Float32Array;
56
+ /** Number of fully-or-partly selected pixels, weighted by coverage (Σ coverage). */
57
+ declare function selectionArea(mask: SelectionMask | null | undefined): number;
58
+ /**
59
+ * Combines `next` with `prev` by `mode` (8-bit, exact: combining a mask with itself is a no-op).
60
+ * Add is the maximum, Subtract `max(prev − next, 0)`, Intersect the minimum. A missing `prev`
61
+ * reads as nothing selected. Returns `null` when nothing remains selected.
62
+ */
63
+ declare function combineSelections(prev: SelectionMask | null | undefined, next: SelectionMask, mode: SelectionMode): SelectionMask | null;
64
+ /** Select › Inverse. */
65
+ declare function invertSelection(mask: SelectionMask): SelectionMask;
66
+ /** Bilinear resample to another size (e.g. an AI mask returned at a different resolution). */
67
+ declare function resizeSelection(mask: SelectionMask, width: number, height: number): SelectionMask;
68
+ type SelectionChannel = 'alpha' | 'luminance' | 'red' | 'green' | 'blue';
69
+ /**
70
+ * A selection from an image: by default its luminance (white = selected, the usual convention
71
+ * of segmentation masks), or one channel. Partly transparent pixels count as black for
72
+ * luminance, so a mask PNG with transparency works either way.
73
+ */
74
+ declare function selectionFromPixels(pixels: PixelBuffer, options?: {
75
+ channel?: SelectionChannel;
76
+ invert?: boolean;
77
+ threshold?: number;
78
+ }): SelectionMask;
79
+ /** The selection as RGBA pixels: grey (white = selected, opaque) or white with coverage as alpha. */
80
+ declare function selectionToPixels(mask: SelectionMask, as?: 'gray' | 'alpha'): {
81
+ width: number;
82
+ height: number;
83
+ data: Uint8ClampedArray;
84
+ };
85
+ /** Run-length encodes coverage bytes: `r1:` + base64 of (value, LEB128 run length) pairs. */
86
+ declare function encodeSelectionRle(data: Uint8Array): string;
87
+ /** Decodes {@link encodeSelectionRle}; throws unless the runs cover exactly `length` bytes. */
88
+ declare function decodeSelectionRle(rle: string, length: number): Uint8Array;
89
+ declare function serializeSelection(mask: SelectionMask): SerializedSelection;
90
+ /** Restores a {@link SerializedSelection}; throws on damaged data. */
91
+ declare function deserializeSelection(value: unknown): SelectionMask;
92
+ /** A short, stable hash of a string or bytes (FNV-1a, 32-bit, hex) for cache keys. */
93
+ declare function hashMaskContent(content: string | Uint8Array): string;
94
+
95
+ /**
96
+ * The image editor's layer stack: pixel, adjustment, group and annotation layers in one ordered
97
+ * tree (bottom to top), all in document space (natural pixels of the document).
98
+ *
99
+ * Layer objects are immutable. Every change builds new objects along the path from the root to
100
+ * the changed layer and shares everything else, so undo history, the compositor's caches and
101
+ * serialization can compare layers by identity.
102
+ */
103
+
104
+ /** Blend modes of a layer; groups also accept `pass-through`. */
105
+ type LayerBlendMode = GroupBlendMode;
106
+
107
+ /**
108
+ * A layer mask (see the raster module's `RasterMask`) whose parametric `source` is a depth map:
109
+ * a depth mask is generated from it instead of painted, and its surface is unused.
110
+ */
111
+ type RasterMask = RasterMask$1<DepthMaskOptions>;
112
+ interface LayerBase extends RasterLayerBase {
113
+ mask?: RasterMask;
114
+ }
115
+ /** The base sliders of the old document-level adjustments, as a filter on one layer. */
116
+ interface DevelopSettings {
117
+ brightness: number;
118
+ contrast: number;
119
+ saturation: number;
120
+ exposure: number;
121
+ temperature: number;
122
+ tint?: number;
123
+ /** Rendering process (see `ImageAdjustments.process`). */
124
+ process?: 1 | 2;
125
+ }
126
+ type DevelopFilter = {
127
+ id: string;
128
+ kind: 'develop';
129
+ enabled: boolean;
130
+ } & DevelopSettings;
131
+ /** Box blur of `radius` pixels (the old `adjustments.blur`), clamped at the canvas edges. */
132
+ interface BlurFilter {
133
+ id: string;
134
+ kind: 'blur';
135
+ enabled: boolean;
136
+ radius: number;
137
+ }
138
+ type LayerFilter = DevelopFilter | BlurFilter;
139
+ type LayerFilterKind = LayerFilter['kind'];
140
+ interface PixelLayer extends LayerBase, Omit<RasterPixelLayer, keyof RasterLayerBase> {
141
+ /** Live filters, applied bottom (first) to top before the mask and blend. */
142
+ filters?: LayerFilter[];
143
+ /**
144
+ * `'document'`: the pixels are the document's source image, unmodified (the Background of an
145
+ * opened image). Such a layer's `surface` is a placeholder; read its pixels through the
146
+ * controller. Painting it drops the flag.
147
+ */
148
+ source?: 'document';
149
+ }
150
+ type AdjustmentLayerNode<K extends AdjustmentKind = AdjustmentKind> = {
151
+ [P in K]: LayerBase & {
152
+ type: 'adjustment';
153
+ kind: P;
154
+ params: AdjustmentParamsMap[P];
155
+ };
156
+ }[K];
157
+ interface GroupLayer extends Omit<LayerBase, 'blend'> {
158
+ type: 'group';
159
+ blend: LayerBlendMode;
160
+ /** Bottom to top. */
161
+ children: ImageLayer[];
162
+ /** UI only. */
163
+ collapsed?: boolean;
164
+ }
165
+ /** One annotation (pen, shape, arrow, text, image) as a layer, drawn by the annotation renderer. */
166
+ interface AnnotationLayer extends LayerBase {
167
+ type: 'annotation';
168
+ annotation: Annotation;
169
+ }
170
+ /** A nested layer document: the contents of a smart object made from layers (or an embedded PSD). */
171
+ interface SmartDocument {
172
+ /** Bottom to top, on a canvas of the contents' `width × height`. */
173
+ layers: ImageLayer[];
174
+ globalLight?: GlobalLight;
175
+ patterns?: EffectPattern[];
176
+ }
177
+ /**
178
+ * What a smart object places: raster pixels or a nested layer document, at their own pixel size.
179
+ * Immutable and shared: every instance placed from the same contents (duplicates, linked
180
+ * instances) holds the same `id`, and replacing or editing the contents updates them all.
181
+ */
182
+ interface SmartContents {
183
+ /** Shared by linked instances; independent copies get a fresh one. */
184
+ id: string;
185
+ /** File name shown in the UI and written to PSD (`Layer 1.psb`, `photo.png`). */
186
+ name: string;
187
+ /** Pixel size of the contents (the placement maps `0..width × 0..height` into the document). */
188
+ width: number;
189
+ height: number;
190
+ /** `raster`: `raster` holds the pixels at (0, 0); `document`: `document` holds the layers. */
191
+ kind: 'raster' | 'document';
192
+ raster?: PixelSurface;
193
+ document?: SmartDocument;
194
+ /**
195
+ * The file the contents came from (a PSD's embedded file), written back by Export Contents and
196
+ * PSD export while the contents are unedited. Without `raster` / `document` the file could not
197
+ * be decoded here (a JPEG outside a browser): the layer keeps its rendered pixels, and changes
198
+ * that re-render it are refused until `prepareSmartObjects` decodes it.
199
+ */
200
+ file?: {
201
+ bytes: Uint8Array;
202
+ type: string;
203
+ };
204
+ /** Linked to an external file (Photoshop `liFE`): only the embedded preview is held. */
205
+ linked?: {
206
+ path: string;
207
+ };
208
+ }
209
+ /**
210
+ * Where a smart object's contents land, non-destructively: a warp in the contents' own pixels
211
+ * (optional) and then the map of the contents' rectangle onto `corners`. Every change re-renders
212
+ * from the contents, so repeated transforms never degrade.
213
+ */
214
+ interface SmartPlacement {
215
+ /** Where the contents' corners land (top-left, top-right, bottom-right, bottom-left), document pixels. */
216
+ corners: Quad;
217
+ /** A Bézier warp over the contents' rectangle, control points in contents pixels (missing = none). */
218
+ warp?: WarpMesh | null;
219
+ /** Resampling (default bicubic). */
220
+ interpolation?: Interpolation;
221
+ }
222
+ /**
223
+ * A Photoshop smart filter we do not model (Gaussian Blur, Camera Raw, …), kept from a PSD so it
224
+ * is listed, can be hidden, reordered or deleted, and is written back unchanged. It draws nothing.
225
+ */
226
+ interface PsdSmartFilter {
227
+ id: string;
228
+ kind: 'psd';
229
+ enabled: boolean;
230
+ /** Photoshop's menu name (`Gaussian Blur...`). */
231
+ name: string;
232
+ /** The filter's `filterFX` item descriptor (versioned descriptor bytes), as read. */
233
+ data: Uint8Array;
234
+ }
235
+ /** One smart filter: a layer filter (or a kept PSD filter) with its own blending over the result below it. */
236
+ type SmartFilter = (LayerFilter | PsdSmartFilter) & {
237
+ /** How the filter's result blends with the result below it (Photoshop's Blending Options). */
238
+ blend: BlendMode;
239
+ /** 0..1 */
240
+ opacity: number;
241
+ };
242
+ type SmartFilterKind = SmartFilter['kind'];
243
+ /** The smart filter stack of a smart object. */
244
+ interface SmartFilterStack {
245
+ /** Layer › Smart Filter › Disable Smart Filters (each filter keeps its own `enabled`). */
246
+ enabled: boolean;
247
+ /** Bottom (applied first) to top. */
248
+ filters: SmartFilter[];
249
+ /**
250
+ * The one filter mask (document space): where the filtered result shows over the unfiltered
251
+ * contents. Missing reveals everywhere.
252
+ */
253
+ mask?: RasterMask;
254
+ }
255
+ /** PSD data of a smart object kept for round trips (see the PSD module). */
256
+ interface SmartPsdData {
257
+ /** The placed-layer block as read (`SoLd`, `SoLE` or `PlLd`) and its key. */
258
+ key: string;
259
+ data: Uint8Array;
260
+ /** The instance id (`placed`), which keys the filter-effects cache. */
261
+ placed?: string;
262
+ }
263
+ /**
264
+ * A smart object: `contents` placed through `placement`, then the smart filters. `surface` is the
265
+ * rendered result in document space (offset 0), rebuilt by every change to the contents,
266
+ * placement or filters; the compositor draws it like a pixel layer's pixels, and masks, effects,
267
+ * clipping and blend apply on top. Never paint into it: rasterize the layer first.
268
+ */
269
+ interface SmartObjectLayer extends LayerBase {
270
+ type: 'smart';
271
+ contents: SmartContents;
272
+ placement: SmartPlacement;
273
+ smartFilters?: SmartFilterStack;
274
+ /** The rendered pixels (a cache of contents → placement → filters), document space. */
275
+ surface: PixelSurface;
276
+ /** Always (0, 0): a smart object moves through its placement. */
277
+ offset: {
278
+ x: number;
279
+ y: number;
280
+ };
281
+ /** 0..1, PSD "Fill" (missing = 1). */
282
+ fillOpacity?: number;
283
+ psd?: SmartPsdData;
284
+ }
285
+ type ImageLayer = PixelLayer | AdjustmentLayerNode | GroupLayer | AnnotationLayer | SmartObjectLayer;
286
+ type ImageLayerType = ImageLayer['type'];
287
+ /** Which part of the active layer painting targets (`filter-mask`: a smart object's filter mask). */
288
+ type LayerTarget = 'pixels' | 'mask' | 'filter-mask';
289
+ /** A fresh id with `prefix` (`layer`, `flt`, …). */
290
+ declare function newLayerId(prefix?: string): string;
291
+ type LayerLocation = LayerLocation$1<ImageLayer>;
292
+
293
+ /** The top-level and nested pixel layers. */
294
+ declare function pixelLayers(layers: readonly ImageLayer[]): PixelLayer[];
295
+ /** The develop filter of a pixel layer, if any. */
296
+ declare function developFilter(layer: PixelLayer): DevelopFilter | undefined;
297
+ declare function blurFilter(layer: PixelLayer): BlurFilter | undefined;
298
+ /** True for filters that change pixels (enabled, non-neutral). */
299
+ declare function isActiveFilter(f: LayerFilter): boolean;
300
+ /** True when a filter needs neighbouring pixels (it can't run tile by tile). */
301
+ declare function isSpatialFilter(f: LayerFilter): boolean;
302
+ /** Document-space bounds of a pixel layer's stored tiles, or `null`. */
303
+ declare function pixelLayerBounds(layer: PixelLayer, surface?: PixelSurface): CropBox | null;
304
+ /** Human label of a layer kind. */
305
+ declare function layerTypeLabel(type: ImageLayerType): string;
306
+ /** Layers whose pixels are a surface the compositor draws (pixel layers and smart objects). */
307
+ declare function hasSurface(layer: ImageLayer): layer is PixelLayer | SmartObjectLayer;
308
+ /** Every smart object in the stack (nested contents not included). */
309
+ declare function smartObjectLayers(layers: readonly ImageLayer[]): SmartObjectLayer[];
310
+
311
+ /**
312
+ * Schema version 3 of the image editor document: the layer tree, with pixel layers and painted
313
+ * masks stored as PNG assets (cropped to their content and placed by position), plus the
314
+ * document-level crop, transform, output opacity, migrated blur and selection.
315
+ *
316
+ * Encoding is synchronous and exact (see `png.ts`). Each surface's encoding is memoised, so
317
+ * saving an unchanged layer again costs nothing.
318
+ */
319
+
320
+ declare const LAYERED_SCHEMA_VERSION: 3;
321
+ /** A stored raster mask. Pixels: `asset` (grey PNG) placed at `x`, `y`; elsewhere `defaultValue`. */
322
+ interface SerializedRasterMask {
323
+ defaultValue: 0 | 255;
324
+ asset?: string;
325
+ x?: number;
326
+ y?: number;
327
+ density?: number;
328
+ feather?: number;
329
+ enabled?: boolean;
330
+ /** `false`: unlinked from the layer's pixels. */
331
+ linked?: boolean;
332
+ source?: DepthMaskOptions;
333
+ }
334
+ interface SerializedLayerBase {
335
+ id: string;
336
+ name: string;
337
+ visible: boolean;
338
+ opacity: number;
339
+ blend: string;
340
+ clipped?: boolean;
341
+ mask?: SerializedRasterMask;
342
+ locks?: LayerLocks;
343
+ effects?: SerializedLayerEffects;
344
+ }
345
+ /** A layer's effects: the effect list as JSON plus the PSD block they came from (base64). */
346
+ interface SerializedLayerEffects {
347
+ enabled: boolean;
348
+ scale?: number;
349
+ items: LayerEffect[];
350
+ psd?: {
351
+ key: string;
352
+ data: string;
353
+ };
354
+ }
355
+ interface SerializedPixelLayer extends SerializedLayerBase {
356
+ type: 'pixel';
357
+ /** Asset id of the pixels (RGBA PNG) placed at `x`, `y` in layer space; missing when empty. */
358
+ asset?: string;
359
+ x?: number;
360
+ y?: number;
361
+ offset: {
362
+ x: number;
363
+ y: number;
364
+ };
365
+ fillOpacity?: number;
366
+ filters?: LayerFilter[];
367
+ /** `'document'`: the pixels are the document's source image (no asset is stored). */
368
+ source?: 'document';
369
+ }
370
+ interface SerializedAdjustmentLayer extends SerializedLayerBase {
371
+ type: 'adjustment';
372
+ kind: string;
373
+ params: unknown;
374
+ }
375
+ interface SerializedGroupLayer extends SerializedLayerBase {
376
+ type: 'group';
377
+ children: SerializedLayer[];
378
+ collapsed?: boolean;
379
+ }
380
+ interface SerializedAnnotationLayer extends SerializedLayerBase {
381
+ type: 'annotation';
382
+ annotation: Annotation;
383
+ }
384
+ /** A smart filter as stored (a Photoshop filter keeps its descriptor as base64). */
385
+ type SerializedSmartFilter = (LayerFilter | (Omit<PsdSmartFilter, 'data'> & {
386
+ data: string;
387
+ })) & {
388
+ blend: string;
389
+ opacity: number;
390
+ };
391
+ interface SerializedSmartObjectLayer extends SerializedLayerBase {
392
+ type: 'smart';
393
+ /** Id of its entry in the document's `smartContents` (shared by linked instances). */
394
+ contents: string;
395
+ placement: {
396
+ corners: Array<{
397
+ x: number;
398
+ y: number;
399
+ }>;
400
+ warp?: WarpMesh;
401
+ interpolation?: string;
402
+ };
403
+ smartFilters?: {
404
+ enabled: boolean;
405
+ filters: SerializedSmartFilter[];
406
+ mask?: SerializedRasterMask;
407
+ };
408
+ /** The rendered pixels (RGBA PNG) at `x`, `y` in document space, so a load does not re-render. */
409
+ asset?: string;
410
+ x?: number;
411
+ y?: number;
412
+ fillOpacity?: number;
413
+ /** The PSD placed-layer block kept for round trips (base64). */
414
+ psd?: {
415
+ key: string;
416
+ data: string;
417
+ placed?: string;
418
+ };
419
+ }
420
+ /** Smart object contents as stored once per document, referenced by id from every instance. */
421
+ interface SerializedSmartContents {
422
+ id: string;
423
+ name: string;
424
+ width: number;
425
+ height: number;
426
+ kind: 'raster' | 'document';
427
+ /** Raster contents: RGBA PNG asset at (0, 0). */
428
+ asset?: string;
429
+ /** Document contents: the nested layers (their assets are in the document's `assets`). */
430
+ layers?: SerializedLayer[];
431
+ globalLight?: GlobalLight;
432
+ patterns?: SerializedPattern[];
433
+ /** The file the contents came from (base64), written back while unedited. */
434
+ file?: {
435
+ type: string;
436
+ data: string;
437
+ };
438
+ linked?: {
439
+ path: string;
440
+ };
441
+ }
442
+ type SerializedLayer = SerializedPixelLayer | SerializedAdjustmentLayer | SerializedGroupLayer | SerializedAnnotationLayer | SerializedSmartObjectLayer;
443
+ /** A version 3 document (layers). */
444
+ interface SerializedLayeredImageEditorState {
445
+ version: 3;
446
+ /** Canvas size in pixels. */
447
+ width: number;
448
+ height: number;
449
+ /** Bottom to top. */
450
+ layers: SerializedLayer[];
451
+ /** Asset id → PNG data URL (or an external reference, see `ImageEditorAssetStore`). */
452
+ assets: Record<string, string>;
453
+ activeLayerId: string | null;
454
+ crop: CropBox | null;
455
+ transform: ImageTransform;
456
+ /** Export alpha, 0..1. */
457
+ opacity: number;
458
+ /** Blur migrated from a v1 / v2 document with adjustment layers (runs after the layers). */
459
+ legacyOutputBlur?: number;
460
+ selection?: SerializedSelection;
461
+ /** Global light for layer effects (missing = 120°, 30°). */
462
+ globalLight?: GlobalLight;
463
+ /** The document's patterns for pattern effects, each an RGBA PNG data URL. */
464
+ patterns?: SerializedPattern[];
465
+ /** Contents of the smart objects (nested ones included), each stored once. */
466
+ smartContents?: SerializedSmartContents[];
467
+ }
468
+ interface SerializedPattern {
469
+ id: string;
470
+ name: string;
471
+ png: string;
472
+ }
473
+ declare function serializePatterns(patterns: readonly EffectPattern[]): SerializedPattern[];
474
+ /** Reads stored patterns, skipping (and reporting in `problems`) any that cannot be decoded. */
475
+ declare function deserializePatterns(raw: unknown, problems?: string[]): EffectPattern[];
476
+ interface EncodedSurface {
477
+ url: string;
478
+ x: number;
479
+ y: number;
480
+ }
481
+ /** The surface's content (non-default pixels) as a PNG data URL and its position, or `null`. */
482
+ declare function encodeSurface(surface: TiledSurface): EncodedSurface | null;
483
+ interface SerializeContext {
484
+ /** Adds an asset and returns its id. */
485
+ addAsset(url: string, hint: string): string;
486
+ /** Stores a smart object's contents (once per id). Without it, smart objects cannot be written. */
487
+ addContents?(contents: SmartContents): void;
488
+ }
489
+ declare function serializeEffects(effects: LayerEffects): SerializedLayerEffects;
490
+ /** Reads stored effects (leniently: broken effects are dropped), or `undefined`. */
491
+ declare function deserializeEffects(raw: unknown): LayerEffects | undefined;
492
+ declare function serializeLayer(layer: ImageLayer, ctx: SerializeContext): SerializedLayer;
493
+ /** Writes one contents entry (nested layers add their own assets and contents to `ctx`). */
494
+ declare function serializeContents(c: SmartContents, ctx: SerializeContext): SerializedSmartContents;
495
+ /**
496
+ * Serializes a layer tree with inline assets (asset ids are derived from layer ids). Smart object
497
+ * contents go to `smartContents`, once each (present only when there are smart objects).
498
+ */
499
+ declare function serializeLayers(layers: readonly ImageLayer[]): {
500
+ layers: SerializedLayer[];
501
+ assets: Record<string, string>;
502
+ smartContents?: SerializedSmartContents[];
503
+ };
504
+ /**
505
+ * Where a host keeps layer pixels outside the JSON (`toJSON({ assets: 'external', store })`):
506
+ * `put` stores a PNG and returns a reference (a URL, a key) that `get` resolves later.
507
+ */
508
+ interface ImageEditorAssetStore {
509
+ put(blob: Blob, info: {
510
+ id: string;
511
+ width: number;
512
+ height: number;
513
+ }): Promise<string>;
514
+ get(ref: string): Promise<Blob>;
515
+ }
516
+ /**
517
+ * Serializes a layer tree with its assets in `store`: each PNG is uploaded once per store (by
518
+ * content), and `assets` maps asset ids to the store's references.
519
+ */
520
+ declare function serializeLayersExternal(layers: readonly ImageLayer[], store: ImageEditorAssetStore): Promise<{
521
+ layers: SerializedLayer[];
522
+ assets: Record<string, string>;
523
+ smartContents?: SerializedSmartContents[];
524
+ }>;
525
+ /**
526
+ * Turns external asset references back into data URLs through `store.get` (data URLs pass through),
527
+ * so the document loads like an inline one. References that fail are reported and left out.
528
+ */
529
+ declare function resolveExternalAssets(assets: Record<string, string>, store: ImageEditorAssetStore | undefined, problems: string[]): Promise<Record<string, string>>;
530
+ interface DeserializeContext {
531
+ assets: Record<string, string>;
532
+ /** The stored `smartContents` table (smart objects reference it by id). */
533
+ smartContents?: unknown[];
534
+ /** Canvas size, for re-rendering a smart object stored without its pixels (default 1 × 1). */
535
+ canvas?: {
536
+ width: number;
537
+ height: number;
538
+ };
539
+ /** @internal Contents read so far by id (`null` while being read). */
540
+ contentsMemo?: Map<string, SmartContents | null>;
541
+ /** Decodes an asset (PNG data URL by default). Throw for an unusable asset. */
542
+ decodeAsset?(ref: string, channels: 1 | 4): {
543
+ width: number;
544
+ height: number;
545
+ data: ArrayLike<number>;
546
+ };
547
+ /** Problems found while reading (unknown kinds, broken assets); the rest still loads. */
548
+ problems: string[];
549
+ }
550
+ /** Decodes a PNG data URL into pixels of `channels` bytes each. */
551
+ declare function decodePngDataUrl(url: string, channels: 1 | 4): {
552
+ width: number;
553
+ height: number;
554
+ data: Uint8ClampedArray;
555
+ };
556
+ declare function deserializeLayer(raw: unknown, ctx: DeserializeContext): ImageLayer | null;
557
+ /** Reads a stored layer tree (layers that cannot be read are dropped and reported). */
558
+ declare function deserializeLayers(raw: unknown, ctx: DeserializeContext): ImageLayer[];
559
+
560
+ /** Display bins of a histogram. */
561
+ declare const HISTOGRAM_BINS = 256;
562
+ interface ImageHistogram {
563
+ /** 256-bin counts per channel. */
564
+ red: Uint32Array;
565
+ green: Uint32Array;
566
+ blue: Uint32Array;
567
+ /** 256-bin counts of Rec.709 luma `round(0.2126 R + 0.7152 G + 0.0722 B)` of the encoded values. */
568
+ luminance: Uint32Array;
569
+ /** Visible pixels counted (alpha > 0). Each counts once, partially transparent ones included. */
570
+ samples: number;
571
+ /** Fully transparent pixels, excluded from every bin. */
572
+ transparent: number;
573
+ /** Per channel (R, G, B): visible pixels at exactly 0. */
574
+ shadows: [number, number, number];
575
+ /** Per channel (R, G, B): visible pixels at exactly 255. */
576
+ highlights: [number, number, number];
577
+ /** Largest bin count over R, G, B and luminance (for scaling a plot). */
578
+ max: number;
579
+ }
580
+ interface PixelSource {
581
+ data: ArrayLike<number>;
582
+ width?: number;
583
+ height?: number;
584
+ }
585
+ /**
586
+ * Per-channel and luminance histograms of straight 8-bit RGBA pixels (an `ImageData` or anything
587
+ * with `data`), with clipping counts at the exact endpoints. Transparent pixels cannot add
588
+ * phantom peaks: they are counted separately and excluded.
589
+ */
590
+ declare function computeHistogram(imageData: PixelSource): ImageHistogram;
591
+ type HistogramChannel = 'red' | 'green' | 'blue' | 'luminance';
592
+ /**
593
+ * An SVG path (`M … Z`) of one channel scaled into a `width` × `height` box, bins left to right,
594
+ * counts up from the bottom. `scale: 'sqrt'` (default) keeps small populations visible.
595
+ */
596
+ declare function histogramPath(histogram: ImageHistogram, channel: HistogramChannel, width: number, height: number, options?: {
597
+ scale?: 'linear' | 'sqrt';
598
+ max?: number;
599
+ }): string;
600
+
601
+ /**
602
+ * The image editor's compositor: the raster engine (`@pixerate/editor/raster`) with the photo
603
+ * layer kinds plugged in. Adjustment layers filter what is below them (a run of adjacent ones is
604
+ * one float pass with one rounding at the end, exactly as `applyAdjustmentLayers` does, so a
605
+ * document migrated from adjustment layers renders identically); annotation layers are drawn by
606
+ * the host; pixel layers can carry develop and blur filters; masks can be depth masks.
607
+ */
608
+
609
+ /** What the compositor needs from its host besides the layers. */
610
+ interface CompositorSources extends RasterSources {
611
+ /** The pixels of a pixel layer or smart object (resolves layers whose pixels are the document source). */
612
+ surfaceOf?(layer: PixelLayer | SmartObjectLayer): PixelSurface;
613
+ /**
614
+ * A parametric mask's depth map as RGBA at canvas size (the red channel is the depth), or
615
+ * `null` while it is unavailable (the mask then reveals everything).
616
+ */
617
+ depthMap?(source: DepthMaskOptions): {
618
+ width: number;
619
+ height: number;
620
+ data: ArrayLike<number>;
621
+ } | null;
622
+ /** Draws one annotation into RGBA for `rect` (canvas hosts only); `null` when unavailable. */
623
+ rasterizeAnnotation?(annotation: Annotation, rect: CropBox): Uint8ClampedArray | null;
624
+ }
625
+ /** The `AdjustmentLayer` shape `applyAdjustmentLayers` evaluates, for a node (memoised). */
626
+ declare function adjustmentNodeAsLayer(node: AdjustmentLayerNode): AdjustmentLayer;
627
+ /** Evaluates a depth mask (see `applyDepthMask`) into one coverage byte per depth sample. */
628
+ declare function depthMaskAlpha(depth: number, options: DepthMaskOptions): number;
629
+ /** Runs layer filters (develop, blur) over RGBA `data` (`width × height`) in place, bottom first. */
630
+ declare function runLayerFilters(data: Uint8ClampedArray, width: number, height: number, filters: readonly LayerFilter[]): void;
631
+ /** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
632
+ declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
633
+ constructor(options?: CompositorOptions);
634
+ }
635
+
636
+ interface RenderOptions {
637
+ viewportWidth?: number;
638
+ viewportHeight?: number;
639
+ showCropOverlay?: boolean;
640
+ showSelectionOverlay?: boolean;
641
+ interactive?: boolean;
642
+ draftAnnotation?: Annotation | null;
643
+ draftCrop?: CropBox | null;
644
+ /** Viewport fit mode: 'contain' scales to full viewport bounds; 'margin' scales to 85% with 1.0 cap (default). */
645
+ fit?: ImageEditorFit;
646
+ /**
647
+ * How the active selection is shown: marching ants (default when there is a selection),
648
+ * a Quick Mask tint over the unselected area (default when `state.quickMask`), or not at all.
649
+ */
650
+ selectionDisplay?: 'ants' | 'quickMask' | 'none';
651
+ /** Dash offset of the marching ants in screen pixels (animate it to make them march). */
652
+ antsOffset?: number;
653
+ /** A selection being drawn (e.g. a lasso), shown as a dashed outline in natural pixels. */
654
+ draftSelectionPath?: ImagePoint[] | null;
655
+ /** Document rectangles that are the only change since the last render (strokes); recomposites only there. */
656
+ dirty?: CropBox[];
657
+ /** The layer being edited: tiles keep a checkpoint below it so strokes and drags on it start there. */
658
+ checkpointLayerId?: string | null;
659
+ /** The paint tools' cursor: a circle of `diameter` px at (`x`, `y`) in natural pixels. */
660
+ brushCursor?: {
661
+ x: number;
662
+ y: number;
663
+ diameter: number;
664
+ } | null;
665
+ /** Free Transform's frame, handles and reference point in natural pixels (`FreeTransformTool.overlay()`). */
666
+ freeTransform?: {
667
+ outline: ImagePoint[];
668
+ handles: ImagePoint[];
669
+ reference: ImagePoint;
670
+ /** Warp mode: the mesh's grid lines as cubics, its anchors, and handles with their anchors. */
671
+ warp?: {
672
+ curves: ImagePoint[][];
673
+ anchors: ImagePoint[];
674
+ handles: Array<{
675
+ point: ImagePoint;
676
+ anchor: ImagePoint;
677
+ }>;
678
+ };
679
+ } | null;
680
+ }
681
+ /**
682
+ * Image Cache to avoid refetching HTMLImageElement objects repeatedly during re-renders.
683
+ */
684
+ declare class ImageCache {
685
+ private cache;
686
+ get(src: string): Promise<HTMLImageElement>;
687
+ set(src: string, img: HTMLImageElement): void;
688
+ /** The loaded image for `src`, or `undefined` while it is not loaded yet. */
689
+ peek(src: string): HTMLImageElement | undefined;
690
+ clear(): void;
691
+ }
692
+ declare const globalImageCache: ImageCache;
693
+ /**
694
+ * Renders an image with all its transformations, filters, depth masks, and annotations
695
+ * to a target HTMLCanvasElement.
696
+ */
697
+ declare class ImageEditorRenderer {
698
+ imageCache: ImageCache;
699
+ private cachedBaseCanvas;
700
+ private cachedBaseKey;
701
+ /**
702
+ * Histogram of the last viewport base render (after adjustments and layers, before
703
+ * annotations); `null` until a render with pixel access has happened.
704
+ */
705
+ previewHistogram: ImageHistogram | null;
706
+ /** Called whenever the viewport base image is re-rendered with the new histogram. */
707
+ onPreviewHistogram?: (histogram: ImageHistogram | null) => void;
708
+ private sourceIds;
709
+ private nextSourceId;
710
+ /**
711
+ * The viewport's compositor. It only ever composites the viewport's stack, so its tile cache can
712
+ * trust dirty rectangles; exports use `exportCompositor`.
713
+ */
714
+ compositor: LayerCompositor;
715
+ private exportCompositor;
716
+ /** How the host resolves layer pixels (set by the controller). */
717
+ layerSources?: LayerSourcesHost;
718
+ /**
719
+ * Debug / test switch: `'legacy'` forces the old base path for documents that still have the
720
+ * v1 / v2 shape, `'layers'` forces the compositor. Default: chosen per document.
721
+ */
722
+ baseRoute: 'auto' | 'legacy' | 'layers';
723
+ /** Which path the last base render took. */
724
+ lastBaseRoute: 'legacy' | 'layers' | null;
725
+ private comp;
726
+ private depthMaps;
727
+ private assetVersion;
728
+ private scratch;
729
+ private cachedPostCanvas;
730
+ private cachedPostKey;
731
+ private cachedPostHistogram;
732
+ /**
733
+ * Helper to create a canvas element safely in browser/JSDOM.
734
+ */
735
+ createCanvas(width: number, height: number): HTMLCanvasElement;
736
+ /**
737
+ * Renders the editor state onto a canvas (viewport or preview).
738
+ */
739
+ renderToCanvas(targetCanvas: HTMLCanvasElement, imageSource: CanvasImageSource | string, state: ImageEditorState, options?: RenderOptions): Promise<void>;
740
+ /**
741
+ * Draws `region` of the source image with rotation/flip applied, then pixel adjustments
742
+ * and the depth mask (which receives the identical transform so it stays aligned).
743
+ */
744
+ private renderBaseImage;
745
+ /** Which base path renders `state` (see `needsLegacyBase`). */
746
+ private chooseRoute;
747
+ /** Compositor sources for a `width × height` canvas: layer pixels, depth maps and annotation rasterizing. */
748
+ compositorSources(width: number, height: number): CompositorSources;
749
+ /** Loads what the compositor reads synchronously: depth maps (at canvas size) and annotation images. */
750
+ /** Loads depth maps and annotation images the compositor reads synchronously. */
751
+ prepareLayerAssets(layers: readonly ImageLayer[], width: number, height: number): Promise<void>;
752
+ /** Whether annotation layers can be rasterized here (a 2D canvas with pixel access). */
753
+ canRasterize(): boolean;
754
+ /** One annotation drawn into a tile-sized RGBA buffer at `rect` (document space), or `null` without a canvas. */
755
+ private rasterizeAnnotation;
756
+ private putTile;
757
+ /**
758
+ * Brings the viewport's composite canvas (document space, covering `region`) up to date,
759
+ * uploading only the tiles that changed since the last render.
760
+ */
761
+ private updateComposite;
762
+ /**
763
+ * The base canvas of a layered document: the composite drawn through the base projection (crop,
764
+ * rotation, flips at scale 1), then output opacity and the migrated blur, which run in the output
765
+ * frame. Cached until the composite or those settings change.
766
+ */
767
+ private layeredBase;
768
+ /** Output opacity then the migrated blur, in place on a base canvas; returns its histogram. */
769
+ private applyPost;
770
+ private publishHistogram;
771
+ private quickMaskCache;
772
+ private outlineCache;
773
+ /** Rubylith over the unselected area, drawn in natural image space. */
774
+ private drawQuickMask;
775
+ /** Marching ants: a white line under a black dashed one, one screen pixel wide. */
776
+ private drawAnts;
777
+ private drawDraftPath;
778
+ private getSourceId;
779
+ /**
780
+ * Draws an individual annotation onto the canvas.
781
+ */
782
+ drawAnnotation(ctx: CanvasRenderingContext2D, ann: Annotation): Promise<void>;
783
+ /**
784
+ * Scalar fallback for blend modes the canvas cannot composite (Photoshop soft light, linear /
785
+ * vivid / pin light, hard mix, subtract, divide, darker / lighter colour, linear burn / dodge):
786
+ * draws the annotation on a same-size layer with the same transform, blends it into a copy of
787
+ * the target's pixels with `blendImageData`, and copies the result back inside the current clip.
788
+ */
789
+ private drawAnnotationBlended;
790
+ private drawAnnotationContent;
791
+ /** Draws an annotation synchronously; an image annotation draws `img` (skipped when `null`). */
792
+ drawAnnotationShape(ctx: CanvasRenderingContext2D, ann: Annotation, img: CanvasImageSource | null): void;
793
+ /**
794
+ * Draws selection box around active annotation.
795
+ */
796
+ private drawAnnotationSelection;
797
+ /** @deprecated Use the standalone `getAnnotationBounds` from the geometry module. */
798
+ getAnnotationBounds(ann: Annotation): {
799
+ x: number;
800
+ y: number;
801
+ width: number;
802
+ height: number;
803
+ };
804
+ /**
805
+ * Draws a rule-of-thirds crop grid overlay. All rectangles come from
806
+ * `getViewportMetrics`, i.e. the crop box projected through rotation/flip.
807
+ */
808
+ private drawCropOverlay;
809
+ private drawFittedImage;
810
+ /**
811
+ * Performs an export render at exact pixel dimensions without zoom/pan viewport offsets.
812
+ */
813
+ renderExport(imageSource: CanvasImageSource | string, state: ImageEditorState, options?: ExportOptions): Promise<HTMLCanvasElement>;
814
+ /**
815
+ * Generates a strictly binary mask canvas (white = area to regenerate, black = keep) in
816
+ * natural image pixel space of the current source image — the same space the crop box
817
+ * and annotations are stored in. Pair it with `toSourceDataURL()` for AI inpainting.
818
+ */
819
+ renderMask(state: ImageEditorState, options?: {
820
+ useAnnotations?: boolean;
821
+ useCrop?: boolean;
822
+ useSelection?: boolean;
823
+ }): Promise<HTMLCanvasElement>;
824
+ private drawAnnotationMask;
825
+ }
826
+ /** What the renderer asks its host (the controller) about layer pixels. */
827
+ interface LayerSourcesHost {
828
+ surfaceOf(layer: PixelLayer): PixelSurface;
829
+ sourceIsOpaque(): boolean;
830
+ version(): string;
831
+ /** The document's global light for layer effects. */
832
+ globalLight?(): GlobalLight | undefined;
833
+ /** A document pattern for pattern effects (built-in patterns are found without it). */
834
+ pattern?(id: string): EffectPattern | null;
835
+ }
836
+
837
+ interface ShapeSelectionOptions {
838
+ /** Partial coverage along the edge (default true). */
839
+ antiAlias?: boolean;
840
+ }
841
+ /**
842
+ * Even-odd polygon coverage over a `width × height` image: exact horizontal coverage on 4
843
+ * sub-rows per pixel row (1 without anti-aliasing, where a pixel is in when its centre is).
844
+ * Each sub-row only tests the edges that cross it (an active edge list over edges sorted by
845
+ * their top), so the cost is about pixels + edges × rows they span.
846
+ */
847
+ declare function polygonCoverage(points: readonly ImagePoint[], width: number, height: number, antiAlias?: boolean): Float32Array;
848
+ /** Lasso / polygonal lasso: an even-odd polygon in natural image pixels. */
849
+ declare function polygonSelection(width: number, height: number, points: readonly ImagePoint[], options?: ShapeSelectionOptions): SelectionMask;
850
+ /** Normalises a rectangle with a negative width or height. */
851
+ declare function normalizeRect(rect: CropBox): CropBox;
852
+ /** Rectangular marquee. Fractional edges get partial coverage when anti-aliased. */
853
+ declare function rectSelection(width: number, height: number, rect: CropBox, options?: ShapeSelectionOptions): SelectionMask;
854
+ /** The ellipse inscribed in `rect` as a polygon fine enough that its chords stay within 0.05 px. */
855
+ declare function ellipsePoints(rect: CropBox): ImagePoint[];
856
+ /** Elliptical marquee inscribed in `rect`. */
857
+ declare function ellipseSelection(width: number, height: number, rect: CropBox, options?: ShapeSelectionOptions): SelectionMask;
858
+
859
+ /** How the magic wand measures colour difference. */
860
+ type ColorDistance = 'rgb' | 'perceptual';
861
+ interface MagicWandOptions {
862
+ /**
863
+ * 0..255. `rgb`: the largest per-channel difference in levels (alpha included), as
864
+ * Photoshop's wand. `perceptual`: CIE76 ΔE*ab, with 255 ≙ ΔE 100. Default 32.
865
+ */
866
+ tolerance?: number;
867
+ /** Only the 4-connected region around the seed (default true); otherwise every similar pixel. */
868
+ contiguous?: boolean;
869
+ /** Soft one-pixel fringe on the edge (default true). */
870
+ antiAlias?: boolean;
871
+ /** Default `rgb`. */
872
+ distance?: ColorDistance;
873
+ }
874
+ /** CIE Lab (D65) of an 8-bit sRGB colour. */
875
+ declare function srgb8ToLab(r: number, g: number, b: number): [number, number, number];
876
+ /**
877
+ * Magic wand: pixels similar to the seed pixel. Contiguous mode is a scanline span fill with an
878
+ * explicit stack (fill a run, then scan the rows above and below it for new runs), so it is
879
+ * linear in the pixels visited and never recurses. An empty selection when the seed is outside.
880
+ */
881
+ declare function magicWand(pixels: PixelBuffer, seed: ImagePoint, options?: MagicWandOptions): SelectionMask;
882
+ /** One Color Range sample: a colour (0..255 levels or `#rrggbb`) and, optionally, where it was picked. */
883
+ interface ColorRangeSample {
884
+ color: readonly [number, number, number] | string;
885
+ at?: ImagePoint;
886
+ }
887
+ interface ColorRangeOptions {
888
+ /** 0..200 levels (default 40): coverage falls off linearly to 0 at `fuzziness + 1` levels. */
889
+ fuzziness?: number;
890
+ /**
891
+ * Localized Color Clusters: a sample with a position also fades with distance from it,
892
+ * reaching 0 at this many pixels.
893
+ */
894
+ localized?: number;
895
+ }
896
+ /**
897
+ * Select › Color Range › Sampled Colors: each pixel takes its best match over the samples; a
898
+ * match falls off linearly with the largest per-channel difference, `1 − d / (fuzziness + 1)`.
899
+ * Transparent pixels are never selected.
900
+ */
901
+ declare function colorRange(pixels: PixelBuffer, samples: readonly ColorRangeSample[], options?: ColorRangeOptions): SelectionMask;
902
+ type ToneRangePreset = 'shadows' | 'midtones' | 'highlights';
903
+ /** Luma bounds (0..255, Rec. 601) of the tonal presets. */
904
+ declare const TONE_RANGE_PRESETS: Record<ToneRangePreset, [number, number]>;
905
+ /**
906
+ * Select › Color Range › Shadows / Midtones / Highlights: pixels whose luma lies in `lo..hi` are
907
+ * selected; outside, selection falls off linearly to 0 over `falloff` levels.
908
+ */
909
+ declare function toneRange(pixels: PixelBuffer, range: ToneRangePreset | readonly [number, number], falloff?: number): SelectionMask;
910
+ /**
911
+ * Select › Color Range › Reds … Magentas (`center` 0, 60, …, 300°): closeness of hue (full at
912
+ * the centre, none 60° away) × chroma × (2 − max), so neutrals are never selected.
913
+ */
914
+ declare function hueRange(pixels: PixelBuffer, center: number): SelectionMask;
915
+
916
+ /** Distance reported for every pixel when there is no seed at all. */
917
+ declare const EMPTY_DISTANCE = 10000000000;
918
+ /**
919
+ * Exact Euclidean distance from every pixel centre to the nearest seed pixel centre (row-major
920
+ * `inside`, non-zero = seed): nearest seeds along columns, then the lower envelope of parabolas
921
+ * along rows (Felzenszwalb & Huttenlocher). Linear in the pixel count.
922
+ */
923
+ declare function distanceTransform(inside: ArrayLike<number | boolean>, width: number, height: number): Float32Array;
924
+ /** Grows coverage (0..1) by `r` pixels: covered when the nearest selected centre is ≤ r away (fractional r → partial). */
925
+ declare function expandCoverage(m: ArrayLike<number>, w: number, h: number, r: number): Float32Array;
926
+ /** Shrinks coverage by `r` pixels (expands the inverse). */
927
+ declare function contractCoverage(m: ArrayLike<number>, w: number, h: number, r: number): Float32Array;
928
+ /**
929
+ * Select › Modify › Border: a soft band `2r` wide centred on the edge. A pixel `D` away (centre
930
+ * to centre) from the nearest pixel on the other side of the edge is selected `1 − (D − 1) / r`.
931
+ */
932
+ declare function borderCoverage(m: ArrayLike<number>, w: number, h: number, r: number): Float32Array;
933
+ /**
934
+ * Select › Modify › Smooth: box average of radius `round(r)` with replicated edges, then
935
+ * re-thresholded at ½ — specks drop, corners round, straight edges stay hard.
936
+ */
937
+ declare function smoothCoverage(m: ArrayLike<number>, w: number, h: number, r: number): Float32Array;
938
+ /** Select › Modify › Feather: Gaussian blur with σ = radius (as Photoshop), zero beyond the image. */
939
+ declare function featherCoverage(m: ArrayLike<number>, w: number, h: number, radius: number): Float32Array;
940
+ /** Grey dilation (`grow`) or erosion with a square of radius `r`, separable, O(1) per pixel. */
941
+ declare function greyMorphology(src: ArrayLike<number>, w: number, h: number, r: number, grow: boolean): Float32Array;
942
+ /**
943
+ * Grows (positive) or shrinks (negative) soft coverage by a fractional distance: whole pixels
944
+ * use one morphology, the remainder blends with the next pixel, so 0.3 px differs from 1 px.
945
+ */
946
+ declare function shiftCoverage(src: ArrayLike<number>, w: number, h: number, amount: number): Float32Array;
947
+ type SelectionModifyOp = 'grow' | 'shrink' | 'feather' | 'border' | 'smooth';
948
+ declare const SELECTION_MODIFY_OPS: readonly SelectionModifyOp[];
949
+ /** Select › Modify (grow = Expand, shrink = Contract, feather, border, smooth) by `radius` pixels. */
950
+ declare function modifySelection(mask: SelectionMask, op: SelectionModifyOp, radius: number): SelectionMask;
951
+
952
+ /**
953
+ * Refine Edge / Select and Mask parameters (Photoshop's units). Refinement is edge-aware: within
954
+ * `radius` pixels of the mask's edge the coverage is re-estimated from the image with a colour
955
+ * guided filter (He et al.), so a coarse or misaligned mask (e.g. an AI segmentation) follows
956
+ * the real edges of the image.
957
+ */
958
+ interface RefineEdgeParams {
959
+ /** Edge-detection band radius in pixels (0 = no edge detection). */
960
+ radius?: number;
961
+ /** Narrow the band where the image edge is sharp (hair stays soft, hard edges stay hard). */
962
+ smartRadius?: boolean;
963
+ /** 0..100: rounds jagged edges. */
964
+ smooth?: number;
965
+ /** Pixels of Gaussian softening. */
966
+ feather?: number;
967
+ /** 0..100: hardens soft edges. */
968
+ contrast?: number;
969
+ /** −100..100: moves the edge out (positive) or in; 100 % is `max(radius, 4)` pixels. */
970
+ shiftEdge?: number;
971
+ }
972
+ declare const DEFAULT_REFINE_EDGE_PARAMS: Required<RefineEdgeParams>;
973
+ /** Guided-filter regulariser (colour guide, samples in 0..1). */
974
+ declare const GUIDED_FILTER_EPS = 0.0001;
975
+ /** Clamped, complete parameters. */
976
+ declare function normalizeRefineEdgeParams(p?: RefineEdgeParams): Required<RefineEdgeParams>;
977
+ /** Pixels around the mask's content that refinement can read or write. */
978
+ declare function refineEdgeHalo(params?: RefineEdgeParams): number;
979
+ /** Box mean of radius `r` (window clipped to the image, divided by its pixel count). O(1) per pixel. */
980
+ declare function boxMean(src: ArrayLike<number>, w: number, h: number, r: number): Float32Array;
981
+ /**
982
+ * Guided filter with a colour guide (He et al., Eq. 19–21: `a_k = (Σ_k + εU)⁻¹ cov_k(I, p)`):
983
+ * filters `p` so that it is locally a linear function of the guide's colour, which makes the
984
+ * output's edges follow the guide's edges. `guide` is three planes of 0..1 values.
985
+ */
986
+ declare function guidedFilterColor(guide: readonly [Float32Array, Float32Array, Float32Array], p: Float32Array, w: number, h: number, r: number, eps?: number): Float32Array;
987
+ /**
988
+ * Per-pixel transition width (pixels) of the guide's luminance: local range over local maximum
989
+ * gradient in a window of radius `r` (a sharp step gives about 2, a ramp of width L about L).
990
+ */
991
+ declare function edgeWidth(guide: readonly [Float32Array, Float32Array, Float32Array], w: number, h: number, r: number): Float32Array;
992
+ /**
993
+ * Refines soft coverage `m` (`w × h`, 0..1) with `guide` (three 0..1 planes, required when
994
+ * `radius > 0`): edge-aware re-estimation in a band around the edge, then smooth, feather,
995
+ * contrast and shift edge, in that order.
996
+ */
997
+ declare function refineCoverage(guide: readonly [Float32Array, Float32Array, Float32Array] | null, m: Float32Array, w: number, h: number, params?: RefineEdgeParams): Float32Array;
998
+ /** Three 0..1 planes of a window of an RGBA buffer. */
999
+ declare function guidePlanes(pixels: PixelBuffer, win?: CropBox): [Float32Array, Float32Array, Float32Array];
1000
+ /**
1001
+ * Refine Edge on a selection (or a coarse AI mask loaded as one) against `pixels` (the image,
1002
+ * same size). Work is confined to the mask's bounds grown by {@link refineEdgeHalo}, so a small
1003
+ * selection on a large image stays cheap.
1004
+ */
1005
+ declare function refineSelection(pixels: PixelBuffer | null, mask: SelectionMask, params?: RefineEdgeParams): SelectionMask;
1006
+
1007
+ type CropHandle = 'nw' | 'ne' | 'se' | 'sw' | 'n' | 's' | 'e' | 'w' | 'inside' | null;
1008
+ interface ViewportMetrics {
1009
+ vpWidth: number;
1010
+ vpHeight: number;
1011
+ centerX: number;
1012
+ centerY: number;
1013
+ baseW: number;
1014
+ baseH: number;
1015
+ fitScale: number;
1016
+ renderW: number;
1017
+ renderH: number;
1018
+ totalScale: number;
1019
+ imageRect: {
1020
+ x: number;
1021
+ y: number;
1022
+ width: number;
1023
+ height: number;
1024
+ };
1025
+ cropRect: {
1026
+ x: number;
1027
+ y: number;
1028
+ width: number;
1029
+ height: number;
1030
+ } | null;
1031
+ }
1032
+ /**
1033
+ * A complete description of the natural-image -> target mapping (see module comment).
1034
+ */
1035
+ interface ImageProjection {
1036
+ /** Visible region in natural image pixels (crop box or full image). */
1037
+ region: CropBox;
1038
+ /** Rotation in degrees, normalised to [0, 360). */
1039
+ angle: number;
1040
+ flipH: boolean;
1041
+ flipV: boolean;
1042
+ /** Uniform scale from image pixels to target pixels. */
1043
+ scale: number;
1044
+ /** Target-space point that the centre of `region` maps to. */
1045
+ originX: number;
1046
+ originY: number;
1047
+ }
1048
+ /** Normalises any rotation in degrees to [0, 360). */
1049
+ declare function normalizeAngle(degrees: number): number;
1050
+ /** Whether the rotation swaps the displayed width and height. */
1051
+ declare function isQuarterTurnSwapped(transform: Pick<ImageTransform, 'rotate'>): boolean;
1052
+ /** Size of a region once rotated (bounding box for arbitrary angles). */
1053
+ declare function getRotatedSize(width: number, height: number, transform: Pick<ImageTransform, 'rotate'>): {
1054
+ width: number;
1055
+ height: number;
1056
+ };
1057
+ /** Maps a natural-image point to target space. */
1058
+ declare function projectImagePoint(pt: ImagePoint, p: ImageProjection): ImagePoint;
1059
+ /** Exact inverse of {@link projectImagePoint}. */
1060
+ declare function unprojectCanvasPoint(pt: ImagePoint, p: ImageProjection): ImagePoint;
1061
+ /**
1062
+ * Applies the projection to a 2D context so that subsequent drawing in natural image
1063
+ * pixels lands exactly where {@link projectImagePoint} says it does.
1064
+ */
1065
+ declare function applyImageProjectionToContext(ctx: Pick<CanvasRenderingContext2D, 'translate' | 'scale' | 'rotate'>, p: ImageProjection): void;
1066
+ /** Projects an image-space rectangle and returns its target-space bounding box. */
1067
+ declare function projectImageRect(rect: CropBox, p: ImageProjection): {
1068
+ x: number;
1069
+ y: number;
1070
+ width: number;
1071
+ height: number;
1072
+ };
1073
+ /** Clamps a crop box to the image bounds. */
1074
+ declare function clampCropToImage(crop: CropBox, width: number, height: number): CropBox;
1075
+ /**
1076
+ * Returns the natural-image -> canvas projection used by the interactive viewport.
1077
+ */
1078
+ declare function getViewportProjection(state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean, cropOverride?: CropBox | null): ImageProjection;
1079
+ /**
1080
+ * Calculates current viewport projection metrics for canvas <-> image coordinate mapping.
1081
+ * `cropRect` is the on-screen (rotated/flipped) rectangle of the crop box.
1082
+ */
1083
+ declare function getViewportMetrics(state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean, cropOverride?: CropBox | null): ViewportMetrics;
1084
+ /**
1085
+ * Converts a point on the canvas (in internal canvas pixel space) to natural image pixel
1086
+ * space, accounting for zoom, pan, fit scale, crop offset, rotation and flips.
1087
+ */
1088
+ declare function canvasToImagePoint(canvasPt: ImagePoint, state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean): ImagePoint;
1089
+ /**
1090
+ * Converts a point in natural image pixel space to internal canvas pixel space.
1091
+ * Exact inverse of {@link canvasToImagePoint}.
1092
+ */
1093
+ declare function imageToCanvasPoint(imgPt: ImagePoint, state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean): ImagePoint;
1094
+ /**
1095
+ * Converts a crop handle in screen orientation to the equivalent edge/corner of the crop
1096
+ * box in natural image space, or the reverse with `toScreen = true`.
1097
+ */
1098
+ declare function mapCropHandle(handle: CropHandle, transform: ImageTransform, toScreen?: boolean): CropHandle;
1099
+ /**
1100
+ * Hit-tests the crop box and its corner/edge handles given a canvas point.
1101
+ * The returned handle names the edge/corner of the crop box in natural image space
1102
+ * (what {@link calculateCropDrag} expects); use `getCropCursor(handle, state.transform)`
1103
+ * to obtain the matching on-screen cursor.
1104
+ */
1105
+ declare function hitTestCrop(canvasPt: ImagePoint, state: ImageEditorState, vpWidth: number, vpHeight: number, handleThreshold?: number, cropOverride?: CropBox | null): CropHandle;
1106
+ /**
1107
+ * Returns CSS cursor style corresponding to a crop handle. Pass the current transform when
1108
+ * the handle comes from {@link hitTestCrop} (image space) so the cursor matches the
1109
+ * on-screen orientation under rotation/flip.
1110
+ */
1111
+ declare function getCropCursor(handle: CropHandle, transform?: ImageTransform): string;
1112
+ /**
1113
+ * Calculates updated crop rectangle given a drag interaction.
1114
+ */
1115
+ declare function calculateCropDrag(startCrop: CropBox, handle: CropHandle, startImgPt: ImagePoint, currentImgPt: ImagePoint, imgWidth: number, imgHeight: number, aspectRatio?: number | null, transform?: ImageTransform): CropBox;
1116
+ /**
1117
+ * Calculates bounding box of an annotation.
1118
+ */
1119
+ declare function getAnnotationBounds(ann: Annotation): {
1120
+ x: number;
1121
+ y: number;
1122
+ width: number;
1123
+ height: number;
1124
+ };
1125
+ /**
1126
+ * Hit tests if a point in image space is within an annotation's bounds.
1127
+ */
1128
+ declare function hitTestAnnotation(imgPt: ImagePoint, ann: Annotation, padding?: number): boolean;
1129
+
1130
+ /** Properties of a layer mask that leave its pixels intact (Photoshop's Properties › Masks). */
1131
+ interface LayerMaskProps {
1132
+ /** 0..1 (default 1): at 0.5 the masked-out areas still get half of the adjustment. */
1133
+ density?: number;
1134
+ /** Gaussian feather σ in pixels, 0..1000 (default 0). */
1135
+ feather?: number;
1136
+ /** Disabled masks are kept but ignored (the layer applies everywhere). Default true. */
1137
+ enabled?: boolean;
1138
+ /**
1139
+ * Layer masks only: `false` unlinks the mask, so moving or transforming the layer's pixels leaves
1140
+ * it in place. Default true. Adjustment-layer masks in canvas space ignore it.
1141
+ */
1142
+ linked?: boolean;
1143
+ }
1144
+ /**
1145
+ * A clean layer mask from stored data, or `null` when it is unusable. Defaults are left out
1146
+ * (density 1, feather 0, enabled), so a plain mask serializes as `{width, height, rle}`. The
1147
+ * RLE is only decoded when rendering.
1148
+ */
1149
+ declare function normalizeLayerMask(raw: unknown): AdjustmentLayerMask | null;
1150
+ /** A layer mask holding the selection's pixels (Add Layer Mask › Reveal Selection). */
1151
+ declare function layerMaskFromSelection(selection: SelectionMask, props?: LayerMaskProps): AdjustmentLayerMask;
1152
+ /** Applies property changes; `undefined` fields keep their value. */
1153
+ declare function updateLayerMaskProps(mask: AdjustmentLayerMask, props: LayerMaskProps): AdjustmentLayerMask;
1154
+ /** The mask's pixels as a selection (Load Selection from mask), or `null` when the data is damaged. */
1155
+ declare function decodeLayerMask(mask: AdjustmentLayerMask): SelectionMask | null;
1156
+ /** Cache key of everything about a mask that changes pixels. */
1157
+ declare function layerMaskKey(mask: AdjustmentLayerMask | undefined): string;
1158
+ /**
1159
+ * The mask's effective coverage (0..1, mask resolution): feathered (σ = feather, borders
1160
+ * replicated), then density applied as `1 − density·(1 − v)`. `null` for a disabled or damaged
1161
+ * mask, meaning "no mask".
1162
+ */
1163
+ declare function layerMaskCoverage(mask: AdjustmentLayerMask): Float32Array | null;
1164
+ /**
1165
+ * Resamples mask coverage (natural-image aligned, any resolution) into a target raster drawn
1166
+ * through `projection` (the base canvas of the renderer: crop, rotation, flips): each target
1167
+ * pixel centre is unprojected to natural pixels and sampled bilinearly.
1168
+ */
1169
+ declare function projectMaskCoverage(coverage: Float32Array, maskWidth: number, maskHeight: number, naturalWidth: number, naturalHeight: number, projection: ImageProjection, targetWidth: number, targetHeight: number): Float32Array;
1170
+ /**
1171
+ * Per-layer mask coverage for a render target, keyed by layer id; `undefined` when no layer has
1172
+ * an enabled, valid mask (so mask-free stacks take the exact same path as before).
1173
+ */
1174
+ declare function buildLayerMaskPlanes(layers: readonly AdjustmentLayer[] | undefined, naturalWidth: number, naturalHeight: number, projection: ImageProjection, targetWidth: number, targetHeight: number): Map<string, Float32Array> | undefined;
1175
+
1176
+ /** A refused layer operation; the message says why and how to fix it. */
1177
+ declare class LayerOperationError extends Error {
1178
+ constructor(message: string);
1179
+ }
1180
+ /** Where a layer goes: above or below a layer (in its group), or at a position in a group. */
1181
+ interface LayerPlacement {
1182
+ /** Directly above this layer, in the same group. */
1183
+ aboveId?: string;
1184
+ /** Directly below this layer, in the same group. */
1185
+ belowId?: string;
1186
+ /** Inside this group (at `index`, default the top). */
1187
+ parentId?: string;
1188
+ /** Index in the parent's children (or the top level), 0 = bottom. */
1189
+ index?: number;
1190
+ }
1191
+ /** The layer with `id`, or a `LayerOperationError` listing the ids that exist. */
1192
+ declare function requireLayer(layers: readonly ImageLayer[], id: string): LayerLocation$1<ImageLayer>;
1193
+ /** Refuses when the layer is locked for `what` (and always for `all`). */
1194
+ declare function requireUnlocked(layer: ImageLayer, what: keyof LayerLocks, action: string): void;
1195
+ /** Inserts `layer` at `placement` (default: the top level at `defaultIndex`, or the top). */
1196
+ declare function placeLayer(layers: readonly ImageLayer[], layer: ImageLayer, placement?: LayerPlacement, defaultIndex?: number): ImageLayer[];
1197
+ /** Removes a layer (refused when it is locked). */
1198
+ declare function removeLayer(layers: readonly ImageLayer[], id: string): ImageLayer[];
1199
+ /** Moves a layer to `placement`. A group cannot go inside itself. Returns the new stack. */
1200
+ declare function moveLayer(layers: readonly ImageLayer[], id: string, placement: LayerPlacement): ImageLayer[];
1201
+ /** Every id in a subtree gets a fresh one (for duplicates). */
1202
+ declare function withNewIds<T extends ImageLayer>(layer: T): T;
1203
+ /** Duplicates a layer (and its children) directly above it. Returns the stack and the copy's id. */
1204
+ declare function duplicateLayer(layers: readonly ImageLayer[], id: string, name?: string): {
1205
+ layers: ImageLayer[];
1206
+ id: string;
1207
+ };
1208
+ /** Properties `setLayerProperties` changes. */
1209
+ interface LayerProperties {
1210
+ name?: string;
1211
+ visible?: boolean;
1212
+ /** 0..1 */
1213
+ opacity?: number;
1214
+ /** 0..1, pixel layers only. */
1215
+ fillOpacity?: number;
1216
+ blend?: LayerBlendMode;
1217
+ clipped?: boolean;
1218
+ locks?: LayerLocks;
1219
+ /** Groups only (UI state). */
1220
+ collapsed?: boolean;
1221
+ }
1222
+ /** Validates and applies property changes to one layer. */
1223
+ declare function setLayerProperties(layers: readonly ImageLayer[], id: string, props: LayerProperties): ImageLayer[];
1224
+ /**
1225
+ * Puts layers (siblings in one group or the top level) into a new group at the position of the
1226
+ * topmost one, keeping their order. Returns the stack and the group id.
1227
+ */
1228
+ declare function groupLayers(layers: readonly ImageLayer[], ids: string[], name?: string): {
1229
+ layers: ImageLayer[];
1230
+ id: string;
1231
+ };
1232
+ /** Replaces a group by its children (the group's own opacity, blend and mask are dropped). */
1233
+ declare function ungroupLayer(layers: readonly ImageLayer[], id: string): {
1234
+ layers: ImageLayer[];
1235
+ ids: string[];
1236
+ };
1237
+ /** Sets a pixel layer's offset (refused when its position is locked). An unlinked mask stays in place. */
1238
+ declare function offsetLayer(layers: readonly ImageLayer[], id: string, offset: {
1239
+ x: number;
1240
+ y: number;
1241
+ }): ImageLayer[];
1242
+ /** Sets or removes (`null`) a layer's mask. */
1243
+ declare function setLayerMask(layers: readonly ImageLayer[], id: string, mask: RasterMask | null): ImageLayer[];
1244
+ /** A clean filter from loose input (for tools and the UI). */
1245
+ declare function normalizeFilter(raw: Partial<LayerFilter> & {
1246
+ kind: LayerFilter['kind'];
1247
+ }, id?: string): LayerFilter;
1248
+ /** Adds a filter on top of a pixel layer's filters. A layer has at most one filter of each kind. */
1249
+ declare function addLayerFilter(layers: readonly ImageLayer[], id: string, filter: LayerFilter): ImageLayer[];
1250
+ /** Changes one filter of a pixel layer (`changes` merged and validated). */
1251
+ declare function updateLayerFilter(layers: readonly ImageLayer[], id: string, filterId: string, changes: Record<string, unknown>): ImageLayer[];
1252
+ declare function removeLayerFilter(layers: readonly ImageLayer[], id: string, filterId: string): ImageLayer[];
1253
+ /** A layer's effects, or an empty, enabled list when it has none. */
1254
+ declare function layerEffectsOf(layer: ImageLayer): LayerEffects;
1255
+ /** Replaces a layer's effects (validated); `null` or an empty list without a PSD block removes them. */
1256
+ declare function setLayerEffects(layers: readonly ImageLayer[], id: string, effects: LayerEffects | null): ImageLayer[];
1257
+ /**
1258
+ * Adds an effect of `kind` (Photoshop's defaults with `params` applied) to a layer's effects, at
1259
+ * `index` in the list (default: the end; the first listed of a kind is on top). Returns the stack
1260
+ * and the new effect.
1261
+ */
1262
+ declare function addLayerEffect(layers: readonly ImageLayer[], id: string, kind: LayerEffectKind, params?: Record<string, unknown>, index?: number): {
1263
+ layers: ImageLayer[];
1264
+ effect: LayerEffect;
1265
+ };
1266
+ /** Changes one effect (only the given fields; nested objects replace whole, `null` removes an optional one). */
1267
+ declare function updateLayerEffect(layers: readonly ImageLayer[], id: string, effectId: string, changes: Record<string, unknown>): ImageLayer[];
1268
+ declare function removeLayerEffect(layers: readonly ImageLayer[], id: string, effectId: string): ImageLayer[];
1269
+ /** Sets the master switch and, when given, Scale Effects (0.01..10, 1 = 100 %). */
1270
+ declare function setLayerEffectsEnabled(layers: readonly ImageLayer[], id: string, enabled: boolean, scale?: number): ImageLayer[];
1271
+ /** A blend mode check for tools. */
1272
+ declare function parseBlend(value: unknown, allowPassThrough?: boolean): BlendMode | 'pass-through' | undefined;
1273
+ /** The union of two rectangles (either may be null). */
1274
+ declare function unionRect(a: CropBox | null, b: CropBox | null): CropBox | null;
1275
+
1276
+ interface PsdHeader {
1277
+ psb: boolean;
1278
+ channels: number;
1279
+ height: number;
1280
+ width: number;
1281
+ depth: number;
1282
+ colorMode: number;
1283
+ }
1284
+ interface PsdRect {
1285
+ top: number;
1286
+ left: number;
1287
+ bottom: number;
1288
+ right: number;
1289
+ }
1290
+ interface PsdChannel {
1291
+ id: number;
1292
+ /** `null` for a zero-length channel. */
1293
+ compression: number | null;
1294
+ /** Encoded bytes, without the compression field. */
1295
+ data: Uint8Array;
1296
+ }
1297
+ interface PsdLayerMask {
1298
+ rect: PsdRect;
1299
+ defaultColor: number;
1300
+ flags: number;
1301
+ userDensity?: number;
1302
+ userFeather?: number;
1303
+ vectorDensity?: number;
1304
+ vectorFeather?: number;
1305
+ /** The real (vector-combined) user mask, when present. */
1306
+ real?: {
1307
+ flags: number;
1308
+ background: number;
1309
+ rect: PsdRect;
1310
+ };
1311
+ }
1312
+ interface PsdTaggedBlock {
1313
+ key: string;
1314
+ data: Uint8Array;
1315
+ }
1316
+ interface PsdLayerRecord {
1317
+ rect: PsdRect;
1318
+ channels: PsdChannel[];
1319
+ blendKey: string;
1320
+ opacity: number;
1321
+ clipping: number;
1322
+ flags: number;
1323
+ mask?: PsdLayerMask;
1324
+ /** The legacy (Pascal) name; `layerName` prefers `luni`. */
1325
+ legacyName: string;
1326
+ blocks: PsdTaggedBlock[];
1327
+ }
1328
+ interface PsdResource {
1329
+ id: number;
1330
+ name: string;
1331
+ data: Uint8Array;
1332
+ }
1333
+ interface PsdFile {
1334
+ header: PsdHeader;
1335
+ colorModeData: Uint8Array;
1336
+ resources: PsdResource[];
1337
+ /** Bottom-most first, as stored. */
1338
+ layers: PsdLayerRecord[];
1339
+ /** The layer count was negative: the merged image's first extra channel is its alpha. */
1340
+ mergedAlpha: boolean;
1341
+ globalBlocks: PsdTaggedBlock[];
1342
+ imageCompression: number;
1343
+ imageData: Uint8Array;
1344
+ }
1345
+ /** Parses a PSD or PSB. Throws `PsdError` with what was wrong. */
1346
+ declare function readPsd(bytes: Uint8Array): PsdFile;
1347
+ declare function layerName(rec: PsdLayerRecord): string;
1348
+ interface PsdTreeNode {
1349
+ /** Index into `PsdFile.layers` (the folder record for a group). */
1350
+ index: number;
1351
+ children?: PsdTreeNode[];
1352
+ }
1353
+ /** The layer tree, bottom-most first (the stored order, as in our model), from the `lsct` dividers. */
1354
+ declare function layerTree(file: PsdFile): PsdTreeNode[];
1355
+ /** One layer channel decoded to `depth`-bit planar bytes, or `undefined` if the layer has none. */
1356
+ declare function decodeLayerChannel(file: PsdFile, rec: PsdLayerRecord, id: number): Uint8Array | undefined;
1357
+ /** The merged image's channels decoded, planar. */
1358
+ declare function decodeMergedImage(file: PsdFile): Uint8Array;
1359
+ /** Serializes a file. Channel data must already be encoded; `encodeName` writes legacy (Pascal) names. */
1360
+ declare function writePsd(file: PsdFile, encodeName: (s: string) => Uint8Array): Uint8Array;
1361
+
1362
+ /** PSD blend keys → our blend modes (`diss` is the one we lack). */
1363
+ declare const PSD_BLEND_KEYS: Record<string, LayerBlendMode>;
1364
+ /** What happened to one PSD layer on import. */
1365
+ interface PsdLayerSummary {
1366
+ name: string;
1367
+ /** Our layer type, or `null` when the layer was dropped. */
1368
+ type: 'pixel' | 'adjustment' | 'group' | null;
1369
+ /** `mapped`: held exactly; `approximated`: imported with losses; `dropped`: left out. */
1370
+ status: 'mapped' | 'approximated' | 'dropped';
1371
+ /** What Photoshop calls it (`pixel`, `type`, `smart-object`, `fill`, `shape`, `Levels` …). */
1372
+ psdKind: string;
1373
+ /** Why it is not `mapped`. */
1374
+ notes: string[];
1375
+ /** Our layer id (when imported). */
1376
+ id?: string;
1377
+ depth: number;
1378
+ }
1379
+ interface PsdImportResult {
1380
+ width: number;
1381
+ height: number;
1382
+ /** Bottom to top, as in our model. */
1383
+ layers: ImageLayer[];
1384
+ /** One sentence per problem, naming the affected layers. */
1385
+ warnings: string[];
1386
+ /** Every PSD layer, bottom first, a group before its children. */
1387
+ summary: PsdLayerSummary[];
1388
+ /** Pixels per inch, when the file says. */
1389
+ resolution?: number;
1390
+ /** The patterns embedded in the file (`Patt` / `Pat2` / `Pat3`), which pattern effects use. */
1391
+ patterns: EffectPattern[];
1392
+ /** The global light (image resources 1037 and 1049), when the file has one. */
1393
+ globalLight?: GlobalLight;
1394
+ info: {
1395
+ psb: boolean;
1396
+ depth: number;
1397
+ colorMode: string;
1398
+ channels: number;
1399
+ };
1400
+ }
1401
+ interface PsdImportOptions {
1402
+ /** Makes layer ids (defaults to `psd-<n>`). */
1403
+ newId?: (hint: string) => string;
1404
+ /** Skip decoding pixels (inspection only). */
1405
+ skipPixels?: boolean;
1406
+ }
1407
+ /** Maps a parsed PSD to our layers. */
1408
+ declare function psdToLayers(file: PsdFile, options?: PsdImportOptions): PsdImportResult;
1409
+ /** Parses and maps PSD / PSB bytes. Throws `PsdError` for files that cannot be read at all. */
1410
+ declare function importPsd(bytes: Uint8Array, options?: PsdImportOptions): PsdImportResult;
1411
+
1412
+ /**
1413
+ * Free Transform geometry for the image editor, as pure functions both the UI bindings and the
1414
+ * agent tools use: a transform is the frame being transformed (`box`, document pixels), where its
1415
+ * four corners land (`corners`, TL, TR, BR, BL) and an optional warp mesh over the box. Scale,
1416
+ * rotate, skew and flips keep the corners a parallelogram (an affine map); Distort and Perspective
1417
+ * move them freely (a projective map); Warp bends the box through a Bézier mesh before the corner
1418
+ * map.
1419
+ *
1420
+ * The maths (homographies, meshes, presets) is shared with the vector editor in `src/geom`.
1421
+ */
1422
+
1423
+ /** Free Transform modes (Edit › Transform). Scale, rotate and skew stay affine. */
1424
+ type FreeTransformMode = 'scale' | 'rotate' | 'skew' | 'distort' | 'perspective' | 'warp';
1425
+ declare const FREE_TRANSFORM_MODES: readonly FreeTransformMode[];
1426
+ /** The nine reference-point positions of the options bar's locator, over the transformed box. */
1427
+ type TransformAnchor = 'top-left' | 'top' | 'top-right' | 'left' | 'center' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right';
1428
+ declare const TRANSFORM_ANCHORS: readonly TransformAnchor[];
1429
+ declare function isTransformAnchor(value: unknown): value is TransformAnchor;
1430
+ /** A transform in progress: the frame, where its corners go and an optional warp over the frame. */
1431
+ interface FreeTransformGeometry {
1432
+ /** The frame being transformed, in document pixels (the content bounds when it began). */
1433
+ box: CropBox;
1434
+ /** Where the frame's corners land: top-left, top-right, bottom-right, bottom-left (document pixels). */
1435
+ corners: Quad;
1436
+ /**
1437
+ * A warp applied in the frame's own space before the corner map: its control points are in
1438
+ * document pixels of the untransformed frame (`identityWarpMesh(box)` is no warp).
1439
+ */
1440
+ warp?: WarpMesh | null;
1441
+ }
1442
+ /** The untransformed geometry of a box. */
1443
+ declare function identityTransform(box: CropBox): FreeTransformGeometry;
1444
+ /** Where `box`'s corners go under the affine `m`. */
1445
+ declare function cornersFromMatrix(box: CropBox, m: AffineMatrix): Quad;
1446
+ /** The affine matrix taking `box` onto `corners` when they form a parallelogram, else `null`. */
1447
+ declare function matrixFromCorners(box: CropBox, corners: Quad, eps?: number): AffineMatrix | null;
1448
+ /** The projective map of the corner move (box → corners), or `null` when the corners are degenerate. */
1449
+ declare function transformHomography(geometry: FreeTransformGeometry): Homography | null;
1450
+ /** Whether a warp mesh changes anything (not the identity over the box). */
1451
+ declare function hasWarp(geometry: FreeTransformGeometry): boolean;
1452
+ /** Whether the geometry changes nothing. */
1453
+ declare function isIdentityTransform(geometry: FreeTransformGeometry): boolean;
1454
+ /**
1455
+ * The forward map of the whole transform (document pixels of the source → document pixels of the
1456
+ * result): the warp over the box, then the corner map. `null` where it is undefined.
1457
+ */
1458
+ declare function transformMap(geometry: FreeTransformGeometry): ((p: Vec2) => Vec2 | null) | null;
1459
+ /** A point of the transformed box at box fractions (`fx`, `fy`), warp included. */
1460
+ declare function transformedPoint(geometry: FreeTransformGeometry, fx: number, fy: number): Vec2 | null;
1461
+ /** Where an anchor of the box is after the corner map (the warp ignored). */
1462
+ declare function anchorPoint(geometry: FreeTransformGeometry, anchor: TransformAnchor): Vec2;
1463
+ /** Bounds of the transformed result's outline (document pixels, not rounded). */
1464
+ declare function transformedBox(geometry: FreeTransformGeometry): CropBox | null;
1465
+ /** Free Transform as numbers, as Photoshop's options bar shows it. */
1466
+ interface TransformParams {
1467
+ /** Width in percent of the frame (default 100; negative flips). */
1468
+ scaleX?: number;
1469
+ /** Height in percent of the frame (default 100; negative flips). */
1470
+ scaleY?: number;
1471
+ /** Rotation in degrees, clockwise (default 0). */
1472
+ rotate?: number;
1473
+ /** Horizontal skew in degrees: vertical edges lean right for positive angles (default 0). */
1474
+ skewX?: number;
1475
+ /** Vertical skew in degrees: horizontal edges lean down for positive angles (default 0). */
1476
+ skewY?: number;
1477
+ /** Move after scaling and rotating, in document pixels (default 0). */
1478
+ dx?: number;
1479
+ dy?: number;
1480
+ /** The fixed point of scale, skew and rotation: an anchor of the frame or a document point (default the centre). */
1481
+ reference?: TransformAnchor | Vec2;
1482
+ }
1483
+ /** The reference point of params over a box (default its centre). */
1484
+ declare function referencePoint(box: CropBox, reference: TransformParams['reference']): Vec2;
1485
+ /**
1486
+ * The affine matrix of `params` over `box`: about the reference point, skew, then scale, then
1487
+ * rotate; then the move.
1488
+ */
1489
+ declare function matrixFromParams(box: CropBox, params: TransformParams): AffineMatrix;
1490
+ /** What the options bar shows for a transform. */
1491
+ interface TransformReadout {
1492
+ /** False for Distort, Perspective and Warp: only `bounds` and `corners` then mean anything. */
1493
+ affine: boolean;
1494
+ /** Width and height in percent of the frame. */
1495
+ scaleX: number;
1496
+ scaleY: number;
1497
+ /** Rotation in degrees, clockwise, in (−180, 180]. */
1498
+ rotate: number;
1499
+ /** Horizontal skew in degrees (vertical skew is folded into rotation and scale). */
1500
+ skewX: number;
1501
+ /** Where the reference point is now (document pixels). */
1502
+ x: number;
1503
+ y: number;
1504
+ /** Bounds of the transformed outline. */
1505
+ bounds: CropBox | null;
1506
+ corners: Quad;
1507
+ }
1508
+ /**
1509
+ * Decomposes a transform for the options bar: the linear part as rotate · scale · skew (QR) with
1510
+ * the given reference point. Non-affine geometry reports its bounds and corners only.
1511
+ */
1512
+ declare function describeTransform(geometry: FreeTransformGeometry, reference?: TransformParams['reference']): TransformReadout;
1513
+ /**
1514
+ * Corners for a transform given as numbers relative to the current corners: `params` are applied
1515
+ * after the current affine part (when the corners are a parallelogram) about the reference point
1516
+ * of the current geometry, so typing "rotate 15" rotates the box as it is now.
1517
+ */
1518
+ declare function applyParams(geometry: FreeTransformGeometry, params: TransformParams): Quad;
1519
+ /** Flip of the corners about the box's centre line (horizontal: left ↔ right). */
1520
+ declare function flipCorners(geometry: FreeTransformGeometry, axis: 'horizontal' | 'vertical'): Quad;
1521
+ /** Inverse of `matrixFromCorners` (box space → document) for mapping a document point back into the box. */
1522
+ declare function boxPointOf(geometry: FreeTransformGeometry, p: Vec2): Vec2 | null;
1523
+ /** A warp mesh from a preset over the box (null when it changes nothing). */
1524
+ declare function presetWarp(box: CropBox, preset: WarpPreset): WarpMesh | null;
1525
+ /** Validates a warp mesh for a box: well formed, finite, the right number of points. */
1526
+ declare function isValidWarp(mesh: unknown): mesh is WarpMesh;
1527
+ /** The geometry's affine map when it is one (no warp, a parallelogram), as a homography; else the projective map. */
1528
+ declare function geometryHomography(geometry: FreeTransformGeometry): Homography | null;
1529
+
1530
+ /**
1531
+ * Free Transform sessions: what a transform moves (a layer's pixels with its linked mask, a lone
1532
+ * unlinked mask, the selected pixels as a floating piece, a copy of them on a new layer, or just
1533
+ * the selection outline), a live preview while the geometry changes, and one undo step when it is
1534
+ * committed with full-quality resampling.
1535
+ */
1536
+
1537
+ /**
1538
+ * What a Free Transform moves:
1539
+ * - `layer`: the layer's pixels with its linked mask (or, with `channel: 'mask'` on an unlinked
1540
+ * mask, the mask alone);
1541
+ * - `selection`: the selected pixels (and the same part of a linked mask), lifted and committed
1542
+ * back into the layer; the selection outline moves with them;
1543
+ * - `selection-copy`: Layer via Copy, then the copy is transformed on a new layer above;
1544
+ * - `selection-outline`: Transform Selection, only the outline;
1545
+ * - `auto` (default): `selection` when something is selected, else `layer`.
1546
+ */
1547
+ type FreeTransformContent = 'auto' | 'layer' | 'selection' | 'selection-copy' | 'selection-outline';
1548
+ declare const FREE_TRANSFORM_CONTENTS: readonly FreeTransformContent[];
1549
+ interface FreeTransformInit {
1550
+ /** The layer (default the active one); not needed for `selection-outline`. */
1551
+ layerId?: string;
1552
+ content?: FreeTransformContent;
1553
+ /**
1554
+ * `pixels` (default) moves the pixels with a linked mask; `mask` moves an unlinked mask alone (a
1555
+ * linked one moves with its pixels). Defaults to the active target on the active layer.
1556
+ */
1557
+ channel?: LayerTarget;
1558
+ /** Resampling of the committed result (default bicubic). */
1559
+ interpolation?: Interpolation;
1560
+ }
1561
+ /** How a live preview is drawn (the UI knows these, the controller doesn't). */
1562
+ interface FreeTransformPreview {
1563
+ /** Screen pixels per document pixel: below 1 the preview samples a reduced copy in blocks. */
1564
+ scale?: number;
1565
+ /** Only this part of the document (the visible area) is resampled. */
1566
+ clip?: CropBox;
1567
+ }
1568
+ /** What a committed transform changed. */
1569
+ interface FreeTransformResult {
1570
+ /** The layer that holds the result (the new layer for `selection-copy`; `null` for `selection-outline`). */
1571
+ layerId: string | null;
1572
+ content: Exclude<FreeTransformContent, 'auto'>;
1573
+ /** What moved. */
1574
+ moved: Array<'pixels' | 'mask' | 'selection'>;
1575
+ /** The frame the transform started from (document pixels). */
1576
+ box: CropBox;
1577
+ /** Where the frame's corners went. */
1578
+ corners: FreeTransformGeometry['corners'];
1579
+ /** Document bounds of the moved content after the transform (pixels, or the mask or the outline). */
1580
+ bounds: CropBox | null;
1581
+ /** False when the geometry was the identity and nothing was recorded. */
1582
+ changed: boolean;
1583
+ }
1584
+ /** @internal What the session needs from the controller. */
1585
+ interface FreeTransformHost {
1586
+ layers(): ImageLayer[];
1587
+ activeLayerId(): string | null;
1588
+ activeTarget(): LayerTarget;
1589
+ selection(): SelectionMask | null;
1590
+ canvas(): {
1591
+ width: number;
1592
+ height: number;
1593
+ };
1594
+ /** The pixels a pixel layer or smart object composites (the decoded source for the Background). */
1595
+ pixelsOf(layer: PixelLayer | SmartObjectLayer): TiledSurface;
1596
+ /** Opaque pre-transform snapshot. */
1597
+ capture(): unknown;
1598
+ /** Shows a live state (no history). */
1599
+ show(layers: ImageLayer[], selection: SelectionMask | null, activeLayerId: string | null): void;
1600
+ /** Records `before` as the undo step of the state now shown. */
1601
+ record(before: unknown): void;
1602
+ /** Puts `before` back (no history). */
1603
+ restore(before: unknown): void;
1604
+ }
1605
+ /** Preview block size for a screen scale: 1 at 100 % and above, then powers of two up to 8. */
1606
+ declare function previewStep(scale: number | undefined): number;
1607
+ /**
1608
+ * A Free Transform in progress (see `ImageEditorController.beginFreeTransform`). Change the
1609
+ * geometry with `update` (each change shows a live preview without history), then `commit()`
1610
+ * (one undo step, resampled at full quality) or `cancel()` (everything back as it was).
1611
+ */
1612
+ declare class FreeTransformSession {
1613
+ private readonly host;
1614
+ readonly content: Exclude<FreeTransformContent, 'auto'>;
1615
+ /** The layer being transformed (`null` for `selection-outline`). */
1616
+ readonly layerId: string | null;
1617
+ /** The new layer of a `selection-copy`. */
1618
+ readonly copyId: string | null;
1619
+ /** The frame (document pixels). */
1620
+ readonly box: CropBox;
1621
+ /** What moves. */
1622
+ readonly moves: ReadonlyArray<'pixels' | 'mask' | 'selection'>;
1623
+ /**
1624
+ * The geometry the transform starts from: the identity over `box`, or for a smart object its
1625
+ * placement (`box` is then the contents' rectangle in contents pixels, and the transform edits
1626
+ * the placement without resampling anything twice).
1627
+ */
1628
+ readonly startGeometry: FreeTransformGeometry;
1629
+ /** True when the layer is a smart object (the transform sets its placement). */
1630
+ readonly smartObject: boolean;
1631
+ private geometryValue;
1632
+ private readonly smart;
1633
+ private readonly smartReduced;
1634
+ private readonly startInterp;
1635
+ private smartMasksMoved;
1636
+ private interp;
1637
+ private readonly parts;
1638
+ private readonly before;
1639
+ private readonly startLayers;
1640
+ private readonly startSelection;
1641
+ private readonly startActive;
1642
+ /** The stack this session last showed: anything else means the document changed under it. */
1643
+ private shown;
1644
+ private closed;
1645
+ constructor(host: FreeTransformHost, init?: FreeTransformInit);
1646
+ /** The current geometry. */
1647
+ get geometry(): FreeTransformGeometry;
1648
+ get interpolation(): Interpolation;
1649
+ get isOpen(): boolean;
1650
+ private check;
1651
+ /**
1652
+ * Changes the geometry (`corners`, `warp`) or the interpolation and, unless `preview` is `false`,
1653
+ * shows the result live (no history). Returns the geometry.
1654
+ */
1655
+ update(changes: {
1656
+ corners?: FreeTransformGeometry['corners'];
1657
+ warp?: FreeTransformGeometry['warp'];
1658
+ interpolation?: Interpolation;
1659
+ }, preview?: FreeTransformPreview | false): FreeTransformGeometry;
1660
+ /** Resamples every part for the current geometry and shows it (or returns it, for commit). */
1661
+ private resample;
1662
+ /** A smart object re-rendered from its contents for the current geometry (previews leave the smart filters out). */
1663
+ private resampleSmart;
1664
+ /** Whether the geometry (or the interpolation of a smart object) differs from where it started. */
1665
+ private isChanged;
1666
+ private render;
1667
+ /** Finishes the transform with full-quality resampling, as one undo step. */
1668
+ commit(): FreeTransformResult;
1669
+ /** Abandons the transform and puts everything back (no history). */
1670
+ cancel(): void;
1671
+ private result;
1672
+ }
1673
+
1674
+ /** What `getSmartObject` reports. */
1675
+ interface SmartObjectInfo {
1676
+ layerId: string;
1677
+ contents: {
1678
+ /** Shared by linked instances. */
1679
+ id: string;
1680
+ name: string;
1681
+ kind: 'raster' | 'document';
1682
+ /** Pixel size of the contents. */
1683
+ width: number;
1684
+ height: number;
1685
+ /** False for a file that could not be decoded here (the layer shows its stored pixels). */
1686
+ decoded: boolean;
1687
+ /** The type of the file the contents came from (`psb`, `png`, `jpg`, …), or `null`. */
1688
+ fileType: string | null;
1689
+ /** The external file of linked contents, or `null` for embedded ones. */
1690
+ linkedPath: string | null;
1691
+ /** Every smart object placed from these contents (this one included), bottom first. */
1692
+ instances: string[];
1693
+ /** Layers in a nested document (0 for raster contents). */
1694
+ layerCount: number;
1695
+ };
1696
+ placement: {
1697
+ /** Where the contents' corners land: top-left, top-right, bottom-right, bottom-left (document pixels). */
1698
+ corners: Quad;
1699
+ /** The warp mesh in contents pixels, or `null`. */
1700
+ warp: WarpMesh | null;
1701
+ interpolation: Interpolation;
1702
+ /** False for distort, perspective and warp (then only corners and bounds describe it). */
1703
+ affine: boolean;
1704
+ /** Percent of the contents' own size. */
1705
+ scaleX: number;
1706
+ scaleY: number;
1707
+ rotate: number;
1708
+ skewX: number;
1709
+ /** Where the contents' centre is (document pixels). */
1710
+ center: {
1711
+ x: number;
1712
+ y: number;
1713
+ };
1714
+ /** Bounds of the placed outline. */
1715
+ bounds: CropBox | null;
1716
+ };
1717
+ smartFilters: {
1718
+ enabled: boolean;
1719
+ /** Bottom (applied first) to top. Photoshop filters we keep but do not draw have kind `psd`. */
1720
+ filters: Array<Record<string, unknown>>;
1721
+ mask: {
1722
+ enabled: boolean;
1723
+ linked: boolean;
1724
+ density: number;
1725
+ feather: number;
1726
+ bounds: CropBox | null;
1727
+ defaultValue: number;
1728
+ } | null;
1729
+ };
1730
+ /** Document bounds of the rendered pixels. */
1731
+ bounds: CropBox | null;
1732
+ }
1733
+ /** Latest serialized schema version (`3`: layers). Documents with the old shape still write 1 or 2. */
1734
+ declare const IMAGE_EDITOR_SCHEMA_VERSION: 3;
1735
+ /** Changes to an adjustment layer; `params` is merged into the current parameters. */
1736
+ interface AdjustmentLayerUpdate<K extends AdjustmentKind = AdjustmentKind> {
1737
+ params?: Partial<AdjustmentParamsMap[K]>;
1738
+ enabled?: boolean;
1739
+ opacity?: number;
1740
+ blend?: BlendMode;
1741
+ name?: string;
1742
+ /** A layer mask, or `null` to remove it. */
1743
+ mask?: AdjustmentLayerMask | null;
1744
+ }
1745
+ /** Options shared by the selection commands. */
1746
+ interface SelectionCommandOptions {
1747
+ /** How the result combines with the current selection (default `replace`). */
1748
+ mode?: SelectionMode;
1749
+ /** Feather the new shape by this many pixels before combining (default 0). */
1750
+ feather?: number;
1751
+ }
1752
+ /** Where an AI result goes (see `applyInpaintedImage`, `applyBackgroundRemovedImage`). */
1753
+ interface AIResultOptions {
1754
+ /** `new-layer`: a new layer (inpainting) or a mask (background removal); `replace`: the Background's pixels. Default: by `isMultiLayer`. */
1755
+ target?: 'new-layer' | 'replace';
1756
+ }
1757
+ /** Where the selection tools read colours from. */
1758
+ interface SampleOptions {
1759
+ /**
1760
+ * `composite` (default): the image, as before (with several layers: the composite of the pixel
1761
+ * layers, or with `sampleMerged` of every layer); `active-layer`: only the active pixel layer.
1762
+ */
1763
+ sample?: 'composite' | 'active-layer';
1764
+ /** Sample the adjusted image (base adjustments and layers) instead of the source pixels. */
1765
+ sampleMerged?: boolean;
1766
+ /** Pixels to use instead of reading the source image (natural size, RGBA). */
1767
+ pixels?: PixelBuffer;
1768
+ }
1769
+ /** A new adjustment layer: its kind plus optional initial values and position. */
1770
+ interface AdjustmentLayerInit<K extends AdjustmentKind = AdjustmentKind> extends AdjustmentLayerUpdate<K> {
1771
+ kind: K;
1772
+ id?: string;
1773
+ /** Put the new layer directly above this layer (in its group). */
1774
+ aboveId?: string;
1775
+ /** Put the new layer at the top of this group. */
1776
+ parentId?: string;
1777
+ }
1778
+ /** A new pixel layer: empty, a solid colour over the canvas, or given pixels, plus its properties and position. */
1779
+ interface PixelLayerInit extends LayerPlacement {
1780
+ id?: string;
1781
+ name?: string;
1782
+ /** Fill the canvas with this colour (`#rgb`, `#rrggbb`, `#rrggbbaa` or `[r, g, b, a?]` 0..255). */
1783
+ color?: string | number[];
1784
+ /** RGBA pixels placed at `x`, `y` (document pixels). */
1785
+ pixels?: PixelBuffer;
1786
+ x?: number;
1787
+ y?: number;
1788
+ visible?: boolean;
1789
+ /** 0..1 (default 1). */
1790
+ opacity?: number;
1791
+ fillOpacity?: number;
1792
+ blend?: BlendMode;
1793
+ }
1794
+ /** The paint tools' options (view state). */
1795
+ interface PaintOptions {
1796
+ brush: BrushSettings;
1797
+ /** `#rrggbb`: what the brush and Fill paint. */
1798
+ foreground: string;
1799
+ /** `#rrggbb`: what the eraser paints on transparency-locked layers and masks. */
1800
+ background: string;
1801
+ /** Mixer Brush options (wet, load, mix, load / clean each stroke, RGB or pigment mixing). */
1802
+ mixer: MixerSettings;
1803
+ /** What the Mixer Brush carries now: its reservoir, how much is left and the picked-up colour. */
1804
+ mixerLoad: MixerLoad;
1805
+ }
1806
+ /** Paint tools a stroke can use. */
1807
+ type StrokeTool = 'brush' | 'eraser' | 'mixer';
1808
+ /** A stroke: tool, target (default the active layer and target) and brush / colour overrides. */
1809
+ interface StrokeInit {
1810
+ tool?: StrokeTool;
1811
+ layerId?: string;
1812
+ channel?: LayerTarget;
1813
+ /** Overrides of the paint options' brush. */
1814
+ brush?: Partial<BrushSettings>;
1815
+ /**
1816
+ * Paint colour (default the foreground). Masks take its grey value. For the mixer, the colour
1817
+ * the reservoir is loaded with: given, it loads the brush whatever `loadEachStroke` says.
1818
+ */
1819
+ color?: string | number[];
1820
+ /** Overrides of the paint options' Mixer Brush settings (tool `mixer`). */
1821
+ mixer?: Partial<MixerSettings>;
1822
+ /** Eraser colour on locked transparency / masks (default the background). */
1823
+ background?: string | number[];
1824
+ /** Kept with the stroke for replays (v1 brushes have no jitter). */
1825
+ seed?: number;
1826
+ }
1827
+ interface FillOptions {
1828
+ layerId?: string;
1829
+ channel?: LayerTarget;
1830
+ /** Default the foreground colour. */
1831
+ color?: string | number[];
1832
+ /** 0..1 (default 1). */
1833
+ opacity?: number;
1834
+ blend?: BlendMode;
1835
+ /** Limit to the selection when there is one (default true). */
1836
+ useSelection?: boolean;
1837
+ }
1838
+ /** What a stroke, fill or clear changed. */
1839
+ interface PaintStrokeResult {
1840
+ layerId: string;
1841
+ channel: LayerTarget;
1842
+ /** Document rectangle that changed (null when nothing did). */
1843
+ bounds: CropBox | null;
1844
+ dabs: number;
1845
+ /** The Mixer Brush's load after the stroke (tool `mixer` only). */
1846
+ mixerLoad?: MixerLoad;
1847
+ }
1848
+ /** An opaque pre-stroke history snapshot. */
1849
+ type HistoryEntryHandle = {
1850
+ readonly __historyEntry: unique symbol;
1851
+ };
1852
+ /** How a Mixer Brush stroke starts (see `PaintStrokeSession`). */
1853
+ interface MixerStrokeInit {
1854
+ /** Makes the stroke over the session's live pixels. */
1855
+ create(pixels: MixerTarget): MixerStroke;
1856
+ /** Stores the brush load when the stroke ends and returns it (after any clean). */
1857
+ end(load: MixerLoad): MixerLoad;
1858
+ }
1859
+ /**
1860
+ * A stroke in progress (see `beginStroke`): `push` input points as they arrive, then `end` (one
1861
+ * undo step) or `cancel`. Brush and eraser strokes build up a coverage map composited over the
1862
+ * pre-stroke pixels; Mixer Brush strokes paint dab by dab into the live pixels, so each dab picks
1863
+ * up what the dabs before it laid down.
1864
+ */
1865
+ declare class PaintStrokeSession {
1866
+ private readonly controller;
1867
+ readonly target: {
1868
+ id: string;
1869
+ channel: LayerTarget;
1870
+ surface: TiledSurface;
1871
+ };
1872
+ private readonly apply;
1873
+ private readonly before;
1874
+ private readonly base;
1875
+ private readonly live;
1876
+ private readonly touched;
1877
+ private readonly stroke;
1878
+ private readonly mixer;
1879
+ private readonly mixerInit;
1880
+ /** Mixer tiles written since the last update: they get new tile objects so the compositor sees them change. */
1881
+ private readonly fresh;
1882
+ private last;
1883
+ private closed;
1884
+ constructor(controller: ImageEditorController, target: {
1885
+ id: string;
1886
+ channel: LayerTarget;
1887
+ surface: TiledSurface;
1888
+ }, stroke: StrokeCoverage | MixerStrokeInit, apply: StrokeApply);
1889
+ /** The live pixels a mixer stroke reads and writes; base tiles are copied on first write. */
1890
+ private mixerPixels;
1891
+ get layerId(): string;
1892
+ /** Adds input points (document pixels); the stack shows the stroke so far. */
1893
+ push(points: readonly StrokePoint[]): void;
1894
+ private update;
1895
+ /** Finishes the stroke (one undo step) and returns what changed. */
1896
+ end(): PaintStrokeResult;
1897
+ /** Abandons the stroke and restores the layer. */
1898
+ cancel(): void;
1899
+ }
1900
+ /** Where a new annotation goes. */
1901
+ interface AnnotationPlacement {
1902
+ /** Put the annotation layer directly above this layer (default: the top of the stack). */
1903
+ aboveId?: string;
1904
+ }
1905
+ declare const DEFAULT_ADJUSTMENTS: ImageAdjustments;
1906
+ /**
1907
+ * Fills in defaults for stored adjustments. A stored object without `process` was saved before
1908
+ * process versions existed and keeps the legacy rendering.
1909
+ */
1910
+ declare function normalizeStoredAdjustments(stored: Partial<ImageAdjustments> | undefined | null): ImageAdjustments;
1911
+ /**
1912
+ * Brings a stored editor state of any schema version to a clean state of the same family:
1913
+ * version 1 (and a missing version) has no adjustment layers, version 2 has them, and both stay
1914
+ * in the document-level schema so old states load and render exactly as before. Version 3
1915
+ * (layers) is returned with its document fields cleaned. To lift a v1 / v2 state into layers,
1916
+ * use {@link migrateToLayeredState}.
1917
+ */
1918
+ declare function migrateSerializedImageEditorState(data: Partial<SerializedImageEditorState> & {
1919
+ version?: number;
1920
+ }): SerializedImageEditorState;
1921
+ /**
1922
+ * Lifts a stored state of any version into the layered schema (v3) for a canvas of
1923
+ * `width × height`: the source becomes the Background layer with the base sliders as its
1924
+ * `develop` filter and the depth mask as its parametric mask, adjustment layers go above it and
1925
+ * annotations on top (see `layersFromLegacy`).
1926
+ */
1927
+ declare function migrateToLayeredState(data: Partial<SerializedImageEditorState> & {
1928
+ version?: number;
1929
+ }, size: ImageDimensions): SerializedLayeredImageEditorState;
1930
+ declare const DEFAULT_TRANSFORM: ImageTransform;
1931
+ declare const DEFAULT_DEPTH_MASK: DepthMaskOptions;
1932
+ /**
1933
+ * ImageEditorController manages the complete lifecycle, canvas rendering pipeline,
1934
+ * tool interaction states, undo/redo history, serialization, and image export.
1935
+ */
1936
+ declare class ImageEditorController {
1937
+ private state;
1938
+ private renderer;
1939
+ private imageElement;
1940
+ private listeners;
1941
+ private histogramListeners;
1942
+ private onStateChange?;
1943
+ private undoStack;
1944
+ private redoStack;
1945
+ private maxHistoryDepth;
1946
+ private maxHistoryBytes;
1947
+ private isApplyingHistory;
1948
+ private initialSnapshot;
1949
+ /** Pixels the selection tools sample (host-provided or read back from the source). */
1950
+ private samplePixels;
1951
+ private samplePixelsOverride;
1952
+ /** Decoded source pixels (the Background's surface), by the buffer or element they came from. */
1953
+ private sourceSurfaces;
1954
+ private viewKey;
1955
+ /** For `compositePixels` and mask evaluation (the renderer keeps its own for the viewport). */
1956
+ private compositor;
1957
+ constructor(options?: ImageEditorOptions);
1958
+ /**
1959
+ * Loads a new source image and makes it the baseline that `reset()` returns to.
1960
+ * Does not record history; use `applyInpaintedImage` / `applyBackgroundRemovedImage`
1961
+ * for undoable source replacement.
1962
+ */
1963
+ loadImage(src: string | HTMLImageElement): Promise<void>;
1964
+ private resolveSource;
1965
+ getImageElement(): CanvasImageSource | null;
1966
+ getState(): ImageEditorState;
1967
+ subscribe(listener: (state: ImageEditorState) => void): () => void;
1968
+ /**
1969
+ * Where layer pixels changed since the last viewport render, when only strokes changed them
1970
+ * (the renderer then recomposites just those rectangles); `'all'` after any other change.
1971
+ */
1972
+ private pendingDirty;
1973
+ private notify;
1974
+ /** Recomputes the compatibility views (`adjustments`, `depthMask`, `annotations`) when the stack changed. */
1975
+ private syncViews;
1976
+ private stack;
1977
+ private captureHistoryEntry;
1978
+ private restoreHistoryEntry;
1979
+ private recordHistory;
1980
+ /**
1981
+ * Bytes of pixel tiles held only by undo / redo steps (not by the current state). The oldest
1982
+ * undo steps are dropped while it exceeds `maxHistoryBytes`.
1983
+ */
1984
+ get historyBytes(): number;
1985
+ private trimHistoryBytes;
1986
+ get canUndo(): boolean;
1987
+ get canRedo(): boolean;
1988
+ undo(): boolean;
1989
+ redo(): boolean;
1990
+ reset(): void;
1991
+ setTool(tool: ImageEditorTool): void;
1992
+ setCrop(crop: CropBox | null, addToHistory?: boolean): void;
1993
+ applyCropPreset(ratio: AspectRatio): void;
1994
+ resetCrop(): void;
1995
+ rotate(degrees: number): void;
1996
+ setRotation(degrees: number): void;
1997
+ flipHorizontal(): void;
1998
+ flipVertical(): void;
1999
+ /** The layer stack, bottom to top (immutable objects). */
2000
+ getLayers(): ImageLayer[];
2001
+ getLayer(id: string): ImageLayer | undefined;
2002
+ /** The active layer, or `undefined`. */
2003
+ getActiveLayer(): ImageLayer | undefined;
2004
+ /**
2005
+ * Makes a layer the active one (or none) and chooses whether painting targets its pixels or
2006
+ * its mask. View state: no undo step. Returns false for an unknown id.
2007
+ */
2008
+ setActiveLayer(id: string | null, target?: LayerTarget): boolean;
2009
+ /**
2010
+ * The pixels of a pixel layer, in document space (`x`, `y` is the top-left of the returned
2011
+ * buffer), or `null` for an unknown or empty layer. Resolves the Background's source pixels.
2012
+ */
2013
+ getLayerPixels(id: string): (PixelBuffer & {
2014
+ x: number;
2015
+ y: number;
2016
+ }) | null;
2017
+ private boundsCache;
2018
+ /**
2019
+ * Document bounds of a layer's non-transparent pixels (pixel layers; the Background's source
2020
+ * included), or `null` for an empty or non-pixel layer.
2021
+ */
2022
+ getLayerBounds(id: string): CropBox | null;
2023
+ /**
2024
+ * Composites the layer stack in document space without a canvas (annotation layers need one
2025
+ * and are left out): `rect` (default: the canvas) as RGBA, straight alpha. Crop, rotation and
2026
+ * output opacity are not applied. With `layerIds`, only those layers are composited.
2027
+ */
2028
+ compositePixels(rect?: CropBox, options?: {
2029
+ layerIds?: string[];
2030
+ }): PixelBuffer;
2031
+ private compositorSources;
2032
+ private sourceIds;
2033
+ private nextSourceId;
2034
+ /** Changes whenever the Background's source pixels can have changed. */
2035
+ private sourceVersion;
2036
+ /** The pixels a pixel layer or smart object composites (the decoded source for the Background). */
2037
+ private surfaceOf;
2038
+ /**
2039
+ * The source image as a surface (cached): host-supplied sample pixels first, then the decoded
2040
+ * element, then pixels read back for the selection tools.
2041
+ */
2042
+ private sourceSurface;
2043
+ private opaqueCache;
2044
+ /** Whether every source pixel is opaque (some legacy renders depend on it). */
2045
+ private sourceIsOpaque;
2046
+ /** Synchronously reads the source element's pixels through a canvas, or `null`. */
2047
+ private decodeSource;
2048
+ /** Records one undo step and applies a new stack (and any other state change in `extra`). */
2049
+ private commitLayers;
2050
+ /** The default position of a new layer: above the active layer, else the top of the stack. */
2051
+ private newLayerPlacement;
2052
+ /**
2053
+ * Adds a pixel layer (one undo step) and makes it active: empty, filled with `color` over the
2054
+ * canvas, or holding `pixels` placed at `x`, `y`. Goes above the active layer unless placed.
2055
+ * Returns its id.
2056
+ */
2057
+ addPixelLayer(init?: PixelLayerInit): string;
2058
+ /**
2059
+ * `addPixelLayer` from an image: a PNG data URL or base64 PNG is decoded without a canvas;
2060
+ * other formats and URLs need a browser. Returns the new layer id.
2061
+ */
2062
+ addPixelLayerFromImage(image: string, init?: Omit<PixelLayerInit, 'pixels' | 'color'>): Promise<string>;
2063
+ /** RGBA pixels of an image given as a data URL, base64 PNG or URL. */
2064
+ decodeImage(image: string): Promise<PixelBuffer>;
2065
+ private nextLayerName;
2066
+ /** Duplicates a layer (with its children) directly above it, as one undo step. Returns the copy's id. */
2067
+ duplicateLayer(id: string, name?: string): string;
2068
+ /** Deletes a layer (refused when locked), as one undo step. */
2069
+ removeLayer(id: string): void;
2070
+ /** Moves a layer above / below another, or into a group, as one undo step. Returns its new parent and index. */
2071
+ moveLayer(id: string, placement: LayerPlacement): {
2072
+ parentId: string | null;
2073
+ index: number;
2074
+ };
2075
+ /**
2076
+ * Changes name, visibility, opacity, fill, blend, clipping, locks or (groups) collapsed state, as
2077
+ * one undo step (`commitToHistory = false` for the moves of a slider drag). Returns the layer.
2078
+ */
2079
+ setLayerProperties(id: string, props: LayerProperties, commitToHistory?: boolean): ImageLayer;
2080
+ /** Adds an empty pass-through group above the active layer (or at `placement`) and makes it active. Returns its id. */
2081
+ addGroup(init?: LayerPlacement & {
2082
+ name?: string;
2083
+ blend?: LayerBlendMode;
2084
+ }): string;
2085
+ /** Puts sibling layers into a new group (one undo step). Returns the group id. */
2086
+ groupLayers(ids: string[], name?: string): string;
2087
+ /** Replaces a group by its children (one undo step). Returns the child ids, bottom to top. */
2088
+ ungroupLayer(id: string): string[];
2089
+ /**
2090
+ * Moves a pixel layer to an absolute offset (`x`, `y`) or by `dx`, `dy`, as one undo step
2091
+ * (`commitToHistory = false` for the moves of a drag). Returns the offset and document bounds.
2092
+ */
2093
+ offsetLayer(id: string, move: {
2094
+ x?: number;
2095
+ y?: number;
2096
+ dx?: number;
2097
+ dy?: number;
2098
+ }, commitToHistory?: boolean): {
2099
+ offset: {
2100
+ x: number;
2101
+ y: number;
2102
+ };
2103
+ bounds: CropBox | null;
2104
+ };
2105
+ /**
2106
+ * Adds a mask to a layer (one undo step): `reveal-all` (white), `hide-all` (black) or
2107
+ * `selection` (the selection's coverage). Replaces an existing mask. Makes the mask the paint
2108
+ * target when the layer is active.
2109
+ */
2110
+ addLayerMask(id: string, from?: 'reveal-all' | 'hide-all' | 'selection', props?: LayerMaskProps): RasterMask;
2111
+ /** Removes a layer's mask (one undo step). */
2112
+ removeLayerMask(id: string): void;
2113
+ /** Changes any layer mask's density, feather, enabled or linked flag (pixels kept). Returns the mask. */
2114
+ setLayerMaskProps(id: string, props: LayerMaskProps, commitToHistory?: boolean): RasterMask;
2115
+ /**
2116
+ * Bakes a pixel layer's mask into its pixels (alpha × coverage, including density, feather or
2117
+ * the depth map) and removes the mask, as one undo step.
2118
+ */
2119
+ applyLayerMask(id: string): Promise<void>;
2120
+ /**
2121
+ * Layer via Copy (or via Cut): a new pixel layer with the selected pixels of a pixel layer (the
2122
+ * active one by default), directly above it, as one undo step. Returns the new id.
2123
+ */
2124
+ layerViaCopy(options?: {
2125
+ layerId?: string;
2126
+ cut?: boolean;
2127
+ name?: string;
2128
+ }): string;
2129
+ private openTransform;
2130
+ /** The Free Transform in progress (see `beginFreeTransform`), or `null`. */
2131
+ getFreeTransform(): FreeTransformSession | null;
2132
+ private transformHost;
2133
+ /**
2134
+ * Starts a Free Transform (Edit › Free Transform, Transform Selection, or a transform of a Layer
2135
+ * via Copy): of the layer's pixels with its linked mask, an unlinked mask alone, the selected
2136
+ * pixels or just the selection outline (see `FreeTransformInit`). Change the geometry with
2137
+ * `session.update` (live preview, no history), then `commit()` (one undo step) or `cancel()`.
2138
+ * Refused while another transform is open.
2139
+ */
2140
+ beginFreeTransform(init?: FreeTransformInit): FreeTransformSession;
2141
+ /**
2142
+ * Transforms in one step (agents, scripts): begins a Free Transform for `spec`, sets its corners
2143
+ * from exactly one of `matrix` (document-space affine), `corners` (where the frame's corners go:
2144
+ * distort / perspective) or `params` (scale, rotate, skew and move about a reference point), and
2145
+ * commits it as one undo step. Returns what changed and the new bounds.
2146
+ */
2147
+ transformLayer(spec: FreeTransformInit & {
2148
+ matrix?: AffineMatrix;
2149
+ corners?: Quad;
2150
+ params?: TransformParams;
2151
+ warp?: WarpMesh | WarpPreset | null;
2152
+ }): FreeTransformResult;
2153
+ /** Flips the layer (or its selected pixels, or the outline) about the frame's centre, exactly (one undo step). */
2154
+ flipLayer(axis: 'horizontal' | 'vertical', init?: FreeTransformInit): FreeTransformResult;
2155
+ /**
2156
+ * Rotates by a quarter turn (90 clockwise, −90 counter-clockwise) or 180 about the frame's
2157
+ * centre, snapped so pixels land on whole pixels: exact, no resampling (one undo step).
2158
+ */
2159
+ rotateLayer90(turn: 90 | -90 | 180, init?: FreeTransformInit): FreeTransformResult;
2160
+ /** Adds a `develop` or `blur` filter to a pixel layer (one undo step). Returns the layer's filters. */
2161
+ addLayerFilter(id: string, filter: Partial<LayerFilter> & {
2162
+ kind: LayerFilter['kind'];
2163
+ }): LayerFilter[];
2164
+ /** Changes one filter (one undo step, or none with `commitToHistory = false` during a drag). Returns the filters. */
2165
+ updateLayerFilter(id: string, filterId: string, changes: Record<string, unknown>, commitToHistory?: boolean): LayerFilter[];
2166
+ /** Removes one filter (one undo step). Returns the remaining filters. */
2167
+ removeLayerFilter(id: string, filterId: string): LayerFilter[];
2168
+ private get canvasSize();
2169
+ /**
2170
+ * Layer › Smart Objects › Convert to Smart Object (one undo step): sibling layers (default the
2171
+ * active one) become one smart object at the topmost one's position. A single pixel layer keeps
2172
+ * its id and properties and its filters become smart filters (it renders exactly as before);
2173
+ * anything else nests as a layer document. Returns the smart object's id.
2174
+ */
2175
+ convertToSmartObject(ids?: string[], options?: {
2176
+ name?: string;
2177
+ }): string;
2178
+ /** Layer › Smart Objects › Rasterize (one undo step): the smart object becomes a pixel layer holding what it renders. */
2179
+ rasterizeSmartObject(id: string): string;
2180
+ /** A smart object's contents, placement and smart filters as plain data (see `SmartObjectInfo`). */
2181
+ getSmartObject(id: string): SmartObjectInfo;
2182
+ /**
2183
+ * Sets a smart object's placement (one undo step, or none with `commitToHistory = false` during a
2184
+ * drag): where the contents' corners land, an optional warp in contents pixels and the
2185
+ * interpolation. The contents render again from the source; a linked mask follows a warp-free
2186
+ * change. Returns the new placement summary.
2187
+ */
2188
+ setSmartObjectPlacement(id: string, placement: SmartPlacement, commitToHistory?: boolean): SmartObjectInfo['placement'] & {
2189
+ masksMoved: boolean;
2190
+ };
2191
+ /**
2192
+ * Sets a smart object's transform absolutely (one undo step): exactly one of `corners` (where the
2193
+ * contents' corners land), `matrix` (contents pixels → document) or the numbers (`scaleX`,
2194
+ * `scaleY` in percent of the contents' own size, `rotate`, `skewX`, `skewY`, and `x`, `y` where
2195
+ * the contents' centre goes, default where it is now); plus, optionally, a warp (a preset, a mesh
2196
+ * in contents pixels, or `null` to remove it) and the interpolation. Unlike `transformLayer`,
2197
+ * nothing is relative to the current transform.
2198
+ */
2199
+ setSmartObjectTransform(id: string, spec: {
2200
+ corners?: Quad;
2201
+ matrix?: AffineMatrix;
2202
+ scaleX?: number;
2203
+ scaleY?: number;
2204
+ rotate?: number;
2205
+ skewX?: number;
2206
+ skewY?: number;
2207
+ x?: number;
2208
+ y?: number;
2209
+ warp?: WarpMesh | WarpPreset | null;
2210
+ interpolation?: Interpolation;
2211
+ }): SmartObjectInfo['placement'] & {
2212
+ masksMoved: boolean;
2213
+ };
2214
+ /**
2215
+ * Layer › Smart Objects › Replace Contents (one undo step): new contents for the smart object and
2216
+ * every instance linked to it, from RGBA pixels, PSD bytes (a layer document) or contents. Each
2217
+ * instance keeps its scale and rotation about the contents' centre. Returns the contents id and
2218
+ * the layers that changed.
2219
+ */
2220
+ replaceSmartObjectContents(id: string, source: {
2221
+ pixels: PixelBuffer;
2222
+ name?: string;
2223
+ } | {
2224
+ psd: Uint8Array;
2225
+ name?: string;
2226
+ } | {
2227
+ contents: SmartContents;
2228
+ }): {
2229
+ contentsId: string;
2230
+ width: number;
2231
+ height: number;
2232
+ layerIds: string[];
2233
+ };
2234
+ /** `replaceSmartObjectContents` from an image (PNG data URL or base64 decodes anywhere; other formats need a browser). */
2235
+ replaceSmartObjectContentsFromImage(id: string, image: string, name?: string): Promise<{
2236
+ contentsId: string;
2237
+ width: number;
2238
+ height: number;
2239
+ layerIds: string[];
2240
+ }>;
2241
+ /**
2242
+ * Layer › Smart Objects › Export Contents: the file the contents came from while unedited, else a
2243
+ * PNG (raster contents) or a PSD / PSB of the nested document. Returns the file name, MIME type
2244
+ * and bytes.
2245
+ */
2246
+ exportSmartObjectContents(id: string): {
2247
+ name: string;
2248
+ mimeType: string;
2249
+ bytes: Uint8Array;
2250
+ };
2251
+ /**
2252
+ * Layer › Smart Objects › New Smart Object via Copy (`linked: false`: independent contents) or a
2253
+ * linked duplicate (`linked: true`: edits to the contents show in both), directly above, as one
2254
+ * undo step. Returns the copy's id.
2255
+ */
2256
+ copySmartObject(id: string, linked?: boolean, name?: string): string;
2257
+ /** Adds a smart filter (`develop` or `blur`, with `blend` and `opacity`) at `index` (default the top), as one undo step. */
2258
+ addSmartFilter(id: string, filter: Record<string, unknown> & {
2259
+ kind: string;
2260
+ }, index?: number): SmartFilter;
2261
+ /** Changes one smart filter (one undo step, or none with `commitToHistory = false` during a drag). */
2262
+ updateSmartFilter(id: string, filterId: string, changes: Record<string, unknown>, commitToHistory?: boolean): SmartFilter;
2263
+ /** Removes one smart filter (one undo step). Returns the remaining filters. */
2264
+ removeSmartFilter(id: string, filterId: string): SmartFilter[];
2265
+ /** Moves one smart filter to `index` (0 = bottom, applied first), as one undo step. Returns the filters. */
2266
+ moveSmartFilter(id: string, filterId: string, index: number): SmartFilter[];
2267
+ /** Turns a smart object's whole filter stack on or off (one undo step). */
2268
+ setSmartFiltersEnabled(id: string, enabled: boolean): boolean;
2269
+ /**
2270
+ * Sets the smart filter mask (one undo step): `reveal-all`, `hide-all`, `selection` (the
2271
+ * selection's coverage), or `none` to remove it; `props` sets density, feather, enabled and linked.
2272
+ * Painting it targets channel `filter-mask`. Returns the mask, or `null` when removed.
2273
+ */
2274
+ setSmartFilterMask(id: string, from: 'reveal-all' | 'hide-all' | 'selection' | 'none' | 'keep', props?: LayerMaskProps, commitToHistory?: boolean): RasterMask | null;
2275
+ /**
2276
+ * Decodes smart object contents that came as files the synchronous decoders cannot read (a JPEG
2277
+ * or TIFF embedded in a PSD), through the browser's image decoder. No undo step: the pixels on
2278
+ * screen do not change. Returns the contents ids still undecoded.
2279
+ */
2280
+ prepareSmartObjects(): Promise<string[]>;
2281
+ private contentsStack;
2282
+ /** The smart objects whose contents are open (outermost first); empty when the document itself is shown. */
2283
+ getEditingContents(): Array<{
2284
+ layerId: string;
2285
+ name: string;
2286
+ }>;
2287
+ /**
2288
+ * Layer › Smart Objects › Edit Contents: shows the smart object's contents as a document of their
2289
+ * own (a layer document, or raster contents as one pixel layer), with their own undo history.
2290
+ * Every editor operation and tool then works on the contents until `closeSmartObjectContents`.
2291
+ * Contents can nest. Returns the contents' size and layer ids.
2292
+ */
2293
+ editSmartObjectContents(id: string): {
2294
+ layerId: string;
2295
+ depth: number;
2296
+ width: number;
2297
+ height: number;
2298
+ layerIds: string[];
2299
+ };
2300
+ /**
2301
+ * Closes the innermost open contents: with `save` (default) the edited layers become the smart
2302
+ * object's contents, and it and every linked instance render again (one undo step in the
2303
+ * document); without, the edits are dropped. Returns the smart object and the layers that changed.
2304
+ */
2305
+ closeSmartObjectContents(save?: boolean): {
2306
+ layerId: string;
2307
+ changed: boolean;
2308
+ layerIds: string[];
2309
+ };
2310
+ /** The document's own layers (not the contents open for editing). */
2311
+ private rootLayers;
2312
+ /** Drops every open contents edit (loading another document). */
2313
+ private abandonContents;
2314
+ /** A layer's effects (an empty, enabled list when it has none). */
2315
+ getLayerEffects(id: string): LayerEffects;
2316
+ /**
2317
+ * Adds an effect of `kind` with Photoshop's defaults and `params` applied (one undo step), at
2318
+ * `index` in the layer's effect list (default the end). Returns the new effect and the effects.
2319
+ */
2320
+ addLayerEffect(id: string, kind: LayerEffectKind, params?: Record<string, unknown>, index?: number): {
2321
+ effect: LayerEffect;
2322
+ effects: LayerEffects;
2323
+ };
2324
+ /** Changes one effect (one undo step, or none with `commitToHistory = false` during a drag). Returns the effects. */
2325
+ updateLayerEffect(id: string, effectId: string, changes: Record<string, unknown>, commitToHistory?: boolean): LayerEffects;
2326
+ /** Removes one effect (one undo step). Returns the remaining effects. */
2327
+ removeLayerEffect(id: string, effectId: string): LayerEffects;
2328
+ /** Turns a layer's effects on or off as a whole and, when given, sets Scale Effects (one undo step). */
2329
+ setLayerEffectsEnabled(id: string, enabled: boolean, scale?: number, commitToHistory?: boolean): LayerEffects;
2330
+ /** Replaces a layer's effects (one undo step; `null` removes them). Returns the effects. */
2331
+ setLayerEffects(id: string, effects: LayerEffects | null, commitToHistory?: boolean): LayerEffects;
2332
+ private patternIds;
2333
+ /** Changes whenever the document's pattern list is replaced (the compositor then drops its caches). */
2334
+ private patternsVersion;
2335
+ /** The document's patterns (from an opened PSD), besides the built-in ones. */
2336
+ getDocumentPatterns(): EffectPattern[];
2337
+ /** The light effects with "use global light" follow. */
2338
+ getGlobalLight(): GlobalLight;
2339
+ /** Sets the global light (one undo step). `angle` -360..360°, `altitude` 0..90°. Returns it. */
2340
+ setGlobalLight(light: Partial<GlobalLight>, commitToHistory?: boolean): GlobalLight;
2341
+ /** Composites `layers` (in isolation) over `rect` into a surface, loading depth maps first. */
2342
+ private compositeToSurface;
2343
+ /** Document bounds a layer can paint into (pixel content, annotations, or the canvas for the rest). */
2344
+ private paintBounds;
2345
+ /**
2346
+ * Merge Down: composites a layer onto the pixel layer directly below it (which keeps its name,
2347
+ * opacity and blend; masks, fill, filters and both layers' effects are baked), as one undo step.
2348
+ * Returns the merged id.
2349
+ */
2350
+ mergeDown(id: string): Promise<string>;
2351
+ /**
2352
+ * Merge Visible: the visible top-level layers become one normal pixel layer (at the position of
2353
+ * the lowest of them); hidden layers stay. One undo step. Returns the merged id.
2354
+ */
2355
+ mergeVisible(): Promise<string>;
2356
+ /** Flatten Image: every visible layer composited into one Background layer; hidden layers are discarded. One undo step. */
2357
+ flattenImage(): Promise<string>;
2358
+ private thumbCache;
2359
+ /**
2360
+ * A small RGBA preview of a pixel layer over the canvas (or of its mask with `mask: true`),
2361
+ * longest side `maxSide` px, point-sampled; `null` for layers without pixels.
2362
+ */
2363
+ getLayerThumbnail(id: string, options?: {
2364
+ maxSide?: number;
2365
+ mask?: boolean;
2366
+ }): PixelBuffer | null;
2367
+ /** Brush settings, colours and tool options of the paint tools (view state, no undo). */
2368
+ setPaintOptions(options: Partial<Omit<PaintOptions, 'brush' | 'mixer' | 'mixerLoad'>> & {
2369
+ brush?: Partial<BrushSettings>;
2370
+ mixer?: Partial<MixerSettings>;
2371
+ }): PaintOptions;
2372
+ /**
2373
+ * Loads the Mixer Brush's reservoir full with `color` (default the foreground); the picked-up
2374
+ * colour is kept. View state, no undo. Returns the brush load.
2375
+ */
2376
+ loadMixerBrush(color?: string | number[]): MixerLoad;
2377
+ /** Cleans the Mixer Brush: empties the pickup well (the reservoir is kept). Returns the brush load. */
2378
+ cleanMixerBrush(): MixerLoad;
2379
+ /** Empties the Mixer Brush entirely (no reservoir, nothing picked up). Returns the brush load. */
2380
+ emptyMixerBrush(): MixerLoad;
2381
+ /** D: black foreground, white background. */
2382
+ resetPaintColors(): void;
2383
+ /** X: swaps the foreground and background colours. */
2384
+ swapPaintColors(): void;
2385
+ /** Resolves and validates what a stroke, fill or clear paints into. */
2386
+ private paintTarget;
2387
+ /** The layer with its pixels (or mask) replaced by `surface`. */
2388
+ private withSurface;
2389
+ /**
2390
+ * Starts a brush or eraser stroke on a layer's pixels or mask (default: the active layer and
2391
+ * target, the paint options' brush and colours). Feed points with `push`, finish with `end()`
2392
+ * (one undo step for the whole stroke) or `cancel()`. While it runs, the stack shows the stroke
2393
+ * without recording history.
2394
+ */
2395
+ beginStroke(init?: StrokeInit): PaintStrokeSession;
2396
+ /** A Mixer Brush stroke's start: the settings, and the load (refilled when asked or set to). */
2397
+ private mixerStroke;
2398
+ /** @internal Shows a stroke's live surface (no history). */
2399
+ _strokeUpdate(layerId: string, channel: LayerTarget, surface: TiledSurface, rects: CropBox[]): void;
2400
+ /** @internal Commits a finished stroke as one undo step (`before` is the pre-stroke state). */
2401
+ _strokeCommit(before: HistoryEntryHandle, layerId: string, channel: LayerTarget, surface: TiledSurface | null): void;
2402
+ /** @internal The pre-stroke snapshot. */
2403
+ _captureForStroke(): HistoryEntryHandle;
2404
+ /**
2405
+ * Paints a whole stroke in one call (agents, replays): `points` in document pixels with optional
2406
+ * pressure, through the brush, eraser or Mixer Brush. One undo step. Returns the layer, channel, the changed
2407
+ * document bounds and the number of dabs.
2408
+ */
2409
+ paintStroke(init: StrokeInit & {
2410
+ points: StrokePoint[];
2411
+ }): PaintStrokeResult;
2412
+ /**
2413
+ * Edit › Fill: fills the selection (or, without one or with `useSelection: false`, the whole
2414
+ * canvas) on a layer's pixels or mask with a colour (default the foreground), at `opacity` with
2415
+ * `blend`. One undo step. Returns the changed bounds.
2416
+ */
2417
+ fillPixels(options?: FillOptions): PaintStrokeResult;
2418
+ /**
2419
+ * Edit › Clear: removes the selected pixels (alpha to 0; on a transparency-locked layer or a mask
2420
+ * it fills with the background colour). Without a selection (or with `useSelection: false`) the
2421
+ * whole canvas. One undo step. Returns the changed bounds.
2422
+ */
2423
+ clearPixels(options?: Omit<FillOptions, 'color' | 'blend'>): PaintStrokeResult;
2424
+ private fillOrClear;
2425
+ /** The pixel layer the base sliders edit: the active pixel layer, else the Background. */
2426
+ private developLayer;
2427
+ /**
2428
+ * Sets base adjustment values as one undo step (unless `commitToHistory` is false). The base
2429
+ * sliders edit the `develop` filter of the active pixel layer (or the Background), `blur` its
2430
+ * blur filter (or `legacyOutputBlur` in migrated documents), `opacity` the output opacity, and
2431
+ * `layers` replaces the adjustment layers.
2432
+ */
2433
+ setAdjustments(partial: Partial<ImageAdjustments>, commitToHistory?: boolean): void;
2434
+ /** True when the adjustments use an older rendering process than {@link LATEST_PROCESS_VERSION}. */
2435
+ get isProcessOutdated(): boolean;
2436
+ /**
2437
+ * Update to Current Process: re-interprets the current adjustment values with the latest
2438
+ * rendering process (one undo step). Returns false when already current.
2439
+ */
2440
+ upgradeProcess(): boolean;
2441
+ /** Resets the base sliders (latest process). Adjustment layers are kept; see `clearAdjustmentLayers`. */
2442
+ resetAdjustments(): void;
2443
+ /** The adjustment layers in stack order, bottom (first) to top (last). */
2444
+ getAdjustmentLayers(): AdjustmentLayer[];
2445
+ getAdjustmentLayer(id: string): AdjustmentLayer | undefined;
2446
+ private adjustmentNodes;
2447
+ /**
2448
+ * Where a new layer goes when no position is given: directly above the topmost layer that is
2449
+ * not an annotation, so it adjusts what adjustment layers always adjusted (annotations stay on top).
2450
+ */
2451
+ private defaultInsertIndex;
2452
+ /** Inserts `node` by `aboveId` / `parentId` / adjustment index / default position. */
2453
+ private placeLayer;
2454
+ /** `setAdjustments({ layers })`: updates nodes by id in place, removes missing ones, adds new ones. */
2455
+ private replaceAdjustmentNodes;
2456
+ /**
2457
+ * Adds an adjustment layer and returns its id, as one undo step. With no position it goes
2458
+ * above the other adjustment layers (or, without any, above the topmost non-annotation layer,
2459
+ * so annotations stay on top); `index` counts adjustment layers (0 = below the others);
2460
+ * `aboveId` / `parentId` place it anywhere in the stack. `kind` alone gives neutral parameters.
2461
+ */
2462
+ addAdjustmentLayer<K extends AdjustmentKind>(init: K | AdjustmentLayerInit<K>, index?: number): string;
2463
+ /**
2464
+ * Updates a layer (`params` merged into the current ones, then normalised). Pass
2465
+ * `commitToHistory = false` for the moves of a drag after recording its start once.
2466
+ * Returns false for an unknown id.
2467
+ */
2468
+ updateAdjustmentLayer(id: string, update: AdjustmentLayerUpdate, commitToHistory?: boolean): boolean;
2469
+ /** Removes a layer as one undo step. Returns false for an unknown id. */
2470
+ removeAdjustmentLayer(id: string): boolean;
2471
+ private moveAdjustmentNode;
2472
+ /** Moves a layer to `toIndex` among the adjustment layers (0 = bottom) as one undo step. Returns false if nothing moved. */
2473
+ moveAdjustmentLayer(id: string, toIndex: number): boolean;
2474
+ /** Shows or hides a layer (flips it without `enabled`) as one undo step. */
2475
+ toggleAdjustmentLayer(id: string, enabled?: boolean): boolean;
2476
+ /** Removes every adjustment layer as one undo step. */
2477
+ clearAdjustmentLayers(): void;
2478
+ /** The active selection, or `null`. */
2479
+ getSelection(): SelectionMask | null;
2480
+ get hasSelection(): boolean;
2481
+ private get naturalSize();
2482
+ private dropMismatchedSelection;
2483
+ /**
2484
+ * Replaces the selection, or combines `mask` with it by `mode`, as one undo step. A mask of a
2485
+ * different size (e.g. an AI segmentation at another resolution) is resampled to the image.
2486
+ * Returns false when nothing changed.
2487
+ */
2488
+ setSelection(mask: SelectionMask | null, mode?: SelectionMode): boolean;
2489
+ /** Select › All. */
2490
+ selectAll(): boolean;
2491
+ /** Select › Deselect. */
2492
+ deselect(): boolean;
2493
+ /** Select › Inverse (with nothing selected, selects everything). */
2494
+ invertSelection(): boolean;
2495
+ private shapeCommand;
2496
+ /** Rectangular marquee in natural image pixels. */
2497
+ selectRect(rect: CropBox, options?: SelectionCommandOptions & ShapeSelectionOptions): boolean;
2498
+ /** Elliptical marquee inscribed in `rect`. */
2499
+ selectEllipse(rect: CropBox, options?: SelectionCommandOptions & ShapeSelectionOptions): boolean;
2500
+ /** Lasso / polygonal lasso. */
2501
+ selectPolygon(points: ImagePoint[], options?: SelectionCommandOptions & ShapeSelectionOptions): boolean;
2502
+ /**
2503
+ * Supplies the pixels the selection tools sample (natural size RGBA), e.g. decoded in a worker
2504
+ * or in a test; `null` goes back to reading the source image. Without a canvas (Node, workers)
2505
+ * these are also the Background layer's pixels.
2506
+ */
2507
+ setSamplePixels(pixels: PixelBuffer | null): void;
2508
+ /**
2509
+ * The pixels the selection tools sample: the source image at natural size, or with
2510
+ * `sampleMerged` the composite of the layers (no crop, rotation or annotations). `null`
2511
+ * without pixel access (no DOM, tainted canvas).
2512
+ */
2513
+ getSamplePixels(options?: SampleOptions): Promise<PixelBuffer | null>;
2514
+ private requirePixels;
2515
+ /**
2516
+ * Magic wand at `point` (natural image pixels). Resolves to the resulting selection (null when
2517
+ * nothing is selected).
2518
+ */
2519
+ magicWand(point: ImagePoint, options?: MagicWandOptions & SelectionCommandOptions & SampleOptions): Promise<SelectionMask | null>;
2520
+ /** Select › Color Range with colour samples (`#rrggbb` or `[r, g, b]`, optionally where they were picked). */
2521
+ selectColorRange(samples: ColorRangeSample[], options?: ColorRangeOptions & SelectionCommandOptions & SampleOptions): Promise<SelectionMask | null>;
2522
+ /** Select › Modify: grow, shrink, feather, border or smooth by `radius` pixels (one undo step). */
2523
+ modifySelection(op: SelectionModifyOp, radius: number): boolean;
2524
+ /**
2525
+ * Refine Edge: snaps the selection (e.g. a coarse AI mask loaded with `loadSelectionFromImage`)
2526
+ * to the image's edges with a guided filter, then smooth / feather / contrast / shift edge.
2527
+ */
2528
+ refineSelection(params: RefineEdgeParams, options?: SampleOptions): Promise<boolean>;
2529
+ /**
2530
+ * Loads a selection from a mask image (an AI segmentation, a saved mask): a data URL / URL or
2531
+ * RGBA pixels, by luminance (white = selected) or a channel. Any size; it is resampled.
2532
+ */
2533
+ loadSelectionFromImage(source: string | PixelBuffer, options?: {
2534
+ channel?: SelectionChannel;
2535
+ invert?: boolean;
2536
+ threshold?: number;
2537
+ mode?: SelectionMode;
2538
+ }): Promise<SelectionMask | null>;
2539
+ /** Quick Mask view (tints the unselected area). View state: no undo step. */
2540
+ setQuickMask(on: boolean): void;
2541
+ private maskFromSelection;
2542
+ /**
2543
+ * Adds the selection as the layer's mask (Reveal Selection), replacing any mask, as one undo
2544
+ * step. Without a selection, returns false.
2545
+ */
2546
+ applySelectionAsLayerMask(layerId: string, props?: LayerMaskProps): boolean;
2547
+ private setLayerMask;
2548
+ /** Sets (from a selection-shaped mask) or removes (`null`) a layer's mask as one undo step. */
2549
+ setAdjustmentLayerMask(layerId: string, mask: SelectionMask | null, props?: LayerMaskProps): boolean;
2550
+ /** Changes a layer mask's density (0..1), feather (px) or enabled flag; the mask pixels are kept. */
2551
+ setAdjustmentLayerMaskProps(layerId: string, props: LayerMaskProps, commitToHistory?: boolean): boolean;
2552
+ /** Load Selection from a layer mask (its pixels; density and feather are not applied). */
2553
+ selectionFromLayerMask(layerId: string, mode?: SelectionMode): boolean;
2554
+ /**
2555
+ * Histogram of the last rendered preview (after adjustments and layers, before annotations),
2556
+ * or `null` before the first render or without canvas pixel access.
2557
+ */
2558
+ getHistogram(): ImageHistogram | null;
2559
+ /** Calls `listener` now and whenever a render produces a new preview histogram. */
2560
+ subscribeHistogram(listener: (histogram: ImageHistogram | null) => void): () => void;
2561
+ /** Sets the depth mask (a parametric mask on the active pixel layer, or the Background) as one undo step. */
2562
+ setDepthMask(partial: Partial<DepthMaskOptions>): void;
2563
+ resetDepthMask(): void;
2564
+ private annotationLayer;
2565
+ /** Adds an annotation as its own layer (top of the stack unless `aboveId` is given). */
2566
+ addAnnotation(annotation: CreateAnnotationPayload, placement?: AnnotationPlacement): string;
2567
+ updateAnnotation(id: string, partial: Partial<Annotation>, commitToHistory?: boolean): void;
2568
+ removeAnnotation(id: string): void;
2569
+ clearAnnotations(): void;
2570
+ selectAnnotation(id: string | null): void;
2571
+ setZoom(zoom: number): void;
2572
+ zoomIn(factor?: number): void;
2573
+ zoomOut(factor?: number): void;
2574
+ resetZoom(): void;
2575
+ setPan(pan: ImagePoint): void;
2576
+ setComparing(comparing: boolean): void;
2577
+ setFit(fit: ImageEditorFit): void;
2578
+ private blankSource;
2579
+ /**
2580
+ * What the renderer draws from: the source image, or, for a document without one (an opened
2581
+ * PSD, a v3 document loaded on its own), a transparent canvas of the document size, since every
2582
+ * pixel then comes from the layers. `null` when there is nothing to draw.
2583
+ */
2584
+ private drawSource;
2585
+ render(targetCanvas: HTMLCanvasElement, options?: RenderOptions): Promise<void>;
2586
+ renderExport(options?: ExportOptions): Promise<HTMLCanvasElement>;
2587
+ /**
2588
+ * Renders the edited image (adjustments, layers and, by default, annotations) in the
2589
+ * source image's own orientation and frame, ignoring crop, rotation and flips, so it lines
2590
+ * up with natural-pixel coordinates (selections, `select_region`). `scale` ≤ 1 shrinks it.
2591
+ * Returns `null` when no image is loaded.
2592
+ */
2593
+ renderSourceView(options?: {
2594
+ scale?: number;
2595
+ includeAnnotations?: boolean;
2596
+ }): Promise<HTMLCanvasElement | null>;
2597
+ toDataURL(options?: ExportOptions): Promise<string>;
2598
+ toBlob(options?: ExportOptions): Promise<Blob>;
2599
+ renderMask(options?: {
2600
+ useAnnotations?: boolean;
2601
+ useCrop?: boolean;
2602
+ useSelection?: boolean;
2603
+ }): Promise<HTMLCanvasElement>;
2604
+ toMaskDataURL(options?: {
2605
+ useAnnotations?: boolean;
2606
+ useCrop?: boolean;
2607
+ useSelection?: boolean;
2608
+ }): Promise<string>;
2609
+ /**
2610
+ * Renders the current source image at natural size with no crop, rotation, flip,
2611
+ * adjustments or annotations. This is the image that `toMaskDataURL()` is aligned with
2612
+ * and the space that `applyInpaintedImage` / `applyBackgroundRemovedImage` expect back.
2613
+ */
2614
+ renderSource(): Promise<HTMLCanvasElement>;
2615
+ /**
2616
+ * The image an AI hook should edit: the source (`toSourceDataURL`) when results replace the
2617
+ * Background, or the composite of the layers without annotations, in the source frame, when
2618
+ * they go to a new layer or mask (see `applyInpaintedImage`).
2619
+ */
2620
+ toAIInputDataURL(options?: AIResultOptions): Promise<string>;
2621
+ toSourceDataURL(options?: Pick<ExportOptions, 'format' | 'quality'>): Promise<string>;
2622
+ /**
2623
+ * Whether AI results go to layers: the document has more than one pixel layer or group (adjustment
2624
+ * and annotation layers are edits of the image, not images of their own).
2625
+ */
2626
+ /** `new-layer` in multi-layer documents and documents without a source image, else `replace`. */
2627
+ private defaultAITarget;
2628
+ /** No source image, and no layer reading one (an opened PSD): the layers are the whole document. */
2629
+ private isSourceless;
2630
+ get isMultiLayer(): boolean;
2631
+ /**
2632
+ * Applies an inpainted result (natural image space, see `toSourceDataURL`) as ONE undoable step.
2633
+ * In a document with several layers (see `isMultiLayer`), or with `target: 'new-layer'`, the
2634
+ * result goes to a new pixel layer above the active layer, limited to the selection. Otherwise
2635
+ * (`target: 'replace'`, the single-layer default) it replaces the Background's pixels and clears
2636
+ * the mask annotations, as before. Resolves to the new layer's id in new-layer mode.
2637
+ */
2638
+ applyInpaintedImage(newDataUrl: string, options?: AIResultOptions): Promise<string | void>;
2639
+ /**
2640
+ * Applies a background-removed result as ONE undoable step. In a document with several layers
2641
+ * (or with `target: 'new-layer'`) the result's alpha becomes the mask of the active pixel layer
2642
+ * (or the Background), replacing its mask. Otherwise (`target: 'replace'`, the single-layer
2643
+ * default) it replaces the Background's pixels; crop and annotations are kept (rescaled if the
2644
+ * new image has different dimensions). Resolves to the masked layer's id in mask mode.
2645
+ */
2646
+ applyBackgroundRemovedImage(newDataUrl: string, options?: AIResultOptions): Promise<string | void>;
2647
+ /**
2648
+ * Rasterize Layer: an annotation layer becomes a pixel layer with the same id, name and
2649
+ * properties (needs a canvas), and a pixel layer's filters are baked into its pixels. One undo
2650
+ * step. Returns the layer id.
2651
+ */
2652
+ rasterizeLayer(id: string): Promise<string>;
2653
+ /** Pixels per inch of the last opened PSD (written back by `toPsd`). */
2654
+ private psdResolution;
2655
+ /**
2656
+ * Opens PSD / PSB bytes as the document (one undo step): the layers replace the stack and the
2657
+ * document has no source image afterwards (every pixel comes from the layers). What the model
2658
+ * cannot hold is approximated or left out; `warnings` says what, naming the layers.
2659
+ * Throws `PsdError` for files that cannot be read at all.
2660
+ */
2661
+ loadPsd(bytes: Uint8Array): {
2662
+ layerIds: string[];
2663
+ warnings: string[];
2664
+ width: number;
2665
+ height: number;
2666
+ summary: PsdImportResult['summary'];
2667
+ };
2668
+ /**
2669
+ * The document as an 8-bit RGB PSD (PSB above 30 000 px) with real layers, groups, adjustment
2670
+ * layers and masks, in the source frame. Layer filters and parametric (depth) masks are baked
2671
+ * into pixels and masks, annotation layers are rasterized (in a browser; elsewhere they are left
2672
+ * out), and the merged image is our composite. `warnings` lists every such change.
2673
+ */
2674
+ toPsd(): Promise<{
2675
+ bytes: Uint8Array;
2676
+ warnings: string[];
2677
+ psb: boolean;
2678
+ width: number;
2679
+ height: number;
2680
+ }>;
2681
+ private replaceSource;
2682
+ /**
2683
+ * The serializable state. A document whose layers still have the v1 / v2 shape (the
2684
+ * Background with its sliders and depth mask, adjustment layers, annotations) is written as
2685
+ * v1 / v2 exactly as before; anything else is written as v3 with the layer pixels as PNG
2686
+ * assets. `selection` is included (run-length encoded) only when something is selected.
2687
+ */
2688
+ toJSON(): SerializedImageEditorState;
2689
+ /**
2690
+ * With `assets: 'external'`, layer pixels go to `store` (each PNG uploaded once per store) and
2691
+ * the v3 document holds the store's references. Old-shape documents have no assets either way.
2692
+ */
2693
+ toJSON(options: {
2694
+ assets: 'external';
2695
+ store: ImageEditorAssetStore;
2696
+ }): Promise<SerializedImageEditorState>;
2697
+ /**
2698
+ * Loads a stored state whose v3 assets may live in an asset store (references other than data
2699
+ * URLs are fetched with `store.get`), as one undo step. Assets that cannot be fetched are left
2700
+ * out and listed in `lastLoadProblems`.
2701
+ */
2702
+ loadJSONAsync(data: SerializedImageEditorState, options?: {
2703
+ store?: ImageEditorAssetStore;
2704
+ }): Promise<void>;
2705
+ /** Loads a stored state of any version (one undo step). */
2706
+ loadJSON(data: SerializedImageEditorState): void;
2707
+ /** Problems found by the last `loadJSON` / `initialState` of a v3 document (unreadable layers or assets). */
2708
+ lastLoadProblems: string[];
2709
+ private applyStored;
2710
+ private serializeState;
2711
+ }
2712
+ /** Bilinear resample of RGBA pixels to `width × height`. */
2713
+ declare function resizePixels(px: PixelBuffer, width: number, height: number): PixelBuffer;
2714
+ /** A colour as RGBA bytes: `#rgb`, `#rrggbb`, `#rrggbbaa` or `[r, g, b, a?]` (0..255). */
2715
+ declare function parseColor(value: string | number[]): [number, number, number, number];
2716
+ /** `#rrggbb` of RGBA bytes (alpha dropped). */
2717
+ declare function toHex(c: readonly number[]): string;
2718
+
2719
+ interface ImageDimensions {
2720
+ width: number;
2721
+ height: number;
2722
+ }
2723
+ interface ImagePoint {
2724
+ x: number;
2725
+ y: number;
2726
+ }
2727
+ interface CropBox {
2728
+ x: number;
2729
+ y: number;
2730
+ width: number;
2731
+ height: number;
2732
+ }
2733
+ type AspectRatio = number | null;
2734
+ interface ImageTransform {
2735
+ rotate: number;
2736
+ flipH: boolean;
2737
+ flipV: boolean;
2738
+ }
2739
+ interface ImageAdjustments {
2740
+ brightness: number;
2741
+ contrast: number;
2742
+ saturation: number;
2743
+ exposure: number;
2744
+ temperature: number;
2745
+ /**
2746
+ * -100 (green) to 100 (magenta), 0 is default. White-balance tint correction in Duv × 3000
2747
+ * (process 2 only; the legacy process ignores it).
2748
+ */
2749
+ tint?: number;
2750
+ blur: number;
2751
+ opacity: number;
2752
+ /**
2753
+ * Rendering process the values are interpreted by. `2` (the latest; new edits and Reset get
2754
+ * it): linear-light exposure, Bradford white balance, LUT tone curve without intermediate
2755
+ * clipping. `1` or missing: the legacy 8-bit rendering, so edits saved before process
2756
+ * versions existed look exactly as they did. See `normalizeProcessVersion`.
2757
+ */
2758
+ process?: 1 | 2;
2759
+ /**
2760
+ * Non-destructive adjustment layers, evaluated bottom (first) to top (last) after the base
2761
+ * adjustments above, on the encoded sRGB result. Missing or empty: no layers (edits saved
2762
+ * before layers existed render exactly as before).
2763
+ */
2764
+ layers?: AdjustmentLayer[];
2765
+ }
2766
+ type AdjustmentKind = 'curves' | 'levels' | 'hueSaturation' | 'vibrance' | 'colorBalance' | 'blackWhite' | 'invert' | 'posterize' | 'threshold' | 'photoFilter';
2767
+ /** A Curves point; `input` and `output` are levels, 0..255. */
2768
+ interface CurvePoint {
2769
+ input: number;
2770
+ output: number;
2771
+ }
2772
+ /** Curves: each channel's curve first, then the master (composite) curve, as in Photoshop. */
2773
+ interface CurvesParams {
2774
+ master: CurvePoint[];
2775
+ red: CurvePoint[];
2776
+ green: CurvePoint[];
2777
+ blue: CurvePoint[];
2778
+ }
2779
+ /** One Levels record. Black / white points are levels (0..255); `gamma` is the midtone slider (0.1..9.99). */
2780
+ interface LevelsChannel {
2781
+ inBlack: number;
2782
+ inWhite: number;
2783
+ gamma: number;
2784
+ outBlack: number;
2785
+ outWhite: number;
2786
+ }
2787
+ /** Levels: each channel's record first, then the master record, as in Photoshop. */
2788
+ interface LevelsParams {
2789
+ master: LevelsChannel;
2790
+ red: LevelsChannel;
2791
+ green: LevelsChannel;
2792
+ blue: LevelsChannel;
2793
+ }
2794
+ /** One of Hue/Saturation's six colour ranges (reds, yellows, greens, cyans, blues, magentas). */
2795
+ interface HueRange {
2796
+ /** Hue shift in degrees (-180..180). */
2797
+ hue: number;
2798
+ /** Saturation change in percent (-100..100). */
2799
+ saturation: number;
2800
+ /** Lightness change in percent (-100..100). */
2801
+ lightness: number;
2802
+ /** Hue degrees where the range starts fading in, is fully on, starts fading out and ends (may wrap). */
2803
+ bounds: [number, number, number, number];
2804
+ }
2805
+ interface HueSaturationParams {
2806
+ /** Master hue shift in degrees (-180..180), or the hue when colorizing (0..360). */
2807
+ hue: number;
2808
+ /** Percent, -100..100 (0..100 when colorizing). */
2809
+ saturation: number;
2810
+ /** Percent, -100..100. */
2811
+ lightness: number;
2812
+ colorize: boolean;
2813
+ /** Six ranges: reds, yellows, greens, cyans, blues, magentas. */
2814
+ ranges: HueRange[];
2815
+ }
2816
+ interface VibranceParams {
2817
+ /** -100..100 */
2818
+ vibrance: number;
2819
+ /** -100..100 */
2820
+ saturation: number;
2821
+ }
2822
+ /** Cyan–red, magenta–green, yellow–blue sliders, -100..100 each. */
2823
+ type ColorBalanceTone = [number, number, number];
2824
+ interface ColorBalanceParams {
2825
+ shadows: ColorBalanceTone;
2826
+ midtones: ColorBalanceTone;
2827
+ highlights: ColorBalanceTone;
2828
+ preserveLuminosity: boolean;
2829
+ }
2830
+ /** Black & White: percent weights of the six colours (Photoshop defaults 40 60 40 60 20 80). */
2831
+ interface BlackWhiteParams {
2832
+ reds: number;
2833
+ yellows: number;
2834
+ greens: number;
2835
+ cyans: number;
2836
+ blues: number;
2837
+ magentas: number;
2838
+ /** Tint colour (`#rrggbb`), applied like the Color blend mode, or `null` for none. */
2839
+ tint: string | null;
2840
+ }
2841
+ interface PhotoFilterParams {
2842
+ /** Filter colour, `#rrggbb`. */
2843
+ color: string;
2844
+ /** Percent, 0..100. */
2845
+ density: number;
2846
+ preserveLuminosity: boolean;
2847
+ }
2848
+ interface PosterizeParams {
2849
+ /** 2..255 */
2850
+ levels: number;
2851
+ }
2852
+ interface ThresholdParams {
2853
+ /** 1..255: luma at or above it becomes white. */
2854
+ level: number;
2855
+ }
2856
+ type InvertParams = Record<string, never>;
2857
+ interface AdjustmentParamsMap {
2858
+ curves: CurvesParams;
2859
+ levels: LevelsParams;
2860
+ hueSaturation: HueSaturationParams;
2861
+ vibrance: VibranceParams;
2862
+ colorBalance: ColorBalanceParams;
2863
+ blackWhite: BlackWhiteParams;
2864
+ invert: InvertParams;
2865
+ posterize: PosterizeParams;
2866
+ threshold: ThresholdParams;
2867
+ photoFilter: PhotoFilterParams;
2868
+ }
2869
+ interface AdjustmentLayerBase {
2870
+ id: string;
2871
+ /** Optional display name. */
2872
+ name?: string;
2873
+ enabled: boolean;
2874
+ /** 0..1 */
2875
+ opacity: number;
2876
+ /** How the adjusted colour is blended over the layers below. */
2877
+ blend: BlendMode;
2878
+ /**
2879
+ * Optional layer mask (natural image pixels): where it is 0 the layer has no effect. Missing:
2880
+ * the layer applies everywhere (layers saved before masks existed are unchanged).
2881
+ */
2882
+ mask?: AdjustmentLayerMask;
2883
+ }
2884
+ /**
2885
+ * A layer mask: 8-bit coverage of the source image, run-length encoded (see
2886
+ * `encodeSelectionRle`), plus non-destructive density and feather.
2887
+ */
2888
+ interface AdjustmentLayerMask {
2889
+ width: number;
2890
+ height: number;
2891
+ /** `r1:` run-length encoding of the mask bytes. */
2892
+ rle: string;
2893
+ /** 0..1 (missing = 1). */
2894
+ density?: number;
2895
+ /** Gaussian σ in pixels (missing = 0). */
2896
+ feather?: number;
2897
+ /** `false` keeps the mask but ignores it. */
2898
+ enabled?: boolean;
2899
+ }
2900
+ /** A non-destructive adjustment layer (Photoshop's Layer › New Adjustment Layer). */
2901
+ type AdjustmentLayer = {
2902
+ [K in AdjustmentKind]: AdjustmentLayerBase & {
2903
+ kind: K;
2904
+ params: AdjustmentParamsMap[K];
2905
+ };
2906
+ }[AdjustmentKind];
2907
+ type AdjustmentLayerOf<K extends AdjustmentKind> = Extract<AdjustmentLayer, {
2908
+ kind: K;
2909
+ }>;
2910
+ interface DepthMaskOptions {
2911
+ enabled: boolean;
2912
+ maskSource: string | null;
2913
+ mode: 'threshold' | 'brightness';
2914
+ depthRange: [number, number];
2915
+ softness: number;
2916
+ invert: boolean;
2917
+ }
2918
+ type AnnotationType = 'pen' | 'rect' | 'circle' | 'arrow' | 'line' | 'text' | 'image';
2919
+ interface BaseAnnotation {
2920
+ id: string;
2921
+ type: AnnotationType;
2922
+ x: number;
2923
+ y: number;
2924
+ rotation?: number;
2925
+ opacity?: number;
2926
+ /** Blend mode the annotation is drawn with (default `normal`). */
2927
+ blend?: BlendMode;
2928
+ }
2929
+ interface PenAnnotation extends BaseAnnotation {
2930
+ type: 'pen';
2931
+ points: ImagePoint[];
2932
+ strokeColor: string;
2933
+ strokeWidth: number;
2934
+ }
2935
+ interface RectAnnotation extends BaseAnnotation {
2936
+ type: 'rect';
2937
+ width: number;
2938
+ height: number;
2939
+ strokeColor: string;
2940
+ strokeWidth: number;
2941
+ fillColor?: string;
2942
+ cornerRadius?: number;
2943
+ }
2944
+ interface CircleAnnotation extends BaseAnnotation {
2945
+ type: 'circle';
2946
+ radiusX: number;
2947
+ radiusY: number;
2948
+ strokeColor: string;
2949
+ strokeWidth: number;
2950
+ fillColor?: string;
2951
+ }
2952
+ interface ArrowAnnotation extends BaseAnnotation {
2953
+ type: 'arrow';
2954
+ endX: number;
2955
+ endY: number;
2956
+ strokeColor: string;
2957
+ strokeWidth: number;
2958
+ }
2959
+ interface LineAnnotation extends BaseAnnotation {
2960
+ type: 'line';
2961
+ endX: number;
2962
+ endY: number;
2963
+ strokeColor: string;
2964
+ strokeWidth: number;
2965
+ }
2966
+ interface TextAnnotation extends BaseAnnotation {
2967
+ type: 'text';
2968
+ text: string;
2969
+ fontSize: number;
2970
+ fontFamily?: string;
2971
+ color: string;
2972
+ fontWeight?: string | number;
2973
+ }
2974
+ interface ImageAnnotation extends BaseAnnotation {
2975
+ type: 'image';
2976
+ src: string;
2977
+ width: number;
2978
+ height: number;
2979
+ }
2980
+ type Annotation = PenAnnotation | RectAnnotation | CircleAnnotation | ArrowAnnotation | LineAnnotation | TextAnnotation | ImageAnnotation;
2981
+ type CreateAnnotationPayload = Omit<PenAnnotation, 'id'> | Omit<RectAnnotation, 'id'> | Omit<CircleAnnotation, 'id'> | Omit<ArrowAnnotation, 'id'> | Omit<LineAnnotation, 'id'> | Omit<TextAnnotation, 'id'> | Omit<ImageAnnotation, 'id'>;
2982
+ type ImageEditorTool = 'select' | 'crop' | 'pen' | 'rect' | 'circle' | 'arrow' | 'line' | 'text' | 'watermark' | 'move' | 'brush' | 'eraser' | 'mixer';
2983
+ type ImageEditorFit = 'contain' | 'margin';
2984
+ interface ImageEditorState {
2985
+ sourceUrl: string | null;
2986
+ /** Canvas (document) size in pixels: the source image's natural size. */
2987
+ imageDimensions: ImageDimensions;
2988
+ crop: CropBox | null;
2989
+ transform: ImageTransform;
2990
+ /**
2991
+ * Compatibility view of the layer stack: the develop filter of the active pixel layer (or the
2992
+ * Background), its blur (or `legacyOutputBlur`), the output opacity and every adjustment layer
2993
+ * in stack order. Edit it with `setAdjustments` and the adjustment-layer methods.
2994
+ */
2995
+ adjustments: ImageAdjustments;
2996
+ /** Compatibility view: the parametric depth mask of the active pixel layer (or the Background). */
2997
+ depthMask: DepthMaskOptions;
2998
+ /** Compatibility view: the annotation of every annotation layer, bottom to top. */
2999
+ annotations: Annotation[];
3000
+ selectedAnnotationId: string | null;
3001
+ activeTool: ImageEditorTool;
3002
+ zoom: number;
3003
+ pan: ImagePoint;
3004
+ isOriginalCompared: boolean;
3005
+ /** Viewport fit mode: 'contain' scales to full viewport bounds; 'margin' scales to 85% with 1.0 cap (default). */
3006
+ fit?: ImageEditorFit;
3007
+ /**
3008
+ * The active selection (8-bit coverage over the source image), or null/missing when nothing is
3009
+ * selected. Recorded in undo history and saved as `selection` in `toJSON()`.
3010
+ */
3011
+ selection?: SelectionMask | null;
3012
+ /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
3013
+ quickMask?: boolean;
3014
+ /**
3015
+ * The layer stack, bottom to top. Opening an image gives one pixel layer, "Background", whose
3016
+ * pixels are the source image. Layers are immutable: compare them by identity.
3017
+ */
3018
+ layers: ImageLayer[];
3019
+ /** The layer that layer commands, painting and `setAdjustments` act on, or `null`. */
3020
+ activeLayerId: string | null;
3021
+ /** Whether painting targets the active layer's pixels or its mask. */
3022
+ activeTarget: LayerTarget;
3023
+ /** Export alpha (0..1), applied after compositing; `adjustments.opacity` mirrors it. */
3024
+ outputOpacity: number;
3025
+ /**
3026
+ * Blur that runs after the adjustment layers, kept from v1 / v2 documents that had both so
3027
+ * they render as before. New edits blur a layer instead (`blur` filter).
3028
+ */
3029
+ legacyOutputBlur?: number;
3030
+ /** Brush settings and foreground / background colours of the paint tools (view state). */
3031
+ paint: PaintOptions;
3032
+ /**
3033
+ * The light layer effects use when they follow the global light (Photoshop's Global Light);
3034
+ * missing = angle 120°, altitude 30°. Recorded in undo history and saved in `toJSON()`.
3035
+ */
3036
+ globalLight?: GlobalLight;
3037
+ /**
3038
+ * The document's patterns (from an opened PSD), which pattern effects use by id besides the
3039
+ * built-in ones. Recorded in undo history and saved in `toJSON()`.
3040
+ */
3041
+ patterns?: EffectPattern[];
3042
+ /**
3043
+ * While a smart object's contents are open for editing (Edit Contents), the smart objects
3044
+ * being edited, outermost first: the state shows the contents as a document of their own until
3045
+ * `closeSmartObjectContents`. Missing when the document itself is shown.
3046
+ */
3047
+ editingContents?: Array<{
3048
+ layerId: string;
3049
+ name: string;
3050
+ }>;
3051
+ }
3052
+ /**
3053
+ * A stored editor state in the document-level schema (versions 1 and 2). Documents whose layer
3054
+ * stack has exactly this shape are still written this way, byte for byte.
3055
+ */
3056
+ interface LegacySerializedImageEditorState {
3057
+ /**
3058
+ * Schema version. `2` adds `adjustments.layers`; states without layers are still written as
3059
+ * `1` so they round-trip unchanged. See `migrateSerializedImageEditorState`.
3060
+ */
3061
+ version: 1 | 2;
3062
+ crop: CropBox | null;
3063
+ transform: ImageTransform;
3064
+ adjustments: ImageAdjustments;
3065
+ depthMask: DepthMaskOptions;
3066
+ annotations: Annotation[];
3067
+ /** The active selection, run-length encoded. Missing when nothing is selected. */
3068
+ selection?: SerializedSelection;
3069
+ }
3070
+ /**
3071
+ * A stored editor state: the document-level schema (v1 / v2) or the layered schema (v3, see
3072
+ * `SerializedLayeredImageEditorState`). `toJSON()` writes v1 / v2 while the layer stack still
3073
+ * has the shape those versions describe.
3074
+ */
3075
+ type SerializedImageEditorState = LegacySerializedImageEditorState | SerializedLayeredImageEditorState;
3076
+ interface ExportOptions {
3077
+ format?: 'image/png' | 'image/jpeg' | 'image/webp';
3078
+ quality?: number;
3079
+ width?: number;
3080
+ height?: number;
3081
+ includeAnnotations?: boolean;
3082
+ /** Render only these layers (groups with their children). */
3083
+ layerIds?: string[];
3084
+ }
3085
+ interface ImageEditorOptions {
3086
+ image?: string | HTMLImageElement;
3087
+ initialState?: Partial<SerializedImageEditorState>;
3088
+ onStateChange?: (state: ImageEditorState) => void;
3089
+ maxHistoryDepth?: number;
3090
+ /**
3091
+ * Upper bound for the pixel data held only by undo / redo steps (tiles shared with the
3092
+ * current state are free). The oldest steps are dropped first. Default 512 MiB.
3093
+ */
3094
+ maxHistoryBytes?: number;
3095
+ /** Viewport fit mode: 'contain' scales to full viewport bounds; 'margin' scales to 85% with 1.0 cap (default). */
3096
+ fit?: ImageEditorFit;
3097
+ }
3098
+ interface InpaintOptions {
3099
+ prompt: string;
3100
+ imageDataUrl: string;
3101
+ maskDataUrl?: string;
3102
+ cropBox?: CropBox | null;
3103
+ }
3104
+ interface RemoveBackgroundOptions {
3105
+ imageDataUrl: string;
3106
+ }
3107
+ type InpaintHook = (options: InpaintOptions) => Promise<string | void> | string | void;
3108
+ type RemoveBackgroundHook = (options: RemoveBackgroundOptions) => Promise<string | void> | string | void;
3109
+ interface ImageEditorAIHooks {
3110
+ onInpaint?: InpaintHook;
3111
+ onRemoveBackground?: RemoveBackgroundHook;
3112
+ }
3113
+
3114
+ interface PsdExportInput {
3115
+ width: number;
3116
+ height: number;
3117
+ /** Prepared stack, bottom to top. */
3118
+ layers: readonly ImageLayer[];
3119
+ /** The composite, `width × height` RGBA, written as the merged image. */
3120
+ merged: ArrayLike<number>;
3121
+ /** Pixels per inch (default 72). */
3122
+ resolution?: number;
3123
+ /** The document's patterns; those the effects use are embedded (built-in ones too). */
3124
+ patterns?: readonly EffectPattern[];
3125
+ /** Written as image resources 1037 and 1049. */
3126
+ globalLight?: GlobalLight;
3127
+ }
3128
+ interface PsdExportResult {
3129
+ bytes: Uint8Array;
3130
+ psb: boolean;
3131
+ /** Layer records written (group folders and dividers included). */
3132
+ records: number;
3133
+ /** One sentence per loss, naming the layers. */
3134
+ warnings: string[];
3135
+ }
3136
+ /** Writes the prepared stack as PSD (or PSB) bytes. */
3137
+ declare function exportPsd(input: PsdExportInput): PsdExportResult;
3138
+
3139
+ export { type ArrowAnnotation as $, type AdjustmentKind as A, type SmartFilter as B, type CurvePoint as C, type DepthMaskOptions as D, type SmartObjectLayer as E, type SmartContents as F, type SmartDocument as G, type HueRange as H, type ImageEditorAssetStore as I, type SmartPlacement as J, type FreeTransformGeometry as K, type LevelsChannel as L, type LayerPlacement as M, type FreeTransformMode as N, type FreeTransformResult as O, type PsdExportResult as P, FreeTransformSession as Q, type RefineEdgeParams as R, type SerializedLayer as S, type FreeTransformInit as T, type TransformAnchor as U, type AIResultOptions as V, type AdjustmentLayerBase as W, type AdjustmentLayerInit as X, type AdjustmentLayerUpdate as Y, type AnnotationPlacement as Z, type AnnotationType as _, type ImageAdjustments as a, PaintStrokeSession as a$, type AspectRatio as a0, type BaseAnnotation as a1, type BlackWhiteParams as a2, type BlurFilter as a3, type CircleAnnotation as a4, type ColorBalanceParams as a5, type ColorBalanceTone as a6, type ColorRangeOptions as a7, type ColorRangeSample as a8, type CreateAnnotationPayload as a9, type ImageEditorFit as aA, type ImageEditorOptions as aB, ImageEditorRenderer as aC, type ImageEditorState as aD, type ImageEditorTool as aE, type ImageLayerType as aF, type InpaintHook as aG, type InpaintOptions as aH, type InvertParams as aI, LAYERED_SCHEMA_VERSION as aJ, type LayerBase as aK, type LayerBlendMode as aL, type LayerFilterKind as aM, type LayerLocation as aN, type LayerMaskProps as aO, LayerOperationError as aP, type LayerProperties as aQ, type LayerSourcesHost as aR, type LayerTarget as aS, type LegacySerializedImageEditorState as aT, type LevelsParams as aU, type LineAnnotation as aV, type MagicWandOptions as aW, type MixerStrokeInit as aX, PSD_BLEND_KEYS as aY, type PaintOptions as aZ, type PaintStrokeResult as a_, type CropHandle as aa, type CurvesParams as ab, DEFAULT_ADJUSTMENTS as ac, DEFAULT_DEPTH_MASK as ad, DEFAULT_REFINE_EDGE_PARAMS as ae, DEFAULT_TRANSFORM as af, type DeserializeContext as ag, type DevelopSettings as ah, EMPTY_DISTANCE as ai, type ExportOptions as aj, FREE_TRANSFORM_CONTENTS as ak, FREE_TRANSFORM_MODES as al, type FillOptions as am, type FreeTransformContent as an, type FreeTransformHost as ao, type FreeTransformPreview as ap, GUIDED_FILTER_EPS as aq, type GroupLayer as ar, HISTOGRAM_BINS as as, type HistogramChannel as at, type HistoryEntryHandle as au, type HueSaturationParams as av, IMAGE_EDITOR_SCHEMA_VERSION as aw, type ImageAnnotation as ax, type ImageDimensions as ay, type ImageEditorAIHooks as az, type SerializedImageEditorState as b, blurFilter as b$, type PenAnnotation as b0, type PhotoFilterParams as b1, type PixelBuffer as b2, type PixelLayerInit as b3, type PosterizeParams as b4, type PsdChannel as b5, type PsdExportInput as b6, type PsdFile as b7, type PsdHeader as b8, type PsdImportOptions as b9, type SerializedPixelLayer as bA, type SerializedRasterMask as bB, type SerializedSelection as bC, type SerializedSmartContents as bD, type SerializedSmartFilter as bE, type SerializedSmartObjectLayer as bF, type ShapeSelectionOptions as bG, type SmartFilterKind as bH, type SmartObjectInfo as bI, type SmartPsdData as bJ, type StrokeInit as bK, type StrokeTool as bL, TONE_RANGE_PRESETS as bM, TRANSFORM_ANCHORS as bN, type TextAnnotation as bO, type ThresholdParams as bP, type ToneRangePreset as bQ, type TransformParams as bR, type TransformReadout as bS, type VibranceParams as bT, type ViewportMetrics as bU, addLayerEffect as bV, addLayerFilter as bW, adjustmentNodeAsLayer as bX, anchorPoint as bY, applyImageProjectionToContext as bZ, applyParams as b_, type PsdImportResult as ba, type PsdLayerMask as bb, type PsdLayerRecord as bc, type PsdLayerSummary as bd, type PsdRect as be, type PsdResource as bf, type PsdSmartFilter as bg, type PsdTaggedBlock as bh, type PsdTreeNode as bi, type RectAnnotation as bj, type RemoveBackgroundHook as bk, type RemoveBackgroundOptions as bl, type RenderOptions as bm, SELECTION_MODES as bn, SELECTION_MODIFY_OPS as bo, type SampleOptions as bp, type SelectionChannel as bq, type SelectionCommandOptions as br, type SelectionModifyOp as bs, type SerializeContext as bt, type SerializedAdjustmentLayer as bu, type SerializedAnnotationLayer as bv, type SerializedGroupLayer as bw, type SerializedLayerEffects as bx, type SerializedLayeredImageEditorState as by, type SerializedPattern as bz, type AdjustmentLayer as c, isIdentityTransform as c$, borderCoverage as c0, boxMean as c1, boxPointOf as c2, buildLayerMaskPlanes as c3, calculateCropDrag as c4, canvasToImagePoint as c5, clampCropToImage as c6, colorRange as c7, combineSelections as c8, computeHistogram as c9, exportPsd as cA, featherCoverage as cB, flipCorners as cC, fullSelection as cD, geometryHomography as cE, getAnnotationBounds as cF, getCropCursor as cG, getRotatedSize as cH, getViewportMetrics as cI, getViewportProjection as cJ, globalImageCache as cK, greyMorphology as cL, groupLayers as cM, guidePlanes as cN, guidedFilterColor as cO, hasSurface as cP, hasWarp as cQ, hashMaskContent as cR, histogramPath as cS, hitTestAnnotation as cT, hitTestCrop as cU, hueRange as cV, identityTransform as cW, imageToCanvasPoint as cX, importPsd as cY, invertSelection as cZ, isActiveFilter as c_, computeSelectionBounds as ca, contractCoverage as cb, cornersFromMatrix as cc, coverageToByte as cd, createSelectionMask as ce, decodeLayerChannel as cf, decodeLayerMask as cg, decodeMergedImage as ch, decodePngDataUrl as ci, decodeSelectionRle as cj, depthMaskAlpha as ck, describeTransform as cl, deserializeEffects as cm, deserializeLayer as cn, deserializeLayers as co, deserializePatterns as cp, deserializeSelection as cq, developFilter as cr, distanceTransform as cs, duplicateLayer as ct, edgeWidth as cu, ellipsePoints as cv, ellipseSelection as cw, encodeSelectionRle as cx, encodeSurface as cy, expandCoverage as cz, type AdjustmentParamsMap as d, selectionToPixels as d$, isQuarterTurnSwapped as d0, isSelectionEmpty as d1, isSelectionMode as d2, isSpatialFilter as d3, isTransformAnchor as d4, isValidWarp as d5, layerEffectsOf as d6, layerMaskCoverage as d7, layerMaskFromSelection as d8, layerMaskKey as d9, polygonCoverage as dA, polygonSelection as dB, presetWarp as dC, previewStep as dD, projectImagePoint as dE, projectImageRect as dF, projectMaskCoverage as dG, psdToLayers as dH, readPsd as dI, rectSelection as dJ, referencePoint as dK, refineCoverage as dL, refineEdgeHalo as dM, refineSelection as dN, removeLayer as dO, removeLayerEffect as dP, removeLayerFilter as dQ, requireLayer as dR, requireUnlocked as dS, resizePixels as dT, resizeSelection as dU, resolveExternalAssets as dV, runLayerFilters as dW, selectionArea as dX, selectionFromCoverage as dY, selectionFromPixels as dZ, selectionToCoverage as d_, layerName as da, layerTree as db, layerTypeLabel as dc, magicWand as dd, mapCropHandle as de, matrixFromCorners as df, matrixFromParams as dg, migrateSerializedImageEditorState as dh, migrateToLayeredState as di, modifySelection as dj, moveLayer as dk, newLayerId as dl, normalizeAngle as dm, normalizeFilter as dn, normalizeLayerMask as dp, normalizeRect as dq, normalizeRefineEdgeParams as dr, normalizeStoredAdjustments as ds, offsetLayer as dt, parseBlend as du, parseColor as dv, parseSelectionMode as dw, pixelLayerBounds as dx, pixelLayers as dy, placeLayer as dz, type AdjustmentLayerMask as e, selectionValueAt as e0, serializeContents as e1, serializeEffects as e2, serializeLayer as e3, serializeLayers as e4, serializeLayersExternal as e5, serializePatterns as e6, serializeSelection as e7, setLayerEffects as e8, setLayerEffectsEnabled as e9, setLayerMask as ea, setLayerProperties as eb, shiftCoverage as ec, smartObjectLayers as ed, smoothCoverage as ee, srgb8ToLab as ef, toHex as eg, toneRange as eh, transformHomography as ei, transformMap as ej, transformedBox as ek, transformedPoint as el, ungroupLayer as em, unionRect as en, unprojectCanvasPoint as eo, updateLayerEffect as ep, updateLayerFilter as eq, updateLayerMaskProps as er, withNewIds as es, writePsd as et, type AdjustmentLayerOf as f, ImageEditorController as g, type SelectionMask as h, type SelectionMode as i, type ColorDistance as j, type ImagePoint as k, type ImageLayer as l, type CropBox as m, type RasterMask as n, type Annotation as o, type AdjustmentLayerNode as p, type AnnotationLayer as q, type LayerFilter as r, type PixelLayer as s, type DevelopFilter as t, type ImageTransform as u, type ImageHistogram as v, type ImageProjection as w, type CompositorSources as x, LayerCompositor as y, type SmartFilterStack as z };