@pixerate/editor 0.135.0 → 0.137.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 (164) hide show
  1. package/dist/animation-editor/index.cjs +63 -55
  2. package/dist/animation-editor/index.cjs.map +1 -1
  3. package/dist/animation-editor/index.d.cts +8 -5
  4. package/dist/animation-editor/index.d.ts +8 -5
  5. package/dist/animation-editor/index.js +10 -2
  6. package/dist/animation-editor/index.js.map +1 -1
  7. package/dist/{bezier-J8i4uqSH.d.cts → bezier-CZ2AhjID.d.cts} +1 -1
  8. package/dist/{bezier-lNEOp0Jx.d.ts → bezier-Dywv3Bw8.d.ts} +1 -1
  9. package/dist/blending-D7jCmjO6.d.cts +1026 -0
  10. package/dist/blending-NgF3qo3d.d.ts +1026 -0
  11. package/dist/brushes-CnvZSYYU.d.cts +744 -0
  12. package/dist/brushes-DJmUbIgz.d.ts +744 -0
  13. package/dist/{brushstrokeRender-SII6MG4R.js → brushstrokeRender-2FAXA3XC.js} +13 -16
  14. package/dist/{brushstrokeRender-SII6MG4R.js.map → brushstrokeRender-2FAXA3XC.js.map} +1 -1
  15. package/dist/{brushstrokeRender-JNHVEUFG.cjs → brushstrokeRender-IMFY3FF6.cjs} +16 -19
  16. package/dist/brushstrokeRender-IMFY3FF6.cjs.map +1 -0
  17. package/dist/canvas/index.cjs +10 -10
  18. package/dist/canvas/index.cjs.map +1 -1
  19. package/dist/canvas/index.d.cts +10 -7
  20. package/dist/canvas/index.d.ts +10 -7
  21. package/dist/canvas/index.js +4 -4
  22. package/dist/{chunk-W4LNKTHT.cjs → chunk-2XS6LTUP.cjs} +63 -63
  23. package/dist/{chunk-W4LNKTHT.cjs.map → chunk-2XS6LTUP.cjs.map} +1 -1
  24. package/dist/{chunk-HHTCDFZC.js → chunk-3D77LOBM.js} +234 -136
  25. package/dist/chunk-3D77LOBM.js.map +1 -0
  26. package/dist/{chunk-BR7EKQUP.js → chunk-3G6J7UFV.js} +322 -2
  27. package/dist/chunk-3G6J7UFV.js.map +1 -0
  28. package/dist/{chunk-62XUO5LB.js → chunk-3X6IAJUQ.js} +2 -2
  29. package/dist/{chunk-EM3FMJWE.js → chunk-5FSRTP6E.js} +320 -3
  30. package/dist/chunk-5FSRTP6E.js.map +1 -0
  31. package/dist/{chunk-Z2H7KECK.cjs → chunk-5OOIUXNJ.cjs} +345 -247
  32. package/dist/chunk-5OOIUXNJ.cjs.map +1 -0
  33. package/dist/{chunk-7RJ6P7MI.js → chunk-6S6362D2.js} +2 -2
  34. package/dist/chunk-A3BVTI3G.js +150 -0
  35. package/dist/chunk-A3BVTI3G.js.map +1 -0
  36. package/dist/{chunk-24ATT2PE.cjs → chunk-AEAAN4AD.cjs} +120 -120
  37. package/dist/{chunk-24ATT2PE.cjs.map → chunk-AEAAN4AD.cjs.map} +1 -1
  38. package/dist/chunk-ECCXGELC.cjs +727 -0
  39. package/dist/chunk-ECCXGELC.cjs.map +1 -0
  40. package/dist/{chunk-SVYE5HIM.js → chunk-EQQMGO42.js} +2 -2
  41. package/dist/{chunk-FHPVDTI2.cjs → chunk-F2Y4E4GU.cjs} +22 -22
  42. package/dist/{chunk-FHPVDTI2.cjs.map → chunk-F2Y4E4GU.cjs.map} +1 -1
  43. package/dist/{chunk-OXSV64JX.js → chunk-G6IKY6PL.js} +4 -4
  44. package/dist/{chunk-UYQUPUR6.js → chunk-HUO3TI4C.js} +3 -3
  45. package/dist/chunk-I52DRGGJ.js +727 -0
  46. package/dist/chunk-I52DRGGJ.js.map +1 -0
  47. package/dist/{chunk-DDSO4HS7.cjs → chunk-KEENUAFN.cjs} +2 -2
  48. package/dist/{chunk-DDSO4HS7.cjs.map → chunk-KEENUAFN.cjs.map} +1 -1
  49. package/dist/{chunk-2HC4DRF4.js → chunk-LLFHCSU2.js} +2 -2
  50. package/dist/{chunk-J5BA3BQ6.cjs → chunk-O7LUD7MH.cjs} +323 -3
  51. package/dist/chunk-O7LUD7MH.cjs.map +1 -0
  52. package/dist/{chunk-ILYFOJUN.js → chunk-OIBIRXRG.js} +3 -3
  53. package/dist/{chunk-FWEO3HSL.js → chunk-PQQF5KOW.js} +8 -8
  54. package/dist/{chunk-T6TV6HY6.cjs → chunk-PRSRYHQD.cjs} +36 -36
  55. package/dist/{chunk-T6TV6HY6.cjs.map → chunk-PRSRYHQD.cjs.map} +1 -1
  56. package/dist/chunk-Q6OUXEK3.cjs +150 -0
  57. package/dist/chunk-Q6OUXEK3.cjs.map +1 -0
  58. package/dist/{chunk-6GFRKLLS.cjs → chunk-QBWEH3IM.cjs} +429 -112
  59. package/dist/chunk-QBWEH3IM.cjs.map +1 -0
  60. package/dist/{chunk-ESIWZMMK.js → chunk-SYT2TLQF.js} +35 -37
  61. package/dist/{chunk-ESIWZMMK.js.map → chunk-SYT2TLQF.js.map} +1 -1
  62. package/dist/{chunk-YT3JL7YM.cjs → chunk-TLCTTQFF.cjs} +53 -53
  63. package/dist/{chunk-YT3JL7YM.cjs.map → chunk-TLCTTQFF.cjs.map} +1 -1
  64. package/dist/{chunk-P3RQ5UJE.cjs → chunk-UTQKC66X.cjs} +4 -4
  65. package/dist/{chunk-P3RQ5UJE.cjs.map → chunk-UTQKC66X.cjs.map} +1 -1
  66. package/dist/{chunk-J3B45HTA.cjs → chunk-XB25WOC2.cjs} +6 -6
  67. package/dist/{chunk-J3B45HTA.cjs.map → chunk-XB25WOC2.cjs.map} +1 -1
  68. package/dist/{chunk-YAFS2QFZ.cjs → chunk-XN2NBLV5.cjs} +313 -315
  69. package/dist/chunk-XN2NBLV5.cjs.map +1 -0
  70. package/dist/effects/index.d.cts +7 -4
  71. package/dist/effects/index.d.ts +7 -4
  72. package/dist/{export-DMeG6E06.d.ts → export-41rL6Cne.d.ts} +1 -1
  73. package/dist/{export-AZSwX087.d.cts → export-CyJdfP_Z.d.cts} +5 -4
  74. package/dist/{export-KzubBKo7.d.cts → export-D9mXp05Q.d.cts} +1 -1
  75. package/dist/{export-BDkyDNM8.d.ts → export-DwnZ_hvQ.d.ts} +5 -4
  76. package/dist/extensions/index.cjs +3 -3
  77. package/dist/extensions/index.cjs.map +1 -1
  78. package/dist/extensions/index.js +2 -2
  79. package/dist/generation/index.cjs +150 -945
  80. package/dist/generation/index.cjs.map +1 -1
  81. package/dist/generation/index.d.cts +3 -423
  82. package/dist/generation/index.d.ts +3 -423
  83. package/dist/generation/index.js +59 -854
  84. package/dist/generation/index.js.map +1 -1
  85. package/dist/grammar/index.cjs +3 -3
  86. package/dist/grammar/index.cjs.map +1 -1
  87. package/dist/grammar/index.js +2 -2
  88. package/dist/{helpers-Coa4e2j_.d.ts → helpers-C-RgeyvH.d.ts} +25 -8
  89. package/dist/{helpers-DHl28yLO.d.cts → helpers-v6GMazzg.d.cts} +25 -8
  90. package/dist/image-editor/index.cjs +986 -986
  91. package/dist/image-editor/index.cjs.map +1 -1
  92. package/dist/image-editor/index.d.cts +15 -11
  93. package/dist/image-editor/index.d.ts +15 -11
  94. package/dist/image-editor/index.js +94 -94
  95. package/dist/index.cjs +28 -28
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.js +36 -36
  98. package/dist/{opentype-BWR7Qv8W.d.cts → opentype-KR_jj6b_.d.cts} +1 -1
  99. package/dist/{opentype-Dfcx6wFx.d.ts → opentype-a1KAW-qs.d.ts} +1 -1
  100. package/dist/paint/index.cjs +5 -8
  101. package/dist/paint/index.cjs.map +1 -1
  102. package/dist/paint/index.d.cts +4 -2
  103. package/dist/paint/index.d.ts +4 -2
  104. package/dist/paint/index.js +15 -18
  105. package/dist/paint-editor/index.cjs +405 -403
  106. package/dist/paint-editor/index.cjs.map +1 -1
  107. package/dist/paint-editor/index.d.cts +15 -748
  108. package/dist/paint-editor/index.d.ts +15 -748
  109. package/dist/paint-editor/index.js +79 -77
  110. package/dist/paint-editor/index.js.map +1 -1
  111. package/dist/{pathfinder-D8KQ6blQ.d.cts → pathfinder-BHS4vQNm.d.cts} +1 -1
  112. package/dist/{pathfinder-Celbi3e6.d.ts → pathfinder-BVvTIBMo.d.ts} +1 -1
  113. package/dist/{pool-8JZGWHvR.d.ts → pool-CZQOrSXZ.d.ts} +1 -1
  114. package/dist/{pool-BfuxBKTd.d.cts → pool-FjZZmZ80.d.cts} +1 -1
  115. package/dist/provenance-DmlcgdzT.d.cts +426 -0
  116. package/dist/provenance-uh5r2TQ8.d.ts +426 -0
  117. package/dist/rand-8xbUb6ur.d.ts +1118 -0
  118. package/dist/rand-Cw9VLrVC.d.cts +1118 -0
  119. package/dist/raster/index.cjs +17 -19
  120. package/dist/raster/index.cjs.map +1 -1
  121. package/dist/raster/index.js +9 -11
  122. package/dist/raster/index.js.map +1 -1
  123. package/dist/{svg-CxxqG3_W.d.ts → svg-CSz3mzkw.d.ts} +2 -2
  124. package/dist/{svg-pwXWq6s2.d.cts → svg-DTb40ICU.d.cts} +2 -2
  125. package/dist/{tools-y1M6O3UT.d.ts → tools-B0wXP5cF.d.ts} +1 -1
  126. package/dist/{tools-3EBjR91b.d.cts → tools-Ca0kI1qo.d.cts} +1 -1
  127. package/dist/vector/index.cjs +2698 -1674
  128. package/dist/vector/index.cjs.map +1 -1
  129. package/dist/vector/index.d.cts +390 -72
  130. package/dist/vector/index.d.ts +390 -72
  131. package/dist/vector/index.js +1665 -641
  132. package/dist/vector/index.js.map +1 -1
  133. package/dist/video-editor/index.d.cts +7 -4
  134. package/dist/video-editor/index.d.ts +7 -4
  135. package/dist/{warp-Ceqdz7kj.d.cts → warp-DTOp0lON.d.cts} +1 -1
  136. package/dist/{warp-C1gwu2G_.d.ts → warp-pd7hrH4T.d.ts} +1 -1
  137. package/package.json +1 -1
  138. package/skills/pixerate-vector/SKILL.md +22 -0
  139. package/dist/brushstrokeRender-JNHVEUFG.cjs.map +0 -1
  140. package/dist/chunk-64PYNEHJ.js +0 -340
  141. package/dist/chunk-64PYNEHJ.js.map +0 -1
  142. package/dist/chunk-6GFRKLLS.cjs.map +0 -1
  143. package/dist/chunk-7QN76ESJ.js +0 -323
  144. package/dist/chunk-7QN76ESJ.js.map +0 -1
  145. package/dist/chunk-BR7EKQUP.js.map +0 -1
  146. package/dist/chunk-DCLJNVZS.cjs +0 -323
  147. package/dist/chunk-DCLJNVZS.cjs.map +0 -1
  148. package/dist/chunk-EM3FMJWE.js.map +0 -1
  149. package/dist/chunk-HHTCDFZC.js.map +0 -1
  150. package/dist/chunk-J5BA3BQ6.cjs.map +0 -1
  151. package/dist/chunk-YAFS2QFZ.cjs.map +0 -1
  152. package/dist/chunk-Z2H7KECK.cjs.map +0 -1
  153. package/dist/chunk-Z6OUCS2K.cjs +0 -340
  154. package/dist/chunk-Z6OUCS2K.cjs.map +0 -1
  155. package/dist/rand-BszBST4s.d.ts +0 -2140
  156. package/dist/rand-DOH_aMvm.d.cts +0 -2140
  157. /package/dist/{chunk-62XUO5LB.js.map → chunk-3X6IAJUQ.js.map} +0 -0
  158. /package/dist/{chunk-7RJ6P7MI.js.map → chunk-6S6362D2.js.map} +0 -0
  159. /package/dist/{chunk-SVYE5HIM.js.map → chunk-EQQMGO42.js.map} +0 -0
  160. /package/dist/{chunk-OXSV64JX.js.map → chunk-G6IKY6PL.js.map} +0 -0
  161. /package/dist/{chunk-UYQUPUR6.js.map → chunk-HUO3TI4C.js.map} +0 -0
  162. /package/dist/{chunk-2HC4DRF4.js.map → chunk-LLFHCSU2.js.map} +0 -0
  163. /package/dist/{chunk-ILYFOJUN.js.map → chunk-OIBIRXRG.js.map} +0 -0
  164. /package/dist/{chunk-FWEO3HSL.js.map → chunk-PQQF5KOW.js.map} +0 -0
