@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
@@ -1,2140 +0,0 @@
1
- import { a as BlendMode, R as RasterRect, j as TileKey, Y as Pigment, T as TiledSurface } 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 ("direction" | "time" | "distance" | "pressure" | "tilt" | "across-tip" | "random" | "velocity")[];
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 ("direction" | "time" | "distance" | "pressure" | "tilt" | "across-tip" | "random" | "velocity")[];
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 ("direction" | "time" | "distance" | "pressure" | "tilt" | "across-tip" | "random" | "velocity")[];
463
- readonly PAINT_GRADIENT_MAPPING_LABELS: Readonly<Record<"direction" | "time" | "distance" | "pressure" | "tilt" | "across-tip" | "random" | "velocity", 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
- /**
1027
- * The retouching tools on the shared brush engine: Clone Stamp, Healing Brush, Spot Healing
1028
- * Brush, Smudge, Dodge, Burn and Sponge. Two stroke shapes cover them all:
1029
- *
1030
- * - **Accumulate, then composite** (Clone Stamp, Dodge, Burn; the Healing tools' result): the
1031
- * stroke's dabs build up one `CoverageMap` (flow builds towards the dab's opacity ceiling), and
1032
- * per-pixel paint taken from the pre-stroke state (a cloned source, a toned copy of the pixel,
1033
- * a healed patch) is composited through it with `applyRetouchToTile`. Pixels painted earlier in
1034
- * the stroke are never sampled again, and overlapping dabs never exceed the stroke's opacity.
1035
- * - **Sequential dabs** (Smudge, Sponge): `RetouchDabStroke` runs each dab in order over live
1036
- * tiles, so dab n sees what dab n − 1 did.
1037
- *
1038
- * The Healing Brush clones while it is drawn and, when the stroke ends, replaces the clone with a
1039
- * seamless clone (`healPixels`, Poisson blending): the source's texture with the colour and light
1040
- * of the pixels around the stroke. The Spot Healing Brush finds its own source (`spotHealPixels`).
1041
- *
1042
- * Tone curves (`dodgeBurnCurve`, `dodgeBurn`, `sponge`) are photocraft's, made to behave like
1043
- * Photoshop's tools: the chosen range moves most, black and white stay put for midtones, and
1044
- * Protect Tones works on luma so hue and saturation are kept. Data in, pixels out; no DOM.
1045
- */
1046
-
1047
- /** The tonal range Dodge and Burn work on most. */
1048
- type ToneRange = 'shadows' | 'midtones' | 'highlights';
1049
- declare const TONE_RANGES: readonly ToneRange[];
1050
- /** Rec. 601 luma of straight RGB (0..1). */
1051
- declare function luma(r: number, g: number, b: number): number;
1052
- /** The dodge (or burn) transfer of one value `v` (0..1) at exposure `e` (0..1). */
1053
- declare function dodgeBurnCurve(v: number, e: number, range: ToneRange, burn: boolean): number;
1054
- /**
1055
- * Dodge (or burn) straight RGB `c` (0..1, written in place) at exposure `e`. With `protectTones`
1056
- * the curve moves luma and the colour is scaled to it (hue and saturation kept); otherwise each
1057
- * channel goes through the curve.
1058
- */
1059
- declare function dodgeBurn(c: number[], e: number, range: ToneRange, burn: boolean, protectTones: boolean): number[];
1060
- /** HSV saturation (chroma / max) of straight RGB. */
1061
- declare function saturationOf(r: number, g: number, b: number): number;
1062
- /**
1063
- * Sponge: moves straight RGB `c` (written in place) away from (`saturate`) or towards its luma
1064
- * grey by `amount` (0..1). `vibrance` damps it where it would clip: saturating touches already
1065
- * saturated colours less, desaturating touches near-greys less. Gamut-compressed, never clipped.
1066
- */
1067
- declare function sponge(c: number[], amount: number, saturate: boolean, vibrance: boolean): number[];
1068
- /** Per-dab strength so that one pass over a point totals about `e`, whatever the spacing. */
1069
- declare function perDabStrength(e: number, spacing: number): number;
1070
- /** Which pixels the Clone Stamp and the Healing tools sample. */
1071
- type RetouchSample = 'current' | 'current-below' | 'all';
1072
- declare const RETOUCH_SAMPLES: readonly RetouchSample[];
1073
- /** How the Spot Healing Brush finds its source. */
1074
- type SpotHealType = 'proximity-match' | 'content-aware' | 'smooth';
1075
- declare const SPOT_HEAL_TYPES: readonly SpotHealType[];
1076
- /** The retouching tools' options. */
1077
- interface RetouchSettings {
1078
- /** Clone / heal: keep the source offset from stroke to stroke (default true); off, every stroke starts at the source point. */
1079
- aligned: boolean;
1080
- /** Clone / heal / spot heal: what is sampled (default `current`: the target layer). */
1081
- sample: RetouchSample;
1082
- /** Spot heal: `proximity-match` (default: the best matching patch nearby), `content-aware` (texture and edges synthesised from patches around it) or `smooth` (a smooth fill from the edges). */
1083
- spotType: SpotHealType;
1084
- /** Dodge / burn: the tonal range (default `midtones`). */
1085
- range: ToneRange;
1086
- /** Dodge / burn: exposure 0..1 (default 0.5). */
1087
- exposure: number;
1088
- /** Dodge / burn: tone luma only, keeping hue and saturation (default true). */
1089
- protectTones: boolean;
1090
- /** Sponge: `desaturate` (default) or `saturate`. */
1091
- spongeMode: 'desaturate' | 'saturate';
1092
- /** Sponge: damp the change where colours would clip (default true). */
1093
- vibrance: boolean;
1094
- /** Smudge: how strongly the finger carries and lays paint; Blur / Sharpen: how strongly they act, 0..1 (default 0.5). */
1095
- strength: number;
1096
- /** History Brush: how many undo steps back it paints from; 0 (default) is the oldest state history keeps. */
1097
- historySteps: number;
1098
- }
1099
- declare const DEFAULT_RETOUCH: Readonly<RetouchSettings>;
1100
- /** Retouch settings with every field valid (missing or invalid fields from `base`). */
1101
- declare function normalizeRetouch(raw?: Partial<RetouchSettings>, base?: RetouchSettings): RetouchSettings;
1102
- /**
1103
- * Box blur of `data` (`w × h × n` floats) in place, `r` pixels each side, edges clamped. RGBA
1104
- * blurs premultiplied, so transparent pixels lend no colour. Run twice for a near-Gaussian.
1105
- */
1106
- declare function boxBlurPixels(data: Float32Array, w: number, h: number, n: 1 | 4, r: number): void;
1107
- /**
1108
- * The Blur / Sharpen tools' filter over a padded region (`w × h × n` floats on the 0..255 scale,
1109
- * in place): `blur` is two box passes of `radius`; `sharpen` is an unsharp mask of `amount`
1110
- * over a radius-1 blur (colour only; alpha kept). Values stay at or above 0.
1111
- */
1112
- declare function retouchFilter(data: Float32Array, w: number, h: number, n: 1 | 4, kind: 'blur' | 'sharpen', radius?: number, amount?: number): void;
1113
- /** Pixels a retouching stroke samples, by document pixel. */
1114
- interface PixelSource {
1115
- /** Straight RGBA 0..255 of document pixel (x, y) into `out`; false (and `out` zeroed) where there is nothing. */
1116
- sample(x: number, y: number, out: number[]): boolean;
1117
- }
1118
- /**
1119
- * Photoshop's Clone Source transform: the cloned pixels appear scaled by `scaleX` / `scaleY`
1120
- * (negative flips) and rotated by `rotate` degrees (clockwise) about the stroke's start.
1121
- */
1122
- interface CloneTransform {
1123
- scaleX: number;
1124
- scaleY: number;
1125
- rotate: number;
1126
- }
1127
- declare const IDENTITY_CLONE_TRANSFORM: CloneTransform;
1128
- declare function isIdentityCloneTransform(t: CloneTransform): boolean;
1129
- /**
1130
- * Where a clone samples for target point (`x`, `y`): `source + M⁻¹ · (p − anchor)`, with `M` the
1131
- * transform (scale then rotate) and `anchor` the target point that maps onto the source point.
1132
- */
1133
- declare function cloneSamplePoint(t: CloneTransform, source: {
1134
- x: number;
1135
- y: number;
1136
- }, anchor: {
1137
- x: number;
1138
- y: number;
1139
- }, x: number, y: number): [number, number];
1140
- /** A source seen through a map from document points to source points (bilinear, premultiplied). */
1141
- declare class TransformedPixelSource implements PixelSource {
1142
- private readonly inner;
1143
- private readonly map;
1144
- private readonly px;
1145
- constructor(inner: PixelSource, map: (x: number, y: number) => [number, number]);
1146
- sample(x: number, y: number, out: number[]): boolean;
1147
- }
1148
- /**
1149
- * A source over raster tiles (layer pixels, a mask, or composite tiles): `readTile` gives tile
1150
- * (tx, ty) in source space or `null` when it is empty; source pixel = document pixel − `offset`.
1151
- * One-channel tiles read as grey (alpha 255). Tiles are cached, so make one per stroke.
1152
- */
1153
- declare class TilePixelSource implements PixelSource {
1154
- /** A tile's bytes, or a float working tile on the same 0..255 scale (16 / 32 bits). */
1155
- private readonly readTile;
1156
- readonly channels: 1 | 4;
1157
- readonly offset: {
1158
- x: number;
1159
- y: number;
1160
- };
1161
- private readonly cache;
1162
- private lastKey;
1163
- private last;
1164
- constructor(
1165
- /** A tile's bytes, or a float working tile on the same 0..255 scale (16 / 32 bits). */
1166
- readTile: (tx: number, ty: number) => PaintPixels | null, channels: 1 | 4, offset?: {
1167
- x: number;
1168
- y: number;
1169
- });
1170
- sample(x: number, y: number, out: number[]): boolean;
1171
- }
1172
- /** A source over a region of float RGBA pixels (0..255), e.g. a healed patch. */
1173
- declare class RegionPixelSource implements PixelSource {
1174
- readonly rect: RasterRect;
1175
- readonly data: Float32Array;
1176
- constructor(rect: RasterRect, data: Float32Array);
1177
- sample(x: number, y: number, out: number[]): boolean;
1178
- }
1179
- /** `rect` of a source, shifted by (dx, dy), as float RGBA 0..255 (`rect.width × rect.height × 4`). */
1180
- declare function readSourceRegion(source: PixelSource, rect: RasterRect, dx?: number, dy?: number): Float32Array;
1181
- /** What a coverage-based retouching stroke lays through its coverage. */
1182
- type RetouchPaint =
1183
- /** Source pixels at document pixel + (dx, dy) (Clone Stamp, a healed patch). */
1184
- {
1185
- kind: 'source';
1186
- source: PixelSource;
1187
- dx: number;
1188
- dy: number;
1189
- }
1190
- /** The target pixel itself, toned (Dodge, Burn): `tone` edits straight RGB 0..1 in place. */
1191
- | {
1192
- kind: 'tone';
1193
- tone: (rgb: number[]) => void;
1194
- }
1195
- /** A flat colour (the Spot Healing Brush's trail while it is drawn), RGBA 0..255. */
1196
- | {
1197
- kind: 'color';
1198
- color: readonly [number, number, number, number];
1199
- };
1200
- /** How a coverage-based retouching stroke composites. */
1201
- interface RetouchApply {
1202
- /** Stroke opacity 0..1. */
1203
- opacity: number;
1204
- /** Blend mode for source paint (default normal). */
1205
- blend?: BlendMode;
1206
- lockTransparency: boolean;
1207
- /** Selection coverage (document space, canvas-sized), or `null`. */
1208
- selection: {
1209
- width: number;
1210
- height: number;
1211
- data: ArrayLike<number>;
1212
- } | null;
1213
- /** Layer offset (target pixels are in layer space). */
1214
- offset: {
1215
- x: number;
1216
- y: number;
1217
- };
1218
- }
1219
- /**
1220
- * Composites `paint` through the stroke's coverage over `tileData` (target tile (tx, ty) as it was
1221
- * before the stroke, layer space; RGBA, or one byte per pixel for masks) and returns the new tile.
1222
- * With `into` (the previous result) and `region` (document space), only `region` is recomputed.
1223
- * Tone paint keeps alpha; source and colour paint composite with `blend` (source-over by
1224
- * default), keeping alpha on a locked layer.
1225
- */
1226
- declare function applyRetouchToTile<T extends PaintPixels>(coverage: CoverageMap, tileData: T, channels: 1 | 4, tx: number, ty: number, paint: RetouchPaint, s: RetouchApply, options?: {
1227
- into?: T;
1228
- region?: RasterRect;
1229
- }): T;
1230
- /**
1231
- * Dodge / Burn at 16 or 32 bits: `applyRetouchToTile`'s tone paint on a layer tile's deep samples
1232
- * (`Uint16Array` sRGB-encoded, or `Float32Array` linear light, RGBA). The tone curves work on
1233
- * encoded values as at 8 bits, so 32-bit samples are encoded, toned and decoded (colour above 1 is
1234
- * toned at 1 and keeps its excess). Returns the new samples.
1235
- */
1236
- declare function applyToneToDeepTile(coverage: CoverageMap, samples: Uint16Array | Float32Array, tx: number, ty: number, tone: (rgb: number[]) => void, s: RetouchApply, options?: {
1237
- into?: Uint16Array | Float32Array;
1238
- region?: RasterRect;
1239
- }): Uint16Array | Float32Array;
1240
- /** The tone function of a Dodge or Burn stroke. */
1241
- declare function toneOf(tool: 'dodge' | 'burn', settings: Pick<RetouchSettings, 'exposure' | 'range' | 'protectTones'>): (rgb: number[]) => void;
1242
- /**
1243
- * Heals `src` into `dst` over `mask` (all `w × h`, RGBA floats 0..255): a seamless clone solved
1244
- * only over the mask's bounds plus a one-pixel fixed ring; elsewhere the result is `dst`.
1245
- */
1246
- declare function healPixels(w: number, h: number, src: Float32Array, dst: Float32Array, mask: ArrayLike<boolean | number>): Float32Array;
1247
- /**
1248
- * Spot healing: fills `hole` (`w × h`) of `img` (RGBA floats 0..255) with no given source, then
1249
- * heals the fill into its surroundings. `proximity-match` copies the best matching nearby patch
1250
- * (falling back to `smooth` when none fits); `smooth` interpolates the edges inwards. `size` is
1251
- * the brush diameter in pixels. Returns the healed pixels over the whole grid.
1252
- */
1253
- declare function spotHealPixels(w: number, h: number, img: Float32Array, hole: ArrayLike<boolean | number>, size: number, type: SpotHealType,
1254
- /** Told the patch offset it copied (`null`: a smooth fill). */
1255
- onOffset?: (offset: [number, number] | null) => void): Float32Array;
1256
- /** The window a Spot Healing stroke over `bounds` looks for texture in: the bounds grown by their size, on the canvas. */
1257
- declare function spotHealWindow(bounds: RasterRect, canvas: {
1258
- width: number;
1259
- height: number;
1260
- }): RasterRect;
1261
- /**
1262
- * One dab of a sequential retouching stroke: `cov` is the tip's pure coverage over `rect`
1263
- * (document space, 0..1); `d.alpha` is the flow and `d.opacity` the pressure opacity.
1264
- */
1265
- type RetouchDabEffect = (medium: TileMedium, d: Dab, rect: RasterRect, cov: Float32Array) => void;
1266
- /**
1267
- * A stroke whose dabs edit live tiles in order (Smudge, Sponge). Feed points with `push` (any
1268
- * chunks give the same dabs), end with `finish`; each returns the document rectangles it touched.
1269
- */
1270
- declare class RetouchDabStroke {
1271
- readonly brush: BrushSettings;
1272
- private readonly effect;
1273
- dabCount: number;
1274
- bounds: RasterRect | null;
1275
- private readonly walker;
1276
- private readonly medium;
1277
- private smoothPos;
1278
- private buf;
1279
- constructor(brush: BrushSettings, target: PaintTileTarget, effect: RetouchDabEffect);
1280
- push(points: readonly StrokePoint[]): RasterRect[];
1281
- finish(last?: StrokePoint): RasterRect[];
1282
- private smooth;
1283
- private paint;
1284
- }
1285
- /** The Smudge's per-dab effect: a `SmudgeTip` whose strength carries and lays the paint. */
1286
- declare function smudgeEffect(brush: BrushSettings, strength: number, mixing?: SmudgeSettings['mixing']): RetouchDabEffect;
1287
- /** The Sponge's per-dab effect: each dab moves saturation by about half its flow, compounding over passes. */
1288
- declare function spongeEffect(brush: BrushSettings, saturate: boolean, vibrance: boolean): RetouchDabEffect;
1289
- /**
1290
- * An RGBA view of one-channel tiles (masks), for the sequential-dab tools: reads expand grey to
1291
- * opaque RGB, writes land in RGBA copies, and `flush` writes their red channel back through
1292
- * `target.writeTile`. Alpha is locked, so a mask's values are all a dab changes.
1293
- */
1294
- declare function rgbaViewOfGrey(target: PaintTileTarget): {
1295
- target: PaintTileTarget;
1296
- flush(): void;
1297
- };
1298
-
1299
- /**
1300
- * The wet-media model: water and pigment on paper, simulated per cell on 256 px tiles.
1301
- *
1302
- * Written from the published literature only (no code, presets or textures from any product):
1303
- *
1304
- * - **Water** is a D2Q9 lattice Boltzmann fluid with BGK collision, τ = 3ν + ½ (any LBM text, e.g.
1305
- * Krüger et al., *The Lattice Boltzmann Method*, 2017). Streaming uses partial bounce-back with a
1306
- * blocking factor from the paper fibres, and the wet front is *pinned* until the water behind it
1307
- * is deep enough: Chu & Tai, "MoXi: Real-Time Ink Dispersion in Absorbent Paper", SIGGRAPH 2005.
1308
- * Tilt and flow maps are a velocity-shift force, with |u| capped at `U_MAX` cells per step.
1309
- * - **Paper** soaks water up until it holds its capacity, and damp paper stops pinning the front
1310
- * (wet-in-wet). Water evaporates faster in a band along the wet front (an "edge" field, 1 at the
1311
- * front and fading over a few cells inwards, as Curtis et al. blur the wet-area mask), so water
1312
- * flows out to the edge and carries pigment there: edge darkening.
1313
- * - **Pigment** (Curtis et al., "Computer-Generated Watercolor", SIGGRAPH 1997) is suspended in the
1314
- * water, advected with a first-order upwind finite-volume flux (it conserves pigment and never
1315
- * goes negative while |u| ≤ ¼), spreads a little between wet cells (bleed), settles into the
1316
- * paper (more in grain valleys: granulation; more at the wet edge: edge darkening), is lifted
1317
- * back by water (more where water arrives: blooms), and settles completely where a cell dries.
1318
- * - **Colour** is Kubelka-Munk: each cell carries pigment as absorption `K` per channel (linear
1319
- * R, G, B) plus one scattering `S`, so mixes are exactly linear (two-constant KM, `K = Σ cᵢKᵢ`,
1320
- * `S = Σ cᵢSᵢ`) and transparent washes multiply like glazes. A colour becomes `K` and `S` with the
1321
- * same two-flux formulas as the shipped pigment model (`K/S = (1 − R)² / 2R`).
1322
- *
1323
- * `stepCellsTs` in `kernel.ts` is the specification of one step, in exact f32 arithmetic. The
1324
- * WebAssembly SIMD kernel (`assembly/wet.ts`) is a lane-for-lane port of it and gives the same bits,
1325
- * so the simulation is deterministic across browsers, kernels and worker counts.
1326
- */
1327
- /** Tile side in cells (the raster tile size). */
1328
- declare const WET_TILE = 256;
1329
- /** A tile slot's side: the tile plus a one-cell halo on each side. */
1330
- declare const SLOT_SIDE = 258;
1331
- /** Floats per plane of a slot. */
1332
- declare const PLANE: number;
1333
- declare const PLANE_BYTES: number;
1334
- /** Planes in each ping-ponged set: f0..f8, depth, velocity, paper water, 4 pigment channels, edge. */
1335
- declare const SET_PLANES = 18;
1336
- declare const P_RHO = 9;
1337
- declare const P_UX = 10;
1338
- declare const P_UY = 11;
1339
- /** Water held in the paper. */
1340
- declare const P_SW = 12;
1341
- /** Suspended pigment: K red, K green, K blue, S. */
1342
- declare const P_P0 = 13;
1343
- /** Edge: 1 at the wet front, fading over a few cells into the wet area (0 where dry). */
1344
- declare const P_EDGE = 17;
1345
- /** Single (not ping-ponged) planes, after the two sets. */
1346
- declare const P_D0: number;
1347
- declare const P_H: number;
1348
- declare const P_FX: number;
1349
- declare const P_FY: number;
1350
- declare const SLOT_PLANES: number;
1351
- declare const SLOT_BYTES: number;
1352
- /** Pigment channels per cell. */
1353
- declare const PIGMENT_CHANNELS = 4;
1354
- /** D2Q9 lattice velocities: 0 rest, 1–4 axis (E, S, W, N in screen space), 5–8 diagonals. */
1355
- declare const EX: readonly [0, 1, 0, -1, 0, 1, -1, -1, 1];
1356
- declare const EY: readonly [0, 0, 1, 0, -1, 1, 1, -1, -1];
1357
- declare const OPP: readonly [0, 3, 4, 1, 2, 7, 8, 5, 6];
1358
- declare const W9: readonly [number, number, number, number, number, number, number, number, number];
1359
- /** Water shallower than this counts as dry. */
1360
- declare const DRY_EPS = 0.001;
1361
- /** Fluid speed cap in cells per step: keeps the LBM low-Mach and the pigment flux CFL-stable. */
1362
- declare const U_MAX = 0.25;
1363
- /** A tile's wet cells within this many cells of an edge wake the neighbouring tile. */
1364
- declare const STRIP = 64;
1365
- /** Steps per batch at most: the wake-up strip stays ahead of anything that moves a cell a step. */
1366
- declare const MAX_BATCH = 32;
1367
- /** Dry tiles are baked and freed only on steps that are multiples of this (so it is deterministic). */
1368
- declare const FREE_INTERVAL = 32;
1369
- /** Per-tile flag bits a step returns. */
1370
- declare const FLAG_ALIVE = 1;
1371
- declare const FLAG_LEFT = 2;
1372
- declare const FLAG_RIGHT = 4;
1373
- declare const FLAG_TOP = 8;
1374
- declare const FLAG_BOTTOM = 16;
1375
- declare const FLAG_WET = 32;
1376
- /** f32 parameters per lane (one per wet layer). */
1377
- declare const PARAM_FLOATS = 32;
1378
- /** Index of each packed parameter (the kernels read these offsets). */
1379
- declare const PARAM: {
1380
- readonly omega: 0;
1381
- readonly tau: 1;
1382
- readonly evap: 2;
1383
- readonly edgeEvap: 3;
1384
- readonly mobility: 4;
1385
- readonly depRate: 5;
1386
- readonly pickup: 6;
1387
- readonly absorption: 7;
1388
- readonly invCap: 8;
1389
- readonly blockScale: 9;
1390
- readonly rough: 10;
1391
- readonly pinT: 11;
1392
- readonly gx: 12;
1393
- readonly gy: 13;
1394
- readonly flowK: 14;
1395
- readonly granulation: 15;
1396
- readonly edgeDep: 16;
1397
- readonly bloom: 17;
1398
- readonly paperEvap: 18;
1399
- readonly hasFlow: 19;
1400
- readonly bleed: 20;
1401
- };
1402
- /**
1403
- * A wet layer's settings, each 0..1 unless noted. The paper's absorption, permeability and
1404
- * roughness come from the paper, not from here.
1405
- */
1406
- interface WetParams {
1407
- /** How thick the water is: 0 runny (ink) .. 1 very thick (oil). Default 0.3 (watercolour). */
1408
- viscosity: number;
1409
- /** How fast the water dries: 0 never .. 1 very fast. Default 0.45. */
1410
- evaporation: number;
1411
- /** Darker, crisper edges where a wash dries: 0 none .. 1 strong. Default 0.5. */
1412
- edgeDarkening: number;
1413
- /** How heavy the pigment is: heavy pigment travels less and settles sooner. Default 0.3. */
1414
- pigmentWeight: number;
1415
- /** How much pigment collects in the paper's valleys. Default 0.4. */
1416
- granulation: number;
1417
- /** How readily water lifts settled pigment back up (re-wetting). Default 0.2. */
1418
- pickup: number;
1419
- /** How fast suspended pigment settles into the paper (staining). Default 0.2. */
1420
- fixture: number;
1421
- /** Blooms (backruns): water flowing into a drying wash pushes its pigment out. Default 0.4. */
1422
- bloom: number;
1423
- /** Colour bleed: pigment spreading between touching wet areas. Default 0.3. */
1424
- bleed: number;
1425
- }
1426
- declare const DEFAULT_WET_PARAMS: Readonly<WetParams>;
1427
- declare const WET_PARAM_KEYS: (keyof WetParams)[];
1428
- /** The paper as wet media read it (all 0..1; from the paint editor's paper settings). */
1429
- interface WetPaper {
1430
- absorption: number;
1431
- permeability: number;
1432
- roughness: number;
1433
- }
1434
- declare const DEFAULT_WET_PAPER: Readonly<WetPaper>;
1435
- /** Canvas tilt (shared by every wet layer): water runs downhill. */
1436
- interface WetTilt {
1437
- /** Degrees from flat, 0 (flat) .. 90 (upright). */
1438
- angle: number;
1439
- /** Which way is downhill, degrees clockwise from canvas down (0 = towards the bottom edge). */
1440
- direction: number;
1441
- }
1442
- declare const FLAT: Readonly<WetTilt>;
1443
- /** Settings with every field in range; missing fields come from `base`. */
1444
- declare function normalizeWetParams(raw?: Partial<WetParams>, base?: WetParams): WetParams;
1445
- declare function normalizeTilt(raw?: Partial<WetTilt>, base?: WetTilt): WetTilt;
1446
- /** Kinematic viscosity (lattice units) for `viscosity` 0..1: 0.02 .. 1.5, exponential. */
1447
- declare function latticeViscosity(v: number): number;
1448
- /** Water a cell of paper holds when saturated, in depth units. */
1449
- declare const PAPER_CAPACITY = 0.08;
1450
- /** Largest tilt acceleration (cells per step²), at 90°. */
1451
- declare const MAX_TILT_ACCEL = 0.025;
1452
- /** Packs a lane's settings into the f32 block the kernels read. `flowStrength` 0 turns the flow map off. */
1453
- declare function packWetParams(params: WetParams, paper: WetPaper, tilt: WetTilt, flowStrength: number, out?: Float32Array<ArrayBuffer>): Float32Array;
1454
- /** Flag bits for a wet (or alive) cell at tile-local (lx, ly): which neighbours it wakes. */
1455
- declare function edgeBits(lx: number, ly: number): number;
1456
-
1457
- /**
1458
- * Loading the wet-media WebAssembly kernel and its Web Workers.
1459
- *
1460
- * - The kernel is two small modules (shared memory for workers, ordinary memory for one thread),
1461
- * embedded base64 in `kernel-wasm.ts`, which is only ever imported dynamically, so bundlers split
1462
- * it into its own chunk and nothing loads until the first wet stroke.
1463
- * - Workers run from a Blob URL of `wetWorkerMain`'s source: no extra file for the host to serve
1464
- * and no bundler configuration. They share one `WebAssembly.Memory`, which needs
1465
- * `SharedArrayBuffer`, which browsers only give cross-origin isolated pages (COOP `same-origin`
1466
- * plus COEP `require-corp` or `credentialless`). Without it the kernel runs on one thread.
1467
- * - Everything here is optional: without WebAssembly SIMD the TypeScript kernel runs, with the same
1468
- * results (only slower).
1469
- */
1470
- /** What the wet engine can use here, and why not when it cannot. */
1471
- interface WetSupport {
1472
- /** WebAssembly with SIMD. */
1473
- simd: boolean;
1474
- /** `SharedArrayBuffer` and shared wasm memory (cross-origin isolation in browsers). */
1475
- sharedMemory: boolean;
1476
- /** Web Workers. */
1477
- workers: boolean;
1478
- /** Logical cores reported (`navigator.hardwareConcurrency`, 1 when unknown). */
1479
- cores: number;
1480
- /** A sentence for the user when wet paint will run slower than it could, else `null`. */
1481
- message: string | null;
1482
- }
1483
- /** Compiles the kernel (`shared`: the shared-memory variant). Cached; loads the chunk once. */
1484
- declare function loadWetKernel(shared: boolean): Promise<WebAssembly.Module>;
1485
- /** Whether shared wasm memory can be made here. */
1486
- declare function canShareMemory(): boolean;
1487
- /** Whether this runtime validates WebAssembly SIMD (checked against the kernel itself). */
1488
- declare function wetSimdSupported(): Promise<boolean>;
1489
- /** What the wet engine can use (SIMD is only known after `wetSimdSupported`). */
1490
- declare function wetSupport(simd?: boolean): WetSupport;
1491
- /** The layout numbers a worker needs. */
1492
- interface WetWorkerLayout {
1493
- ctl: number;
1494
- params: number;
1495
- table: number;
1496
- list: number;
1497
- flags: number;
1498
- last: number;
1499
- slots: number;
1500
- slotBytes: number;
1501
- perLane: number;
1502
- tilesX: number;
1503
- tilesY: number;
1504
- keys: number;
1505
- }
1506
- /** A pool of workers stepping one shared memory together. */
1507
- declare class WetWorkerPool {
1508
- private readonly workers;
1509
- private readonly url;
1510
- private constructor();
1511
- get size(): number;
1512
- /** Starts `count` workers on `memory` (shared) with the shared kernel. Rejects if any fails. */
1513
- static create(count: number, module: WebAssembly.Module, memory: WebAssembly.Memory, layout: WetWorkerLayout): Promise<WetWorkerPool>;
1514
- /** Runs `steps` steps over the active list in shared memory; resolves when every worker is done. */
1515
- run(steps: number): Promise<void>;
1516
- destroy(): void;
1517
- }
1518
-
1519
- /**
1520
- * Engine B: the wet-media step and display shading on WebGPU, an accelerator for large wet areas.
1521
- *
1522
- * The CPU engine (`WetWorld`, WebAssembly SIMD) stays the authority: it owns the clock, edits,
1523
- * drying, save, export and replay, and gives the same bits everywhere. This engine mirrors the CPU
1524
- * state on the GPU (same slot layout, uploaded region by region as the CPU edits it), steps it and
1525
- * shades it into RGBA8 tiles for display. Its results stay within about 1/255 of the CPU's but are
1526
- * not bit-identical (shader compilers differ), so nothing it computes is ever saved.
1527
- *
1528
- * DOM-free and dependency-free: it takes a `navigator.gpu`-like object (or finds one) and uses
1529
- * only the WebGPU API. Without one, `createWetGpuEngine` says why and the CPU does everything.
1530
- */
1531
- interface GBuffer {
1532
- readonly size: number;
1533
- destroy(): void;
1534
- mapAsync(mode: number, offset?: number, size?: number): Promise<void>;
1535
- getMappedRange(offset?: number, size?: number): ArrayBuffer;
1536
- unmap(): void;
1537
- }
1538
- interface GPass {
1539
- setPipeline(p: unknown): void;
1540
- setBindGroup(i: number, g: unknown): void;
1541
- dispatchWorkgroups(x: number, y?: number, z?: number): void;
1542
- end(): void;
1543
- }
1544
- interface GEncoder {
1545
- beginComputePass(): GPass;
1546
- copyBufferToBuffer(src: GBuffer, so: number, dst: GBuffer, d: number, size: number): void;
1547
- clearBuffer(b: GBuffer, offset?: number, size?: number): void;
1548
- finish(): unknown;
1549
- }
1550
- interface GQueue {
1551
- writeBuffer(b: GBuffer, offset: number, data: ArrayBufferView | ArrayBufferLike, dataOffset?: number, size?: number): void;
1552
- submit(cmds: unknown[]): void;
1553
- onSubmittedWorkDone(): Promise<void>;
1554
- }
1555
- interface GPipeline {
1556
- getBindGroupLayout(i: number): unknown;
1557
- }
1558
- interface GDevice {
1559
- readonly queue: GQueue;
1560
- readonly limits: Record<string, number>;
1561
- readonly lost?: Promise<{
1562
- message?: string;
1563
- reason?: string;
1564
- }>;
1565
- createBuffer(d: {
1566
- size: number;
1567
- usage: number;
1568
- label?: string;
1569
- }): GBuffer;
1570
- createShaderModule(d: {
1571
- code: string;
1572
- label?: string;
1573
- }): unknown;
1574
- createComputePipeline(d: {
1575
- layout: 'auto';
1576
- compute: {
1577
- module: unknown;
1578
- entryPoint: string;
1579
- };
1580
- label?: string;
1581
- }): GPipeline;
1582
- createComputePipelineAsync?(d: {
1583
- layout: 'auto';
1584
- compute: {
1585
- module: unknown;
1586
- entryPoint: string;
1587
- };
1588
- label?: string;
1589
- }): Promise<GPipeline>;
1590
- createBindGroup(d: {
1591
- layout: unknown;
1592
- entries: {
1593
- binding: number;
1594
- resource: {
1595
- buffer: GBuffer;
1596
- offset?: number;
1597
- size?: number;
1598
- };
1599
- }[];
1600
- }): unknown;
1601
- createCommandEncoder(): GEncoder;
1602
- pushErrorScope?(filter: string): void;
1603
- popErrorScope?(): Promise<{
1604
- message: string;
1605
- } | null>;
1606
- destroy(): void;
1607
- }
1608
- /** A `navigator.gpu`-like object. */
1609
- interface WetGpuProvider {
1610
- requestAdapter(options?: {
1611
- powerPreference?: 'high-performance' | 'low-power';
1612
- }): Promise<unknown>;
1613
- }
1614
- /** Whether Engine B can run here, and if not, why. */
1615
- interface WetGpuSupport {
1616
- supported: boolean;
1617
- /** Why not (no WebGPU, no adapter, too little buffer space, the device failed), or `null`. */
1618
- reason: string | null;
1619
- /** The GPU's vendor and architecture, when known. */
1620
- adapter: string | null;
1621
- /** Most wet tiles (256 px, all layers) the GPU can hold. */
1622
- maxSlots: number;
1623
- }
1624
- /** A tile to step or shade: its slot, lane, the slots of its 8 neighbours, and its size on the canvas. */
1625
- interface WetGpuJob {
1626
- slot: number;
1627
- lane: number;
1628
- /** Neighbour slots in the order n, s, w, e, nw, ne, sw, se; −1 for none. */
1629
- neighbours: readonly number[];
1630
- /** Pixels of the tile on the canvas (256 except at the right and bottom edges). */
1631
- width: number;
1632
- height: number;
1633
- }
1634
- /** The `navigator.gpu` of this context, if any. */
1635
- declare function findWebGpu(): WetGpuProvider | null;
1636
- /**
1637
- * Starts Engine B on `gpu` (default `navigator.gpu`). Resolves with the engine, or `null` and the
1638
- * reason it cannot run (no WebGPU, no adapter, too little storage-buffer space, a failed device).
1639
- */
1640
- declare function createWetGpuEngine(gpu?: WetGpuProvider | null): Promise<{
1641
- engine: WetGpuEngine | null;
1642
- support: WetGpuSupport;
1643
- }>;
1644
- /** Whether Engine B can run here (starts and stops a device). */
1645
- declare function wetGpuSupport(gpu?: WetGpuProvider | null): Promise<WetGpuSupport>;
1646
- /** The engine on a started device. Make one with `createWetGpuEngine`. */
1647
- declare class WetGpuEngine {
1648
- private readonly device;
1649
- /** Most tiles the GPU can hold. */
1650
- readonly maxSlots: number;
1651
- /** The GPU, when known. */
1652
- readonly adapter: string | null;
1653
- private readonly stepPipe;
1654
- private readonly haloPipe;
1655
- private readonly shadePipe;
1656
- /** Steps run on the GPU so far (for stats). */
1657
- steps: number;
1658
- /** Set once the device is lost; the engine does nothing after that. */
1659
- lostReason: string | null;
1660
- private state;
1661
- private slots;
1662
- private readonly params;
1663
- private jobs;
1664
- private jobCap;
1665
- private flags;
1666
- private flagsRead;
1667
- private readonly uni;
1668
- private readonly shadeUni;
1669
- private srcTiles;
1670
- private outTiles;
1671
- private outRead;
1672
- private tileCap;
1673
- private groups;
1674
- private constructor();
1675
- /** @internal Compiles the kernels; use `createWetGpuEngine`. */
1676
- static start(device: GDevice, maxSlots: number, adapter: string | null): Promise<WetGpuEngine>;
1677
- /** Bytes of GPU memory in use. */
1678
- memoryBytes(): number;
1679
- /** Slots the state buffer holds now. */
1680
- get capacity(): number;
1681
- /** Grows the state buffer to hold `n` slots (keeping its contents). Returns false past `maxSlots`. */
1682
- ensureSlots(n: number): boolean;
1683
- /** Writes floats into a slot (`offset` floats from the slot's start). */
1684
- writeSlot(slot: number, offset: number, data: Float32Array): void;
1685
- /** Sets a lane's packed parameters (`packWetParams`). */
1686
- setParams(lane: number, prm: Float32Array): void;
1687
- private writeJobs;
1688
- private bindGroups;
1689
- /**
1690
- * Runs `steps` steps of `jobs` from set `parity` (as the CPU's `runHere`: each step, then each
1691
- * tile's halo in the new set). With `readFlags`, resolves with each job's flag bits OR-ed over
1692
- * the steps (`FLAG_*`); otherwise once the work is submitted.
1693
- */
1694
- step(jobs: readonly WetGpuJob[], parity: number, steps: number, readFlags?: boolean): Promise<Uint32Array | null>;
1695
- /** Refills each job's halo in set `parity` from its neighbours (after uploads). */
1696
- refreshHalos(jobs: readonly WetGpuJob[], parity: number): void;
1697
- /**
1698
- * Shades `jobs` (set `parity`) over their base tiles (`src[i]`: RGBA8 256 × 256, or `null` for
1699
- * transparent) on paper `paper` (sRGB bytes). `mode` 'display' darkens wet paper a little;
1700
- * 'dried' shows the paint as it would dry. Resolves with one RGBA8 tile per job.
1701
- */
1702
- shade(jobs: readonly WetGpuJob[], parity: number, src: readonly (Uint8ClampedArray | null)[], paper: readonly [number, number, number], mode?: 'display' | 'dried'): Promise<Uint8ClampedArray[]>;
1703
- /** Reads floats back from a slot (tests and diagnostics). */
1704
- readSlot(slot: number, offset: number, count: number): Promise<Float32Array>;
1705
- /** Waits for submitted work. */
1706
- idle(): Promise<void>;
1707
- destroy(): void;
1708
- }
1709
-
1710
- /**
1711
- * `WetWorld`: the wet-media simulation of one canvas. It holds up to `MAX_WET_LANES` wet layers
1712
- * ("lanes"), each a sparse set of 256 px tile slots in one `WebAssembly.Memory`, and steps all of
1713
- * them together.
1714
- *
1715
- * - **Kernels.** It starts on the TypeScript kernel at once and switches to the WebAssembly SIMD
1716
- * kernel (in Web Workers when the page is cross-origin isolated, else on this thread) as soon as
1717
- * that has loaded. The kernels give identical bits, so the switch never changes the painting.
1718
- * - **Determinism.** Time is counted in steps (`clock`), never in wall time. Batches never cross a
1719
- * multiple of `FREE_INTERVAL` and are at most `MAX_BATCH` steps, tiles wake up when anything
1720
- * alive comes within `STRIP` cells of their edge (so earlier or later wake-ups change nothing),
1721
- * and dry tiles are baked and freed only on multiples of `FREE_INTERVAL`, judged by their state
1722
- * at that step. So the result depends only on the edits and the steps at which they were made,
1723
- * not on frame rate, batch sizes, worker counts or the browser.
1724
- * - **Edits** (dabs, water, drying, blowing, blending, smudging, blotting, re-wetting) run on this
1725
- * thread between batches; while a batch runs (`busy`) they throw, so callers queue them.
1726
- * - **Display.** Each lane has a `base` surface: the layer's pixels under the wet paint. `display`
1727
- * gives the base with every live tile shaded (`filmPixel`), as an ordinary RGBA surface for the
1728
- * compositor. Tiles that dry are baked into the base and their slots freed.
1729
- * - **Engine B (WebGPU).** With a GPU engine attached (`attachGpu`) and at least `gpuMinTiles` live
1730
- * tiles, the GPU keeps a mirror of the slots: each edit uploads the rows it changed, each batch
1731
- * runs on the GPU too, and `displayGpu` shades every stale tile there at once. In lead mode
1732
- * (between strokes) the GPU steps a batch frame by frame while the CPU computes it (`gpuLead`).
1733
- * The CPU state stays the authority (edits, reads, drying, snapshots, baking); the GPU's copy
1734
- * only feeds the display.
1735
- */
1736
-
1737
- /** Wet layers a world can hold at once. */
1738
- declare const MAX_WET_LANES = 8;
1739
- /** Tile slots at most (about 3.8 GB, inside wasm32's 4 GB). Water reaching a tile past this is lost. */
1740
- declare const MAX_WET_SLOTS = 352;
1741
- /** A flow map: a vector per document pixel, in cells per step² at strength 1. */
1742
- type WetFlowField = (x: number, y: number) => readonly [number, number];
1743
- /** The paper as the simulation reads it. */
1744
- interface WetPaperInput extends WetPaper {
1745
- /** Grain height 0..1 at a document position (the paper's wet height map). */
1746
- heightAt(x: number, y: number): number;
1747
- /** Paper colour, sRGB bytes. */
1748
- color: readonly [number, number, number];
1749
- }
1750
- type WetKernelKind = 'typescript' | 'wasm' | 'wasm-threads';
1751
- interface WetBackendInfo {
1752
- kind: WetKernelKind;
1753
- /** Threads stepping the simulation. */
1754
- threads: number;
1755
- /** Why it runs slower than it could, or `null`. */
1756
- message: string | null;
1757
- }
1758
- interface WetWorldOptions {
1759
- /** Which kernel: `auto` (default: the fastest available), or force `typescript` / `wasm` (one thread). */
1760
- kernel?: 'auto' | 'typescript' | 'wasm';
1761
- /** Worker threads for the wasm kernel: `auto` (default: cores − 1, at most 10) or a number (0: none). */
1762
- threads?: number | 'auto';
1763
- }
1764
- interface WetLaneOptions {
1765
- params?: Partial<WetParams>;
1766
- /** The layer's pixels under the wet paint (default empty). */
1767
- base?: TiledSurface;
1768
- flow?: WetFlowField | null;
1769
- /** Flow map strength (default 1 when a flow map is given). */
1770
- flowStrength?: number;
1771
- }
1772
- /** Totals for one wet layer. */
1773
- interface WetLaneStats {
1774
- /** Tiles being simulated. */
1775
- activeTiles: number;
1776
- /** Cells with surface water (at least `DRY_EPS` deep). */
1777
- wetCells: number;
1778
- /** Water on the surface plus in the paper, in cell-depth units. */
1779
- water: number;
1780
- /** Suspended plus settled pigment mass (sum of the four channels). */
1781
- pigment: number;
1782
- /** Document rectangle of the wet cells, or `null` when nothing is wet. */
1783
- wetBounds: RasterRect | null;
1784
- }
1785
- /** A copy of the whole simulation (for undo). */
1786
- interface WetSnapshot {
1787
- readonly clock: number;
1788
- readonly tilt: WetTilt;
1789
- readonly lanes: ReadonlyArray<{
1790
- id: string;
1791
- index: number;
1792
- params: WetParams;
1793
- flow: WetFlowField | null;
1794
- flowStrength: number;
1795
- base: TiledSurface;
1796
- }>;
1797
- /** Per active key: the current set and the deposit planes; and its last flags. */
1798
- readonly slots: ReadonlyMap<number, {
1799
- planes: Float32Array;
1800
- last: number;
1801
- }>;
1802
- /** Bytes held. */
1803
- readonly bytes: number;
1804
- }
1805
- /** Thrown for edits while a batch is running, and for bad arguments. */
1806
- declare class WetWorldError extends Error {
1807
- constructor(message: string);
1808
- }
1809
- interface Layout extends WetWorkerLayout {
1810
- width: number;
1811
- height: number;
1812
- }
1813
- declare class WetWorld {
1814
- readonly width: number;
1815
- readonly height: number;
1816
- readonly layout: Layout;
1817
- /** Steps simulated so far. */
1818
- clock: number;
1819
- /** Messages for the user (slots ran out, a backend failed). */
1820
- readonly warnings: string[];
1821
- private memory;
1822
- private readonly shared;
1823
- private f32;
1824
- private i32;
1825
- private u32;
1826
- private slotCount;
1827
- private readonly freeSlots;
1828
- private readonly active;
1829
- private parity;
1830
- private readonly lanes;
1831
- private tilt;
1832
- private paper;
1833
- private paperBytes;
1834
- private kind;
1835
- private wasm;
1836
- private pool;
1837
- private busyFlag;
1838
- private destroyed;
1839
- private support;
1840
- private readonly prm;
1841
- private gpu;
1842
- /** Live tiles at which the GPU takes over the display (default 16: a fully wet 1024² canvas). */
1843
- gpuMinTiles: number;
1844
- private gpuLive;
1845
- private gpuFailure;
1846
- /** Slot → rows (1..256, inclusive) to upload; `[0, 257]` means the whole slot. */
1847
- private readonly gpuDirty;
1848
- private gpuParamsDirty;
1849
- /**
1850
- * Lead mode (between strokes): a batch is not sent to the GPU all at once; `gpuLead` steps it
1851
- * frame by frame while the CPU works through it, so the display moves on before the CPU is done.
1852
- */
1853
- gpuLeadMode: boolean;
1854
- /** The batch the GPU is working through in lead mode. */
1855
- private gpuPlan;
1856
- /** Resolves when the fastest available kernel is running (it may stay `typescript`). */
1857
- readonly ready: Promise<WetBackendInfo>;
1858
- constructor(width: number, height: number, paper: WetPaperInput, options?: WetWorldOptions);
1859
- private views;
1860
- private start;
1861
- /** The kernel in use and why it is not faster. */
1862
- backend(): WetBackendInfo;
1863
- /** Whether a batch is running (edits throw until it ends). */
1864
- get busy(): boolean;
1865
- destroy(): void;
1866
- private laneOf;
1867
- hasLane(id: string): boolean;
1868
- laneIds(): string[];
1869
- /** Adds a wet layer (or returns it). Throws when `MAX_WET_LANES` are in use. */
1870
- ensureLane(id: string, options?: WetLaneOptions): void;
1871
- /** Drops a wet layer and its wet paint (nothing is baked). */
1872
- removeLane(id: string): void;
1873
- getParams(id: string): WetParams;
1874
- setParams(id: string, params: Partial<WetParams>): WetParams;
1875
- getTilt(): WetTilt;
1876
- setTilt(tilt: Partial<WetTilt>): WetTilt;
1877
- /** Changes the paper: heights of live tiles are refilled, rates repacked. */
1878
- setPaper(paper: WetPaperInput): void;
1879
- /** Sets a layer's flow map (`null` removes it). */
1880
- setFlow(id: string, flow: WetFlowField | null, strength?: number): void;
1881
- hasFlow(id: string): boolean;
1882
- /** The pixels under a layer's wet paint. */
1883
- getBase(id: string): TiledSurface;
1884
- /** Replaces the pixels under a layer's wet paint. */
1885
- setBase(id: string, base: TiledSurface): void;
1886
- private pack;
1887
- private touchAll;
1888
- private laneIndexOf;
1889
- private slotOf;
1890
- private slotBase;
1891
- private requireIdle;
1892
- /** Writes the active list into memory for the kernels. */
1893
- private syncList;
1894
- private grow;
1895
- /** Activates a tile of a lane. Returns false when no slot can be had. */
1896
- private activate;
1897
- private free;
1898
- private tileXY;
1899
- private tileKeyOf;
1900
- private fillPaper;
1901
- private fillFlow;
1902
- /** Activates every tile of a lane within `STRIP` cells of a document rectangle. */
1903
- private wake;
1904
- /** Fills one slot's halo in the current set from its neighbours (TypeScript). */
1905
- private haloTs;
1906
- private halo;
1907
- /** Refills every live slot's halo in the current set (after edits or wake-ups). */
1908
- private refreshHalos;
1909
- /** Whether anything is being simulated (in `id`, or any layer). */
1910
- isActive(id?: string): boolean;
1911
- /** Steps until the next batch boundary: at most `want`, `MAX_BATCH`, and the next free check. */
1912
- private batchSize;
1913
- /** Runs `n` steps on this thread (TypeScript or wasm kernel). */
1914
- private runHere;
1915
- /** After a batch: wake neighbours from the flags, free dead tiles on a free step, restamp. */
1916
- private afterBatch;
1917
- /**
1918
- * Steps the simulation `steps` times on this thread (the TypeScript or single-thread wasm
1919
- * kernel). Throws while worker threads are in use; use `step` there.
1920
- */
1921
- stepSync(steps: number): number;
1922
- /** Steps the simulation `steps` times (in worker threads when there are any). */
1923
- step(steps: number): Promise<number>;
1924
- /** Steps until nothing is wet or `maxSteps` have run. Returns the steps run. */
1925
- stepUntilDry(maxSteps: number): Promise<number>;
1926
- /**
1927
- * Steps only the live tiles that touch `region` (canvas pixels): a viewport, say, so a huge wet
1928
- * canvas costs what is on screen. The other live tiles hold still (no flow, no drying) until a
1929
- * later step reaches them, and water flowing out of the region stops at their edge; tiles the
1930
- * region's water wakes up are stepped as usual. Holding tiles makes the result depend on the
1931
- * regions, so a replay is only bit-identical with the same regions. Returns the clock.
1932
- */
1933
- stepRegion(steps: number, region: RasterRect): Promise<number>;
1934
- /** `stepRegion` on this thread (throws while worker threads are in use, like `stepSync`). */
1935
- stepRegionSync(steps: number, region: RasterRect): number;
1936
- /**
1937
- * Takes the live tiles that miss `region` out of the active list until the returned release
1938
- * is called. Their state is copied into the other ping-pong set first, so they read the same
1939
- * whichever set is current when they come back.
1940
- */
1941
- private hold;
1942
- /** Float index of a cell's plane-0 value in the current set, or −1 (inactive / off the canvas). */
1943
- private cell;
1944
- /** Index of the same cell in the single (deposit, height, flow) planes, given `cell`. */
1945
- private single;
1946
- /**
1947
- * Runs `fn` for every cell of a footprint (`cov` over `rect`, document cells) on a lane, waking
1948
- * the tiles around it first, then refreshes halos. `fn` gets the cell index and its coverage.
1949
- */
1950
- private footprint;
1951
- private markEdited;
1952
- /** Adds water (depth units) at rest. */
1953
- private addWaterAt;
1954
- /**
1955
- * Lays paint: `water` depth and pigment mass `pigment` (K r, g, b, S) per unit of coverage, or
1956
- * per pixel from `field` (4 values per pixel of `rect`). Pigment without water lands straight in
1957
- * the paper (settled).
1958
- */
1959
- paint(id: string, rect: RasterRect, cov: ArrayLike<number> | null, water: number, pigment: ArrayLike<number> | null, field?: ArrayLike<number> | null): void;
1960
- /** Damp paper: fills the paper's water towards its capacity by `amount` (0..1) × coverage, plus a thin film of surface water. */
1961
- wetPaper(id: string, rect: RasterRect, cov: ArrayLike<number> | null, amount: number): void;
1962
- /**
1963
- * Takes water (and suspended pigment, and settled pigment) away: `water`, `suspended`,
1964
- * `settled` are fractions 0..1 × coverage. A hair dryer takes water only (the pigment stays and
1965
- * settles); blotting takes all three.
1966
- */
1967
- remove(id: string, rect: RasterRect, cov: ArrayLike<number> | null, water: number, suspended: number, settled: number, paperWater?: number): void;
1968
- /** Pushes the water: adds velocity (`ux`, `uy` cells per step, each at most 0.1) × coverage. */
1969
- push(id: string, rect: RasterRect, cov: ArrayLike<number> | null, ux: number, uy: number): void;
1970
- /** Blends wet paint: each wet cell's suspended pigment moves `strength` × coverage towards the footprint's mean. */
1971
- blend(id: string, rect: RasterRect, cov: ArrayLike<number> | null, strength: number): void;
1972
- /**
1973
- * Drags wet paint by (`dx`, `dy`) cells: each footprint cell takes `strength` × coverage of the
1974
- * water and suspended pigment of the cell it came from (a smudge through wet paint).
1975
- */
1976
- drag(id: string, rect: RasterRect, cov: ArrayLike<number> | null, dx: number, dy: number, strength: number): void;
1977
- /**
1978
- * Re-wetting: lifts `amount` × coverage of the dried paint in the layer's pixels (`base`) back into
1979
- * the paper as settled pigment, so water can move it again. Only where there is surface water.
1980
- */
1981
- rewet(id: string, rect: RasterRect, cov: ArrayLike<number> | null, amount: number, paper: readonly [number, number, number]): void;
1982
- /**
1983
- * Dries wet paint at once (everything settles where it is, the water goes): in `rect`, or the
1984
- * whole layer. Whole tiles that end up dry are baked into the base and freed. Returns the
1985
- * document rectangle that changed, or `null`.
1986
- */
1987
- dry(id: string, rect?: RasterRect): RasterRect | null;
1988
- /**
1989
- * Bakes a slot's pigment into its lane's base (settling suspended pigment too when `all`).
1990
- * Returns whether any pixel changed.
1991
- */
1992
- private bakeKey;
1993
- /**
1994
- * Shades one slot over a base tile. `display`: the wet look (suspended + settled, wet paper a
1995
- * little darker); `bake`: settled pigment only; `bake-all`: suspended and settled (dry now).
1996
- * Returns `null` when nothing would change the base.
1997
- */
1998
- private shadeSlot;
1999
- /** The layer's pixels with all its wet paint dried where it is (the bake of "dry on save"); changes nothing. */
2000
- bakedSurface(id: string): TiledSurface;
2001
- /**
2002
- * Sums over a footprint of a lane, weighted by coverage: `weight` (coverage), `water` (surface
2003
- * depth), `wet` (coverage of wet cells), `pigment` (suspended, 4 channels), `settled` (4 channels).
2004
- */
2005
- sample(id: string, rect: RasterRect, cov: ArrayLike<number> | null): {
2006
- weight: number;
2007
- water: number;
2008
- wet: number;
2009
- pigment: number[];
2010
- settled: number[];
2011
- };
2012
- /** Surface water depth at a document cell (0 where nothing is simulated). */
2013
- waterAt(id: string, x: number, y: number): number;
2014
- /** Totals for a layer (scans its live tiles). */
2015
- stats(id: string): WetLaneStats;
2016
- /** Whether a layer (or any) has surface water anywhere. */
2017
- isWet(id?: string): boolean;
2018
- /** Document rectangles of a layer's live tiles. */
2019
- activeRects(id: string): RasterRect[];
2020
- /** A number that changes whenever a layer's display may have changed. */
2021
- stamp(id: string): number;
2022
- /**
2023
- * The layer's pixels with its wet paint shaded in: the base with each live tile shaded over it.
2024
- * At most `maxTiles` stale tiles are reshaded per call (edited tiles first); the rest keep their
2025
- * last shading until the next call. Returns the surface and the tiles reshaded.
2026
- */
2027
- display(id: string, maxTiles?: number): {
2028
- surface: TiledSurface;
2029
- changed: RasterRect[];
2030
- pending: number;
2031
- };
2032
- /**
2033
- * The water as an overlay tile (RGBA, blue, alpha from surface depth and damp paper) for a
2034
- * document tile, or `null` where nothing is wet.
2035
- */
2036
- waterOverlay(id: string, tx: number, ty: number): Uint8ClampedArray | null;
2037
- /**
2038
- * Attaches a GPU engine (or `null` to detach). From then on, whenever at least `gpuMinTiles` tiles
2039
- * are live, the GPU mirrors the slots and `displayGpu` shades on it. The CPU stays the authority.
2040
- */
2041
- attachGpu(engine: WetGpuEngine | null): void;
2042
- /** The attached GPU engine, if any. */
2043
- get gpuEngine(): WetGpuEngine | null;
2044
- /** Whether the GPU mirror is in use now (an engine is attached and the wet area is large). */
2045
- get gpuActive(): boolean;
2046
- /** Why the GPU stopped (a lost device, out of GPU memory), or `null`. */
2047
- get gpuError(): string | null;
2048
- private gpuDirtySlot;
2049
- private gpuStop;
2050
- /** Turns the mirror on or off for the current wet area. Returns whether it is on. */
2051
- private gpuDecide;
2052
- /** Sends the rest of a lead batch to the GPU (before anything else touches its state). */
2053
- private gpuFinishPlan;
2054
- /**
2055
- * Lead mode: steps the GPU up to `steps` further into the batch the CPU is running (never past
2056
- * it, so the GPU is never ahead of the CPU's next edit). Returns the steps taken.
2057
- */
2058
- gpuLead(steps: number): number;
2059
- /** Steps the GPU is ahead of the CPU's last finished batch (lead mode), 0 otherwise. */
2060
- get gpuAhead(): number;
2061
- /** Whether the GPU can show steps the CPU has not finished (a lead batch is running). */
2062
- get gpuLeading(): boolean;
2063
- /** Uploads what the CPU changed since the last upload (the current set and deposits; whole slots on wake-up). */
2064
- private gpuSync;
2065
- private gpuJobs;
2066
- /** Runs the coming CPU batch of `n` steps on the GPU mirror too. */
2067
- private gpuBatch;
2068
- /**
2069
- * Like `display`, but shades every stale tile on the GPU at once (when the mirror is in use;
2070
- * otherwise, or when the world changed while the GPU worked, it is `display(id, maxCpuTiles)`).
2071
- * Call between batches.
2072
- */
2073
- displayGpu(id: string, maxCpuTiles?: number, lead?: boolean): Promise<{
2074
- surface: TiledSurface;
2075
- changed: RasterRect[];
2076
- pending: number;
2077
- gpu: boolean;
2078
- }>;
2079
- /** A copy of everything (for undo): live slots, layers, clock and tilt. */
2080
- snapshot(): WetSnapshot;
2081
- /** Puts a snapshot back exactly (the simulation then continues as it would have). */
2082
- restore(snap: WetSnapshot): void;
2083
- /** Bytes of wasm memory held. */
2084
- memoryBytes(): number;
2085
- /** Live tiles across all layers. */
2086
- activeTiles(): number;
2087
- /** Memory for tests and tools: the raw f32 view (read only, please). */
2088
- get rawFloats(): Float32Array;
2089
- /** The current set's parity (tests). */
2090
- get currentParity(): number;
2091
- /** Float index of a slot's first plane (tests). */
2092
- slotIndex(id: string, tx: number, ty: number): number;
2093
- }
2094
-
2095
- /**
2096
- * Flow maps: a push on the water at every cell (cells per step², like tilt), from a few generated
2097
- * patterns. Every pattern is computed with + − × ÷ only, so it is the same in every engine.
2098
- */
2099
-
2100
- type WetFlowKind = 'none' | 'linear' | 'swirl' | 'radial' | 'turbulent';
2101
- declare const WET_FLOW_KINDS: readonly WetFlowKind[];
2102
- /** A flow map as settings (saved with the document). */
2103
- interface WetFlowSpec {
2104
- kind: WetFlowKind;
2105
- /** 0..1: how hard it pushes (1 is about as strong as a 25° tilt). */
2106
- strength: number;
2107
- /** `linear`: direction in degrees clockwise from canvas down. */
2108
- angle: number;
2109
- /** `swirl` / `radial`: centre in document pixels (default the canvas centre). */
2110
- center?: {
2111
- x: number;
2112
- y: number;
2113
- };
2114
- /** `swirl`: true turns clockwise. */
2115
- clockwise: boolean;
2116
- /** `turbulent`: eddy size in pixels (8..1024). */
2117
- scale: number;
2118
- seed: number;
2119
- }
2120
- declare const DEFAULT_FLOW: Readonly<WetFlowSpec>;
2121
- /** sin and cos of an angle in radians, by range reduction and Taylor series (deterministic). */
2122
- declare function dsincos(a: number): [number, number];
2123
- /** Settings with every field in range. */
2124
- declare function normalizeFlow(raw?: Partial<WetFlowSpec>, base?: WetFlowSpec): WetFlowSpec;
2125
- /** The flow field of a spec on a canvas, or `null` for `none` (strength is applied by the world). */
2126
- declare function flowField(spec: WetFlowSpec, width: number, height: number): WetFlowField | null;
2127
-
2128
- /**
2129
- * Counter-based seeded randomness on 32-bit integer hashes: every value is a pure function of its
2130
- * inputs, so strokes and papers regenerate identically on every machine. Shared by the paint
2131
- * engine (`/paint`) and the paint editor (which re-exports it from `noise.ts`).
2132
- */
2133
- /** A 32-bit finaliser (murmur3's `fmix32`). */
2134
- declare function mix32(h: number): number;
2135
- /** Hash of integer coordinates and a salt to [0, 1). */
2136
- declare function hash2(x: number, y: number, salt: number): number;
2137
- /** Uniform in [0, 1) from a seed, an index and a stream (one stream per random quantity). */
2138
- declare function rand01(seed: number, index: number, stream: number): number;
2139
-
2140
- export { type PaintGradientRepeat as $, MAX_BRUSH_SIZE as A, type BrushSettings as B, type CloneTransform as C, type Dab as D, PAINT_COLOR_JSON_SCHEMA as E, PAINT_GRADIENT_FULL_SPEED as F, PAINT_GRADIENT_JSON_SCHEMA as G, PAINT_GRADIENT_KIT as H, PAINT_GRADIENT_MAPPINGS as I, PAINT_GRADIENT_MAPPING_LABELS as J, PAINT_GRADIENT_PRESETS as K, PAINT_GRADIENT_REPEATS as L, type MixerSettings as M, PAINT_GRADIENT_REPEAT_LABELS as N, PAINT_GRADIENT_SPACES as O, type PaintSource as P, PAINT_GRADIENT_SPACE_LABELS as Q, type RetouchSettings as R, StrokeCoverage as S, TileMedium as T, PAINT_SOURCE_JSON_SCHEMA as U, type PaintColorSource as V, WetWorld as W, type PaintDabParams as X, type PaintGradientKit as Y, type PaintGradientMapping as Z, type PaintGradientMappingKind as _, type MixerLoad as a, PARAM_FLOATS as a$, type PaintGradientSample as a0, PaintGradientSampler as a1, type PaintGradientSpace as a2, type PaintGradientStop as a3, type PaintRgb as a4, PaintSourceError as a5, WET_FLOW_KINDS as a6, WET_PARAM_KEYS as a7, type WetFlowKind as a8, type WetSupport as a9, DEFAULT_SMUDGE as aA, DEFAULT_WET_PAPER as aB, DRY_EPS as aC, DabWalker as aD, EMPTY_MIXER_LOAD as aE, EX as aF, EY as aG, type ExchangeRates as aH, FLAG_ALIVE as aI, FLAG_BOTTOM as aJ, FLAG_LEFT as aK, FLAG_RIGHT as aL, FLAG_TOP as aM, FLAG_WET as aN, FLAT as aO, FREE_INTERVAL as aP, IDENTITY_CLONE_TRANSFORM as aQ, MAX_BATCH as aR, MAX_TILT_ACCEL as aS, MAX_WET_LANES as aT, MAX_WET_SLOTS as aU, MIXER_DRAIN as aV, MIXER_PICKUP_RATE as aW, MIXING_MODELS as aX, OPP as aY, PAPER_CAPACITY as aZ, PARAM as a_, addPaintGradientStop as aa, hash2 as ab, hexToPaintRgb as ac, isGradientPaint as ad, mix32 as ae, normalizeFlow as af, normalizeHexColor as ag, normalizeWetParams as ah, paintGradientMappingFor as ai, paintSourceColor as aj, paintSourceCss as ak, parsePaintSource as al, rand01 as am, readPaintSource as an, removePaintGradientStop as ao, resolvePaintGradient as ap, reversePaintGradient as aq, scalePaintGradient as ar, solidPaint as as, updatePaintGradientStop as at, wetSupport as au, type BlenderScratch as av, DEFAULT_BLENDER as aw, DEFAULT_BRUSH as ax, DEFAULT_MIXER as ay, DEFAULT_RETOUCH as az, type MixerTarget as b, createBlenderScratch as b$, PIGMENT_CHANNELS as b0, PLANE as b1, PLANE_BYTES as b2, P_D0 as b3, P_EDGE as b4, P_FX as b5, P_FY as b6, P_H as b7, P_P0 as b8, P_RHO as b9, type ToneRange as bA, TransformedPixelSource as bB, U_MAX as bC, W9 as bD, WET_TILE as bE, type WetFlowField as bF, WetGpuEngine as bG, type WetGpuJob as bH, type WetGpuSupport as bI, type WetKernelKind as bJ, type WetLaneOptions as bK, type WetLaneStats as bL, type WetPaper as bM, type WetWorkerLayout as bN, WetWorkerPool as bO, WetWorldError as bP, applyRetouchToTile as bQ, applyStrokeToDeepTile as bR, applyStrokeToTile as bS, applyToneToDeepTile as bT, blendRadius as bU, blenderDab as bV, boxBlurPixels as bW, canShareMemory as bX, cleanMixer as bY, cloneSamplePoint as bZ, coverageTileCoord as b_, P_SW as ba, P_UX as bb, P_UY as bc, PaintExchange as bd, type PaintMedium as be, type PaintPixels as bf, type PaintSample as bg, type PixelSource as bh, RETOUCH_SAMPLES as bi, RegionPixelSource as bj, type RetouchDabEffect as bk, RetouchDabStroke as bl, type RetouchSample as bm, SET_PLANES as bn, SLOT_BYTES as bo, SLOT_PLANES as bp, SLOT_SIDE as bq, SPOT_HEAL_TYPES as br, STRIP as bs, type SmudgeDab as bt, SmudgeTip as bu, type SpotHealType as bv, TIP_GRADIENT_STEPS as bw, TONE_RANGES as bx, type TileMediumOptions as by, TilePixelSource as bz, MixerStroke as c, createWetGpuEngine as c0, dabCoverage as c1, dabRect as c2, dodgeBurn as c3, dodgeBurnCurve as c4, dsincos as c5, edgeBits as c6, exchangeRates as c7, findWebGpu as c8, flowField as c9, spotHealPixels as cA, spotHealWindow as cB, tipFalloff as cC, tipReach as cD, tipShape as cE, toneOf as cF, walkDabs as cG, wetGpuSupport as cH, wetSimdSupported as cI, gradientDabColor as ca, healPixels as cb, isIdentityCloneTransform as cc, latticeViscosity as cd, loadMixer as ce, loadWetKernel as cf, luma as cg, mixerRates as ch, normalizeBlender as ci, normalizeBrush as cj, normalizeMixer as ck, normalizeRetouch as cl, normalizeSmudge as cm, normalizeTilt as cn, packWetParams as co, perDabStrength as cp, put255 as cq, rasterizeDab as cr, readSourceRegion as cs, retouchFilter as ct, rgbaViewOfGrey as cu, saturationOf as cv, smoothPoints as cw, smudgeEffect as cx, sponge as cy, spongeEffect as cz, type RetouchApply as d, type StrokePoint as e, type RetouchPaint as f, CoverageMap as g, type PaintTileTarget as h, type StrokeApply as i, type PaintGradientSource as j, type MixingModel as k, type BlenderSettings as l, type SmudgeSettings as m, type WetTilt as n, type WetParams as o, type WetFlowSpec as p, type RgbTriple as q, type WetWorldOptions as r, type WetGpuProvider as s, type WetBackendInfo as t, type WetSnapshot as u, type WetPaperInput as v, DEFAULT_FLOW as w, DEFAULT_PAINT_GRADIENT as x, DEFAULT_PAINT_GRADIENT_MAPPING as y, DEFAULT_WET_PARAMS as z };