@pixerate/editor 0.102.0 → 0.103.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 (58) hide show
  1. package/THIRD_PARTY_NOTICES.md +1 -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 +3330 -0
  39. package/dist/paint-editor/index.cjs.map +1 -0
  40. package/dist/paint-editor/index.d.cts +1233 -0
  41. package/dist/paint-editor/index.d.ts +1233 -0
  42. package/dist/paint-editor/index.js +3330 -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-paint-editor/SKILL.md +162 -0
  58. /package/dist/{chunk-IB53LVGV.js.map → chunk-DS7KDKPX.js.map} +0 -0
@@ -0,0 +1,282 @@
1
+ import { B as BlendMode, R as RasterRect, T as TileKey } from './surface-AfHCH7BR.js';
2
+
3
+ /**
4
+ * The brush engine (a subset of photocraft's `crates/paint`): a computed round tip with size,
5
+ * hardness, angle, roundness and spacing; pressure to size and opacity; smoothing; and Photoshop's
6
+ * opacity-versus-flow build-up. Strokes are data, so one replays the same way for undo, agents
7
+ * and tests.
8
+ *
9
+ * - Points are walked into dabs at `spacing × diameter` (`walkDabs`).
10
+ * - Each dab's coverage (`dabCoverage`, photocraft's measured Photoshop profile) builds up in a
11
+ * stroke coverage map with flow towards the dab's opacity ceiling: `c ← c + v·(ceil − c)`.
12
+ * - The stroke composites once at the stroke opacity over the layer as it was before the stroke,
13
+ * so overlapping dabs never exceed the opacity.
14
+ */
15
+
16
+ /** One input sample. Missing pressure is a mouse (1). */
17
+ interface StrokePoint {
18
+ x: number;
19
+ y: number;
20
+ /** 0..1 (default 1). */
21
+ pressure?: number;
22
+ /** Milliseconds (optional; kept for replays). */
23
+ t?: number;
24
+ }
25
+ interface BrushSettings {
26
+ /** Diameter in pixels (1..2500, default 20). */
27
+ size: number;
28
+ /** 0 (soft) .. 1 (hard, default). */
29
+ hardness: number;
30
+ /** Dab spacing as a fraction of the diameter (0.01..10, default 0.1). */
31
+ spacing: number;
32
+ /** Tip angle in degrees (default 0). */
33
+ angle: number;
34
+ /** Tip roundness 0.01..1 (default 1). */
35
+ roundness: number;
36
+ /** Stroke opacity ceiling 0..1 (default 1). */
37
+ opacity: number;
38
+ /** Build-up per dab 0..1 (default 1). */
39
+ flow: number;
40
+ /** Input smoothing 0..0.95 (default 0). */
41
+ smoothing: number;
42
+ /** Pressure controls size (default true). */
43
+ pressureSize: boolean;
44
+ /** Pressure controls opacity (default false). */
45
+ pressureOpacity: boolean;
46
+ /** How the stroke paints onto the layer (default normal). */
47
+ blend: BlendMode;
48
+ }
49
+ declare const DEFAULT_BRUSH: Readonly<BrushSettings>;
50
+ declare const MAX_BRUSH_SIZE = 2500;
51
+ /** A brush with every field in range (missing fields from `DEFAULT_BRUSH`). */
52
+ declare function normalizeBrush(raw?: Partial<BrushSettings>): BrushSettings;
53
+ /** The core radius and shoulder width (pixels) of a round tip of `radius` and `hardness`. */
54
+ declare function tipShape(radius: number, hardness: number): [number, number];
55
+ /** Opacity of a round tip at distance `d` from its centre. */
56
+ declare function tipFalloff(d: number, radius: number, hardness: number): number;
57
+ /** How far from its centre a round tip still paints. */
58
+ declare function tipReach(radius: number, hardness: number): number;
59
+ /** Coverage of a round dab at distance `d` from its centre. */
60
+ declare function dabCoverage(d: number, radius: number, hardness: number): number;
61
+ interface Dab {
62
+ x: number;
63
+ y: number;
64
+ radius: number;
65
+ /** Flow (per-dab build-up). */
66
+ alpha: number;
67
+ /** Opacity ceiling this dab builds up to. */
68
+ opacity: number;
69
+ /** Radians. */
70
+ angle: number;
71
+ roundness: number;
72
+ }
73
+ /** Exponential smoothing of input points; the first and last point are kept. */
74
+ declare function smoothPoints(points: readonly StrokePoint[], amount: number): StrokePoint[];
75
+ /**
76
+ * Walks points into dabs (incrementally: feed points in any chunks and get the same dabs). The
77
+ * first point is a dab; then one every `spacing × diameter` along the path.
78
+ */
79
+ declare class DabWalker {
80
+ readonly brush: BrushSettings;
81
+ private last;
82
+ private nextAt;
83
+ private pendingFirst;
84
+ constructor(brush: BrushSettings);
85
+ private diameter;
86
+ private stepLen;
87
+ private dab;
88
+ push(p: StrokePoint, out: Dab[]): void;
89
+ finish(out: Dab[]): void;
90
+ }
91
+ /** All dabs of a point list (smoothing applied). */
92
+ declare function walkDabs(points: readonly StrokePoint[], brush: BrushSettings): Dab[];
93
+ /** The pixel rectangle a dab can touch. */
94
+ declare function dabRect(d: Dab, hardness: number): RasterRect;
95
+ /**
96
+ * Rasterizes one dab over `rect` into `out` (rect.width × rect.height values, coverage × flow).
97
+ * Small tips (radius below 3 px) take each pixel's covered area from a 4×4 grid.
98
+ */
99
+ declare function rasterizeDab(d: Dab, hardness: number, rect: RasterRect, out: Float32Array): void;
100
+ /** A stroke's accumulated coverage, in 256 px tiles over document space. */
101
+ declare class CoverageMap {
102
+ readonly tiles: Map<number, Float32Array<ArrayBufferLike>>;
103
+ bounds: RasterRect | null;
104
+ private dirty;
105
+ get(x: number, y: number): number;
106
+ /** Builds up `vals` (over `rect`) with flow towards `ceil`: `c ← c + v·(ceil − c)`. */
107
+ accumulate(rect: RasterRect, vals: Float32Array, ceil: number): void;
108
+ /** Tiles changed since the last call. */
109
+ takeDirty(): TileKey[];
110
+ }
111
+ /** Paints a stroke's dabs into a coverage map, incrementally. */
112
+ declare class StrokeCoverage {
113
+ readonly brush: BrushSettings;
114
+ readonly coverage: CoverageMap;
115
+ private walker;
116
+ private smoothing;
117
+ private smoothPos;
118
+ private buf;
119
+ dabCount: number;
120
+ constructor(brush: BrushSettings);
121
+ /** Feeds input points; returns the document rectangles the new dabs touched. */
122
+ push(points: readonly StrokePoint[]): RasterRect[];
123
+ /** Ends the stroke (a single click still paints one dab). */
124
+ finish(last?: StrokePoint): RasterRect[];
125
+ private smooth;
126
+ private paint;
127
+ }
128
+ /** How a stroke changes pixels. */
129
+ interface StrokeApply {
130
+ /** `paint` puts colour down; `erase` removes alpha (or, with transparency locked, paints `background`). */
131
+ mode: 'paint' | 'erase';
132
+ /** RGBA 0..255 (paint colour, or the mask grey in `r`). */
133
+ color: readonly [number, number, number, number];
134
+ /** RGBA 0..255 the eraser paints on a transparency-locked layer (and on masks). */
135
+ background: readonly [number, number, number, number];
136
+ opacity: number;
137
+ blend: BlendMode;
138
+ lockTransparency: boolean;
139
+ /** Selection coverage (document space, canvas-sized), or `null`. */
140
+ selection: {
141
+ width: number;
142
+ height: number;
143
+ data: ArrayLike<number>;
144
+ } | null;
145
+ /** Layer offset (target pixels are in layer space). */
146
+ offset: {
147
+ x: number;
148
+ y: number;
149
+ };
150
+ }
151
+ /**
152
+ * Composites the stroke's coverage over `tileData` (the target's tile (tx, ty) as it was before
153
+ * the stroke, in layer space; RGBA or one byte for masks) and returns the new tile data. With
154
+ * `into` (the tile's previous result) and `region` (document space), only `region` is recomputed
155
+ * and the rest of `into` is kept.
156
+ */
157
+ declare function applyStrokeToTile(coverage: CoverageMap, tileData: Uint8ClampedArray, channels: 1 | 4, tx: number, ty: number, s: StrokeApply, options?: {
158
+ into?: Uint8ClampedArray;
159
+ region?: RasterRect;
160
+ }): Uint8ClampedArray;
161
+ /** Tile coordinates of coverage tile keys (for callers that track dirty tiles). */
162
+ declare function coverageTileCoord(key: TileKey): {
163
+ tx: number;
164
+ ty: number;
165
+ };
166
+
167
+ /**
168
+ * The Mixer Brush: a reservoir of loaded paint plus a pickup well that takes up colour from the
169
+ * canvas under each dab (photocraft's behavioural model):
170
+ *
171
+ * - Each dab samples the canvas under its footprint (coverage- and alpha-weighted). The pickup
172
+ * colour `P` moves towards that sample by `PICKUP_RATE × wet` (scaled by the sample's coverage).
173
+ * - The dab's paint mixes the reservoir `R` (weight `amount × (1 − mix·wet)`) with the pickup
174
+ * (weight `mix·wet`) and lands at `flow × opacity` times the total weight, over the pixels as
175
+ * they are now, so earlier dabs of the same stroke are picked up again (smearing).
176
+ * - The reservoir drains per dab unless `load` is 1, so low-load strokes run dry. A dry brush
177
+ * (`wet` 0) paints only reservoir colour; an empty, wet brush smears canvas colour.
178
+ *
179
+ * `mixing: 'pigment'` does all three mixes (pickup, paint and deposit) with the Kubelka-Munk model
180
+ * of `./pigment`, so a blue brush dragged through yellow makes green instead of grey. Everything
181
+ * is data in, pixels out: the same canvas, points, settings and load replay identically.
182
+ */
183
+
184
+ /** How the Mixer Brush mixes colours: straight sRGB averages, or like paint (Kubelka-Munk). */
185
+ type MixingModel = 'rgb' | 'pigment';
186
+ declare const MIXING_MODELS: readonly MixingModel[];
187
+ /** Mixer Brush options. */
188
+ interface MixerSettings {
189
+ /** How much colour the brush picks up from the canvas, 0 (dry) .. 1 (default 0.5). */
190
+ wet: number;
191
+ /** How much paint the reservoir holds, 0 .. 1 (default 0.5): below 1 it drains per dab and the stroke runs dry. */
192
+ load: number;
193
+ /** How far each dab's paint moves from the reservoir towards the picked-up colour, 0 .. 1 (default 0.5). */
194
+ mix: number;
195
+ /** Refill the reservoir with the paint colour at the start of each stroke (default true). */
196
+ loadEachStroke: boolean;
197
+ /** Empty the pickup well after each stroke (default true); off keeps the brush dirty for the next stroke. */
198
+ cleanEachStroke: boolean;
199
+ /** `rgb` (default) or `pigment` (blue and yellow make green). */
200
+ mixing: MixingModel;
201
+ }
202
+ declare const DEFAULT_MIXER: Readonly<MixerSettings>;
203
+ /** Fraction of the way the pickup colour moves towards the canvas per dab at 100 % wet. */
204
+ declare const MIXER_PICKUP_RATE = 0.25;
205
+ /** Fraction of the reservoir that drains per dab at 0 % load. */
206
+ declare const MIXER_DRAIN = 0.08;
207
+ /** Mixer settings with every field in range (missing fields from `DEFAULT_MIXER`). */
208
+ declare function normalizeMixer(raw?: Partial<MixerSettings>): MixerSettings;
209
+ /** What the brush carries between strokes. Colours are straight sRGB, 0..1. */
210
+ interface MixerLoad {
211
+ /** Loaded paint, or `null` when the brush is unloaded. */
212
+ readonly reservoir: readonly [number, number, number] | null;
213
+ /** Reservoir left, 0..1. */
214
+ readonly amount: number;
215
+ /** Colour picked up from the canvas, or `null` when the brush is clean. */
216
+ readonly pickup: readonly [number, number, number] | null;
217
+ }
218
+ declare const EMPTY_MIXER_LOAD: MixerLoad;
219
+ /** The brush loaded full with `color` (sRGB 0..1); the pickup well is kept. */
220
+ declare function loadMixer(load: MixerLoad, color: readonly [number, number, number]): MixerLoad;
221
+ /** The brush with its pickup well emptied; the reservoir is kept. */
222
+ declare function cleanMixer(load: MixerLoad): MixerLoad;
223
+ /** The pixels a mixer stroke reads and writes (RGBA tiles in layer space). */
224
+ interface MixerTarget {
225
+ /** The tile (TILE × TILE × 4) at (tx, ty) as it is now, or `null` when it is fully transparent. */
226
+ readTile(tx: number, ty: number): Uint8ClampedArray | null;
227
+ /** A writable tile at (tx, ty), returned by `readTile` from then on. */
228
+ writeTile(tx: number, ty: number): Uint8ClampedArray;
229
+ /** Layer offset: layer pixel = document pixel − offset. */
230
+ offset: {
231
+ x: number;
232
+ y: number;
233
+ };
234
+ /** Keep alpha and paint only where the layer has pixels. */
235
+ lockTransparency: boolean;
236
+ /** Selection coverage (document space, canvas-sized), or `null`. */
237
+ selection: {
238
+ width: number;
239
+ height: number;
240
+ data: ArrayLike<number>;
241
+ } | null;
242
+ }
243
+ /**
244
+ * One Mixer Brush stroke. Feed points with `push` (any chunks give the same dabs), end with
245
+ * `finish`; `load` is the brush load after the dabs so far. Each call returns the document
246
+ * rectangles it touched.
247
+ */
248
+ declare class MixerStroke {
249
+ readonly brush: BrushSettings;
250
+ private readonly target;
251
+ dabCount: number;
252
+ bounds: RasterRect | null;
253
+ private walker;
254
+ private smoothPos;
255
+ private buf;
256
+ private readonly settings;
257
+ private readonly reservoir;
258
+ private amount;
259
+ private pickup;
260
+ private readonly pig;
261
+ private readonly out;
262
+ constructor(brush: BrushSettings, settings: Partial<MixerSettings>, load: MixerLoad, target: MixerTarget);
263
+ /** The brush load after the dabs so far. */
264
+ get load(): MixerLoad;
265
+ push(points: readonly StrokePoint[]): RasterRect[];
266
+ finish(last?: StrokePoint): RasterRect[];
267
+ private smooth;
268
+ /** `a` mixed `t` of the way to `b` into `this.out`, by the mixing model. */
269
+ private mix;
270
+ private paint;
271
+ private tx;
272
+ private ty;
273
+ private tile;
274
+ private writable;
275
+ /** Index of document pixel (dx, dy) in `this.tile`, or -1 when reading an empty tile. */
276
+ private locate;
277
+ private dab;
278
+ /** Lays `paint` over the pixels under the dab at `dep` × coverage (× selection). */
279
+ private deposit;
280
+ }
281
+
282
+ export { walkDabs as A, type BrushSettings as B, CoverageMap as C, type Dab as D, EMPTY_MIXER_LOAD as E, type MixerSettings as M, type StrokePoint as S, type MixerLoad as a, MAX_BRUSH_SIZE as b, StrokeCoverage as c, type MixerTarget as d, MixerStroke as e, type StrokeApply as f, DEFAULT_BRUSH as g, DEFAULT_MIXER as h, DabWalker as i, MIXER_DRAIN as j, MIXER_PICKUP_RATE as k, MIXING_MODELS as l, type MixingModel as m, applyStrokeToTile as n, cleanMixer as o, coverageTileCoord as p, dabCoverage as q, dabRect as r, loadMixer as s, normalizeBrush as t, normalizeMixer as u, rasterizeDab as v, smoothPoints as w, tipFalloff as x, tipReach as y, tipShape as z };
@@ -0,0 +1,282 @@
1
+ import { B as BlendMode, R as RasterRect, T as TileKey } from './surface-AfHCH7BR.cjs';
2
+
3
+ /**
4
+ * The brush engine (a subset of photocraft's `crates/paint`): a computed round tip with size,
5
+ * hardness, angle, roundness and spacing; pressure to size and opacity; smoothing; and Photoshop's
6
+ * opacity-versus-flow build-up. Strokes are data, so one replays the same way for undo, agents
7
+ * and tests.
8
+ *
9
+ * - Points are walked into dabs at `spacing × diameter` (`walkDabs`).
10
+ * - Each dab's coverage (`dabCoverage`, photocraft's measured Photoshop profile) builds up in a
11
+ * stroke coverage map with flow towards the dab's opacity ceiling: `c ← c + v·(ceil − c)`.
12
+ * - The stroke composites once at the stroke opacity over the layer as it was before the stroke,
13
+ * so overlapping dabs never exceed the opacity.
14
+ */
15
+
16
+ /** One input sample. Missing pressure is a mouse (1). */
17
+ interface StrokePoint {
18
+ x: number;
19
+ y: number;
20
+ /** 0..1 (default 1). */
21
+ pressure?: number;
22
+ /** Milliseconds (optional; kept for replays). */
23
+ t?: number;
24
+ }
25
+ interface BrushSettings {
26
+ /** Diameter in pixels (1..2500, default 20). */
27
+ size: number;
28
+ /** 0 (soft) .. 1 (hard, default). */
29
+ hardness: number;
30
+ /** Dab spacing as a fraction of the diameter (0.01..10, default 0.1). */
31
+ spacing: number;
32
+ /** Tip angle in degrees (default 0). */
33
+ angle: number;
34
+ /** Tip roundness 0.01..1 (default 1). */
35
+ roundness: number;
36
+ /** Stroke opacity ceiling 0..1 (default 1). */
37
+ opacity: number;
38
+ /** Build-up per dab 0..1 (default 1). */
39
+ flow: number;
40
+ /** Input smoothing 0..0.95 (default 0). */
41
+ smoothing: number;
42
+ /** Pressure controls size (default true). */
43
+ pressureSize: boolean;
44
+ /** Pressure controls opacity (default false). */
45
+ pressureOpacity: boolean;
46
+ /** How the stroke paints onto the layer (default normal). */
47
+ blend: BlendMode;
48
+ }
49
+ declare const DEFAULT_BRUSH: Readonly<BrushSettings>;
50
+ declare const MAX_BRUSH_SIZE = 2500;
51
+ /** A brush with every field in range (missing fields from `DEFAULT_BRUSH`). */
52
+ declare function normalizeBrush(raw?: Partial<BrushSettings>): BrushSettings;
53
+ /** The core radius and shoulder width (pixels) of a round tip of `radius` and `hardness`. */
54
+ declare function tipShape(radius: number, hardness: number): [number, number];
55
+ /** Opacity of a round tip at distance `d` from its centre. */
56
+ declare function tipFalloff(d: number, radius: number, hardness: number): number;
57
+ /** How far from its centre a round tip still paints. */
58
+ declare function tipReach(radius: number, hardness: number): number;
59
+ /** Coverage of a round dab at distance `d` from its centre. */
60
+ declare function dabCoverage(d: number, radius: number, hardness: number): number;
61
+ interface Dab {
62
+ x: number;
63
+ y: number;
64
+ radius: number;
65
+ /** Flow (per-dab build-up). */
66
+ alpha: number;
67
+ /** Opacity ceiling this dab builds up to. */
68
+ opacity: number;
69
+ /** Radians. */
70
+ angle: number;
71
+ roundness: number;
72
+ }
73
+ /** Exponential smoothing of input points; the first and last point are kept. */
74
+ declare function smoothPoints(points: readonly StrokePoint[], amount: number): StrokePoint[];
75
+ /**
76
+ * Walks points into dabs (incrementally: feed points in any chunks and get the same dabs). The
77
+ * first point is a dab; then one every `spacing × diameter` along the path.
78
+ */
79
+ declare class DabWalker {
80
+ readonly brush: BrushSettings;
81
+ private last;
82
+ private nextAt;
83
+ private pendingFirst;
84
+ constructor(brush: BrushSettings);
85
+ private diameter;
86
+ private stepLen;
87
+ private dab;
88
+ push(p: StrokePoint, out: Dab[]): void;
89
+ finish(out: Dab[]): void;
90
+ }
91
+ /** All dabs of a point list (smoothing applied). */
92
+ declare function walkDabs(points: readonly StrokePoint[], brush: BrushSettings): Dab[];
93
+ /** The pixel rectangle a dab can touch. */
94
+ declare function dabRect(d: Dab, hardness: number): RasterRect;
95
+ /**
96
+ * Rasterizes one dab over `rect` into `out` (rect.width × rect.height values, coverage × flow).
97
+ * Small tips (radius below 3 px) take each pixel's covered area from a 4×4 grid.
98
+ */
99
+ declare function rasterizeDab(d: Dab, hardness: number, rect: RasterRect, out: Float32Array): void;
100
+ /** A stroke's accumulated coverage, in 256 px tiles over document space. */
101
+ declare class CoverageMap {
102
+ readonly tiles: Map<number, Float32Array<ArrayBufferLike>>;
103
+ bounds: RasterRect | null;
104
+ private dirty;
105
+ get(x: number, y: number): number;
106
+ /** Builds up `vals` (over `rect`) with flow towards `ceil`: `c ← c + v·(ceil − c)`. */
107
+ accumulate(rect: RasterRect, vals: Float32Array, ceil: number): void;
108
+ /** Tiles changed since the last call. */
109
+ takeDirty(): TileKey[];
110
+ }
111
+ /** Paints a stroke's dabs into a coverage map, incrementally. */
112
+ declare class StrokeCoverage {
113
+ readonly brush: BrushSettings;
114
+ readonly coverage: CoverageMap;
115
+ private walker;
116
+ private smoothing;
117
+ private smoothPos;
118
+ private buf;
119
+ dabCount: number;
120
+ constructor(brush: BrushSettings);
121
+ /** Feeds input points; returns the document rectangles the new dabs touched. */
122
+ push(points: readonly StrokePoint[]): RasterRect[];
123
+ /** Ends the stroke (a single click still paints one dab). */
124
+ finish(last?: StrokePoint): RasterRect[];
125
+ private smooth;
126
+ private paint;
127
+ }
128
+ /** How a stroke changes pixels. */
129
+ interface StrokeApply {
130
+ /** `paint` puts colour down; `erase` removes alpha (or, with transparency locked, paints `background`). */
131
+ mode: 'paint' | 'erase';
132
+ /** RGBA 0..255 (paint colour, or the mask grey in `r`). */
133
+ color: readonly [number, number, number, number];
134
+ /** RGBA 0..255 the eraser paints on a transparency-locked layer (and on masks). */
135
+ background: readonly [number, number, number, number];
136
+ opacity: number;
137
+ blend: BlendMode;
138
+ lockTransparency: boolean;
139
+ /** Selection coverage (document space, canvas-sized), or `null`. */
140
+ selection: {
141
+ width: number;
142
+ height: number;
143
+ data: ArrayLike<number>;
144
+ } | null;
145
+ /** Layer offset (target pixels are in layer space). */
146
+ offset: {
147
+ x: number;
148
+ y: number;
149
+ };
150
+ }
151
+ /**
152
+ * Composites the stroke's coverage over `tileData` (the target's tile (tx, ty) as it was before
153
+ * the stroke, in layer space; RGBA or one byte for masks) and returns the new tile data. With
154
+ * `into` (the tile's previous result) and `region` (document space), only `region` is recomputed
155
+ * and the rest of `into` is kept.
156
+ */
157
+ declare function applyStrokeToTile(coverage: CoverageMap, tileData: Uint8ClampedArray, channels: 1 | 4, tx: number, ty: number, s: StrokeApply, options?: {
158
+ into?: Uint8ClampedArray;
159
+ region?: RasterRect;
160
+ }): Uint8ClampedArray;
161
+ /** Tile coordinates of coverage tile keys (for callers that track dirty tiles). */
162
+ declare function coverageTileCoord(key: TileKey): {
163
+ tx: number;
164
+ ty: number;
165
+ };
166
+
167
+ /**
168
+ * The Mixer Brush: a reservoir of loaded paint plus a pickup well that takes up colour from the
169
+ * canvas under each dab (photocraft's behavioural model):
170
+ *
171
+ * - Each dab samples the canvas under its footprint (coverage- and alpha-weighted). The pickup
172
+ * colour `P` moves towards that sample by `PICKUP_RATE × wet` (scaled by the sample's coverage).
173
+ * - The dab's paint mixes the reservoir `R` (weight `amount × (1 − mix·wet)`) with the pickup
174
+ * (weight `mix·wet`) and lands at `flow × opacity` times the total weight, over the pixels as
175
+ * they are now, so earlier dabs of the same stroke are picked up again (smearing).
176
+ * - The reservoir drains per dab unless `load` is 1, so low-load strokes run dry. A dry brush
177
+ * (`wet` 0) paints only reservoir colour; an empty, wet brush smears canvas colour.
178
+ *
179
+ * `mixing: 'pigment'` does all three mixes (pickup, paint and deposit) with the Kubelka-Munk model
180
+ * of `./pigment`, so a blue brush dragged through yellow makes green instead of grey. Everything
181
+ * is data in, pixels out: the same canvas, points, settings and load replay identically.
182
+ */
183
+
184
+ /** How the Mixer Brush mixes colours: straight sRGB averages, or like paint (Kubelka-Munk). */
185
+ type MixingModel = 'rgb' | 'pigment';
186
+ declare const MIXING_MODELS: readonly MixingModel[];
187
+ /** Mixer Brush options. */
188
+ interface MixerSettings {
189
+ /** How much colour the brush picks up from the canvas, 0 (dry) .. 1 (default 0.5). */
190
+ wet: number;
191
+ /** How much paint the reservoir holds, 0 .. 1 (default 0.5): below 1 it drains per dab and the stroke runs dry. */
192
+ load: number;
193
+ /** How far each dab's paint moves from the reservoir towards the picked-up colour, 0 .. 1 (default 0.5). */
194
+ mix: number;
195
+ /** Refill the reservoir with the paint colour at the start of each stroke (default true). */
196
+ loadEachStroke: boolean;
197
+ /** Empty the pickup well after each stroke (default true); off keeps the brush dirty for the next stroke. */
198
+ cleanEachStroke: boolean;
199
+ /** `rgb` (default) or `pigment` (blue and yellow make green). */
200
+ mixing: MixingModel;
201
+ }
202
+ declare const DEFAULT_MIXER: Readonly<MixerSettings>;
203
+ /** Fraction of the way the pickup colour moves towards the canvas per dab at 100 % wet. */
204
+ declare const MIXER_PICKUP_RATE = 0.25;
205
+ /** Fraction of the reservoir that drains per dab at 0 % load. */
206
+ declare const MIXER_DRAIN = 0.08;
207
+ /** Mixer settings with every field in range (missing fields from `DEFAULT_MIXER`). */
208
+ declare function normalizeMixer(raw?: Partial<MixerSettings>): MixerSettings;
209
+ /** What the brush carries between strokes. Colours are straight sRGB, 0..1. */
210
+ interface MixerLoad {
211
+ /** Loaded paint, or `null` when the brush is unloaded. */
212
+ readonly reservoir: readonly [number, number, number] | null;
213
+ /** Reservoir left, 0..1. */
214
+ readonly amount: number;
215
+ /** Colour picked up from the canvas, or `null` when the brush is clean. */
216
+ readonly pickup: readonly [number, number, number] | null;
217
+ }
218
+ declare const EMPTY_MIXER_LOAD: MixerLoad;
219
+ /** The brush loaded full with `color` (sRGB 0..1); the pickup well is kept. */
220
+ declare function loadMixer(load: MixerLoad, color: readonly [number, number, number]): MixerLoad;
221
+ /** The brush with its pickup well emptied; the reservoir is kept. */
222
+ declare function cleanMixer(load: MixerLoad): MixerLoad;
223
+ /** The pixels a mixer stroke reads and writes (RGBA tiles in layer space). */
224
+ interface MixerTarget {
225
+ /** The tile (TILE × TILE × 4) at (tx, ty) as it is now, or `null` when it is fully transparent. */
226
+ readTile(tx: number, ty: number): Uint8ClampedArray | null;
227
+ /** A writable tile at (tx, ty), returned by `readTile` from then on. */
228
+ writeTile(tx: number, ty: number): Uint8ClampedArray;
229
+ /** Layer offset: layer pixel = document pixel − offset. */
230
+ offset: {
231
+ x: number;
232
+ y: number;
233
+ };
234
+ /** Keep alpha and paint only where the layer has pixels. */
235
+ lockTransparency: boolean;
236
+ /** Selection coverage (document space, canvas-sized), or `null`. */
237
+ selection: {
238
+ width: number;
239
+ height: number;
240
+ data: ArrayLike<number>;
241
+ } | null;
242
+ }
243
+ /**
244
+ * One Mixer Brush stroke. Feed points with `push` (any chunks give the same dabs), end with
245
+ * `finish`; `load` is the brush load after the dabs so far. Each call returns the document
246
+ * rectangles it touched.
247
+ */
248
+ declare class MixerStroke {
249
+ readonly brush: BrushSettings;
250
+ private readonly target;
251
+ dabCount: number;
252
+ bounds: RasterRect | null;
253
+ private walker;
254
+ private smoothPos;
255
+ private buf;
256
+ private readonly settings;
257
+ private readonly reservoir;
258
+ private amount;
259
+ private pickup;
260
+ private readonly pig;
261
+ private readonly out;
262
+ constructor(brush: BrushSettings, settings: Partial<MixerSettings>, load: MixerLoad, target: MixerTarget);
263
+ /** The brush load after the dabs so far. */
264
+ get load(): MixerLoad;
265
+ push(points: readonly StrokePoint[]): RasterRect[];
266
+ finish(last?: StrokePoint): RasterRect[];
267
+ private smooth;
268
+ /** `a` mixed `t` of the way to `b` into `this.out`, by the mixing model. */
269
+ private mix;
270
+ private paint;
271
+ private tx;
272
+ private ty;
273
+ private tile;
274
+ private writable;
275
+ /** Index of document pixel (dx, dy) in `this.tile`, or -1 when reading an empty tile. */
276
+ private locate;
277
+ private dab;
278
+ /** Lays `paint` over the pixels under the dab at `dep` × coverage (× selection). */
279
+ private deposit;
280
+ }
281
+
282
+ export { walkDabs as A, type BrushSettings as B, CoverageMap as C, type Dab as D, EMPTY_MIXER_LOAD as E, type MixerSettings as M, type StrokePoint as S, type MixerLoad as a, MAX_BRUSH_SIZE as b, StrokeCoverage as c, type MixerTarget as d, MixerStroke as e, type StrokeApply as f, DEFAULT_BRUSH as g, DEFAULT_MIXER as h, DabWalker as i, MIXER_DRAIN as j, MIXER_PICKUP_RATE as k, MIXING_MODELS as l, type MixingModel as m, applyStrokeToTile as n, cleanMixer as o, coverageTileCoord as p, dabCoverage as q, dabRect as r, loadMixer as s, normalizeBrush as t, normalizeMixer as u, rasterizeDab as v, smoothPoints as w, tipFalloff as x, tipReach as y, tipShape as z };
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});require('../chunk-6BU5WU4R.cjs');
2
2
 
