@pixerate/editor 0.189.0 → 0.191.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.
- package/dist/{chunk-ED4L4UD6.cjs → chunk-2UR4OX3B.cjs} +1002 -787
- package/dist/{chunk-M7XIC2FC.js → chunk-RQ2EM5TY.js} +333 -0
- package/dist/{chunk-ZEHTUR7Z.js → chunk-TXYXF32N.js} +968 -753
- package/dist/{chunk-5UEFNX24.cjs → chunk-ZQGZWFPU.cjs} +344 -11
- package/dist/image-editor/controller.d.cts +73 -1
- package/dist/image-editor/controller.d.ts +73 -1
- package/dist/image-editor/free-transform.d.cts +3 -6
- package/dist/image-editor/free-transform.d.ts +3 -6
- package/dist/image-editor/index.cjs +1382 -904
- package/dist/image-editor/index.d.cts +1 -0
- package/dist/image-editor/index.d.ts +1 -0
- package/dist/image-editor/index.js +485 -7
- package/dist/image-editor/layer-tools.d.cts +1 -1
- package/dist/image-editor/layer-tools.d.ts +1 -1
- package/dist/image-editor/layers/compositor.d.cts +3 -0
- package/dist/image-editor/layers/compositor.d.ts +3 -0
- package/dist/image-editor/layers/model.d.cts +23 -0
- package/dist/image-editor/layers/model.d.ts +23 -0
- package/dist/image-editor/layers/panel.d.cts +11 -1
- package/dist/image-editor/layers/panel.d.ts +11 -1
- package/dist/image-editor/layers/serialize.d.cts +11 -0
- package/dist/image-editor/layers/serialize.d.ts +11 -0
- package/dist/image-editor/layers/shape.d.cts +5 -0
- package/dist/image-editor/layers/shape.d.ts +5 -0
- package/dist/image-editor/layers/vector-mask.d.cts +51 -0
- package/dist/image-editor/layers/vector-mask.d.ts +51 -0
- package/dist/image-editor/psd/import.d.cts +0 -7
- package/dist/image-editor/psd/import.d.ts +0 -7
- package/dist/image-editor/selection/index.d.cts +1 -0
- package/dist/image-editor/selection/index.d.ts +1 -0
- package/dist/image-editor/selection/path.d.cts +37 -0
- package/dist/image-editor/selection/path.d.ts +37 -0
- package/dist/paint-editor/index.cjs +6 -6
- package/dist/paint-editor/index.js +1 -1
- package/dist/vector/index.cjs +21 -303
- package/dist/vector/index.js +15 -297
- package/dist/vector/trace.d.cts +8 -1
- package/dist/vector/trace.d.ts +8 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +8 -0
- package/skills/pixerate-image-selection-masking/SKILL.md +10 -2
|
@@ -6,7 +6,7 @@ import type { CropBox } from './types.cjs';
|
|
|
6
6
|
export declare function maskSummary(mask: RasterMask | undefined): Record<string, unknown> | null;
|
|
7
7
|
/**
|
|
8
8
|
* A plain description of a layer (and its children) for agents: ids, types, names, document
|
|
9
|
-
* bounds, visibility, opacity, fill, blend, clipping, locks, mask and effects summaries and
|
|
9
|
+
* bounds, visibility, opacity, fill, blend, clipping, locks, mask, vector mask and effects summaries and
|
|
10
10
|
* whether it is active. `boundsOf` gives a pixel layer's document bounds.
|
|
11
11
|
*/
|
|
12
12
|
export declare function describeLayer(layer: ImageLayer, activeId: string | null, boundsOf: (layer: ImageLayer) => CropBox | null): Record<string, unknown>;
|
|
@@ -6,7 +6,7 @@ import type { CropBox } from './types.js';
|
|
|
6
6
|
export declare function maskSummary(mask: RasterMask | undefined): Record<string, unknown> | null;
|
|
7
7
|
/**
|
|
8
8
|
* A plain description of a layer (and its children) for agents: ids, types, names, document
|
|
9
|
-
* bounds, visibility, opacity, fill, blend, clipping, locks, mask and effects summaries and
|
|
9
|
+
* bounds, visibility, opacity, fill, blend, clipping, locks, mask, vector mask and effects summaries and
|
|
10
10
|
* whether it is active. `boundsOf` gives a pixel layer's document bounds.
|
|
11
11
|
*/
|
|
12
12
|
export declare function describeLayer(layer: ImageLayer, activeId: string | null, boundsOf: (layer: ImageLayer) => CropBox | null): Record<string, unknown>;
|
|
@@ -42,6 +42,9 @@ export declare function runLayerFiltersFloat(data: Float32Array, width: number,
|
|
|
42
42
|
/** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
|
|
43
43
|
export declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
|
|
44
44
|
constructor(options?: CompositorOptions);
|
|
45
|
+
composite(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, options?: CompositeCallOptions): Uint8ClampedArray;
|
|
46
|
+
compositeTiles(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, onTile: (tx: number, ty: number, data: Uint8ClampedArray, changed: boolean) => void, options?: CompositeCallOptions): void;
|
|
47
|
+
tile(layers: readonly ImageLayer[], tx: number, ty: number, sources: CompositorSources): Uint8ClampedArray;
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* The composite of an image editor stack at 16 or 32 bits (see `raster/deep-compositor`): float
|
|
@@ -42,6 +42,9 @@ export declare function runLayerFiltersFloat(data: Float32Array, width: number,
|
|
|
42
42
|
/** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
|
|
43
43
|
export declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
|
|
44
44
|
constructor(options?: CompositorOptions);
|
|
45
|
+
composite(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, options?: CompositeCallOptions): Uint8ClampedArray;
|
|
46
|
+
compositeTiles(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, onTile: (tx: number, ty: number, data: Uint8ClampedArray, changed: boolean) => void, options?: CompositeCallOptions): void;
|
|
47
|
+
tile(layers: readonly ImageLayer[], tx: number, ty: number, sources: CompositorSources): Uint8ClampedArray;
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* The composite of an image editor stack at 16 or 32 bits (see `raster/deep-compositor`): float
|
|
@@ -28,6 +28,29 @@ export type { LayerLocks };
|
|
|
28
28
|
export type RasterMask = BaseRasterMask<DepthMaskOptions>;
|
|
29
29
|
export interface LayerBase extends RasterLayerBase {
|
|
30
30
|
mask?: RasterMask;
|
|
31
|
+
/** A vector mask (Photoshop's): a path that reveals the layer, on top of `mask`. Not on shape layers. */
|
|
32
|
+
vectorMask?: VectorMask;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A vector mask: shape components in document pixels (the same paths shape layers use) that
|
|
36
|
+
* reveal the layer inside them (outside with `inverted`). It stays sharp at any size; the
|
|
37
|
+
* compositor multiplies it with the layer's raster mask. No components hides everything
|
|
38
|
+
* (reveals everything when inverted).
|
|
39
|
+
*/
|
|
40
|
+
export interface VectorMask {
|
|
41
|
+
components: ShapeComponent[];
|
|
42
|
+
/** How overlapping subpaths fill (default nonzero). */
|
|
43
|
+
fillRule?: ShapeFillRule;
|
|
44
|
+
/** Reveal outside the path instead (default false). */
|
|
45
|
+
inverted?: boolean;
|
|
46
|
+
/** Disabled masks are kept but ignored (default true). */
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
/** `false`: the path stays put when the layer moves or transforms (default true). */
|
|
49
|
+
linked?: boolean;
|
|
50
|
+
/** 0..1 (default 1): at 0.5 the hidden area still shows half. */
|
|
51
|
+
density?: number;
|
|
52
|
+
/** Gaussian feather σ in px (default 0). */
|
|
53
|
+
feather?: number;
|
|
31
54
|
}
|
|
32
55
|
/** The base sliders of the old document-level adjustments, as a filter on one layer. */
|
|
33
56
|
export interface DevelopSettings {
|
|
@@ -28,6 +28,29 @@ export type { LayerLocks };
|
|
|
28
28
|
export type RasterMask = BaseRasterMask<DepthMaskOptions>;
|
|
29
29
|
export interface LayerBase extends RasterLayerBase {
|
|
30
30
|
mask?: RasterMask;
|
|
31
|
+
/** A vector mask (Photoshop's): a path that reveals the layer, on top of `mask`. Not on shape layers. */
|
|
32
|
+
vectorMask?: VectorMask;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A vector mask: shape components in document pixels (the same paths shape layers use) that
|
|
36
|
+
* reveal the layer inside them (outside with `inverted`). It stays sharp at any size; the
|
|
37
|
+
* compositor multiplies it with the layer's raster mask. No components hides everything
|
|
38
|
+
* (reveals everything when inverted).
|
|
39
|
+
*/
|
|
40
|
+
export interface VectorMask {
|
|
41
|
+
components: ShapeComponent[];
|
|
42
|
+
/** How overlapping subpaths fill (default nonzero). */
|
|
43
|
+
fillRule?: ShapeFillRule;
|
|
44
|
+
/** Reveal outside the path instead (default false). */
|
|
45
|
+
inverted?: boolean;
|
|
46
|
+
/** Disabled masks are kept but ignored (default true). */
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
/** `false`: the path stays put when the layer moves or transforms (default true). */
|
|
49
|
+
linked?: boolean;
|
|
50
|
+
/** 0..1 (default 1): at 0.5 the hidden area still shows half. */
|
|
51
|
+
density?: number;
|
|
52
|
+
/** Gaussian feather σ in px (default 0). */
|
|
53
|
+
feather?: number;
|
|
31
54
|
}
|
|
32
55
|
/** The base sliders of the old document-level adjustments, as a filter on one layer. */
|
|
33
56
|
export interface DevelopSettings {
|
|
@@ -18,6 +18,16 @@ export declare function maskThumbnailClick(controller: ImageEditorController, la
|
|
|
18
18
|
shiftKey?: boolean;
|
|
19
19
|
altKey?: boolean;
|
|
20
20
|
}): void;
|
|
21
|
+
/**
|
|
22
|
+
* A click on a layer's vector mask thumbnail: Ctrl/⌘-click loads the path as the selection (Shift
|
|
23
|
+
* adds, Alt subtracts, both intersect) and Shift-click turns the vector mask on or off.
|
|
24
|
+
*/
|
|
25
|
+
export declare function vectorMaskThumbnailClick(controller: ImageEditorController, layer: ImageLayer, e: {
|
|
26
|
+
ctrlKey?: boolean;
|
|
27
|
+
metaKey?: boolean;
|
|
28
|
+
shiftKey?: boolean;
|
|
29
|
+
altKey?: boolean;
|
|
30
|
+
}): void;
|
|
21
31
|
/**
|
|
22
32
|
* A click on a layer's pixel thumbnail: Ctrl/⌘-click loads the layer's transparency as the
|
|
23
33
|
* selection (Shift adds, Alt subtracts, both intersect) and returns true; other clicks are left
|
|
@@ -57,7 +67,7 @@ export type DropZone = 'above' | 'below' | 'inside';
|
|
|
57
67
|
export declare function dropZone(fraction: number, isGroup: boolean): DropZone;
|
|
58
68
|
/** The placement for `move_layer` when `dragId` is dropped on row `targetId` in `zone`, or `null` for a no-op. */
|
|
59
69
|
export declare function dropPlacement(layers: readonly ImageLayer[], dragId: string, targetId: string, zone: DropZone): LayerPlacement | null;
|
|
60
|
-
export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
|
|
70
|
+
export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-vector-mask' | 'add-vector-mask-hide' | 'add-vector-mask-selection' | 'delete-vector-mask' | 'toggle-vector-mask' | 'rasterize-vector-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
|
|
61
71
|
export interface LayerMenuItem {
|
|
62
72
|
action: LayerMenuAction;
|
|
63
73
|
label: string;
|
|
@@ -18,6 +18,16 @@ export declare function maskThumbnailClick(controller: ImageEditorController, la
|
|
|
18
18
|
shiftKey?: boolean;
|
|
19
19
|
altKey?: boolean;
|
|
20
20
|
}): void;
|
|
21
|
+
/**
|
|
22
|
+
* A click on a layer's vector mask thumbnail: Ctrl/⌘-click loads the path as the selection (Shift
|
|
23
|
+
* adds, Alt subtracts, both intersect) and Shift-click turns the vector mask on or off.
|
|
24
|
+
*/
|
|
25
|
+
export declare function vectorMaskThumbnailClick(controller: ImageEditorController, layer: ImageLayer, e: {
|
|
26
|
+
ctrlKey?: boolean;
|
|
27
|
+
metaKey?: boolean;
|
|
28
|
+
shiftKey?: boolean;
|
|
29
|
+
altKey?: boolean;
|
|
30
|
+
}): void;
|
|
21
31
|
/**
|
|
22
32
|
* A click on a layer's pixel thumbnail: Ctrl/⌘-click loads the layer's transparency as the
|
|
23
33
|
* selection (Shift adds, Alt subtracts, both intersect) and returns true; other clicks are left
|
|
@@ -57,7 +67,7 @@ export type DropZone = 'above' | 'below' | 'inside';
|
|
|
57
67
|
export declare function dropZone(fraction: number, isGroup: boolean): DropZone;
|
|
58
68
|
/** The placement for `move_layer` when `dragId` is dropped on row `targetId` in `zone`, or `null` for a no-op. */
|
|
59
69
|
export declare function dropPlacement(layers: readonly ImageLayer[], dragId: string, targetId: string, zone: DropZone): LayerPlacement | null;
|
|
60
|
-
export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
|
|
70
|
+
export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-vector-mask' | 'add-vector-mask-hide' | 'add-vector-mask-selection' | 'delete-vector-mask' | 'toggle-vector-mask' | 'rasterize-vector-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
|
|
61
71
|
export interface LayerMenuItem {
|
|
62
72
|
action: LayerMenuAction;
|
|
63
73
|
label: string;
|
|
@@ -40,9 +40,20 @@ interface SerializedLayerBase {
|
|
|
40
40
|
blend: string;
|
|
41
41
|
clipped?: boolean;
|
|
42
42
|
mask?: SerializedRasterMask;
|
|
43
|
+
vectorMask?: SerializedVectorMask;
|
|
43
44
|
locks?: LayerLocks;
|
|
44
45
|
effects?: SerializedLayerEffects;
|
|
45
46
|
}
|
|
47
|
+
/** A stored vector mask: its components as JSON (document pixels) and its properties. */
|
|
48
|
+
export interface SerializedVectorMask {
|
|
49
|
+
components: unknown[];
|
|
50
|
+
fillRule?: 'evenodd';
|
|
51
|
+
inverted?: true;
|
|
52
|
+
enabled?: false;
|
|
53
|
+
linked?: false;
|
|
54
|
+
density?: number;
|
|
55
|
+
feather?: number;
|
|
56
|
+
}
|
|
46
57
|
/** A layer's effects: the effect list as JSON plus the PSD block they came from (base64). */
|
|
47
58
|
export interface SerializedLayerEffects {
|
|
48
59
|
enabled: boolean;
|
|
@@ -40,9 +40,20 @@ interface SerializedLayerBase {
|
|
|
40
40
|
blend: string;
|
|
41
41
|
clipped?: boolean;
|
|
42
42
|
mask?: SerializedRasterMask;
|
|
43
|
+
vectorMask?: SerializedVectorMask;
|
|
43
44
|
locks?: LayerLocks;
|
|
44
45
|
effects?: SerializedLayerEffects;
|
|
45
46
|
}
|
|
47
|
+
/** A stored vector mask: its components as JSON (document pixels) and its properties. */
|
|
48
|
+
export interface SerializedVectorMask {
|
|
49
|
+
components: unknown[];
|
|
50
|
+
fillRule?: 'evenodd';
|
|
51
|
+
inverted?: true;
|
|
52
|
+
enabled?: false;
|
|
53
|
+
linked?: false;
|
|
54
|
+
density?: number;
|
|
55
|
+
feather?: number;
|
|
56
|
+
}
|
|
46
57
|
/** A layer's effects: the effect list as JSON plus the PSD block they came from (base64). */
|
|
47
58
|
export interface SerializedLayerEffects {
|
|
48
59
|
enabled: boolean;
|
|
@@ -50,6 +50,11 @@ export declare function shapeGeometryBounds(layer: Pick<ShapeLayer, 'components'
|
|
|
50
50
|
width: number;
|
|
51
51
|
height: number;
|
|
52
52
|
} | null;
|
|
53
|
+
/**
|
|
54
|
+
* Coverage (0..1) of `components` over `rect`, filled with `rule` and combined by their ops;
|
|
55
|
+
* `inverted` covers the outside instead. Used for vector masks.
|
|
56
|
+
*/
|
|
57
|
+
export declare function componentsCoverage(components: readonly ShapeComponent[], rect: CropBox, rule?: ShapeFillRule, inverted?: boolean): Float32Array;
|
|
53
58
|
/** Where a shape renders: the canvas grown by the margin. */
|
|
54
59
|
export declare function shapeRenderClip(env: Pick<ShapeEnv, 'width' | 'height'>): CropBox;
|
|
55
60
|
/**
|
|
@@ -50,6 +50,11 @@ export declare function shapeGeometryBounds(layer: Pick<ShapeLayer, 'components'
|
|
|
50
50
|
width: number;
|
|
51
51
|
height: number;
|
|
52
52
|
} | null;
|
|
53
|
+
/**
|
|
54
|
+
* Coverage (0..1) of `components` over `rect`, filled with `rule` and combined by their ops;
|
|
55
|
+
* `inverted` covers the outside instead. Used for vector masks.
|
|
56
|
+
*/
|
|
57
|
+
export declare function componentsCoverage(components: readonly ShapeComponent[], rect: CropBox, rule?: ShapeFillRule, inverted?: boolean): Float32Array;
|
|
53
58
|
/** Where a shape renders: the canvas grown by the margin. */
|
|
54
59
|
export declare function shapeRenderClip(env: Pick<ShapeEnv, 'width' | 'height'>): CropBox;
|
|
55
60
|
/**
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector masks: a path on a layer that reveals it (Photoshop's Layer › Vector Mask). The paths are
|
|
3
|
+
* shape components in document pixels; the compositor sees them through `resolveVectorMasks`, which
|
|
4
|
+
* gives each layer with a vector mask a raster mask that is its raster mask times the path's
|
|
5
|
+
* coverage, so the tile engine and its caches need nothing new.
|
|
6
|
+
*/
|
|
7
|
+
import type { CropBox } from '../types.cjs';
|
|
8
|
+
import type { AffineMatrix } from '../../geom/types.cjs';
|
|
9
|
+
import type { ImageLayer, RasterMask, VectorMask } from './model.cjs';
|
|
10
|
+
/** Whether a vector mask takes part in compositing. */
|
|
11
|
+
export declare function isActiveVectorMask(vm: VectorMask | undefined): vm is VectorMask;
|
|
12
|
+
/**
|
|
13
|
+
* The vector mask's coverage (0..1) over the `width × height` canvas, with its feather and
|
|
14
|
+
* density applied.
|
|
15
|
+
*/
|
|
16
|
+
export declare function vectorMaskCoverage(vm: VectorMask, width: number, height: number): Float32Array;
|
|
17
|
+
/**
|
|
18
|
+
* The raster mask a layer composites with: its raster mask times its vector mask (layer space,
|
|
19
|
+
* feather and density baked in), or the raster mask alone when there is no active vector mask.
|
|
20
|
+
* A depth (parametric) mask cannot be combined, so it wins.
|
|
21
|
+
*/
|
|
22
|
+
export declare function effectiveLayerMask(layer: ImageLayer, width: number, height: number): RasterMask | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The stack as the compositor should see it: each layer with an active vector mask replaced by a
|
|
25
|
+
* copy whose `mask` is `effectiveLayerMask` (cached per layer object and canvas size, so the
|
|
26
|
+
* engine's tile caches keep working). Returns `layers` itself when there are none.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveVectorMasks(layers: readonly ImageLayer[], width: number, height: number): readonly ImageLayer[];
|
|
29
|
+
/** A vector mask moved by (dx, dy) document pixels. */
|
|
30
|
+
export declare function offsetVectorMask(vm: VectorMask, dx: number, dy: number): VectorMask;
|
|
31
|
+
/** A vector mask with its paths mapped by `f` (document space); `affine` keeps live shapes live. */
|
|
32
|
+
export declare function transformVectorMask(vm: VectorMask, f: (p: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
}) => {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
}, affine?: AffineMatrix): VectorMask;
|
|
39
|
+
/** A linked vector mask moved with its layer by (dx, dy); unlinked ones stay. */
|
|
40
|
+
export declare function followLayerMove<T extends ImageLayer>(layer: T, dx: number, dy: number): T;
|
|
41
|
+
/** Bounds of the area a vector mask's paths enclose (document pixels, the curves themselves, not their handles), or `null` when it has none. */
|
|
42
|
+
export declare function vectorMaskBounds(vm: Pick<VectorMask, 'components'>): CropBox | null;
|
|
43
|
+
/**
|
|
44
|
+
* A vector mask's thumbnail over the `width × height` canvas, at most `maxSide` px: grey RGBA,
|
|
45
|
+
* white where the path reveals (cached per mask object).
|
|
46
|
+
*/
|
|
47
|
+
export declare function vectorMaskThumbnail(vm: VectorMask, width: number, height: number, maxSide?: number): {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
data: Uint8ClampedArray;
|
|
51
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector masks: a path on a layer that reveals it (Photoshop's Layer › Vector Mask). The paths are
|
|
3
|
+
* shape components in document pixels; the compositor sees them through `resolveVectorMasks`, which
|
|
4
|
+
* gives each layer with a vector mask a raster mask that is its raster mask times the path's
|
|
5
|
+
* coverage, so the tile engine and its caches need nothing new.
|
|
6
|
+
*/
|
|
7
|
+
import type { CropBox } from '../types.js';
|
|
8
|
+
import type { AffineMatrix } from '../../geom/types.js';
|
|
9
|
+
import type { ImageLayer, RasterMask, VectorMask } from './model.js';
|
|
10
|
+
/** Whether a vector mask takes part in compositing. */
|
|
11
|
+
export declare function isActiveVectorMask(vm: VectorMask | undefined): vm is VectorMask;
|
|
12
|
+
/**
|
|
13
|
+
* The vector mask's coverage (0..1) over the `width × height` canvas, with its feather and
|
|
14
|
+
* density applied.
|
|
15
|
+
*/
|
|
16
|
+
export declare function vectorMaskCoverage(vm: VectorMask, width: number, height: number): Float32Array;
|
|
17
|
+
/**
|
|
18
|
+
* The raster mask a layer composites with: its raster mask times its vector mask (layer space,
|
|
19
|
+
* feather and density baked in), or the raster mask alone when there is no active vector mask.
|
|
20
|
+
* A depth (parametric) mask cannot be combined, so it wins.
|
|
21
|
+
*/
|
|
22
|
+
export declare function effectiveLayerMask(layer: ImageLayer, width: number, height: number): RasterMask | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The stack as the compositor should see it: each layer with an active vector mask replaced by a
|
|
25
|
+
* copy whose `mask` is `effectiveLayerMask` (cached per layer object and canvas size, so the
|
|
26
|
+
* engine's tile caches keep working). Returns `layers` itself when there are none.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveVectorMasks(layers: readonly ImageLayer[], width: number, height: number): readonly ImageLayer[];
|
|
29
|
+
/** A vector mask moved by (dx, dy) document pixels. */
|
|
30
|
+
export declare function offsetVectorMask(vm: VectorMask, dx: number, dy: number): VectorMask;
|
|
31
|
+
/** A vector mask with its paths mapped by `f` (document space); `affine` keeps live shapes live. */
|
|
32
|
+
export declare function transformVectorMask(vm: VectorMask, f: (p: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
}) => {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
}, affine?: AffineMatrix): VectorMask;
|
|
39
|
+
/** A linked vector mask moved with its layer by (dx, dy); unlinked ones stay. */
|
|
40
|
+
export declare function followLayerMove<T extends ImageLayer>(layer: T, dx: number, dy: number): T;
|
|
41
|
+
/** Bounds of the area a vector mask's paths enclose (document pixels, the curves themselves, not their handles), or `null` when it has none. */
|
|
42
|
+
export declare function vectorMaskBounds(vm: Pick<VectorMask, 'components'>): CropBox | null;
|
|
43
|
+
/**
|
|
44
|
+
* A vector mask's thumbnail over the `width × height` canvas, at most `maxSide` px: grey RGBA,
|
|
45
|
+
* white where the path reveals (cached per mask object).
|
|
46
|
+
*/
|
|
47
|
+
export declare function vectorMaskThumbnail(vm: VectorMask, width: number, height: number, maxSide?: number): {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
data: Uint8ClampedArray;
|
|
51
|
+
};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PSD / PSB → our layer tree. Everything the model holds maps directly (smart objects with their
|
|
3
|
-
* embedded contents, placement, warp, smart filters and filter mask; shape layers with their
|
|
4
|
-
* paths, fill, stroke and live shapes; type layers with their text, styles and Photoshop's pixels
|
|
5
|
-
* until edited); everything else comes in as close as it can (fill layers as their stored pixels)
|
|
6
|
-
* with a warning naming the layers.
|
|
7
|
-
*/
|
|
8
1
|
import { type PsdAlphaChannel } from './channels.cjs';
|
|
9
2
|
import type { ImageLayer, LayerBlendMode } from '../layers/model.cjs';
|
|
10
3
|
import { type PsdFile } from './file.cjs';
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PSD / PSB → our layer tree. Everything the model holds maps directly (smart objects with their
|
|
3
|
-
* embedded contents, placement, warp, smart filters and filter mask; shape layers with their
|
|
4
|
-
* paths, fill, stroke and live shapes; type layers with their text, styles and Photoshop's pixels
|
|
5
|
-
* until edited); everything else comes in as close as it can (fill layers as their stored pixels)
|
|
6
|
-
* with a warning naming the layers.
|
|
7
|
-
*/
|
|
8
1
|
import { type PsdAlphaChannel } from './channels.js';
|
|
9
2
|
import type { ImageLayer, LayerBlendMode } from '../layers/model.js';
|
|
10
3
|
import { type PsdFile } from './file.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selections ⇄ paths (Photoshop's Make Work Path and Make Selection): a selection's outline traced
|
|
3
|
+
* into Bézier subpaths, and a path filled into a selection.
|
|
4
|
+
*/
|
|
5
|
+
import type { VectorSubpath } from '../../vector/types.cjs';
|
|
6
|
+
import type { ShapeComponent, ShapeFillRule } from '../layers/model.cjs';
|
|
7
|
+
import { type SelectionMask } from './mask.cjs';
|
|
8
|
+
export interface SelectionToPathOptions {
|
|
9
|
+
/** How far (px) the path may stray from the outline (default 0.5; Photoshop's Make Work Path tolerance). */
|
|
10
|
+
tolerance?: number;
|
|
11
|
+
/** 0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5). */
|
|
12
|
+
cornerRoundness?: number;
|
|
13
|
+
/** Islands and holes smaller than this many square pixels are left out (default 4). */
|
|
14
|
+
minArea?: number;
|
|
15
|
+
/** Coverage (0..255) where the outline runs (default 128, half selected). */
|
|
16
|
+
threshold?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The outline of a selection as closed subpaths in document pixels (holes included, so fill them
|
|
20
|
+
* even-odd). Traced in the selection's bounds only; an empty selection gives none.
|
|
21
|
+
*/
|
|
22
|
+
export declare function selectionToSubpaths(mask: SelectionMask, options?: SelectionToPathOptions): VectorSubpath[];
|
|
23
|
+
/** Subpaths as compact SVG path data (absolute, `precision` decimals). */
|
|
24
|
+
export declare function subpathsToSvgPath(subpaths: readonly VectorSubpath[], precision?: number): string;
|
|
25
|
+
export interface PathToSelectionOptions {
|
|
26
|
+
/** Feather radius in px applied after filling (default 0). */
|
|
27
|
+
feather?: number;
|
|
28
|
+
/** Soft edges (default true); false makes every pixel fully in or out. */
|
|
29
|
+
antiAlias?: boolean;
|
|
30
|
+
/** How overlapping subpaths fill (default nonzero). */
|
|
31
|
+
fillRule?: ShapeFillRule;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Make Selection: the area of `components` (document pixels, combined by their ops) over a
|
|
35
|
+
* `width × height` image, computed in the path's bounds only.
|
|
36
|
+
*/
|
|
37
|
+
export declare function pathToSelection(components: readonly ShapeComponent[], width: number, height: number, options?: PathToSelectionOptions): SelectionMask;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selections ⇄ paths (Photoshop's Make Work Path and Make Selection): a selection's outline traced
|
|
3
|
+
* into Bézier subpaths, and a path filled into a selection.
|
|
4
|
+
*/
|
|
5
|
+
import type { VectorSubpath } from '../../vector/types.js';
|
|
6
|
+
import type { ShapeComponent, ShapeFillRule } from '../layers/model.js';
|
|
7
|
+
import { type SelectionMask } from './mask.js';
|
|
8
|
+
export interface SelectionToPathOptions {
|
|
9
|
+
/** How far (px) the path may stray from the outline (default 0.5; Photoshop's Make Work Path tolerance). */
|
|
10
|
+
tolerance?: number;
|
|
11
|
+
/** 0: straight sides and sharp corners; 1: as smooth as the outline allows (default 0.5). */
|
|
12
|
+
cornerRoundness?: number;
|
|
13
|
+
/** Islands and holes smaller than this many square pixels are left out (default 4). */
|
|
14
|
+
minArea?: number;
|
|
15
|
+
/** Coverage (0..255) where the outline runs (default 128, half selected). */
|
|
16
|
+
threshold?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The outline of a selection as closed subpaths in document pixels (holes included, so fill them
|
|
20
|
+
* even-odd). Traced in the selection's bounds only; an empty selection gives none.
|
|
21
|
+
*/
|
|
22
|
+
export declare function selectionToSubpaths(mask: SelectionMask, options?: SelectionToPathOptions): VectorSubpath[];
|
|
23
|
+
/** Subpaths as compact SVG path data (absolute, `precision` decimals). */
|
|
24
|
+
export declare function subpathsToSvgPath(subpaths: readonly VectorSubpath[], precision?: number): string;
|
|
25
|
+
export interface PathToSelectionOptions {
|
|
26
|
+
/** Feather radius in px applied after filling (default 0). */
|
|
27
|
+
feather?: number;
|
|
28
|
+
/** Soft edges (default true); false makes every pixel fully in or out. */
|
|
29
|
+
antiAlias?: boolean;
|
|
30
|
+
/** How overlapping subpaths fill (default nonzero). */
|
|
31
|
+
fillRule?: ShapeFillRule;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Make Selection: the area of `components` (document pixels, combined by their ops) over a
|
|
35
|
+
* `width × height` image, computed in the path's bounds only.
|
|
36
|
+
*/
|
|
37
|
+
export declare function pathToSelection(components: readonly ShapeComponent[], width: number, height: number, options?: PathToSelectionOptions): SelectionMask;
|
|
@@ -7,7 +7,7 @@ var _chunkZ64NRDEHcjs = require('../chunk-Z64NRDEH.cjs');
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunk2UR4OX3Bcjs = require('../chunk-2UR4OX3B.cjs');
|
|
11
11
|
require('../chunk-A2IS7EHE.cjs');
|
|
12
12
|
|
|
13
13
|
|
|
@@ -1653,11 +1653,11 @@ function header(state, options) {
|
|
|
1653
1653
|
return out;
|
|
1654
1654
|
}
|
|
1655
1655
|
function serializePaintDocument(state, options = {}) {
|
|
1656
|
-
const { layers, assets } =
|
|
1656
|
+
const { layers, assets } = _chunk2UR4OX3Bcjs.serializeLayers.call(void 0, state.layers);
|
|
1657
1657
|
return { ...header(state, options), layers, assets };
|
|
1658
1658
|
}
|
|
1659
1659
|
async function serializePaintDocumentExternal(state, store, options = {}) {
|
|
1660
|
-
const { layers, assets } = await
|
|
1660
|
+
const { layers, assets } = await _chunk2UR4OX3Bcjs.serializeLayersExternal.call(void 0, state.layers, store);
|
|
1661
1661
|
return { ...header(state, options), layers, assets };
|
|
1662
1662
|
}
|
|
1663
1663
|
function paintLayersOnly(layers, problems) {
|
|
@@ -1694,7 +1694,7 @@ function deserializePaintDocument(raw) {
|
|
|
1694
1694
|
const canvas = normalizeCanvas({ width: r.width, height: r.height, dpi: _nullishCoalesce(r.dpi, () => ( void 0)) });
|
|
1695
1695
|
const paper = _chunkYQ5C5BVKcjs.normalizePaper.call(void 0, _nullishCoalesce(r.paper, () => ( {})));
|
|
1696
1696
|
const assets = r.assets && typeof r.assets === "object" ? r.assets : {};
|
|
1697
|
-
const layers = paintLayersOnly(
|
|
1697
|
+
const layers = paintLayersOnly(_chunk2UR4OX3Bcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
|
|
1698
1698
|
const wanted = typeof r.activeLayerId === "string" ? r.activeLayerId : null;
|
|
1699
1699
|
const activeLayerId = wanted && _chunkHGTDMKNEcjs.findLayer.call(void 0, layers, wanted) ? wanted : _nullishCoalesce(_optionalChain([_chunkHGTDMKNEcjs.flattenLayers.call(void 0, layers), 'access', _70 => _70.filter, 'call', _71 => _71((l) => l.type === "pixel"), 'access', _72 => _72.pop, 'call', _73 => _73(), 'optionalAccess', _74 => _74.id]), () => ( null));
|
|
1700
1700
|
const wetEvents = Array.isArray(r.wetEvents) ? r.wetEvents.filter((e) => e && typeof e === "object" && Number.isInteger(e.clock) && typeof e.kind === "string") : [];
|
|
@@ -1710,7 +1710,7 @@ async function deserializePaintDocumentAsync(raw, store) {
|
|
|
1710
1710
|
if (!raw || typeof raw !== "object") throw new Error("Not a paint document: expected a JSON object.");
|
|
1711
1711
|
const r = raw;
|
|
1712
1712
|
const problems = [];
|
|
1713
|
-
const assets = await
|
|
1713
|
+
const assets = await _chunk2UR4OX3Bcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
|
|
1714
1714
|
const loaded = deserializePaintDocument({ ...r, assets });
|
|
1715
1715
|
return { ...loaded, problems: [...problems, ...loaded.problems] };
|
|
1716
1716
|
}
|
|
@@ -5409,7 +5409,7 @@ var PaintEditorController = (_class10 = class _PaintEditorController {
|
|
|
5409
5409
|
return next.type === "group" ? { ...next, children: toStandard(next.children) } : next;
|
|
5410
5410
|
});
|
|
5411
5411
|
const layers = toStandard(dried);
|
|
5412
|
-
const result =
|
|
5412
|
+
const result = _chunk2UR4OX3Bcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
|
|
5413
5413
|
for (const [mode, names] of changed) {
|
|
5414
5414
|
const label = _nullishCoalesce(_optionalChain([_chunkCWM3CUUXcjs.BLEND_MODES, 'access', _271 => _271.find, 'call', _272 => _272((m) => m.id === mode), 'optionalAccess', _273 => _273.label]), () => ( mode));
|
|
5415
5415
|
const std = _nullishCoalesce(_optionalChain([_chunkCWM3CUUXcjs.BLEND_MODES, 'access', _274 => _274.find, 'call', _275 => _275((m) => m.id === _chunkCWM3CUUXcjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _276 => _276.label]), () => ( _chunkCWM3CUUXcjs.standardBlendMode.call(void 0, mode)));
|