@pixerate/editor 0.88.0 → 0.90.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/THIRD_PARTY_NOTICES.md +8 -2
- package/dist/agent/index.cjs +28 -28
- package/dist/agent/index.js +3 -3
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.js +16 -16
- package/dist/{chunk-TYOAJDFD.cjs → chunk-66IMF6OD.cjs} +3 -3
- package/dist/{chunk-TYOAJDFD.cjs.map → chunk-66IMF6OD.cjs.map} +1 -1
- package/dist/chunk-D4ZCU72J.js +604 -0
- package/dist/chunk-D4ZCU72J.js.map +1 -0
- package/dist/{chunk-IMSX4IVG.js → chunk-FWEO3HSL.js} +4 -4
- package/dist/chunk-H543GD6T.cjs +1696 -0
- package/dist/chunk-H543GD6T.cjs.map +1 -0
- package/dist/{chunk-ROXFP2LQ.cjs → chunk-HNDSPZ5T.cjs} +4 -4
- package/dist/{chunk-ROXFP2LQ.cjs.map → chunk-HNDSPZ5T.cjs.map} +1 -1
- package/dist/{chunk-MXF4DP2C.cjs → chunk-J3B45HTA.cjs} +74 -74
- package/dist/{chunk-MXF4DP2C.cjs.map → chunk-J3B45HTA.cjs.map} +1 -1
- package/dist/{chunk-GNO5PEMH.js → chunk-OK7T2LHK.js} +2 -2
- package/dist/chunk-P5TPUNBZ.js +1696 -0
- package/dist/chunk-P5TPUNBZ.js.map +1 -0
- package/dist/chunk-PDCITYBT.cjs +416 -0
- package/dist/chunk-PDCITYBT.cjs.map +1 -0
- package/dist/{chunk-RKJDBXTY.cjs → chunk-T4XWVPJK.cjs} +3 -3
- package/dist/{chunk-RKJDBXTY.cjs.map → chunk-T4XWVPJK.cjs.map} +1 -1
- package/dist/{chunk-ZQNDSUZO.js → chunk-UKI64KUD.js} +6 -6
- package/dist/{chunk-SVW2UNUC.js → chunk-WON34HKM.js} +2 -2
- package/dist/chunk-YLB2WVNA.js +416 -0
- package/dist/chunk-YLB2WVNA.js.map +1 -0
- package/dist/chunk-ZOOEX3WT.cjs +604 -0
- package/dist/chunk-ZOOEX3WT.cjs.map +1 -0
- package/dist/extensions/index.cjs +6 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +5 -5
- package/dist/image-editor/index.cjs +7146 -855
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +2050 -532
- package/dist/image-editor/index.d.ts +2050 -532
- package/dist/image-editor/index.js +8134 -1843
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index.cjs +27 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/paint/index.cjs +38 -0
- package/dist/paint/index.cjs.map +1 -0
- package/dist/paint/index.d.cts +167 -0
- package/dist/paint/index.d.ts +167 -0
- package/dist/paint/index.js +38 -0
- package/dist/paint/index.js.map +1 -0
- package/dist/raster/index.cjs +121 -0
- package/dist/raster/index.cjs.map +1 -0
- package/dist/raster/index.d.cts +365 -0
- package/dist/raster/index.d.ts +365 -0
- package/dist/raster/index.js +121 -0
- package/dist/raster/index.js.map +1 -0
- package/dist/serializers/index.cjs +4 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.js +3 -3
- package/dist/surface-fJhkmNQn.d.cts +181 -0
- package/dist/surface-fJhkmNQn.d.ts +181 -0
- package/package.json +13 -2
- package/skills/pixerate-image-editor/SKILL.md +111 -11
- /package/dist/{chunk-IMSX4IVG.js.map → chunk-FWEO3HSL.js.map} +0 -0
- /package/dist/{chunk-GNO5PEMH.js.map → chunk-OK7T2LHK.js.map} +0 -0
- /package/dist/{chunk-ZQNDSUZO.js.map → chunk-UKI64KUD.js.map} +0 -0
- /package/dist/{chunk-SVW2UNUC.js.map → chunk-WON34HKM.js.map} +0 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Photoshop blend modes on straight (non-premultiplied) 0..1 colours.
|
|
3
|
+
*
|
|
4
|
+
* Formulas follow the W3C Compositing / ISO 32000 definitions, with Photoshop's variants where
|
|
5
|
+
* they differ (Soft Light, Vivid / Linear / Pin Light, Hard Mix, Linear Burn / Dodge, Subtract,
|
|
6
|
+
* Divide, Darker / Lighter Color), and the hardened `ClipColor` for the non-separable modes.
|
|
7
|
+
*/
|
|
8
|
+
type BlendMode = 'normal' | 'darken' | 'multiply' | 'color-burn' | 'linear-burn' | 'darker-color' | 'lighten' | 'screen' | 'color-dodge' | 'linear-dodge' | 'lighter-color' | 'overlay' | 'soft-light' | 'hard-light' | 'vivid-light' | 'linear-light' | 'pin-light' | 'hard-mix' | 'difference' | 'exclusion' | 'subtract' | 'divide' | 'hue' | 'saturation' | 'color' | 'luminosity';
|
|
9
|
+
interface BlendModeInfo {
|
|
10
|
+
id: BlendMode;
|
|
11
|
+
label: string;
|
|
12
|
+
/** Canvas `globalCompositeOperation` with the same maths, when there is one. */
|
|
13
|
+
canvas: GlobalCompositeOperation | null;
|
|
14
|
+
}
|
|
15
|
+
/** Every blend mode, in Photoshop's menu order. */
|
|
16
|
+
declare const BLEND_MODES: readonly BlendModeInfo[];
|
|
17
|
+
declare function isBlendMode(value: unknown): value is BlendMode;
|
|
18
|
+
/** A stored blend mode, or `normal` for a missing / unknown value. */
|
|
19
|
+
declare function normalizeBlendMode(value: unknown): BlendMode;
|
|
20
|
+
/**
|
|
21
|
+
* The canvas composite operation that implements `mode` exactly, or `null` when the canvas has
|
|
22
|
+
* none (draw through {@link blendImageData} instead).
|
|
23
|
+
*/
|
|
24
|
+
declare function canvasCompositeOperation(mode: BlendMode | undefined): GlobalCompositeOperation | null;
|
|
25
|
+
/** Whether a mode works on whole colours (hue, saturation, colour, luminosity, darker / lighter colour). */
|
|
26
|
+
declare function isSeparableBlendMode(mode: BlendMode): boolean;
|
|
27
|
+
/** Separable blend function `B(cb, cs)` of one channel. Non-separable modes return `cs`. */
|
|
28
|
+
declare function blendChannel(mode: BlendMode, cb: number, cs: number): number;
|
|
29
|
+
type Rgb = [number, number, number];
|
|
30
|
+
type Rgba = [number, number, number, number];
|
|
31
|
+
/** Luminosity of the non-separable modes (W3C / ISO 32000 weights). */
|
|
32
|
+
declare function blendLum(c: readonly number[]): number;
|
|
33
|
+
/** `c` with its luminosity moved to `l`, clipped back into gamut towards grey (SetLum + ClipColor). */
|
|
34
|
+
declare function setLum(c: readonly number[], l: number): Rgb;
|
|
35
|
+
/** `B(Cb, Cs)` of a whole colour, including the non-separable modes. */
|
|
36
|
+
declare function blendRgb(mode: BlendMode, cb: readonly number[], cs: readonly number[]): Rgb;
|
|
37
|
+
/**
|
|
38
|
+
* Composites a straight-alpha `source` over a straight-alpha `backdrop` (both RGBA in 0..1) with a
|
|
39
|
+
* blend mode and an extra `alpha` (opacity), using the W3C general formula:
|
|
40
|
+
* `αo = αs + αb(1 − αs)`, `αo·Co = (1 − αs)·αb·Cb + (1 − αb)·αs·Cs + αs·αb·B(Cb, Cs)`.
|
|
41
|
+
*/
|
|
42
|
+
declare function blendPixel(mode: BlendMode, backdrop: readonly number[], source: readonly number[], alpha?: number): Rgba;
|
|
43
|
+
/**
|
|
44
|
+
* Blends `source` into `backdrop` in place (8-bit straight RGBA of the same size) with
|
|
45
|
+
* {@link blendPixel}. The scalar fallback for modes the canvas cannot composite.
|
|
46
|
+
*/
|
|
47
|
+
declare function blendImageData(backdrop: ImageData, source: ImageData, mode: BlendMode, alpha?: number): void;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tiled, sparse, copy-on-write pixel storage (photocraft's `Surface`): 256 px square tiles in a
|
|
51
|
+
* map keyed by tile coordinate, a default value for every missing tile, and shared tiles.
|
|
52
|
+
*
|
|
53
|
+
* - Pixels are 8-bit, straight alpha: RGBA for layers (4 channels, the same as `ImageData`), one
|
|
54
|
+
* byte for masks.
|
|
55
|
+
* - A surface is never mutated. A write returns a new surface that shares every tile it did not
|
|
56
|
+
* touch, so undo history and caches can keep old surfaces by reference.
|
|
57
|
+
* - A tile whose pixels all hold one value is a *uniform* tile; one instance per value is shared,
|
|
58
|
+
* so a solid fill or a "hide all" mask costs one tile however large it is.
|
|
59
|
+
* - Coordinates are document pixels and may be negative or past the canvas (layers can extend
|
|
60
|
+
* beyond it, as PSD layer rects do).
|
|
61
|
+
*
|
|
62
|
+
* The module has no DOM dependency, so it runs in Node, tests and workers.
|
|
63
|
+
*/
|
|
64
|
+
/** A pixel rectangle (`x`, `y` of the top-left corner, `width`, `height`). */
|
|
65
|
+
interface RasterRect {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
width: number;
|
|
69
|
+
height: number;
|
|
70
|
+
}
|
|
71
|
+
type CropBox = RasterRect;
|
|
72
|
+
declare const TILE = 256;
|
|
73
|
+
declare const TILE_SHIFT = 8;
|
|
74
|
+
/** A tile coordinate packed into one number (tx, ty in −32768..32767). */
|
|
75
|
+
type TileKey = number;
|
|
76
|
+
declare function tileKey(tx: number, ty: number): TileKey;
|
|
77
|
+
declare function tileCoord(key: TileKey): {
|
|
78
|
+
tx: number;
|
|
79
|
+
ty: number;
|
|
80
|
+
};
|
|
81
|
+
/** A tile's pixel rectangle in document space. */
|
|
82
|
+
declare function tileRect(tx: number, ty: number): CropBox;
|
|
83
|
+
/** One tile: TILE × TILE pixels of `channels` bytes. Never mutate `data` after it is shared. */
|
|
84
|
+
interface Tile {
|
|
85
|
+
readonly data: Uint8ClampedArray;
|
|
86
|
+
/** The packed value of every pixel when the tile is uniform, else `null`. */
|
|
87
|
+
readonly uniform: number | null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* An immutable tiled surface. `channels` is 4 (RGBA) for layers and 1 for masks. Missing tiles
|
|
91
|
+
* hold `defaultValue` in every byte (0: transparent black for layers; 0 or 255 for masks).
|
|
92
|
+
*/
|
|
93
|
+
declare class TiledSurface {
|
|
94
|
+
readonly channels: 1 | 4;
|
|
95
|
+
readonly tiles: ReadonlyMap<TileKey, Tile>;
|
|
96
|
+
readonly defaultValue: number;
|
|
97
|
+
/** Unique per instance: a cache key that changes whenever the pixels can have changed. */
|
|
98
|
+
readonly id: number;
|
|
99
|
+
private boundsCache;
|
|
100
|
+
constructor(channels: 1 | 4, tiles: ReadonlyMap<TileKey, Tile>, defaultValue?: number);
|
|
101
|
+
/** Bounds of the stored (non-default) tiles, tile-aligned; `null` when there are none. */
|
|
102
|
+
get bounds(): CropBox | null;
|
|
103
|
+
get tileCount(): number;
|
|
104
|
+
getTile(tx: number, ty: number): Tile | undefined;
|
|
105
|
+
}
|
|
106
|
+
type PixelSurface = TiledSurface;
|
|
107
|
+
type MaskSurface = TiledSurface;
|
|
108
|
+
/** The shared tile whose every pixel is `value` (packed RGBA, or one byte for masks). */
|
|
109
|
+
declare function uniformTile(channels: 1 | 4, value: number): Tile;
|
|
110
|
+
/** Packs an RGBA colour (0..255 each) the way uniform tiles store it. */
|
|
111
|
+
declare function packRgba(r: number, g: number, b: number, a: number): number;
|
|
112
|
+
/**
|
|
113
|
+
* Turns freshly written tile data into a stored tile: `null` when it equals the surface default
|
|
114
|
+
* (the tile is dropped), the shared uniform tile when it is uniform, else a tile owning `data`.
|
|
115
|
+
*/
|
|
116
|
+
declare function finishTile(data: Uint8ClampedArray, channels: 1 | 4, defaultValue: number): Tile | null;
|
|
117
|
+
/** An empty surface (every pixel is `defaultValue`). */
|
|
118
|
+
declare function emptySurface(channels?: 1 | 4, defaultValue?: number): TiledSurface;
|
|
119
|
+
/** A surface filled with one value over `rect` (packed RGBA for layers, a byte for masks). */
|
|
120
|
+
declare function solidSurface(channels: 1 | 4, rect: CropBox, value: number, defaultValue?: number): TiledSurface;
|
|
121
|
+
/**
|
|
122
|
+
* A surface holding `width × height` interleaved pixels placed at (`x`, `y`) in document space.
|
|
123
|
+
* Tiles equal to the default are left out; uniform tiles are shared.
|
|
124
|
+
*/
|
|
125
|
+
declare function surfaceFromPixels(pixels: {
|
|
126
|
+
width: number;
|
|
127
|
+
height: number;
|
|
128
|
+
data: ArrayLike<number>;
|
|
129
|
+
}, channels?: 1 | 4, x?: number, y?: number, defaultValue?: number): TiledSurface;
|
|
130
|
+
/** Tile coordinates covering `rect`, row by row. */
|
|
131
|
+
declare function tilesInRect(rect: CropBox): Array<{
|
|
132
|
+
tx: number;
|
|
133
|
+
ty: number;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Copies `rect` of the surface into `out` (or a new buffer), `rect.width × rect.height`
|
|
137
|
+
* interleaved pixels; missing tiles read as the default.
|
|
138
|
+
*/
|
|
139
|
+
declare function readRegion(surface: TiledSurface, rect: CropBox, out?: Uint8ClampedArray): Uint8ClampedArray;
|
|
140
|
+
/** One pixel's bytes (RGBA or the mask byte). */
|
|
141
|
+
declare function readPixel(surface: TiledSurface, x: number, y: number): number[];
|
|
142
|
+
/**
|
|
143
|
+
* Exact bounds of the pixels that differ from the default (for a layer: non-transparent), or
|
|
144
|
+
* `null`. Scans the stored tiles; uniform tiles count whole.
|
|
145
|
+
*/
|
|
146
|
+
declare function contentBounds(surface: TiledSurface): CropBox | null;
|
|
147
|
+
/**
|
|
148
|
+
* A private, mutable copy of the tiles a write touches. `tile(tx, ty)` copies the tile on first
|
|
149
|
+
* use; `commit()` returns a new surface sharing every untouched tile. Use it for strokes:
|
|
150
|
+
* edit through the draft while the pointer moves and commit once at pointer-up.
|
|
151
|
+
*/
|
|
152
|
+
declare class SurfaceDraft {
|
|
153
|
+
readonly base: TiledSurface;
|
|
154
|
+
private readonly copies;
|
|
155
|
+
constructor(base: TiledSurface);
|
|
156
|
+
get channels(): 1 | 4;
|
|
157
|
+
/** The writable data of tile (tx, ty), copied from the base on first use. */
|
|
158
|
+
tile(tx: number, ty: number): Uint8ClampedArray;
|
|
159
|
+
/** Replaces a whole tile with `data` (owned by the draft from now on). */
|
|
160
|
+
replace(tx: number, ty: number, data: Uint8ClampedArray): void;
|
|
161
|
+
/** The current data of a tile without copying it (base tile, draft copy or `null` for default). */
|
|
162
|
+
peek(tx: number, ty: number): Uint8ClampedArray | null;
|
|
163
|
+
/** Keys of the tiles changed so far. */
|
|
164
|
+
get touched(): TileKey[];
|
|
165
|
+
/** A new surface with the draft's tiles; the draft must not be used afterwards. */
|
|
166
|
+
commit(): TiledSurface;
|
|
167
|
+
}
|
|
168
|
+
/** Writes `rect.width × rect.height` interleaved pixels at `rect`; returns the new surface. */
|
|
169
|
+
declare function writeRegion(surface: TiledSurface, rect: CropBox, data: ArrayLike<number>): TiledSurface;
|
|
170
|
+
/** Fills `rect` with one pixel value (`bytes`, `channels` long). */
|
|
171
|
+
declare function fillRect(surface: TiledSurface, rect: CropBox, bytes: ArrayLike<number>): TiledSurface;
|
|
172
|
+
/** A copy of the surface moved by an integer offset (re-tiles; layers normally move by `offset`). */
|
|
173
|
+
declare function translateSurface(surface: TiledSurface, dx: number, dy: number): TiledSurface;
|
|
174
|
+
/** Bytes held by a surface's non-uniform tiles (uniform tiles are shared and nearly free). */
|
|
175
|
+
declare function surfaceBytes(surface: TiledSurface): number;
|
|
176
|
+
/** Adds every non-uniform tile of `surface` to `into` (for counting bytes shared across states). */
|
|
177
|
+
declare function collectTiles(surface: TiledSurface, into: Set<Tile>): void;
|
|
178
|
+
/** True when two surfaces hold the same pixels (cheap when they share tiles). */
|
|
179
|
+
declare function surfacesEqual(a: TiledSurface, b: TiledSurface): boolean;
|
|
180
|
+
|
|
181
|
+
export { solidSurface as A, type BlendMode as B, surfaceBytes as C, surfaceFromPixels as D, surfacesEqual as E, tileCoord as F, tileKey as G, tileRect as H, tilesInRect as I, translateSurface as J, uniformTile as K, writeRegion as L, type MaskSurface as M, type PixelSurface as P, type RasterRect as R, SurfaceDraft as S, TiledSurface as T, BLEND_MODES as a, type BlendModeInfo as b, type Rgb as c, type Rgba as d, TILE as e, TILE_SHIFT as f, type Tile as g, type TileKey as h, blendChannel as i, blendImageData as j, blendLum as k, blendPixel as l, blendRgb as m, canvasCompositeOperation as n, collectTiles as o, contentBounds as p, emptySurface as q, fillRect as r, finishTile as s, isBlendMode as t, isSeparableBlendMode as u, normalizeBlendMode as v, packRgba as w, readPixel as x, readRegion as y, setLum as z };
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Photoshop blend modes on straight (non-premultiplied) 0..1 colours.
|
|
3
|
+
*
|
|
4
|
+
* Formulas follow the W3C Compositing / ISO 32000 definitions, with Photoshop's variants where
|
|
5
|
+
* they differ (Soft Light, Vivid / Linear / Pin Light, Hard Mix, Linear Burn / Dodge, Subtract,
|
|
6
|
+
* Divide, Darker / Lighter Color), and the hardened `ClipColor` for the non-separable modes.
|
|
7
|
+
*/
|
|
8
|
+
type BlendMode = 'normal' | 'darken' | 'multiply' | 'color-burn' | 'linear-burn' | 'darker-color' | 'lighten' | 'screen' | 'color-dodge' | 'linear-dodge' | 'lighter-color' | 'overlay' | 'soft-light' | 'hard-light' | 'vivid-light' | 'linear-light' | 'pin-light' | 'hard-mix' | 'difference' | 'exclusion' | 'subtract' | 'divide' | 'hue' | 'saturation' | 'color' | 'luminosity';
|
|
9
|
+
interface BlendModeInfo {
|
|
10
|
+
id: BlendMode;
|
|
11
|
+
label: string;
|
|
12
|
+
/** Canvas `globalCompositeOperation` with the same maths, when there is one. */
|
|
13
|
+
canvas: GlobalCompositeOperation | null;
|
|
14
|
+
}
|
|
15
|
+
/** Every blend mode, in Photoshop's menu order. */
|
|
16
|
+
declare const BLEND_MODES: readonly BlendModeInfo[];
|
|
17
|
+
declare function isBlendMode(value: unknown): value is BlendMode;
|
|
18
|
+
/** A stored blend mode, or `normal` for a missing / unknown value. */
|
|
19
|
+
declare function normalizeBlendMode(value: unknown): BlendMode;
|
|
20
|
+
/**
|
|
21
|
+
* The canvas composite operation that implements `mode` exactly, or `null` when the canvas has
|
|
22
|
+
* none (draw through {@link blendImageData} instead).
|
|
23
|
+
*/
|
|
24
|
+
declare function canvasCompositeOperation(mode: BlendMode | undefined): GlobalCompositeOperation | null;
|
|
25
|
+
/** Whether a mode works on whole colours (hue, saturation, colour, luminosity, darker / lighter colour). */
|
|
26
|
+
declare function isSeparableBlendMode(mode: BlendMode): boolean;
|
|
27
|
+
/** Separable blend function `B(cb, cs)` of one channel. Non-separable modes return `cs`. */
|
|
28
|
+
declare function blendChannel(mode: BlendMode, cb: number, cs: number): number;
|
|
29
|
+
type Rgb = [number, number, number];
|
|
30
|
+
type Rgba = [number, number, number, number];
|
|
31
|
+
/** Luminosity of the non-separable modes (W3C / ISO 32000 weights). */
|
|
32
|
+
declare function blendLum(c: readonly number[]): number;
|
|
33
|
+
/** `c` with its luminosity moved to `l`, clipped back into gamut towards grey (SetLum + ClipColor). */
|
|
34
|
+
declare function setLum(c: readonly number[], l: number): Rgb;
|
|
35
|
+
/** `B(Cb, Cs)` of a whole colour, including the non-separable modes. */
|
|
36
|
+
declare function blendRgb(mode: BlendMode, cb: readonly number[], cs: readonly number[]): Rgb;
|
|
37
|
+
/**
|
|
38
|
+
* Composites a straight-alpha `source` over a straight-alpha `backdrop` (both RGBA in 0..1) with a
|
|
39
|
+
* blend mode and an extra `alpha` (opacity), using the W3C general formula:
|
|
40
|
+
* `αo = αs + αb(1 − αs)`, `αo·Co = (1 − αs)·αb·Cb + (1 − αb)·αs·Cs + αs·αb·B(Cb, Cs)`.
|
|
41
|
+
*/
|
|
42
|
+
declare function blendPixel(mode: BlendMode, backdrop: readonly number[], source: readonly number[], alpha?: number): Rgba;
|
|
43
|
+
/**
|
|
44
|
+
* Blends `source` into `backdrop` in place (8-bit straight RGBA of the same size) with
|
|
45
|
+
* {@link blendPixel}. The scalar fallback for modes the canvas cannot composite.
|
|
46
|
+
*/
|
|
47
|
+
declare function blendImageData(backdrop: ImageData, source: ImageData, mode: BlendMode, alpha?: number): void;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tiled, sparse, copy-on-write pixel storage (photocraft's `Surface`): 256 px square tiles in a
|
|
51
|
+
* map keyed by tile coordinate, a default value for every missing tile, and shared tiles.
|
|
52
|
+
*
|
|
53
|
+
* - Pixels are 8-bit, straight alpha: RGBA for layers (4 channels, the same as `ImageData`), one
|
|
54
|
+
* byte for masks.
|
|
55
|
+
* - A surface is never mutated. A write returns a new surface that shares every tile it did not
|
|
56
|
+
* touch, so undo history and caches can keep old surfaces by reference.
|
|
57
|
+
* - A tile whose pixels all hold one value is a *uniform* tile; one instance per value is shared,
|
|
58
|
+
* so a solid fill or a "hide all" mask costs one tile however large it is.
|
|
59
|
+
* - Coordinates are document pixels and may be negative or past the canvas (layers can extend
|
|
60
|
+
* beyond it, as PSD layer rects do).
|
|
61
|
+
*
|
|
62
|
+
* The module has no DOM dependency, so it runs in Node, tests and workers.
|
|
63
|
+
*/
|
|
64
|
+
/** A pixel rectangle (`x`, `y` of the top-left corner, `width`, `height`). */
|
|
65
|
+
interface RasterRect {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
width: number;
|
|
69
|
+
height: number;
|
|
70
|
+
}
|
|
71
|
+
type CropBox = RasterRect;
|
|
72
|
+
declare const TILE = 256;
|
|
73
|
+
declare const TILE_SHIFT = 8;
|
|
74
|
+
/** A tile coordinate packed into one number (tx, ty in −32768..32767). */
|
|
75
|
+
type TileKey = number;
|
|
76
|
+
declare function tileKey(tx: number, ty: number): TileKey;
|
|
77
|
+
declare function tileCoord(key: TileKey): {
|
|
78
|
+
tx: number;
|
|
79
|
+
ty: number;
|
|
80
|
+
};
|
|
81
|
+
/** A tile's pixel rectangle in document space. */
|
|
82
|
+
declare function tileRect(tx: number, ty: number): CropBox;
|
|
83
|
+
/** One tile: TILE × TILE pixels of `channels` bytes. Never mutate `data` after it is shared. */
|
|
84
|
+
interface Tile {
|
|
85
|
+
readonly data: Uint8ClampedArray;
|
|
86
|
+
/** The packed value of every pixel when the tile is uniform, else `null`. */
|
|
87
|
+
readonly uniform: number | null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* An immutable tiled surface. `channels` is 4 (RGBA) for layers and 1 for masks. Missing tiles
|
|
91
|
+
* hold `defaultValue` in every byte (0: transparent black for layers; 0 or 255 for masks).
|
|
92
|
+
*/
|
|
93
|
+
declare class TiledSurface {
|
|
94
|
+
readonly channels: 1 | 4;
|
|
95
|
+
readonly tiles: ReadonlyMap<TileKey, Tile>;
|
|
96
|
+
readonly defaultValue: number;
|
|
97
|
+
/** Unique per instance: a cache key that changes whenever the pixels can have changed. */
|
|
98
|
+
readonly id: number;
|
|
99
|
+
private boundsCache;
|
|
100
|
+
constructor(channels: 1 | 4, tiles: ReadonlyMap<TileKey, Tile>, defaultValue?: number);
|
|
101
|
+
/** Bounds of the stored (non-default) tiles, tile-aligned; `null` when there are none. */
|
|
102
|
+
get bounds(): CropBox | null;
|
|
103
|
+
get tileCount(): number;
|
|
104
|
+
getTile(tx: number, ty: number): Tile | undefined;
|
|
105
|
+
}
|
|
106
|
+
type PixelSurface = TiledSurface;
|
|
107
|
+
type MaskSurface = TiledSurface;
|
|
108
|
+
/** The shared tile whose every pixel is `value` (packed RGBA, or one byte for masks). */
|
|
109
|
+
declare function uniformTile(channels: 1 | 4, value: number): Tile;
|
|
110
|
+
/** Packs an RGBA colour (0..255 each) the way uniform tiles store it. */
|
|
111
|
+
declare function packRgba(r: number, g: number, b: number, a: number): number;
|
|
112
|
+
/**
|
|
113
|
+
* Turns freshly written tile data into a stored tile: `null` when it equals the surface default
|
|
114
|
+
* (the tile is dropped), the shared uniform tile when it is uniform, else a tile owning `data`.
|
|
115
|
+
*/
|
|
116
|
+
declare function finishTile(data: Uint8ClampedArray, channels: 1 | 4, defaultValue: number): Tile | null;
|
|
117
|
+
/** An empty surface (every pixel is `defaultValue`). */
|
|
118
|
+
declare function emptySurface(channels?: 1 | 4, defaultValue?: number): TiledSurface;
|
|
119
|
+
/** A surface filled with one value over `rect` (packed RGBA for layers, a byte for masks). */
|
|
120
|
+
declare function solidSurface(channels: 1 | 4, rect: CropBox, value: number, defaultValue?: number): TiledSurface;
|
|
121
|
+
/**
|
|
122
|
+
* A surface holding `width × height` interleaved pixels placed at (`x`, `y`) in document space.
|
|
123
|
+
* Tiles equal to the default are left out; uniform tiles are shared.
|
|
124
|
+
*/
|
|
125
|
+
declare function surfaceFromPixels(pixels: {
|
|
126
|
+
width: number;
|
|
127
|
+
height: number;
|
|
128
|
+
data: ArrayLike<number>;
|
|
129
|
+
}, channels?: 1 | 4, x?: number, y?: number, defaultValue?: number): TiledSurface;
|
|
130
|
+
/** Tile coordinates covering `rect`, row by row. */
|
|
131
|
+
declare function tilesInRect(rect: CropBox): Array<{
|
|
132
|
+
tx: number;
|
|
133
|
+
ty: number;
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Copies `rect` of the surface into `out` (or a new buffer), `rect.width × rect.height`
|
|
137
|
+
* interleaved pixels; missing tiles read as the default.
|
|
138
|
+
*/
|
|
139
|
+
declare function readRegion(surface: TiledSurface, rect: CropBox, out?: Uint8ClampedArray): Uint8ClampedArray;
|
|
140
|
+
/** One pixel's bytes (RGBA or the mask byte). */
|
|
141
|
+
declare function readPixel(surface: TiledSurface, x: number, y: number): number[];
|
|
142
|
+
/**
|
|
143
|
+
* Exact bounds of the pixels that differ from the default (for a layer: non-transparent), or
|
|
144
|
+
* `null`. Scans the stored tiles; uniform tiles count whole.
|
|
145
|
+
*/
|
|
146
|
+
declare function contentBounds(surface: TiledSurface): CropBox | null;
|
|
147
|
+
/**
|
|
148
|
+
* A private, mutable copy of the tiles a write touches. `tile(tx, ty)` copies the tile on first
|
|
149
|
+
* use; `commit()` returns a new surface sharing every untouched tile. Use it for strokes:
|
|
150
|
+
* edit through the draft while the pointer moves and commit once at pointer-up.
|
|
151
|
+
*/
|
|
152
|
+
declare class SurfaceDraft {
|
|
153
|
+
readonly base: TiledSurface;
|
|
154
|
+
private readonly copies;
|
|
155
|
+
constructor(base: TiledSurface);
|
|
156
|
+
get channels(): 1 | 4;
|
|
157
|
+
/** The writable data of tile (tx, ty), copied from the base on first use. */
|
|
158
|
+
tile(tx: number, ty: number): Uint8ClampedArray;
|
|
159
|
+
/** Replaces a whole tile with `data` (owned by the draft from now on). */
|
|
160
|
+
replace(tx: number, ty: number, data: Uint8ClampedArray): void;
|
|
161
|
+
/** The current data of a tile without copying it (base tile, draft copy or `null` for default). */
|
|
162
|
+
peek(tx: number, ty: number): Uint8ClampedArray | null;
|
|
163
|
+
/** Keys of the tiles changed so far. */
|
|
164
|
+
get touched(): TileKey[];
|
|
165
|
+
/** A new surface with the draft's tiles; the draft must not be used afterwards. */
|
|
166
|
+
commit(): TiledSurface;
|
|
167
|
+
}
|
|
168
|
+
/** Writes `rect.width × rect.height` interleaved pixels at `rect`; returns the new surface. */
|
|
169
|
+
declare function writeRegion(surface: TiledSurface, rect: CropBox, data: ArrayLike<number>): TiledSurface;
|
|
170
|
+
/** Fills `rect` with one pixel value (`bytes`, `channels` long). */
|
|
171
|
+
declare function fillRect(surface: TiledSurface, rect: CropBox, bytes: ArrayLike<number>): TiledSurface;
|
|
172
|
+
/** A copy of the surface moved by an integer offset (re-tiles; layers normally move by `offset`). */
|
|
173
|
+
declare function translateSurface(surface: TiledSurface, dx: number, dy: number): TiledSurface;
|
|
174
|
+
/** Bytes held by a surface's non-uniform tiles (uniform tiles are shared and nearly free). */
|
|
175
|
+
declare function surfaceBytes(surface: TiledSurface): number;
|
|
176
|
+
/** Adds every non-uniform tile of `surface` to `into` (for counting bytes shared across states). */
|
|
177
|
+
declare function collectTiles(surface: TiledSurface, into: Set<Tile>): void;
|
|
178
|
+
/** True when two surfaces hold the same pixels (cheap when they share tiles). */
|
|
179
|
+
declare function surfacesEqual(a: TiledSurface, b: TiledSurface): boolean;
|
|
180
|
+
|
|
181
|
+
export { solidSurface as A, type BlendMode as B, surfaceBytes as C, surfaceFromPixels as D, surfacesEqual as E, tileCoord as F, tileKey as G, tileRect as H, tilesInRect as I, translateSurface as J, uniformTile as K, writeRegion as L, type MaskSurface as M, type PixelSurface as P, type RasterRect as R, SurfaceDraft as S, TiledSurface as T, BLEND_MODES as a, type BlendModeInfo as b, type Rgb as c, type Rgba as d, TILE as e, TILE_SHIFT as f, type Tile as g, type TileKey as h, blendChannel as i, blendImageData as j, blendLum as k, blendPixel as l, blendRgb as m, canvasCompositeOperation as n, collectTiles as o, contentBounds as p, emptySurface as q, fillRect as r, finishTile as s, isBlendMode as t, isSeparableBlendMode as u, normalizeBlendMode as v, packRgba as w, readPixel as x, readRegion as y, setLum as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.0",
|
|
4
4
|
"description": "UI-agnostic rich text and media editing core library",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -74,6 +74,16 @@
|
|
|
74
74
|
"import": "./dist/tools/index.js",
|
|
75
75
|
"require": "./dist/tools/index.cjs"
|
|
76
76
|
},
|
|
77
|
+
"./raster": {
|
|
78
|
+
"types": "./dist/raster/index.d.ts",
|
|
79
|
+
"import": "./dist/raster/index.js",
|
|
80
|
+
"require": "./dist/raster/index.cjs"
|
|
81
|
+
},
|
|
82
|
+
"./paint": {
|
|
83
|
+
"types": "./dist/paint/index.d.ts",
|
|
84
|
+
"import": "./dist/paint/index.js",
|
|
85
|
+
"require": "./dist/paint/index.cjs"
|
|
86
|
+
},
|
|
77
87
|
"./image-editor": {
|
|
78
88
|
"types": "./dist/image-editor/index.d.ts",
|
|
79
89
|
"import": "./dist/image-editor/index.js",
|
|
@@ -197,6 +207,7 @@
|
|
|
197
207
|
"scripts": {
|
|
198
208
|
"build": "tsup",
|
|
199
209
|
"typecheck": "tsc --noEmit",
|
|
200
|
-
"test": "vitest run"
|
|
210
|
+
"test": "vitest run",
|
|
211
|
+
"test:corpus": "node scripts/fetch-psd-corpus.mjs && PSD_CORPUS=1 vitest run tests/psd-corpus.test.ts"
|
|
201
212
|
}
|
|
202
213
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pixerate-image-editor
|
|
3
|
-
description: How to colour-correct and retouch a photo in the Pixerate image editor through its agent tools - base adjustments (exposure, contrast, white balance temperature and tint, saturation, blur), non-destructive adjustment layers (curves, levels, hue/saturation, vibrance, color balance, black and white, photo filter, invert, posterize, threshold) with blend modes and opacity, selections (
|
|
3
|
+
description: How to colour-correct, composite and retouch a photo in the Pixerate image editor through its agent tools - layers (pixel, adjustment, group and annotation layers with masks, clipping, blend modes, opacity and fill; merge, flatten, layer via copy, develop and blur filters), base adjustments (exposure, contrast, white balance temperature and tint, saturation, blur), non-destructive adjustment layers (curves, levels, hue/saturation, vibrance, color balance, black and white, photo filter, invert, posterize, threshold) with blend modes and opacity, selections (wand, color range, grow, shrink, feather, refine edge) and layer masks, crop, rotate and flip, undo, PSD open and save, the histogram, and PNG previews. Use when asked to edit, grade, correct, brighten, warm up, desaturate, crop, straighten or mask an image, or to open or save a PSD, or when get_adjustments, set_adjustments, add_adjustment_layer, list_layers, paint_stroke, select_by_color, import_psd or render_preview are available.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Image editor
|
|
7
7
|
|
|
8
|
-
The image editor holds
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
The image editor holds a layer stack over a canvas the size of the source image. Opening an
|
|
9
|
+
image gives one pixel layer, **Background**, whose pixels are the source and whose `develop`
|
|
10
|
+
filter holds the base adjustments; adjustment layers sit above it (bottom to top, each through
|
|
11
|
+
its mask, opacity and blend mode) and every annotation is its own layer on top. `list_layers`
|
|
12
|
+
shows the whole stack. Edits never touch the source pixels. The base controls edit the develop
|
|
13
|
+
filter of the active pixel layer (the Background unless another pixel layer is active); base
|
|
14
|
+
`blur` blurs that layer (or, in documents saved before layers with blur and adjustment layers,
|
|
15
|
+
the output after the layers), and base `opacity` is the output's alpha. Also an optional
|
|
16
|
+
selection and crop/rotate/flip. See `pixerate-agent-tools` for errors, namespacing and command
|
|
17
|
+
batching.
|
|
13
18
|
|
|
14
19
|
- **Control ids** for base adjustments: `brightness`, `contrast`, `exposure`, `temperature`,
|
|
15
20
|
`tint`, `saturation` (all -100..100, default 0), `blur` (0..30 px) and `opacity` (0..1).
|
|
@@ -43,6 +48,54 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
43
48
|
preview; read the `applied` and `clamped` lists that writes return. `undo_image` takes back a
|
|
44
49
|
step that made things worse.
|
|
45
50
|
|
|
51
|
+
### Working with layers
|
|
52
|
+
|
|
53
|
+
1. **Read the stack** with `list_layers`. A photo opened in the editor has only the Background;
|
|
54
|
+
documents made elsewhere may have pixel layers, groups and masks. Note which layer is
|
|
55
|
+
`active`: `set_adjustments` base controls edit the active pixel layer's `develop` filter.
|
|
56
|
+
2. **New content goes on its own layer**: `add_pixel_layer` (empty, a colour fill or a PNG at
|
|
57
|
+
`x`, `y`), `layer_via_copy` to lift a selection onto a layer, `duplicate_layer` before a
|
|
58
|
+
risky change. Name layers by intent.
|
|
59
|
+
3. **Shape layers non-destructively**: masks (`add_layer_mask` from the selection, or
|
|
60
|
+
reveal-all / hide-all, softened with `density` / `feather`) instead of deleting pixels;
|
|
61
|
+
`clipped: true` (via `set_layer_properties`) to limit a layer or adjustment to the layer below;
|
|
62
|
+
`blend`, `opacity` and `fillOpacity` instead of baking.
|
|
63
|
+
4. **Organise** with `group_layers` (pass-through by default: blend modes inside still see the
|
|
64
|
+
layers below; give the group a blend mode to isolate them), `move_layer` and
|
|
65
|
+
`offset_layer`.
|
|
66
|
+
5. **Bake only when asked** (`merge_down`, `merge_visible`, `flatten_image`, `apply_layer_mask`):
|
|
67
|
+
they are destructive. `render_preview` with `layerId` shows one layer alone.
|
|
68
|
+
|
|
69
|
+
### Painting
|
|
70
|
+
|
|
71
|
+
1. **Paint on its own layer** (`add_pixel_layer`), never on the Background, unless asked: a
|
|
72
|
+
layer can be faded, masked, blended or deleted later. Painting the Background bakes it.
|
|
73
|
+
2. **Retouch through masks**: to hide part of a layer, add a mask and paint black on it
|
|
74
|
+
(`paint_stroke` with `channel: "mask"`, `color: "#000000"`); paint white to bring it back.
|
|
75
|
+
A soft brush (`hardness` 0..0.3) gives soft transitions.
|
|
76
|
+
3. **One stroke is one call and one undo step.** Send the path's points in order (a few points
|
|
77
|
+
per brush diameter is enough; dabs are spaced by `brush.spacing`). Use `opacity` for the
|
|
78
|
+
overall strength and `flow` for gradual build-up within the stroke.
|
|
79
|
+
4. **Limit paint with a selection** (select first, then `paint_stroke` / `fill_pixels`), or lock
|
|
80
|
+
transparency (`set_layer_properties` `locks.transparency`) to paint only where the layer
|
|
81
|
+
already has pixels.
|
|
82
|
+
|
|
83
|
+
### Photoshop files
|
|
84
|
+
|
|
85
|
+
1. **Inspect before opening.** `inspect_psd` lists every layer with the type it would become and
|
|
86
|
+
a status: `mapped` (held exactly), `approximated` (type, smart-object, fill and shape layers
|
|
87
|
+
come in as their stored pixels; layer effects are not rendered; Dissolve becomes Normal) or
|
|
88
|
+
`dropped` (Brightness/Contrast, Exposure, Channel Mixer, Gradient Map, Selective Color and
|
|
89
|
+
Color Lookup adjustments). Tell the user what would be lost before `import_psd` when anything
|
|
90
|
+
is not `mapped`.
|
|
91
|
+
2. **`import_psd` replaces the document** (one undo step). CMYK, Lab, grayscale, indexed, 16-bit
|
|
92
|
+
and 32-bit files are converted to 8-bit RGB, and ICC profiles are ignored; the warnings say
|
|
93
|
+
so. Afterwards work with `list_layers` as usual: there is no Background source image, every
|
|
94
|
+
pixel comes from the layers.
|
|
95
|
+
3. **`export_psd` writes the stack as it is**, in the source frame: crop and rotation are not
|
|
96
|
+
applied, layer filters and depth masks are baked, annotation layers are rasterized (only in
|
|
97
|
+
a browser). Read its `warnings` and pass them on. The file comes back as base64 (`data`).
|
|
98
|
+
|
|
46
99
|
## Best practices
|
|
47
100
|
|
|
48
101
|
- **Non-destructive first.** Prefer an adjustment layer over pushing a base slider to its
|
|
@@ -77,6 +130,10 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
77
130
|
levels layer without shifting hue or saturation; `color` applies a tint without changing
|
|
78
131
|
brightness; `soft-light` / `overlay` for gentle contrast.
|
|
79
132
|
- **Name layers** (`name`) by intent ("Sky darken", "Warm skin"), so the stack stays readable.
|
|
133
|
+
- **Merge Down only onto an opaque, 100 % lower layer** when the result must look the same:
|
|
134
|
+
the merged layer keeps the lower layer's opacity and blend, so a faded lower layer fades the
|
|
135
|
+
merged content too, and a blend mode on the upper layer stops seeing what was below the lower
|
|
136
|
+
layer where the lower one is transparent. Check with `render_preview` after merging.
|
|
80
137
|
|
|
81
138
|
## Tool reference
|
|
82
139
|
|
|
@@ -87,9 +144,12 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
87
144
|
| `get_adjustments` | Read the current state: `values` by control id, `process` version (1 or 2), and `layers` bottom to top with id, kind, params, opacity, blend, enabled and mask. Call first, and to find layer ids. |
|
|
88
145
|
| `list_controls` | Get every base control (id, label, section, min, max, step, default, `requiresProcess`), each layer kind's settable controls (plus `opacity`), the blend mode ids and the `layers.<layerId>.<controlId>` path format. Call once per session before writing. |
|
|
89
146
|
| `get_selection` | Read the active selection: `empty`, `bounds` (natural pixels), `area` in pixels, `percent` of the image and a description. Check it after every select or modify step and before masking: a wand that covered 90% of the image is the wrong selection. |
|
|
147
|
+
| `list_layers` | Read the layer stack bottom to top as a tree: id, type (`pixel`, `adjustment`, `group`, `annotation`), name, document `bounds` of pixel layers, visibility, opacity, `fillOpacity`, blend, `clipped`, locks, a `mask` summary, pixel-layer `filters` (the Background's `develop` filter is the base adjustments), adjustment `kind` and `params`, group `children`, and which layer is `active`. Call it to understand a document with more than the Background before changing it; adjustment layer ids work with every adjustment-layer tool. |
|
|
90
148
|
| `get_image_info` | Read the geometry: source `image` size, `crop` box (source pixels, or null), `rotation` degrees, `flipH` / `flipV`, the `output` size after crop and rotation, the layer count and `canUndo` / `canRedo`. Call before cropping or selecting so coordinates are right. |
|
|
91
149
|
| `get_histogram` | Judge exposure numerically: per channel and luminance `mean`, `p1` and `p99` (0..255), and `clipping` percent at pure black and white per channel. Reads the last render, so call `render_preview` first. `bins` (16, 32, 64 or 256) adds the counts. Cheaper than another image for checking a tonal change. |
|
|
92
|
-
| `render_preview` | See the edited image (crop, adjustments, layers, annotations) as a PNG. `maxSide` 16..2048 px, default 512. Images cost tokens: render after a group of edits, use 512 for overall look and 1024+ to judge clipping, edges and masks. Fails in environments without rendering. |
|
|
150
|
+
| `render_preview` | See the edited image (crop, adjustments, layers, annotations) as a PNG, or with `layerId` one layer alone. `maxSide` 16..2048 px, default 512. Images cost tokens: render after a group of edits, use 512 for overall look and 1024+ to judge clipping, edges and masks. Fails in environments without rendering. |
|
|
151
|
+
| `inspect_psd` | Read a PSD / PSB (`data` base64) without opening it: size, `depth`, `colorMode`, `resolution`, every layer bottom first with `psdKind`, the `type` it would become and `status` (`mapped`, `approximated`, `dropped`) with `notes`, and the import `warnings`. Call before `import_psd`. |
|
|
152
|
+
| `export_psd` | Save the document as an 8-bit RGB PSD (PSB above 30000 px) with layers, groups, adjustment layers and masks. Returns `data` (base64), `size` in bytes, `psb` and `warnings` (crop or rotation not applied, filters and depth masks baked, annotations rasterized or left out). Changes nothing. |
|
|
93
153
|
| `capture_view` | Inspect detail in natural image pixels (the space of `get_selection` and `select_region`, before crop and rotation): `region` `content` (whole image, default), `selection` or `rect`; small regions are enlarged up to 8× with hard pixel edges, so a 64 px rect shows real pixels. `includeOverlays: true` shades unselected pixels and outlines the crop: use it to check a mask's edge before `apply_selection_as_mask`. Returns the `rect` and `scale`. Use `render_preview` for the final framed result. |
|
|
94
154
|
|
|
95
155
|
### Write
|
|
@@ -97,9 +157,9 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
97
157
|
| Tool | Use it to |
|
|
98
158
|
| --- | --- |
|
|
99
159
|
| `set_adjustments` | Set base controls and existing layer parameters in one undo step: `values` is a map of control id or `layers.<layerId>.<controlId>` path to number; `label` is echoed. Values are clamped to range; returns `applied` and `clamped` (and a note when `tint` is set on a process 1 edit). Unknown ids fail and change nothing. Cannot set curves points, booleans or colours. |
|
|
100
|
-
| `add_adjustment_layer` | Add a layer
|
|
160
|
+
| `add_adjustment_layer` | Add a layer above the other adjustment layers and below annotations (or directly above `aboveId`, or at the top of group `parentId`): `kind` (curves, levels, hueSaturation, vibrance, colorBalance, blackWhite, photoFilter, invert, posterize, threshold), optional `params` merged with neutral defaults, `opacity` 0..1 (default 1), `blend` (default normal), `name`. Returns the layer (with its new id) and its stack index. The only tool that sets curves points, `colorize`, `preserveLuminosity`, a black and white `tint` or a photo filter `color`. |
|
|
101
161
|
| `select_region` | Select by `shape`: `rect` or `ellipse` (with `x`, `y`, `width`, `height` in natural pixels), `all`, `none` or `invert`. `mode` replace (default), add, subtract or intersect; `feather` px (default 0). Returns the selection summary. |
|
|
102
|
-
| `select_by_color` | Magic wand from a seed (`x`, `y`, `tolerance` 0..255 default 32, `contiguous` default true) or Color Range for every similar pixel (`color` "#rrggbb", `fuzziness` 0..200 default 40). `sampleMerged: true` samples the adjusted image instead of the source. `mode` as above. Use it for skies, backdrops and flat-coloured objects. |
|
|
162
|
+
| `select_by_color` | Magic wand from a seed (`x`, `y`, `tolerance` 0..255 default 32, `contiguous` default true) or Color Range for every similar pixel (`color` "#rrggbb", `fuzziness` 0..200 default 40). `sampleMerged: true` samples the adjusted image instead of the source; `sample: "active-layer"` samples only the active pixel layer (pick it with `set_active_layer`) instead of the whole image. `mode` as above. Use it for skies, backdrops and flat-coloured objects. |
|
|
103
163
|
| `modify_selection` | Grow, shrink, feather, border or smooth the selection (`op`) by `radius` px (0..500). Use shrink then feather to keep an adjustment from bleeding over an edge; smooth to remove wand speckle. Fails when nothing is selected. |
|
|
104
164
|
| `refine_selection` | Refine Edge: snap the selection edge to image edges within `radius` px (0..64, default 8), with `smartRadius`, `smooth` 0..100, `feather` px, `contrast` 0..100 and `shiftEdge` -100..100 %. Use on hair, fur, foliage or after loading a coarse AI mask. |
|
|
105
165
|
| `update_adjustment_layer` | Change a layer's `enabled` (false hides it without deleting), `blend`, `opacity` (0..1) or `name` in one undo step. Returns the layer. Its parameters change with `set_adjustments`. Toggle `enabled` to compare a layer's effect before and after. |
|
|
@@ -111,6 +171,32 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
111
171
|
| `flip_image` | Mirror the image `horizontal` (left-right) or `vertical`. Avoid flipping images with text, logos or recognisable places. |
|
|
112
172
|
| `undo_image` | Undo the last edit (one tool call is one step). Returns `canUndo` / `canRedo`. |
|
|
113
173
|
| `redo_image` | Redo the edit you just undid. |
|
|
174
|
+
| `set_active_layer` | Make a layer active (`layerId`) and choose the paint `target` (`pixels` or `mask`). Base sliders then edit that pixel layer's develop filter. Not an undo step. Returns the layer. |
|
|
175
|
+
| `add_pixel_layer` | Add a pixel layer above the active layer (or at `aboveId` / `belowId` / `parentId` / `index`): empty, `color` `#rrggbb[aa]` over the canvas, or `image` (PNG data URL or base64; other formats only in a browser) at `x`, `y`; with `name`, `opacity`, `blend`. Returns the layer with its id and bounds. |
|
|
176
|
+
| `duplicate_layer` | Copy a layer (groups with their children) directly above it, e.g. before a destructive change. Returns the copy. |
|
|
177
|
+
| `remove_layer` | Delete a layer or group (refused when locked). Prefer hiding with `set_layer_properties`. Returns the remaining top-level ids. |
|
|
178
|
+
| `move_layer` | Reorder: give exactly one of `aboveId`, `belowId`, `parentId` (into a group, optional `index`) or `index` alone. Returns the new `parentId` and `index`. |
|
|
179
|
+
| `set_layer_properties` | Change `name`, `visible`, `opacity`, `fillOpacity`, `blend` (`pass-through` for groups), `clipped`, `locks` or `collapsed`. Only given fields change. Returns the layer. |
|
|
180
|
+
| `group_layers` | Put sibling layers (`layerIds`) into a new pass-through group at the topmost one's position. Returns the group and its children. |
|
|
181
|
+
| `add_group` | Add an empty group (pass-through unless `blend` is given) above the active layer or at a placement, then move layers into it with `move_layer` `parentId`. Returns the group. |
|
|
182
|
+
| `ungroup_layer` | Replace a group by its children (its own opacity, blend and mask are dropped). Returns the child ids. |
|
|
183
|
+
| `merge_down` | Composite a layer onto the pixel layer below it (masks, fill and filters baked). Destructive; see Best practices. Returns the merged layer. |
|
|
184
|
+
| `merge_visible` | Composite all visible top-level layers into one layer; hidden ones stay. Destructive. |
|
|
185
|
+
| `flatten_image` | Composite everything visible into one Background; hidden layers are discarded. Destructive; do it only when asked. |
|
|
186
|
+
| `offset_layer` | Move a pixel layer and its mask to `x`, `y` or by `dx`, `dy` (integers, document px). Refused when its position is locked. Returns the offset and bounds. |
|
|
187
|
+
| `add_layer_mask` | Mask any layer `from` `reveal-all`, `hide-all` or `selection`, with optional `density` / `feather`. Replaces an existing mask. Returns the mask summary. |
|
|
188
|
+
| `set_layer_mask_properties` | Change any layer mask's `density`, `feather` or `enabled` without touching its pixels. |
|
|
189
|
+
| `apply_layer_mask` | Bake a pixel layer's mask into its alpha and remove it. Destructive. |
|
|
190
|
+
| `remove_layer_mask` | Delete a mask without applying it; disable it instead to keep it. |
|
|
191
|
+
| `layer_via_copy` | Copy the selected pixels of a pixel layer (default the active one) to a new layer above it; `cut: true` removes them from the source. Needs a selection. Returns the new layer. |
|
|
192
|
+
| `rasterize_layer` | Turn an annotation layer into pixels (same id and properties; browser only) or bake a pixel layer's develop / blur filters into its pixels. Do it before painting over an annotation, or when the user wants the filters fixed. Destructive. |
|
|
193
|
+
| `add_layer_filter` | Add a live `develop` (brightness … tint) or `blur` (`radius`) filter to a pixel layer; one of each kind. Returns the filters with ids. |
|
|
194
|
+
| `update_layer_filter` | Change a filter's values (`filterId` from `list_layers`). Returns the filters. |
|
|
195
|
+
| `remove_layer_filter` | Remove a filter. Returns the remaining filters. |
|
|
196
|
+
| `paint_stroke` | Paint (`tool` brush) or erase (`tool` eraser) along `points` in document px (optional `pressure` 0..1) on a pixel layer or its mask (`channel`); `color` #rrggbb (on a mask: black hides, white reveals); `brush` `{size, hardness, spacing, angle, roundness, opacity, flow, smoothing, pressureSize, pressureOpacity, blend}`. The selection limits the paint. One undo step per call. Returns the layer, channel, changed `bounds` and dab count. |
|
|
197
|
+
| `fill_pixels` | Fill the selection (or the canvas without one / with `useSelection: false`) on pixels or a mask with `color`, `opacity`, `blend`. Returns the changed bounds. |
|
|
198
|
+
| `clear_pixels` | Remove the selected pixels (alpha to 0; a transparency-locked layer or a mask gets the background colour). Returns the changed bounds. |
|
|
199
|
+
| `import_psd` | Open a PSD / PSB (`data` base64, a `data:` URL is fine) as the document, replacing the layers, crop and rotation (one undo step). Returns the new `layers` tree with ids, `layerIds`, size and `warnings` naming the affected layers. Call `inspect_psd` first when losses matter. |
|
|
114
200
|
| `apply_selection_as_mask` | Mask a layer with the current selection: pass `layerId` to mask an existing layer, or `kind` (plus optional `params`) to create a new masked layer in one step. `density` 0..1 and `feather` px soften the mask non-destructively. Returns `layerId`, kind, mask info and the selection summary. |
|
|
115
201
|
|
|
116
202
|
## Pitfalls
|
|
@@ -118,6 +204,17 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
118
204
|
- A layer's `kind` cannot change; to switch kinds add a new layer and remove the old one.
|
|
119
205
|
Curves points, `colorize` and colours are only set when the layer is added, so get them
|
|
120
206
|
right in `add_adjustment_layer` (or replace the layer).
|
|
207
|
+
- Layer write tools refuse locked layers with the reason: unlock with `set_layer_properties`
|
|
208
|
+
`locks` only when the user wants that layer changed.
|
|
209
|
+
- `merge_down`, `merge_visible` and `flatten_image` merge annotation layers only in a browser
|
|
210
|
+
(they need a canvas); elsewhere they fail and say so.
|
|
211
|
+
- `paint_stroke`, `fill_pixels` and `clear_pixels` need a visible pixel layer (or a layer with a
|
|
212
|
+
raster mask and `channel: "mask"`); adjustment, group and annotation layers can only be
|
|
213
|
+
painted through their masks. A layer with `locks.pixels` refuses paint.
|
|
214
|
+
- `export_psd` is in the source frame: a crop or rotation is not in the file (the warnings say
|
|
215
|
+
so). Crop with `set_crop` only for PNG / JPEG output, or flatten the look you need first.
|
|
216
|
+
- PSD opacity, fill and mask density come in as whole percents (Photoshop stores bytes), so a
|
|
217
|
+
layer at 0.8 exported and reopened is 0.8, but 0.333 becomes 0.33.
|
|
121
218
|
- `get_histogram` describes the last render, not the current state: render after edits, then
|
|
122
219
|
read it.
|
|
123
220
|
- Crop coordinates are always in the un-rotated source; after `set_rotation` the output size
|
|
@@ -126,8 +223,11 @@ then blur. See `pixerate-agent-tools` for errors, namespacing and command batchi
|
|
|
126
223
|
the correction needs a layer (e.g. levels or curves), not a bigger number.
|
|
127
224
|
- `tint` has no effect on process 1 edits (older saved documents); the result carries a note.
|
|
128
225
|
Do not keep raising it; correct the green-magenta cast with a `colorBalance` layer instead.
|
|
129
|
-
- Base `opacity` fades the whole image, not an adjustment. Base `blur` blurs
|
|
130
|
-
|
|
226
|
+
- Base `opacity` fades the whole image, not an adjustment. Base `blur` blurs a whole layer (the
|
|
227
|
+
Background by default, before the adjustment layers; in older documents with blur and
|
|
228
|
+
adjustment layers, the output after them). It is not a local softening tool.
|
|
229
|
+
- New adjustment layers go above the other adjustment layers and below annotations, so they
|
|
230
|
+
never tint arrows, labels or watermarks. `list_layers` shows where each one went.
|
|
131
231
|
- `invert` and `threshold` are destructive-looking creative effects; never use them for a
|
|
132
232
|
correction the user did not ask for. Posterize `levels` below ~8 bands visibly.
|
|
133
233
|
- Selection coordinates ignore crop and rotation: they are source-image pixels. A rectangle in
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|