3
3
 
4
4
 
@@ -25,7 +25,8 @@
25
25
 
26
26
 
27
27
 
28
- var _chunkZZOTHJ4Kcjs = require('../chunk-ZZOTHJ4K.cjs');
28
+
29
+ var _chunkPV42JITTcjs = require('../chunk-PV42JITT.cjs');
29
30
 
30
31
 
31
32
 
@@ -77,5 +78,5 @@ var _chunkCRMAXIUVcjs = require('../chunk-CRMAXIUV.cjs');
77
78
 
78
79
 
79
80
 
80
- exports.CoverageMap = _chunkZZOTHJ4Kcjs.CoverageMap; exports.DEFAULT_BRUSH = _chunkZZOTHJ4Kcjs.DEFAULT_BRUSH; exports.DEFAULT_MIXER = _chunkZZOTHJ4Kcjs.DEFAULT_MIXER; exports.DabWalker = _chunkZZOTHJ4Kcjs.DabWalker; exports.EMPTY_MIXER_LOAD = _chunkZZOTHJ4Kcjs.EMPTY_MIXER_LOAD; exports.MAX_BRUSH_SIZE = _chunkZZOTHJ4Kcjs.MAX_BRUSH_SIZE; exports.MIXER_DRAIN = _chunkZZOTHJ4Kcjs.MIXER_DRAIN; exports.MIXER_PICKUP_RATE = _chunkZZOTHJ4Kcjs.MIXER_PICKUP_RATE; exports.MIXING_MODELS = _chunkZZOTHJ4Kcjs.MIXING_MODELS; exports.MixerStroke = _chunkZZOTHJ4Kcjs.MixerStroke; exports.PIGMENT_BANDS = _chunkCRMAXIUVcjs.PIGMENT_BANDS; exports.PIGMENT_LAMBDA_MIN = _chunkCRMAXIUVcjs.PIGMENT_LAMBDA_MIN; exports.PIGMENT_LAMBDA_STEP = _chunkCRMAXIUVcjs.PIGMENT_LAMBDA_STEP; exports.PIGMENT_WAVELENGTHS = _chunkCRMAXIUVcjs.PIGMENT_WAVELENGTHS; exports.StrokeCoverage = _chunkZZOTHJ4Kcjs.StrokeCoverage; exports.applyStrokeToTile = _chunkZZOTHJ4Kcjs.applyStrokeToTile; exports.cleanMixer = _chunkZZOTHJ4Kcjs.cleanMixer; exports.coverageTileCoord = _chunkZZOTHJ4Kcjs.coverageTileCoord; exports.createPigment = _chunkCRMAXIUVcjs.createPigment; exports.createPigmentTools = _chunkZZOTHJ4Kcjs.createPigmentTools; exports.dabCoverage = _chunkZZOTHJ4Kcjs.dabCoverage; exports.dabRect = _chunkZZOTHJ4Kcjs.dabRect; exports.lerpPigment = _chunkCRMAXIUVcjs.lerpPigment; exports.lerpPigmentInto = _chunkCRMAXIUVcjs.lerpPigmentInto; exports.loadMixer = _chunkZZOTHJ4Kcjs.loadMixer; exports.mixPigments = _chunkCRMAXIUVcjs.mixPigments; exports.normalizeBrush = _chunkZZOTHJ4Kcjs.normalizeBrush; exports.normalizeMixer = _chunkZZOTHJ4Kcjs.normalizeMixer; exports.rasterizeDab = _chunkZZOTHJ4Kcjs.rasterizeDab; exports.reflectanceToRgb = _chunkCRMAXIUVcjs.reflectanceToRgb; exports.rgbToReflectance = _chunkCRMAXIUVcjs.rgbToReflectance; exports.setPigmentColor = _chunkCRMAXIUVcjs.setPigmentColor; exports.smoothPoints = _chunkZZOTHJ4Kcjs.smoothPoints; exports.tipFalloff = _chunkZZOTHJ4Kcjs.tipFalloff; exports.tipReach = _chunkZZOTHJ4Kcjs.tipReach; exports.tipShape = _chunkZZOTHJ4Kcjs.tipShape; exports.walkDabs = _chunkZZOTHJ4Kcjs.walkDabs;
81
+ exports.CoverageMap = _chunkPV42JITTcjs.CoverageMap; exports.DEFAULT_BRUSH = _chunkPV42JITTcjs.DEFAULT_BRUSH; exports.DEFAULT_MIXER = _chunkPV42JITTcjs.DEFAULT_MIXER; exports.DabWalker = _chunkPV42JITTcjs.DabWalker; exports.EMPTY_MIXER_LOAD = _chunkPV42JITTcjs.EMPTY_MIXER_LOAD; exports.MAX_BRUSH_SIZE = _chunkPV42JITTcjs.MAX_BRUSH_SIZE; exports.MIXER_DRAIN = _chunkPV42JITTcjs.MIXER_DRAIN; exports.MIXER_PICKUP_RATE = _chunkPV42JITTcjs.MIXER_PICKUP_RATE; exports.MIXING_MODELS = _chunkPV42JITTcjs.MIXING_MODELS; exports.MixerStroke = _chunkPV42JITTcjs.MixerStroke; exports.PIGMENT_BANDS = _chunkCRMAXIUVcjs.PIGMENT_BANDS; exports.PIGMENT_LAMBDA_MIN = _chunkCRMAXIUVcjs.PIGMENT_LAMBDA_MIN; exports.PIGMENT_LAMBDA_STEP = _chunkCRMAXIUVcjs.PIGMENT_LAMBDA_STEP; exports.PIGMENT_WAVELENGTHS = _chunkCRMAXIUVcjs.PIGMENT_WAVELENGTHS; exports.StrokeCoverage = _chunkPV42JITTcjs.StrokeCoverage; exports.applyStrokeToTile = _chunkPV42JITTcjs.applyStrokeToTile; exports.cleanMixer = _chunkPV42JITTcjs.cleanMixer; exports.coverageTileCoord = _chunkPV42JITTcjs.coverageTileCoord; exports.createPigment = _chunkCRMAXIUVcjs.createPigment; exports.createPigmentTools = _chunkPV42JITTcjs.createPigmentTools; exports.dabCoverage = _chunkPV42JITTcjs.dabCoverage; exports.dabRect = _chunkPV42JITTcjs.dabRect; exports.lerpPigment = _chunkCRMAXIUVcjs.lerpPigment; exports.lerpPigmentInto = _chunkCRMAXIUVcjs.lerpPigmentInto; exports.loadMixer = _chunkPV42JITTcjs.loadMixer; exports.mixPigments = _chunkCRMAXIUVcjs.mixPigments; exports.normalizeBrush = _chunkPV42JITTcjs.normalizeBrush; exports.normalizeMixer = _chunkPV42JITTcjs.normalizeMixer; exports.rasterizeDab = _chunkPV42JITTcjs.rasterizeDab; exports.reflectanceToRgb = _chunkCRMAXIUVcjs.reflectanceToRgb; exports.rgbToReflectance = _chunkCRMAXIUVcjs.rgbToReflectance; exports.setPigmentColor = _chunkCRMAXIUVcjs.setPigmentColor; exports.smoothPoints = _chunkPV42JITTcjs.smoothPoints; exports.tipFalloff = _chunkPV42JITTcjs.tipFalloff; exports.tipReach = _chunkPV42JITTcjs.tipReach; exports.tipShape = _chunkPV42JITTcjs.tipShape; exports.walkDabs = _chunkPV42JITTcjs.walkDabs;
81
82
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/paint/index.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,gkEAAC","file":"/home/runner/work/editor/editor/packages/core/dist/paint/index.cjs"}
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/paint/index.cjs"],"names":[],"mappings":"AAAA,0GAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,gkEAAC","file":"/home/runner/work/editor/editor/packages/core/dist/paint/index.cjs"}