@@ -0,0 +1,1026 @@
1
+ import { a as BlendMode, R as RasterRect, j as TileKey, Y as Pigment } from './surface-1MRncXpF.cjs';
2
+
3
+ /**
4
+ * Gradient paint: a colour slot (the paint colour, a swatch, a stroke's colour) holds a
5
+ * `PaintSource`, either one colour or a gradient that the brush samples dab by dab as the stroke
6
+ * goes: by distance travelled, pen pressure, tilt, speed, direction, time, across the tip, or at
7
+ * random. Shared by the paint editor and the image editor.
8
+ *
9
+ * - **Stops** have the vector and image gradients' shape (`offset`, `color`, `opacity`,
10
+ * `midpoint`); opacity multiplies the brush's flow. `softness` (1, smooth, by default) turns
11
+ * each blend into a harder step towards 0.
12
+ * - **Space**: `pigment` (default) mixes neighbouring stops like paint (Kubelka-Munk, so blue to
13
+ * yellow passes through green), `oklab` perceptually, `srgb` as plain numbers.
14
+ * - **Mapping**: what moves along the gradient. `distance` (default: 300 px, mirrored) is the
15
+ * "drag it out" paint: the colour changes as the stroke travels. It never stretches over the
16
+ * whole stroke, whose length is unknown until the pen lifts.
17
+ *
18
+ * Everything is a pure function of the stroke's recorded samples and seed, so replays, time-lapses,
19
+ * re-rendered exports and drying stay exact.
20
+ */
21
+ /** sRGB 0..1. */
22
+ type PaintRgb = [number, number, number];
23
+ /** A gradient stop: the vector and image gradients' shape. */
24
+ interface PaintGradientStop {
25
+ /** 0..1 along the gradient. */
26
+ offset: number;
27
+ /**
28
+ * #rrggbb, or `paint` / `secondary`: the editor's paint and second colours when the stroke
29
+ * starts (a gradient between whatever two colours are chosen).
30
+ */
31
+ color: string;
32
+ /** 0..1 (default 1): multiplies the brush's flow where this colour is laid. */
33
+ opacity?: number;
34
+ /** Where between this stop and the next (0.05..0.95 of the way) the colours are half mixed (default 0.5). */
35
+ midpoint?: number;
36
+ }
37
+ /** How neighbouring stops mix. */
38
+ type PaintGradientSpace = 'pigment' | 'oklab' | 'srgb';
39
+ declare const PAINT_GRADIENT_SPACES: readonly PaintGradientSpace[];
40
+ /** Past the end of the gradient: hold the last colour, start again, or run back. */
41
+ type PaintGradientRepeat = 'clamp' | 'repeat' | 'mirror';
42
+ declare const PAINT_GRADIENT_REPEATS: readonly PaintGradientRepeat[];
43
+ /** What moves a stroke along its gradient. */
44
+ type PaintGradientMapping =
45
+ /** The stroke's travel: `length` px (or dabs, `unit` 'dabs') per run through the gradient. */
46
+ {
47
+ by: 'distance';
48
+ length: number;
49
+ repeat: PaintGradientRepeat;
50
+ unit?: 'px' | 'dabs';
51
+ }
52
+ /** Pen pressure 0..1; tilt 0 (upright) .. 1 (flat); speed 0 .. 2.5 px/ms; direction of travel 0..360°. */
53
+ | {
54
+ by: 'pressure';
55
+ } | {
56
+ by: 'tilt';
57
+ } | {
58
+ by: 'velocity';
59
+ } | {
60
+ by: 'direction';
61
+ }
62
+ /** Time since the stroke began: `duration` ms per run through the gradient (needs timed samples). */
63
+ | {
64
+ by: 'time';
65
+ duration: number;
66
+ repeat?: PaintGradientRepeat;
67
+ }
68
+ /** Across the tip, left to right looking along the stroke: a brush loaded with several colours. */
69
+ | {
70
+ by: 'across-tip';
71
+ }
72
+ /** A random point of the gradient per dab (or per `every` dabs), seeded so replays match. */
73
+ | {
74
+ by: 'random';
75
+ every?: number;
76
+ };
77
+ type PaintGradientMappingKind = PaintGradientMapping['by'];
78
+ declare const PAINT_GRADIENT_MAPPINGS: readonly PaintGradientMappingKind[];
79
+ interface PaintColorSource {
80
+ type: 'color';
81
+ /** #rrggbb */
82
+ color: string;
83
+ }
84
+ interface PaintGradientSource {
85
+ type: 'gradient';
86
+ /** 2..16 stops, sorted by offset. */
87
+ stops: PaintGradientStop[];
88
+ space: PaintGradientSpace;
89
+ map: PaintGradientMapping;
90
+ /** 0 (hard steps) .. 1 (smooth blends, default). */
91
+ softness?: number;
92
+ }
93
+ /** What a colour slot holds: one colour or a gradient. */
94
+ type PaintSource = PaintColorSource | PaintGradientSource;
95
+ /** "Drag it out": 300 px of travel per run, mirrored back and forth. */
96
+ declare const DEFAULT_PAINT_GRADIENT_MAPPING: Readonly<PaintGradientMapping>;
97
+ /** Speed (px/ms) at which `velocity` reaches the end of the gradient (the brush dynamics' full speed). */
98
+ declare const PAINT_GRADIENT_FULL_SPEED = 2.5;
99
+ declare class PaintSourceError extends Error {
100
+ constructor(message: string);
101
+ }
102
+ /** `#rgb` / `#rrggbb` → lowercase `#rrggbb`, or null. */
103
+ declare function normalizeHexColor(v: unknown): string | null;
104
+ /** `#rrggbb` → sRGB 0..1. */
105
+ declare function hexToPaintRgb(hex: string): PaintRgb;
106
+ /**
107
+ * Reads a paint source strictly: what tools and APIs take. Throws `PaintSourceError` saying what
108
+ * is wrong (unknown type, an unreadable colour, fewer than 2 or more than 16 stops, offsets
109
+ * outside 0..1 or not sorted, a bad mapping) and how to fix it. Missing optional fields get their
110
+ * defaults (space `pigment`, the `distance` mapping, softness 1).
111
+ */
112
+ declare function parsePaintSource(raw: unknown, at?: string): PaintSource;
113
+ /** Reads a paint source leniently (documents, presets): `null` when it is not one. */
114
+ declare function readPaintSource(raw: unknown): PaintSource | null;
115
+ /**
116
+ * The gradient with its `paint` / `secondary` stops replaced by those colours (#rrggbb): what a
117
+ * stroke records and samples.
118
+ */
119
+ declare function resolvePaintGradient(g: PaintGradientSource, colors: {
120
+ paint: string;
121
+ secondary: string;
122
+ }): PaintGradientSource;
123
+ /** The gradient for a stroke scaled by `k` (a high-resolution re-render): pixel distances scale with it. */
124
+ declare function scalePaintGradient(g: PaintGradientSource, k: number): PaintGradientSource;
125
+ /** A colour source. */
126
+ declare function solidPaint(color: string): PaintColorSource;
127
+ /** Whether a source is a gradient. */
128
+ declare function isGradientPaint(s: PaintSource | null | undefined): s is PaintGradientSource;
129
+ /** One colour that stands for the source (the colour, or a gradient's first stop): what a plain colour slot shows. */
130
+ declare function paintSourceColor(s: PaintSource, colors?: {
131
+ paint: string;
132
+ secondary: string;
133
+ }): string;
134
+ /** The source as a CSS `linear-gradient` (or colour) for colour wells and swatches. */
135
+ declare function paintSourceCss(s: PaintSource, colors?: {
136
+ paint: string;
137
+ secondary: string;
138
+ }): string;
139
+ /** A gradient's colour at a dab: sRGB 0..1 and the opacity to multiply the flow by. */
140
+ interface PaintGradientSample {
141
+ rgb: PaintRgb;
142
+ alpha: number;
143
+ }
144
+ /** What a dab offers a mapping: all pure functions of the stroke's recorded samples and seed. */
145
+ interface PaintDabParams {
146
+ /** Path length (px) the stroke has travelled at this dab. */
147
+ travel: number;
148
+ /** The dab's index in its copy of the stroke (0, 1, 2 …). */
149
+ index: number;
150
+ /** 0..1 */
151
+ pressure: number;
152
+ /** 0 upright .. 1 flat. */
153
+ lean: number;
154
+ /** px/ms, or `null` without timed samples. */
155
+ speed: number | null;
156
+ /** Direction of travel, radians counter-clockwise with y up. */
157
+ direction: number;
158
+ /** ms since the stroke's first timed sample, or `null` without timed samples. */
159
+ time: number | null;
160
+ }
161
+ /**
162
+ * A gradient prepared for sampling. `dab` gives the colour of one dab; `across-tip` gradients give
163
+ * a table across the tip instead (`tipTable`).
164
+ */
165
+ declare class PaintGradientSampler {
166
+ readonly source: PaintGradientSource;
167
+ private readonly seed;
168
+ private readonly stops;
169
+ private readonly softness;
170
+ /** Stops at exactly k/(n − 1): segments found by arithmetic, as the multi-colour brush did. */
171
+ private readonly uniform;
172
+ readonly acrossTip: boolean;
173
+ constructor(source: PaintGradientSource, seed?: number);
174
+ /** The two stops' mix `u` of the way (0..1), in the gradient's space. */
175
+ private mix;
176
+ /** The colour at `t` (clamped to 0..1). */
177
+ sample(t: number, out?: PaintGradientSample): PaintGradientSample;
178
+ /** Sampled `travel / length` runs along, past the end as `repeat` says. */
179
+ private along;
180
+ /** The colour of one dab (not for `across-tip`: see `tipTable`). */
181
+ dab(d: PaintDabParams, out?: PaintGradientSample): PaintGradientSample;
182
+ /** `steps` colours from the left edge of the tip to the right (`across-tip`). */
183
+ tipTable(steps: number): PaintGradientSample[];
184
+ }
185
+ /** JSON Schema of a gradient `PaintSource` (what tools accept for gradient paint). */
186
+ declare const PAINT_GRADIENT_JSON_SCHEMA: {
187
+ readonly type: "object";
188
+ readonly description: "Gradient paint: the brush samples it dab by dab as the stroke goes. Example (blue to yellow over 300 px of stroke, and back): { \"type\": \"gradient\", \"stops\": [{ \"offset\": 0, \"color\": \"#1d4ed8\" }, { \"offset\": 1, \"color\": \"#facc15\" }], \"map\": { \"by\": \"distance\", \"length\": 300, \"repeat\": \"mirror\" } }.";
189
+ readonly properties: {
190
+ readonly type: {
191
+ readonly type: "string";
192
+ readonly enum: readonly ["gradient"];
193
+ readonly description: "Always \"gradient\".";
194
+ };
195
+ readonly stops: {
196
+ readonly type: "array";
197
+ readonly minItems: 2;
198
+ readonly maxItems: 16;
199
+ readonly items: {
200
+ readonly type: "object";
201
+ readonly description: "A gradient stop.";
202
+ readonly properties: {
203
+ readonly offset: {
204
+ readonly type: "number";
205
+ readonly minimum: 0;
206
+ readonly maximum: 1;
207
+ readonly description: "Where the stop is, 0..1 along the gradient. Stops must be sorted by offset.";
208
+ };
209
+ readonly color: {
210
+ readonly type: "string";
211
+ readonly pattern: "^(#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|paint|secondary)$";
212
+ readonly description: "#rrggbb, or \"paint\" / \"secondary\": the editor's paint or second colour when the stroke starts.";
213
+ };
214
+ readonly opacity: {
215
+ readonly type: "number";
216
+ readonly minimum: 0;
217
+ readonly maximum: 1;
218
+ readonly description: "0..1 (default 1): multiplies the brush's flow where this colour is laid.";
219
+ };
220
+ readonly midpoint: {
221
+ readonly type: "number";
222
+ readonly minimum: 0.05;
223
+ readonly maximum: 0.95;
224
+ readonly description: "Where between this stop and the next the colours are half mixed, 0.05..0.95 of the way (default 0.5).";
225
+ };
226
+ };
227
+ readonly required: readonly ["offset", "color"];
228
+ readonly additionalProperties: false;
229
+ };
230
+ readonly description: "2..16 stops sorted by offset.";
231
+ };
232
+ readonly space: {
233
+ readonly type: "string";
234
+ readonly enum: readonly PaintGradientSpace[];
235
+ readonly description: "How neighbouring stops mix: pigment (default; like paint, blue to yellow passes green), oklab (perceptual) or srgb (plain numbers).";
236
+ };
237
+ readonly map: {
238
+ readonly type: "object";
239
+ readonly description: "What moves the stroke along the gradient (default distance, 300 px, mirror).";
240
+ readonly properties: {
241
+ readonly by: {
242
+ readonly type: "string";
243
+ readonly enum: readonly ("velocity" | "distance" | "pressure" | "tilt" | "direction" | "across-tip" | "random" | "time")[];
244
+ readonly description: "distance: px (or dabs) of stroke travelled; pressure: pen pressure 0..1; tilt: pen lean, upright 0 .. flat 1; velocity: pen speed, 0 .. 2.5 px/ms; direction: direction of travel, once round per 360°; time: ms since the stroke began (needs timed points); across-tip: left to right across the tip, looking along the stroke (a brush loaded with several colours); random: a random point per dab (seeded, so replays match).";
245
+ };
246
+ readonly length: {
247
+ readonly type: "number";
248
+ readonly minimum: 1;
249
+ readonly maximum: 100000;
250
+ readonly description: "distance: px of travel (or dabs, with unit \"dabs\") per run through the gradient (default 300).";
251
+ };
252
+ readonly repeat: {
253
+ readonly type: "string";
254
+ readonly enum: readonly PaintGradientRepeat[];
255
+ readonly description: "distance and time: past the end, clamp (hold the last colour), repeat (start again) or mirror (run back; default for distance, clamp for time).";
256
+ };
257
+ readonly unit: {
258
+ readonly type: "string";
259
+ readonly enum: readonly ["px", "dabs"];
260
+ readonly description: "distance: px (default) or dabs.";
261
+ };
262
+ readonly duration: {
263
+ readonly type: "number";
264
+ readonly minimum: 1;
265
+ readonly maximum: 600000;
266
+ readonly description: "time: ms per run through the gradient (default 2000).";
267
+ };
268
+ readonly every: {
269
+ readonly type: "integer";
270
+ readonly minimum: 1;
271
+ readonly maximum: 500;
272
+ readonly description: "random: dabs per random pick (default 1).";
273
+ };
274
+ };
275
+ readonly required: readonly ["by"];
276
+ readonly additionalProperties: false;
277
+ };
278
+ readonly softness: {
279
+ readonly type: "number";
280
+ readonly minimum: 0;
281
+ readonly maximum: 1;
282
+ readonly description: "How softly neighbouring stops blend: 1 smooth (default) .. 0 hard steps.";
283
+ };
284
+ };
285
+ readonly required: readonly ["type", "stops"];
286
+ readonly additionalProperties: false;
287
+ };
288
+ /** JSON Schema of a colour `PaintSource`. */
289
+ declare const PAINT_COLOR_JSON_SCHEMA: {
290
+ readonly type: "object";
291
+ readonly description: "One colour: { \"type\": \"color\", \"color\": \"#rrggbb\" }.";
292
+ readonly properties: {
293
+ readonly type: {
294
+ readonly type: "string";
295
+ readonly enum: readonly ["color"];
296
+ readonly description: "Always \"color\".";
297
+ };
298
+ readonly color: {
299
+ readonly type: "string";
300
+ readonly pattern: "^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$";
301
+ readonly description: "#rrggbb";
302
+ };
303
+ };
304
+ readonly required: readonly ["type", "color"];
305
+ readonly additionalProperties: false;
306
+ };
307
+ /** JSON Schema of a `PaintSource`: a colour or gradient paint. */
308
+ declare const PAINT_SOURCE_JSON_SCHEMA: {
309
+ readonly description: "What to paint with: one colour, or gradient paint.";
310
+ readonly oneOf: readonly [{
311
+ readonly type: "object";
312
+ readonly description: "One colour: { \"type\": \"color\", \"color\": \"#rrggbb\" }.";
313
+ readonly properties: {
314
+ readonly type: {
315
+ readonly type: "string";
316
+ readonly enum: readonly ["color"];
317
+ readonly description: "Always \"color\".";
318
+ };
319
+ readonly color: {
320
+ readonly type: "string";
321
+ readonly pattern: "^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$";
322
+ readonly description: "#rrggbb";
323
+ };
324
+ };
325
+ readonly required: readonly ["type", "color"];
326
+ readonly additionalProperties: false;
327
+ }, {
328
+ readonly type: "object";
329
+ readonly description: "Gradient paint: the brush samples it dab by dab as the stroke goes. Example (blue to yellow over 300 px of stroke, and back): { \"type\": \"gradient\", \"stops\": [{ \"offset\": 0, \"color\": \"#1d4ed8\" }, { \"offset\": 1, \"color\": \"#facc15\" }], \"map\": { \"by\": \"distance\", \"length\": 300, \"repeat\": \"mirror\" } }.";
330
+ readonly properties: {
331
+ readonly type: {
332
+ readonly type: "string";
333
+ readonly enum: readonly ["gradient"];
334
+ readonly description: "Always \"gradient\".";
335
+ };
336
+ readonly stops: {
337
+ readonly type: "array";
338
+ readonly minItems: 2;
339
+ readonly maxItems: 16;
340
+ readonly items: {
341
+ readonly type: "object";
342
+ readonly description: "A gradient stop.";
343
+ readonly properties: {
344
+ readonly offset: {
345
+ readonly type: "number";
346
+ readonly minimum: 0;
347
+ readonly maximum: 1;
348
+ readonly description: "Where the stop is, 0..1 along the gradient. Stops must be sorted by offset.";
349
+ };
350
+ readonly color: {
351
+ readonly type: "string";
352
+ readonly pattern: "^(#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|paint|secondary)$";
353
+ readonly description: "#rrggbb, or \"paint\" / \"secondary\": the editor's paint or second colour when the stroke starts.";
354
+ };
355
+ readonly opacity: {
356
+ readonly type: "number";
357
+ readonly minimum: 0;
358
+ readonly maximum: 1;
359
+ readonly description: "0..1 (default 1): multiplies the brush's flow where this colour is laid.";
360
+ };
361
+ readonly midpoint: {
362
+ readonly type: "number";
363
+ readonly minimum: 0.05;
364
+ readonly maximum: 0.95;
365
+ readonly description: "Where between this stop and the next the colours are half mixed, 0.05..0.95 of the way (default 0.5).";
366
+ };
367
+ };
368
+ readonly required: readonly ["offset", "color"];
369
+ readonly additionalProperties: false;
370
+ };
371
+ readonly description: "2..16 stops sorted by offset.";
372
+ };
373
+ readonly space: {
374
+ readonly type: "string";
375
+ readonly enum: readonly PaintGradientSpace[];
376
+ readonly description: "How neighbouring stops mix: pigment (default; like paint, blue to yellow passes green), oklab (perceptual) or srgb (plain numbers).";
377
+ };
378
+ readonly map: {
379
+ readonly type: "object";
380
+ readonly description: "What moves the stroke along the gradient (default distance, 300 px, mirror).";
381
+ readonly properties: {
382
+ readonly by: {
383
+ readonly type: "string";
384
+ readonly enum: readonly ("velocity" | "distance" | "pressure" | "tilt" | "direction" | "across-tip" | "random" | "time")[];
385
+ readonly description: "distance: px (or dabs) of stroke travelled; pressure: pen pressure 0..1; tilt: pen lean, upright 0 .. flat 1; velocity: pen speed, 0 .. 2.5 px/ms; direction: direction of travel, once round per 360°; time: ms since the stroke began (needs timed points); across-tip: left to right across the tip, looking along the stroke (a brush loaded with several colours); random: a random point per dab (seeded, so replays match).";
386
+ };
387
+ readonly length: {
388
+ readonly type: "number";
389
+ readonly minimum: 1;
390
+ readonly maximum: 100000;
391
+ readonly description: "distance: px of travel (or dabs, with unit \"dabs\") per run through the gradient (default 300).";
392
+ };
393
+ readonly repeat: {
394
+ readonly type: "string";
395
+ readonly enum: readonly PaintGradientRepeat[];
396
+ readonly description: "distance and time: past the end, clamp (hold the last colour), repeat (start again) or mirror (run back; default for distance, clamp for time).";
397
+ };
398
+ readonly unit: {
399
+ readonly type: "string";
400
+ readonly enum: readonly ["px", "dabs"];
401
+ readonly description: "distance: px (default) or dabs.";
402
+ };
403
+ readonly duration: {
404
+ readonly type: "number";
405
+ readonly minimum: 1;
406
+ readonly maximum: 600000;
407
+ readonly description: "time: ms per run through the gradient (default 2000).";
408
+ };
409
+ readonly every: {
410
+ readonly type: "integer";
411
+ readonly minimum: 1;
412
+ readonly maximum: 500;
413
+ readonly description: "random: dabs per random pick (default 1).";
414
+ };
415
+ };
416
+ readonly required: readonly ["by"];
417
+ readonly additionalProperties: false;
418
+ };
419
+ readonly softness: {
420
+ readonly type: "number";
421
+ readonly minimum: 0;
422
+ readonly maximum: 1;
423
+ readonly description: "How softly neighbouring stops blend: 1 smooth (default) .. 0 hard steps.";
424
+ };
425
+ };
426
+ readonly required: readonly ["type", "stops"];
427
+ readonly additionalProperties: false;
428
+ }];
429
+ };
430
+ /** Labels for the mapping picker. */
431
+ declare const PAINT_GRADIENT_MAPPING_LABELS: Readonly<Record<PaintGradientMappingKind, string>>;
432
+ declare const PAINT_GRADIENT_SPACE_LABELS: Readonly<Record<PaintGradientSpace, string>>;
433
+ declare const PAINT_GRADIENT_REPEAT_LABELS: Readonly<Record<PaintGradientRepeat, string>>;
434
+ /** The paint colour to the second colour, changing as the stroke is dragged out: where a gradient editor starts. */
435
+ declare const DEFAULT_PAINT_GRADIENT: Readonly<PaintGradientSource>;
436
+ /** A few starting gradients for gradient editors. */
437
+ declare const PAINT_GRADIENT_PRESETS: readonly {
438
+ name: string;
439
+ paint: PaintGradientSource;
440
+ }[];
441
+ /** Adds a stop at `offset` with the gradient's colour there. Returns the gradient and the new stop's index. */
442
+ declare function addPaintGradientStop(g: PaintGradientSource, offset: number, colors?: {
443
+ paint: string;
444
+ secondary: string;
445
+ }): {
446
+ paint: PaintGradientSource;
447
+ index: number;
448
+ };
449
+ /** Removes stop `index` (a gradient keeps at least 2). */
450
+ declare function removePaintGradientStop(g: PaintGradientSource, index: number): PaintGradientSource;
451
+ /** Changes stop `index` (moving it keeps the stops sorted). Returns the gradient and where the stop is now. */
452
+ declare function updatePaintGradientStop(g: PaintGradientSource, index: number, patch: Partial<PaintGradientStop>): {
453
+ paint: PaintGradientSource;
454
+ index: number;
455
+ };
456
+ /** The gradient with its stops reversed (midpoints measured from the other side). */
457
+ declare function reversePaintGradient(g: PaintGradientSource): PaintGradientSource;
458
+ /** The mapping for `by`, keeping what carries over from `current` (length, repeat, duration …). */
459
+ declare function paintGradientMappingFor(by: PaintGradientMappingKind, current: PaintGradientMapping): PaintGradientMapping;
460
+ /** Everything a gradient-paint editor UI needs, in one object (framework bindings take it from their own entry). */
461
+ declare const PAINT_GRADIENT_KIT: {
462
+ readonly PAINT_GRADIENT_MAPPINGS: readonly ("velocity" | "distance" | "pressure" | "tilt" | "direction" | "across-tip" | "random" | "time")[];
463
+ readonly PAINT_GRADIENT_MAPPING_LABELS: Readonly<Record<"velocity" | "distance" | "pressure" | "tilt" | "direction" | "across-tip" | "random" | "time", string>>;
464
+ readonly PAINT_GRADIENT_PRESETS: readonly {
465
+ name: string;
466
+ paint: PaintGradientSource;
467
+ }[];
468
+ readonly PAINT_GRADIENT_REPEATS: readonly PaintGradientRepeat[];
469
+ readonly PAINT_GRADIENT_REPEAT_LABELS: Readonly<Record<PaintGradientRepeat, string>>;
470
+ readonly PAINT_GRADIENT_SPACES: readonly PaintGradientSpace[];
471
+ readonly PAINT_GRADIENT_SPACE_LABELS: Readonly<Record<PaintGradientSpace, string>>;
472
+ readonly addPaintGradientStop: typeof addPaintGradientStop;
473
+ readonly paintGradientMappingFor: typeof paintGradientMappingFor;
474
+ readonly paintSourceCss: typeof paintSourceCss;
475
+ readonly removePaintGradientStop: typeof removePaintGradientStop;
476
+ readonly reversePaintGradient: typeof reversePaintGradient;
477
+ readonly updatePaintGradientStop: typeof updatePaintGradientStop;
478
+ };
479
+ type PaintGradientKit = typeof PAINT_GRADIENT_KIT;
480
+
481
+ /**
482
+ * The brush engine (a subset of photocraft's `crates/paint`): a computed round tip with size,
483
+ * hardness, angle, roundness and spacing; pressure to size and opacity; smoothing; and Photoshop's
484
+ * opacity-versus-flow build-up. Strokes are data, so one replays the same way for undo, agents
485
+ * and tests.
486
+ *
487
+ * - Points are walked into dabs at `spacing × diameter` (`walkDabs`).
488
+ * - Each dab's coverage (`dabCoverage`, photocraft's measured Photoshop profile) builds up in a
489
+ * stroke coverage map with flow towards the dab's opacity ceiling: `c ← c + v·(ceil − c)`.
490
+ * - The stroke composites once at the stroke opacity over the layer as it was before the stroke,
491
+ * so overlapping dabs never exceed the opacity.
492
+ */
493
+
494
+ /** One input sample. Missing pressure is a mouse (1). */
495
+ interface StrokePoint {
496
+ x: number;
497
+ y: number;
498
+ /** 0..1 (default 1). */
499
+ pressure?: number;
500
+ /** Milliseconds (optional; kept for replays). */
501
+ t?: number;
502
+ }
503
+ interface BrushSettings {
504
+ /** Diameter in pixels (1..2500, default 20). */
505
+ size: number;
506
+ /** 0 (soft) .. 1 (hard, default). */
507
+ hardness: number;
508
+ /** Dab spacing as a fraction of the diameter (0.01..10, default 0.1). */
509
+ spacing: number;
510
+ /** Tip angle in degrees (default 0). */
511
+ angle: number;
512
+ /** Tip roundness 0.01..1 (default 1). */
513
+ roundness: number;
514
+ /** Stroke opacity ceiling 0..1 (default 1). */
515
+ opacity: number;
516
+ /** Build-up per dab 0..1 (default 1). */
517
+ flow: number;
518
+ /** Input smoothing 0..0.95 (default 0). */
519
+ smoothing: number;
520
+ /** Pressure controls size (default true). */
521
+ pressureSize: boolean;
522
+ /** Pressure controls opacity (default false). */
523
+ pressureOpacity: boolean;
524
+ /** How the stroke paints onto the layer (default normal). */
525
+ blend: BlendMode;
526
+ }
527
+ declare const DEFAULT_BRUSH: Readonly<BrushSettings>;
528
+ declare const MAX_BRUSH_SIZE = 2500;
529
+ /** A brush with every field in range (missing fields from `DEFAULT_BRUSH`). */
530
+ declare function normalizeBrush(raw?: Partial<BrushSettings>): BrushSettings;
531
+ /** The core radius and shoulder width (pixels) of a round tip of `radius` and `hardness`. */
532
+ declare function tipShape(radius: number, hardness: number): [number, number];
533
+ /** Opacity of a round tip at distance `d` from its centre. */
534
+ declare function tipFalloff(d: number, radius: number, hardness: number): number;
535
+ /** How far from its centre a round tip still paints. */
536
+ declare function tipReach(radius: number, hardness: number): number;
537
+ /** Coverage of a round dab at distance `d` from its centre. */
538
+ declare function dabCoverage(d: number, radius: number, hardness: number): number;
539
+ interface Dab {
540
+ x: number;
541
+ y: number;
542
+ radius: number;
543
+ /** Flow (per-dab build-up). */
544
+ alpha: number;
545
+ /** Opacity ceiling this dab builds up to. */
546
+ opacity: number;
547
+ /** Radians. */
548
+ angle: number;
549
+ roundness: number;
550
+ /** What gradient paint maps by (travel, index, pressure, time …); set by the dab walkers. */
551
+ params?: PaintDabParams;
552
+ }
553
+ /** Exponential smoothing of input points; the first and last point are kept. */
554
+ declare function smoothPoints(points: readonly StrokePoint[], amount: number): StrokePoint[];
555
+ /**
556
+ * Walks points into dabs (incrementally: feed points in any chunks and get the same dabs). The
557
+ * first point is a dab; then one every `spacing × diameter` along the path.
558
+ */
559
+ declare class DabWalker {
560
+ readonly brush: BrushSettings;
561
+ private last;
562
+ private nextAt;
563
+ private pendingFirst;
564
+ private index;
565
+ private travelled;
566
+ private dir;
567
+ private firstT;
568
+ private lastSpeedT;
569
+ private lastSpeedAt;
570
+ private speed;
571
+ constructor(brush: BrushSettings);
572
+ private diameter;
573
+ private stepLen;
574
+ private dab;
575
+ /** Tracks time and a smoothed speed from timed samples (for gradient paint). */
576
+ private timeOf;
577
+ push(p: StrokePoint, out: Dab[]): void;
578
+ finish(out: Dab[]): void;
579
+ }
580
+ /** All dabs of a point list (smoothing applied). */
581
+ declare function walkDabs(points: readonly StrokePoint[], brush: BrushSettings): Dab[];
582
+ /** The pixel rectangle a dab can touch. */
583
+ declare function dabRect(d: Dab, hardness: number): RasterRect;
584
+ /**
585
+ * Rasterizes one dab over `rect` into `out` (rect.width × rect.height values, coverage × flow).
586
+ * Small tips (radius below 3 px) take each pixel's covered area from a 4×4 grid.
587
+ */
588
+ declare function rasterizeDab(d: Dab, hardness: number, rect: RasterRect, out: Float32Array): void;
589
+ /** A stroke's accumulated coverage, in 256 px tiles over document space. */
590
+ declare class CoverageMap {
591
+ readonly tiles: Map<number, Float32Array<ArrayBufferLike>>;
592
+ /**
593
+ * Gradient paint: the colour laid at each pixel (sRGB 0..1, 3 per pixel), when dabs carry their
594
+ * own colours. Each dab blends its colour in by the share of coverage it adds. Missing: the
595
+ * stroke's one colour (`StrokeApply.color`).
596
+ */
597
+ readonly colors: Map<number, Float32Array<ArrayBufferLike>>;
598
+ bounds: RasterRect | null;
599
+ private dirty;
600
+ /** The colour at a pixel (gradient paint), or `null` when the stroke has one colour there. */
601
+ colorAt(x: number, y: number, out: number[]): number[] | null;
602
+ get(x: number, y: number): number;
603
+ /**
604
+ * Builds up `vals` (over `rect`) with flow towards `ceil`: `c ← c + v·(ceil − c)`. With `color`
605
+ * (one sRGB colour for the dab) or `field` (sRGB per pixel of `rect`, 3 each), the pixels' colours
606
+ * move towards it by the share of coverage this dab adds (gradient paint).
607
+ */
608
+ accumulate(rect: RasterRect, vals: Float32Array, ceil: number, color?: readonly number[] | null, field?: Float32Array | null): void;
609
+ /** Tiles changed since the last call. */
610
+ takeDirty(): TileKey[];
611
+ }
612
+ /** Paints a stroke's dabs into a coverage map, incrementally. */
613
+ declare class StrokeCoverage {
614
+ readonly brush: BrushSettings;
615
+ readonly coverage: CoverageMap;
616
+ private walker;
617
+ private smoothing;
618
+ private smoothPos;
619
+ private buf;
620
+ private field;
621
+ private readonly sampler;
622
+ private readonly tipTable;
623
+ private readonly sample;
624
+ dabCount: number;
625
+ /** `paint`: a gradient source (resolved: no `paint` / `secondary` stops) for gradient paint; `seed` for its `random` mapping. */
626
+ constructor(brush: BrushSettings, paint?: PaintGradientSource | null, seed?: number);
627
+ /** Feeds input points; returns the document rectangles the new dabs touched. */
628
+ push(points: readonly StrokePoint[]): RasterRect[];
629
+ /** Ends the stroke (a single click still paints one dab). */
630
+ finish(last?: StrokePoint): RasterRect[];
631
+ private smooth;
632
+ private paint;
633
+ private paintDab;
634
+ }
635
+ /** Colours across the tip of an `across-tip` gradient. */
636
+ declare const TIP_GRADIENT_STEPS = 32;
637
+ /**
638
+ * Gradient paint for one dab: the dab's colour (or, across the tip, a colour per pixel) and its
639
+ * opacity, which multiplies the flow. Shared by every coverage stroke.
640
+ */
641
+ declare function gradientDabColor(sampler: PaintGradientSampler, tipTable: readonly PaintGradientSample[] | null, d: Dab, params: PaintDabParams, rect: RasterRect, vals: Float32Array, scratch: {
642
+ field: Float32Array;
643
+ sample: PaintGradientSample;
644
+ }): {
645
+ color: PaintRgb | null;
646
+ field: Float32Array | null;
647
+ };
648
+ /** How a stroke changes pixels. */
649
+ interface StrokeApply {
650
+ /** `paint` puts colour down; `erase` removes alpha (or, with transparency locked, paints `background`). */
651
+ mode: 'paint' | 'erase';
652
+ /** RGBA 0..255 (paint colour, or the mask grey in `r`). */
653
+ color: readonly [number, number, number, number];
654
+ /** RGBA 0..255 the eraser paints on a transparency-locked layer (and on masks). */
655
+ background: readonly [number, number, number, number];
656
+ opacity: number;
657
+ blend: BlendMode;
658
+ lockTransparency: boolean;
659
+ /** Selection coverage (document space, canvas-sized), or `null`. */
660
+ selection: {
661
+ width: number;
662
+ height: number;
663
+ data: ArrayLike<number>;
664
+ } | null;
665
+ /** Layer offset (target pixels are in layer space). */
666
+ offset: {
667
+ x: number;
668
+ y: number;
669
+ };
670
+ }
671
+ /**
672
+ * Composites the stroke's coverage over `tileData` (the target's tile (tx, ty) as it was before
673
+ * the stroke, in layer space; RGBA or one byte for masks) and returns the new tile data. With
674
+ * `into` (the tile's previous result) and `region` (document space), only `region` is recomputed
675
+ * and the rest of `into` is kept.
676
+ */
677
+ declare function applyStrokeToTile(coverage: CoverageMap, tileData: Uint8ClampedArray, channels: 1 | 4, tx: number, ty: number, s: StrokeApply, options?: {
678
+ into?: Uint8ClampedArray;
679
+ region?: RasterRect;
680
+ }): Uint8ClampedArray;
681
+ /**
682
+ * `applyStrokeToTile` at high bit depth: composites the stroke's coverage over deep RGBA
683
+ * samples (`Uint16Array` sRGB or `Float32Array` linear light, see `raster/deep`) of the target's
684
+ * tile (tx, ty) as it was before the stroke, and returns new samples of the same type. Blending
685
+ * runs in float in the samples' own space (sRGB at 16 bits, linear light at 32 bits); the paint
686
+ * colours are 8-bit sRGB and converted. With `options.channels` 1, a mask's coverage samples
687
+ * (16-bit, or float 0..1) move towards the paint's grey, as `applyStrokeToTile` does at 8 bits.
688
+ */
689
+ declare function applyStrokeToDeepTile(coverage: CoverageMap, samples: Uint16Array | Float32Array, tx: number, ty: number, s: StrokeApply, options?: {
690
+ into?: Uint16Array | Float32Array;
691
+ region?: RasterRect;
692
+ channels?: 1 | 4;
693
+ }): Uint16Array | Float32Array;
694
+ /** Tile coordinates of coverage tile keys (for callers that track dirty tiles). */
695
+ declare function coverageTileCoord(key: TileKey): {
696
+ tx: number;
697
+ ty: number;
698
+ };
699
+
700
+ /**
701
+ * The pick-up / mix exchange between a loaded brush and the paint under it: the one mechanism
702
+ * every wet-capable brush shares. The Mixer Brush (`MixerStroke`) and the paint editor's
703
+ * wet-capable brushes run on it now; the wet layer (phase 3) plugs in as another `PaintMedium`.
704
+ *
705
+ * Two halves:
706
+ *
707
+ * - `PaintExchange` is what the brush carries: a **reservoir** of loaded paint (with an amount
708
+ * that drains per dab) and a **well** of paint picked up from the canvas. Per dab it
709
+ * 1. picks up: the well moves towards the colour under the dab by the pick-up rate times how
710
+ * much of the footprint holds paint (`pickUp`);
711
+ * 2. mixes the dab's paint: reservoir weight `amount × (1 − mix)`, well weight `mix`
712
+ * (`paint`);
713
+ * 3. drains the reservoir (`drain`).
714
+ * Every mix is RGB or Kubelka-Munk pigment (`mixing`), so a blue brush through yellow goes
715
+ * green.
716
+ * - `PaintMedium` is what the brush touches: `sample` (the colour and contact under a footprint)
717
+ * and `deposit` (lay paint at per-pixel amounts). `TileMedium` is the raster layer's pixels.
718
+ * A wet layer implements the same two calls over its wet pigment and water (sampling only wet
719
+ * paint, so pick-up lifts what is still wet), and nothing in `PaintExchange` changes.
720
+ *
721
+ * The ratios a user sets are `pickup` (pick-up ratio, 0..1) and `mix` (mix ratio, 0..1);
722
+ * `exchangeRates` turns them into per-dab rates with the Mixer Brush's constants, so a Mixer
723
+ * Brush's `wet` and `mix` are exactly these two ratios. Everything is data in, pixels out.
724
+ */
725
+
726
+ /** How colours mix: straight sRGB averages, or like paint (Kubelka-Munk). */
727
+ type MixingModel = 'rgb' | 'pigment';
728
+ declare const MIXING_MODELS: readonly MixingModel[];
729
+ /** An sRGB colour, 0..1 per channel. */
730
+ type RgbTriple = [number, number, number];
731
+ /** What a brush carries between dabs and strokes. Colours are straight sRGB, 0..1. */
732
+ interface MixerLoad {
733
+ /** Loaded paint, or `null` when the brush is unloaded. */
734
+ readonly reservoir: readonly [number, number, number] | null;
735
+ /** Reservoir left, 0..1. */
736
+ readonly amount: number;
737
+ /** Colour picked up from the canvas (the well), or `null` when the brush is clean. */
738
+ readonly pickup: readonly [number, number, number] | null;
739
+ }
740
+ declare const EMPTY_MIXER_LOAD: MixerLoad;
741
+ /** Fraction of the way the well moves towards the canvas colour per dab at a pick-up ratio of 1. */
742
+ declare const MIXER_PICKUP_RATE = 0.25;
743
+ /** Fraction of the reservoir that drains per dab at a load of 0. */
744
+ declare const MIXER_DRAIN = 0.08;
745
+ /** Per-dab rates of an exchange. */
746
+ interface ExchangeRates {
747
+ /** How far the well moves towards the canvas colour per dab at full contact (0: lifts nothing). */
748
+ pickup: number;
749
+ /** The well's weight in each dab's paint; the reservoir gets `amount × (1 − mix)`. */
750
+ mix: number;
751
+ /** Fraction of the reservoir lost per dab. */
752
+ drain: number;
753
+ mixing: MixingModel;
754
+ }
755
+ /**
756
+ * Per-dab rates from the ratios a user sets: pick-up ratio `pickup` (0 dry .. 1), mix ratio
757
+ * `mix` (0 .. 1, scaled by the pick-up ratio: a brush that lifts nothing has nothing to mix) and
758
+ * `load` (1 never drains). The Mixer Brush passes its `wet`, `mix` and `load`.
759
+ */
760
+ declare function exchangeRates(pickup: number, mix: number, load: number, mixing: MixingModel): ExchangeRates;
761
+ /** What a footprint holds: its average paint colour and how much of it holds paint (0..1). */
762
+ interface PaintSample {
763
+ rgb: RgbTriple;
764
+ /** Paint-weighted share of the footprint (alpha × coverage over coverage). */
765
+ contact: number;
766
+ }
767
+ /** The brush side of the exchange: reservoir, amount and well, mixed per dab. */
768
+ declare class PaintExchange {
769
+ readonly rates: ExchangeRates;
770
+ private reservoir;
771
+ private amount;
772
+ private well;
773
+ private readonly pig;
774
+ private readonly out;
775
+ constructor(rates: ExchangeRates, load: MixerLoad);
776
+ /** What the brush carries now. */
777
+ get load(): MixerLoad;
778
+ /** The colour picked up so far, or `null`. */
779
+ get pickup(): Readonly<RgbTriple> | null;
780
+ /** The prepared pigment of the last `paint` colour (pigment mixing only), for `deposit`. */
781
+ get paintPigment(): Pigment | null;
782
+ /** Recolours the reservoir without refilling it (a multi-coloured brush, per dab). */
783
+ setReservoirColor(r: number, g: number, b: number): void;
784
+ /** `a` mixed `t` of the way to `b` into `this.out`, by the mixing model. */
785
+ private mix;
786
+ /** `a` mixed `t` of the way to `b` (sRGB 0..1) by the mixing model, into `out`. */
787
+ mixColors(a: readonly number[], b: readonly number[], t: number, out: number[]): number[];
788
+ /** 1. Pick-up: the well moves towards the sampled colour (or takes it, when clean). */
789
+ pickUp(sample: PaintSample | null): void;
790
+ /**
791
+ * 2. The dab's paint: the reservoir mixed with the well, written to `out`. Returns the total
792
+ * weight (≤ 0: nothing to lay down); deposit `min(1, weight)` of it. With pigment mixing,
793
+ * `paintPigment` is the result's pigment afterwards.
794
+ */
795
+ paint(out: RgbTriple): number;
796
+ /** 3. The reservoir drains. */
797
+ drain(): void;
798
+ }
799
+ /**
800
+ * A brush's working tile: 8-bit bytes, or (on a 16- or 32-bit document) floats on the same
801
+ * 0..255 sRGB-encoded scale, which keep the fractions an 8-bit tile would round away.
802
+ */
803
+ type PaintPixels = Uint8ClampedArray | Float32Array;
804
+ /** Writes `v` (0..1) into a working tile: rounded for bytes, kept exact (not below 0) for floats. */
805
+ declare function put255(px: PaintPixels, i: number, v: number): void;
806
+ /** The pixels a brush reads and writes (RGBA tiles in layer space). */
807
+ interface PaintTileTarget {
808
+ /** The tile (TILE × TILE × 4) at (tx, ty) as it is now, or `null` when it is fully transparent. */
809
+ readTile(tx: number, ty: number): PaintPixels | null;
810
+ /** A writable tile at (tx, ty), returned by `readTile` from then on. */
811
+ writeTile(tx: number, ty: number): PaintPixels;
812
+ /** Layer offset: layer pixel = document pixel − offset. */
813
+ offset: {
814
+ x: number;
815
+ y: number;
816
+ };
817
+ /** Keep alpha and paint only where the layer has pixels. */
818
+ lockTransparency: boolean;
819
+ /** Selection coverage (document space, canvas-sized), or `null`. */
820
+ selection: {
821
+ width: number;
822
+ height: number;
823
+ data: ArrayLike<number>;
824
+ } | null;
825
+ }
826
+ /** The canvas side of the exchange: where a brush picks paint up and lays it down. */
827
+ interface PaintMedium {
828
+ /** The paint under a footprint (`cov` over `rect`, document space), or `null` where there is none. */
829
+ sample(rect: RasterRect, cov: ArrayLike<number>): PaintSample | null;
830
+ /**
831
+ * Lays paint over the footprint at `dep × cov` per pixel. The colour is `paint` (with its
832
+ * prepared pigment for pigment mixing), or per pixel from `field` (3 floats per pixel of
833
+ * `rect`, sRGB 0..1) when given.
834
+ */
835
+ deposit(rect: RasterRect, cov: ArrayLike<number>, dep: number, paint: Readonly<RgbTriple>, paintPigment: Pigment | null, field?: Float32Array | null): void;
836
+ }
837
+ interface TileMediumOptions {
838
+ /**
839
+ * How deposits combine with the pixels. Missing: the exchange's mixing (source-over, or a
840
+ * pigment mix with pigment mixing), as the Mixer Brush paints. Otherwise any blend mode, as a
841
+ * brush tip's `blend`.
842
+ */
843
+ blend?: BlendMode;
844
+ }
845
+ /** A raster layer's pixels as a `PaintMedium` (through live tiles). */
846
+ declare class TileMedium implements PaintMedium {
847
+ readonly target: PaintTileTarget;
848
+ private readonly options;
849
+ private tx;
850
+ private ty;
851
+ private tile;
852
+ private writable;
853
+ private readonly canvasPig;
854
+ private readonly paintPig;
855
+ private paintKey;
856
+ private readonly cb;
857
+ private readonly src;
858
+ private readonly o;
859
+ constructor(target: PaintTileTarget, options?: TileMediumOptions);
860
+ /** Forgets the cached tile (call when another writer may have replaced tiles). */
861
+ reset(): void;
862
+ /**
863
+ * The tile holding document pixel (dx, dy) (`pixels`) and the pixel's byte index in it, or -1
864
+ * when reading an empty tile.
865
+ */
866
+ locate(dx: number, dy: number, write: boolean): number;
867
+ /** The tile `locate` last found. */
868
+ get pixels(): PaintPixels;
869
+ /** Selection coverage at a document pixel, 0..1 (1 without a selection). */
870
+ selectionAt(dx: number, dy: number): number;
871
+ sample(rect: RasterRect, cov: ArrayLike<number>): PaintSample | null;
872
+ deposit(rect: RasterRect, cov: ArrayLike<number>, dep: number, paint: Readonly<RgbTriple>, paintPigment: Pigment | null, field?: Float32Array | null): void;
873
+ }
874
+
875
+ /**
876
+ * The Mixer Brush: a reservoir of loaded paint plus a pickup well that takes up colour from the
877
+ * canvas under each dab (photocraft's behavioural model):
878
+ *
879
+ * - Each dab samples the canvas under its footprint (coverage- and alpha-weighted). The pickup
880
+ * colour `P` moves towards that sample by `PICKUP_RATE × wet` (scaled by the sample's coverage).
881
+ * - The dab's paint mixes the reservoir `R` (weight `amount × (1 − mix·wet)`) with the pickup
882
+ * (weight `mix·wet`) and lands at `flow × opacity` times the total weight, over the pixels as
883
+ * they are now, so earlier dabs of the same stroke are picked up again (smearing).
884
+ * - The reservoir drains per dab unless `load` is 1, so low-load strokes run dry. A dry brush
885
+ * (`wet` 0) paints only reservoir colour; an empty, wet brush smears canvas colour.
886
+ *
887
+ * `mixing: 'pigment'` does all three mixes (pickup, paint and deposit) with the Kubelka-Munk model
888
+ * of `./pigment`, so a blue brush dragged through yellow makes green instead of grey. Everything
889
+ * is data in, pixels out: the same canvas, points, settings and load replay identically.
890
+ */
891
+
892
+ /** Mixer Brush options. */
893
+ interface MixerSettings {
894
+ /** How much colour the brush picks up from the canvas (the pick-up ratio), 0 (dry) .. 1 (default 0.5). */
895
+ wet: number;
896
+ /** How much paint the reservoir holds, 0 .. 1 (default 0.5): below 1 it drains per dab and the stroke runs dry. */
897
+ load: number;
898
+ /** How far each dab's paint moves from the reservoir towards the picked-up colour (the mix ratio), 0 .. 1 (default 0.5). */
899
+ mix: number;
900
+ /** Refill the reservoir with the paint colour at the start of each stroke (default true). */
901
+ loadEachStroke: boolean;
902
+ /** Empty the pickup well after each stroke (default true); off keeps the brush dirty for the next stroke. */
903
+ cleanEachStroke: boolean;
904
+ /** `rgb` (default) or `pigment` (blue and yellow make green). */
905
+ mixing: MixingModel;
906
+ }
907
+ declare const DEFAULT_MIXER: Readonly<MixerSettings>;
908
+ /** Mixer settings with every field in range (missing fields from `DEFAULT_MIXER`). */
909
+ declare function normalizeMixer(raw?: Partial<MixerSettings>): MixerSettings;
910
+ /** The per-dab exchange rates of Mixer Brush settings (`wet` is the pick-up ratio, `mix` the mix ratio). */
911
+ declare function mixerRates(settings: MixerSettings): ExchangeRates;
912
+ /** The brush loaded full with `color` (sRGB 0..1); the pickup well is kept. */
913
+ declare function loadMixer(load: MixerLoad, color: readonly [number, number, number]): MixerLoad;
914
+ /** The brush with its pickup well emptied; the reservoir is kept. */
915
+ declare function cleanMixer(load: MixerLoad): MixerLoad;
916
+ /** The pixels a mixer stroke reads and writes (RGBA tiles in layer space). */
917
+ type MixerTarget = PaintTileTarget;
918
+ /**
919
+ * One Mixer Brush stroke. Feed points with `push` (any chunks give the same dabs), end with
920
+ * `finish`; `load` is the brush load after the dabs so far. Each call returns the document
921
+ * rectangles it touched. Runs on the shared pick-up / mix exchange (`./exchange`).
922
+ */
923
+ declare class MixerStroke {
924
+ readonly brush: BrushSettings;
925
+ dabCount: number;
926
+ bounds: RasterRect | null;
927
+ private walker;
928
+ private smoothPos;
929
+ private buf;
930
+ private readonly exchange;
931
+ private readonly medium;
932
+ private readonly paintColor;
933
+ constructor(brush: BrushSettings, settings: Partial<MixerSettings>, load: MixerLoad, target: MixerTarget);
934
+ /** The brush load after the dabs so far. */
935
+ get load(): MixerLoad;
936
+ push(points: readonly StrokePoint[]): RasterRect[];
937
+ finish(last?: StrokePoint): RasterRect[];
938
+ private smooth;
939
+ private paint;
940
+ private dab;
941
+ }
942
+
943
+ /**
944
+ * Blending tools on raster tiles, dab by dab (any dab walker can drive them):
945
+ *
946
+ * - **Blender** (`blenderDab`): every pixel under the tip moves towards the mix of the paint
947
+ * within `distance` of it, so neighbouring colours run together. The mix is taken through the
948
+ * pigment model: the neighbourhood's K/S spectra are averaged with Kubelka-Munk weights
949
+ * (alpha × scattering), so blue next to yellow blends into green, not grey. `rgb` averages
950
+ * straight colours instead.
951
+ * - **Smudge** (`SmudgeTip`): a finger that carries a patch of paint. Each dab lays the patch it
952
+ * carries over the pixels under the tip (`strength`) and picks up some of the paint that was
953
+ * there (`1 − length`), so the paint's pattern is dragged along the stroke and fades with distance.
954
+ * Optional pigment mixing for both steps.
955
+ *
956
+ * Both blend alpha too: blending next to bare canvas softens an edge, smudging drags paint out
957
+ * into it. A transparency-locked layer keeps its alpha. Pure data in, pixels out.
958
+ */
959
+
960
+ /** Blender options. */
961
+ interface BlenderSettings {
962
+ /** How far each pixel moves towards the neighbourhood mix per dab at full coverage, 0..1 (default 0.5). */
963
+ strength: number;
964
+ /** Blend distance: the neighbourhood's radius as a fraction of the tip diameter, 0..1 (default 0.35). */
965
+ distance: number;
966
+ /** `pigment` (default: Kubelka-Munk, blue next to yellow blends green) or `rgb`. */
967
+ mixing: MixingModel;
968
+ }
969
+ declare const DEFAULT_BLENDER: Readonly<BlenderSettings>;
970
+ /** Smudge options. */
971
+ interface SmudgeSettings {
972
+ /** How strongly the carried paint covers the pixels under the tip per dab, 0..1 (default 0.7). */
973
+ strength: number;
974
+ /** How far paint is dragged: the share of the carried patch kept per dab, 0..1 (default 0.85). */
975
+ length: number;
976
+ /** `rgb` (default) or `pigment` (smeared colours mix like paint). */
977
+ mixing: MixingModel;
978
+ }
979
+ declare const DEFAULT_SMUDGE: Readonly<SmudgeSettings>;
980
+ declare function normalizeBlender(raw?: Partial<BlenderSettings>, base?: BlenderSettings): BlenderSettings;
981
+ declare function normalizeSmudge(raw?: Partial<SmudgeSettings>, base?: SmudgeSettings): SmudgeSettings;
982
+ /** The neighbourhood radius in pixels of a blender dab of `radius`. */
983
+ declare function blendRadius(settings: BlenderSettings, radius: number): number;
984
+ /** Reusable buffers for `blenderDab` (one per stroke). */
985
+ interface BlenderScratch {
986
+ sums: Float64Array;
987
+ mix: Pigment;
988
+ out: number[];
989
+ }
990
+ declare function createBlenderScratch(): BlenderScratch;
991
+ /**
992
+ * One blender dab: `cov` is the tip's coverage over `rect` (document space, 0..1), `opacity` the
993
+ * dab's (pressure) opacity, `radius` its radius in pixels. Reads the neighbourhood as it was
994
+ * before the dab, then writes every covered pixel.
995
+ */
996
+ declare function blenderDab(medium: TileMedium, rect: RasterRect, cov: ArrayLike<number>, opacity: number, radius: number, settings: BlenderSettings, scratch: BlenderScratch): void;
997
+ /** A dab as the smudge needs it: centre, radius (px) and pressure opacity. */
998
+ interface SmudgeDab {
999
+ x: number;
1000
+ y: number;
1001
+ radius: number;
1002
+ opacity: number;
1003
+ }
1004
+ /**
1005
+ * A smudging finger: carries a patch of paint (straight RGBA, `size × size` cells over the tip,
1006
+ * in canvas orientation) from dab to dab. The first dab only picks the patch up.
1007
+ */
1008
+ declare class SmudgeTip {
1009
+ readonly settings: SmudgeSettings;
1010
+ private readonly size;
1011
+ private readonly patch;
1012
+ private loaded;
1013
+ private readonly out;
1014
+ constructor(settings: SmudgeSettings,
1015
+ /** The largest tip diameter of the stroke, in pixels (the patch resolution). */
1016
+ diameter: number);
1017
+ private cell;
1018
+ /** Fills the patch from the pixels under the dab. */
1019
+ private load;
1020
+ /** `a` mixed `t` of the way to `b` (both 0..1) into `out`. */
1021
+ private mix;
1022
+ /** One dab: lays the carried patch over the tip's footprint (`cov` over `rect`), then picks up. */
1023
+ dab(medium: TileMedium, d: SmudgeDab, rect: RasterRect, cov: ArrayLike<number>): void;
1024
+ }
1025
+
1026
+ export { parsePaintSource as $, type PaintColorSource as A, type BrushSettings as B, CoverageMap as C, type Dab as D, type PaintDabParams as E, type PaintGradientKit as F, type PaintGradientMapping as G, type PaintGradientMappingKind as H, type PaintGradientRepeat as I, type PaintGradientSample as J, PaintGradientSampler as K, type PaintGradientSpace as L, type MixerSettings as M, type PaintGradientStop as N, type PaintRgb as O, type PaintTileTarget as P, PaintSourceError as Q, type RgbTriple as R, type StrokePoint as S, TileMedium as T, addPaintGradientStop as U, hexToPaintRgb as V, isGradientPaint as W, normalizeHexColor as X, paintGradientMappingFor as Y, paintSourceColor as Z, paintSourceCss as _, type PaintGradientSource as a, readPaintSource as a0, removePaintGradientStop as a1, resolvePaintGradient as a2, reversePaintGradient as a3, scalePaintGradient as a4, solidPaint as a5, updatePaintGradientStop as a6, type BlenderScratch as a7, DEFAULT_BLENDER as a8, DEFAULT_BRUSH as a9, loadMixer as aA, mixerRates as aB, normalizeBlender as aC, normalizeBrush as aD, normalizeMixer as aE, normalizeSmudge as aF, put255 as aG, rasterizeDab as aH, smoothPoints as aI, tipFalloff as aJ, tipReach as aK, tipShape as aL, walkDabs as aM, DEFAULT_MIXER as aa, DEFAULT_SMUDGE as ab, DabWalker as ac, EMPTY_MIXER_LOAD as ad, type ExchangeRates as ae, MIXER_DRAIN as af, MIXER_PICKUP_RATE as ag, MIXING_MODELS as ah, PaintExchange as ai, type PaintMedium as aj, type PaintSample as ak, type SmudgeDab as al, SmudgeTip as am, TIP_GRADIENT_STEPS as an, type TileMediumOptions as ao, applyStrokeToDeepTile as ap, applyStrokeToTile as aq, blendRadius as ar, blenderDab as as, cleanMixer as at, coverageTileCoord as au, createBlenderScratch as av, dabCoverage as aw, dabRect as ax, exchangeRates as ay, gradientDabColor as az, type MixingModel as b, type BlenderSettings as c, type SmudgeSettings as d, type PaintSource as e, type PaintPixels as f, type MixerLoad as g, StrokeCoverage as h, type MixerTarget as i, MixerStroke as j, type StrokeApply as k, DEFAULT_PAINT_GRADIENT as l, DEFAULT_PAINT_GRADIENT_MAPPING as m, MAX_BRUSH_SIZE as n, PAINT_COLOR_JSON_SCHEMA as o, PAINT_GRADIENT_FULL_SPEED as p, PAINT_GRADIENT_JSON_SCHEMA as q, PAINT_GRADIENT_KIT as r, PAINT_GRADIENT_MAPPINGS as s, PAINT_GRADIENT_MAPPING_LABELS as t, PAINT_GRADIENT_PRESETS as u, PAINT_GRADIENT_REPEATS as v, PAINT_GRADIENT_REPEAT_LABELS as w, PAINT_GRADIENT_SPACES as x, PAINT_GRADIENT_SPACE_LABELS as y, PAINT_SOURCE_JSON_SCHEMA as z };