@pixerate/editor 0.135.0 → 0.137.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/animation-editor/index.cjs +63 -55
  2. package/dist/animation-editor/index.cjs.map +1 -1
  3. package/dist/animation-editor/index.d.cts +8 -5
  4. package/dist/animation-editor/index.d.ts +8 -5
  5. package/dist/animation-editor/index.js +10 -2
  6. package/dist/animation-editor/index.js.map +1 -1
  7. package/dist/{bezier-J8i4uqSH.d.cts → bezier-CZ2AhjID.d.cts} +1 -1
  8. package/dist/{bezier-lNEOp0Jx.d.ts → bezier-Dywv3Bw8.d.ts} +1 -1
  9. package/dist/blending-D7jCmjO6.d.cts +1026 -0
  10. package/dist/blending-NgF3qo3d.d.ts +1026 -0
  11. package/dist/brushes-CnvZSYYU.d.cts +744 -0
  12. package/dist/brushes-DJmUbIgz.d.ts +744 -0
  13. package/dist/{brushstrokeRender-SII6MG4R.js → brushstrokeRender-2FAXA3XC.js} +13 -16
  14. package/dist/{brushstrokeRender-SII6MG4R.js.map → brushstrokeRender-2FAXA3XC.js.map} +1 -1
  15. package/dist/{brushstrokeRender-JNHVEUFG.cjs → brushstrokeRender-IMFY3FF6.cjs} +16 -19
  16. package/dist/brushstrokeRender-IMFY3FF6.cjs.map +1 -0
  17. package/dist/canvas/index.cjs +10 -10
  18. package/dist/canvas/index.cjs.map +1 -1
  19. package/dist/canvas/index.d.cts +10 -7
  20. package/dist/canvas/index.d.ts +10 -7
  21. package/dist/canvas/index.js +4 -4
  22. package/dist/{chunk-W4LNKTHT.cjs → chunk-2XS6LTUP.cjs} +63 -63
  23. package/dist/{chunk-W4LNKTHT.cjs.map → chunk-2XS6LTUP.cjs.map} +1 -1
  24. package/dist/{chunk-HHTCDFZC.js → chunk-3D77LOBM.js} +234 -136
  25. package/dist/chunk-3D77LOBM.js.map +1 -0
  26. package/dist/{chunk-BR7EKQUP.js → chunk-3G6J7UFV.js} +322 -2
  27. package/dist/chunk-3G6J7UFV.js.map +1 -0
  28. package/dist/{chunk-62XUO5LB.js → chunk-3X6IAJUQ.js} +2 -2
  29. package/dist/{chunk-EM3FMJWE.js → chunk-5FSRTP6E.js} +320 -3
  30. package/dist/chunk-5FSRTP6E.js.map +1 -0
  31. package/dist/{chunk-Z2H7KECK.cjs → chunk-5OOIUXNJ.cjs} +345 -247
  32. package/dist/chunk-5OOIUXNJ.cjs.map +1 -0
  33. package/dist/{chunk-7RJ6P7MI.js → chunk-6S6362D2.js} +2 -2
  34. package/dist/chunk-A3BVTI3G.js +150 -0
  35. package/dist/chunk-A3BVTI3G.js.map +1 -0
  36. package/dist/{chunk-24ATT2PE.cjs → chunk-AEAAN4AD.cjs} +120 -120
  37. package/dist/{chunk-24ATT2PE.cjs.map → chunk-AEAAN4AD.cjs.map} +1 -1
  38. package/dist/chunk-ECCXGELC.cjs +727 -0
  39. package/dist/chunk-ECCXGELC.cjs.map +1 -0
  40. package/dist/{chunk-SVYE5HIM.js → chunk-EQQMGO42.js} +2 -2
  41. package/dist/{chunk-FHPVDTI2.cjs → chunk-F2Y4E4GU.cjs} +22 -22
  42. package/dist/{chunk-FHPVDTI2.cjs.map → chunk-F2Y4E4GU.cjs.map} +1 -1
  43. package/dist/{chunk-OXSV64JX.js → chunk-G6IKY6PL.js} +4 -4
  44. package/dist/{chunk-UYQUPUR6.js → chunk-HUO3TI4C.js} +3 -3
  45. package/dist/chunk-I52DRGGJ.js +727 -0
  46. package/dist/chunk-I52DRGGJ.js.map +1 -0
  47. package/dist/{chunk-DDSO4HS7.cjs → chunk-KEENUAFN.cjs} +2 -2
  48. package/dist/{chunk-DDSO4HS7.cjs.map → chunk-KEENUAFN.cjs.map} +1 -1
  49. package/dist/{chunk-2HC4DRF4.js → chunk-LLFHCSU2.js} +2 -2
  50. package/dist/{chunk-J5BA3BQ6.cjs → chunk-O7LUD7MH.cjs} +323 -3
  51. package/dist/chunk-O7LUD7MH.cjs.map +1 -0
  52. package/dist/{chunk-ILYFOJUN.js → chunk-OIBIRXRG.js} +3 -3
  53. package/dist/{chunk-FWEO3HSL.js → chunk-PQQF5KOW.js} +8 -8
  54. package/dist/{chunk-T6TV6HY6.cjs → chunk-PRSRYHQD.cjs} +36 -36
  55. package/dist/{chunk-T6TV6HY6.cjs.map → chunk-PRSRYHQD.cjs.map} +1 -1
  56. package/dist/chunk-Q6OUXEK3.cjs +150 -0
  57. package/dist/chunk-Q6OUXEK3.cjs.map +1 -0
  58. package/dist/{chunk-6GFRKLLS.cjs → chunk-QBWEH3IM.cjs} +429 -112
  59. package/dist/chunk-QBWEH3IM.cjs.map +1 -0
  60. package/dist/{chunk-ESIWZMMK.js → chunk-SYT2TLQF.js} +35 -37
  61. package/dist/{chunk-ESIWZMMK.js.map → chunk-SYT2TLQF.js.map} +1 -1
  62. package/dist/{chunk-YT3JL7YM.cjs → chunk-TLCTTQFF.cjs} +53 -53
  63. package/dist/{chunk-YT3JL7YM.cjs.map → chunk-TLCTTQFF.cjs.map} +1 -1
  64. package/dist/{chunk-P3RQ5UJE.cjs → chunk-UTQKC66X.cjs} +4 -4
  65. package/dist/{chunk-P3RQ5UJE.cjs.map → chunk-UTQKC66X.cjs.map} +1 -1
  66. package/dist/{chunk-J3B45HTA.cjs → chunk-XB25WOC2.cjs} +6 -6
  67. package/dist/{chunk-J3B45HTA.cjs.map → chunk-XB25WOC2.cjs.map} +1 -1
  68. package/dist/{chunk-YAFS2QFZ.cjs → chunk-XN2NBLV5.cjs} +313 -315
  69. package/dist/chunk-XN2NBLV5.cjs.map +1 -0
  70. package/dist/effects/index.d.cts +7 -4
  71. package/dist/effects/index.d.ts +7 -4
  72. package/dist/{export-DMeG6E06.d.ts → export-41rL6Cne.d.ts} +1 -1
  73. package/dist/{export-AZSwX087.d.cts → export-CyJdfP_Z.d.cts} +5 -4
  74. package/dist/{export-KzubBKo7.d.cts → export-D9mXp05Q.d.cts} +1 -1
  75. package/dist/{export-BDkyDNM8.d.ts → export-DwnZ_hvQ.d.ts} +5 -4
  76. package/dist/extensions/index.cjs +3 -3
  77. package/dist/extensions/index.cjs.map +1 -1
  78. package/dist/extensions/index.js +2 -2
  79. package/dist/generation/index.cjs +150 -945
  80. package/dist/generation/index.cjs.map +1 -1
  81. package/dist/generation/index.d.cts +3 -423
  82. package/dist/generation/index.d.ts +3 -423
  83. package/dist/generation/index.js +59 -854
  84. package/dist/generation/index.js.map +1 -1
  85. package/dist/grammar/index.cjs +3 -3
  86. package/dist/grammar/index.cjs.map +1 -1
  87. package/dist/grammar/index.js +2 -2
  88. package/dist/{helpers-Coa4e2j_.d.ts → helpers-C-RgeyvH.d.ts} +25 -8
  89. package/dist/{helpers-DHl28yLO.d.cts → helpers-v6GMazzg.d.cts} +25 -8
  90. package/dist/image-editor/index.cjs +986 -986
  91. package/dist/image-editor/index.cjs.map +1 -1
  92. package/dist/image-editor/index.d.cts +15 -11
  93. package/dist/image-editor/index.d.ts +15 -11
  94. package/dist/image-editor/index.js +94 -94
  95. package/dist/index.cjs +28 -28
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.js +36 -36
  98. package/dist/{opentype-BWR7Qv8W.d.cts → opentype-KR_jj6b_.d.cts} +1 -1
  99. package/dist/{opentype-Dfcx6wFx.d.ts → opentype-a1KAW-qs.d.ts} +1 -1
  100. package/dist/paint/index.cjs +5 -8
  101. package/dist/paint/index.cjs.map +1 -1
  102. package/dist/paint/index.d.cts +4 -2
  103. package/dist/paint/index.d.ts +4 -2
  104. package/dist/paint/index.js +15 -18
  105. package/dist/paint-editor/index.cjs +405 -403
  106. package/dist/paint-editor/index.cjs.map +1 -1
  107. package/dist/paint-editor/index.d.cts +15 -748
  108. package/dist/paint-editor/index.d.ts +15 -748
  109. package/dist/paint-editor/index.js +79 -77
  110. package/dist/paint-editor/index.js.map +1 -1
  111. package/dist/{pathfinder-D8KQ6blQ.d.cts → pathfinder-BHS4vQNm.d.cts} +1 -1
  112. package/dist/{pathfinder-Celbi3e6.d.ts → pathfinder-BVvTIBMo.d.ts} +1 -1
  113. package/dist/{pool-8JZGWHvR.d.ts → pool-CZQOrSXZ.d.ts} +1 -1
  114. package/dist/{pool-BfuxBKTd.d.cts → pool-FjZZmZ80.d.cts} +1 -1
  115. package/dist/provenance-DmlcgdzT.d.cts +426 -0
  116. package/dist/provenance-uh5r2TQ8.d.ts +426 -0
  117. package/dist/rand-8xbUb6ur.d.ts +1118 -0
  118. package/dist/rand-Cw9VLrVC.d.cts +1118 -0
  119. package/dist/raster/index.cjs +17 -19
  120. package/dist/raster/index.cjs.map +1 -1
  121. package/dist/raster/index.js +9 -11
  122. package/dist/raster/index.js.map +1 -1
  123. package/dist/{svg-CxxqG3_W.d.ts → svg-CSz3mzkw.d.ts} +2 -2
  124. package/dist/{svg-pwXWq6s2.d.cts → svg-DTb40ICU.d.cts} +2 -2
  125. package/dist/{tools-y1M6O3UT.d.ts → tools-B0wXP5cF.d.ts} +1 -1
  126. package/dist/{tools-3EBjR91b.d.cts → tools-Ca0kI1qo.d.cts} +1 -1
  127. package/dist/vector/index.cjs +2698 -1674
  128. package/dist/vector/index.cjs.map +1 -1
  129. package/dist/vector/index.d.cts +390 -72
  130. package/dist/vector/index.d.ts +390 -72
  131. package/dist/vector/index.js +1665 -641
  132. package/dist/vector/index.js.map +1 -1
  133. package/dist/video-editor/index.d.cts +7 -4
  134. package/dist/video-editor/index.d.ts +7 -4
  135. package/dist/{warp-Ceqdz7kj.d.cts → warp-DTOp0lON.d.cts} +1 -1
  136. package/dist/{warp-C1gwu2G_.d.ts → warp-pd7hrH4T.d.ts} +1 -1
  137. package/package.json +1 -1
  138. package/skills/pixerate-vector/SKILL.md +22 -0
  139. package/dist/brushstrokeRender-JNHVEUFG.cjs.map +0 -1
  140. package/dist/chunk-64PYNEHJ.js +0 -340
  141. package/dist/chunk-64PYNEHJ.js.map +0 -1
  142. package/dist/chunk-6GFRKLLS.cjs.map +0 -1
  143. package/dist/chunk-7QN76ESJ.js +0 -323
  144. package/dist/chunk-7QN76ESJ.js.map +0 -1
  145. package/dist/chunk-BR7EKQUP.js.map +0 -1
  146. package/dist/chunk-DCLJNVZS.cjs +0 -323
  147. package/dist/chunk-DCLJNVZS.cjs.map +0 -1
  148. package/dist/chunk-EM3FMJWE.js.map +0 -1
  149. package/dist/chunk-HHTCDFZC.js.map +0 -1
  150. package/dist/chunk-J5BA3BQ6.cjs.map +0 -1
  151. package/dist/chunk-YAFS2QFZ.cjs.map +0 -1
  152. package/dist/chunk-Z2H7KECK.cjs.map +0 -1
  153. package/dist/chunk-Z6OUCS2K.cjs +0 -340
  154. package/dist/chunk-Z6OUCS2K.cjs.map +0 -1
  155. package/dist/rand-BszBST4s.d.ts +0 -2140
  156. package/dist/rand-DOH_aMvm.d.cts +0 -2140
  157. /package/dist/{chunk-62XUO5LB.js.map → chunk-3X6IAJUQ.js.map} +0 -0
  158. /package/dist/{chunk-7RJ6P7MI.js.map → chunk-6S6362D2.js.map} +0 -0
  159. /package/dist/{chunk-SVYE5HIM.js.map → chunk-EQQMGO42.js.map} +0 -0
  160. /package/dist/{chunk-OXSV64JX.js.map → chunk-G6IKY6PL.js.map} +0 -0
  161. /package/dist/{chunk-UYQUPUR6.js.map → chunk-HUO3TI4C.js.map} +0 -0
  162. /package/dist/{chunk-2HC4DRF4.js.map → chunk-LLFHCSU2.js.map} +0 -0
  163. /package/dist/{chunk-ILYFOJUN.js.map → chunk-OIBIRXRG.js.map} +0 -0
  164. /package/dist/{chunk-FWEO3HSL.js.map → chunk-PQQF5KOW.js.map} +0 -0
@@ -0,0 +1,1118 @@
1
+ import { a as BlendMode, R as RasterRect, T as TiledSurface } from './surface-1MRncXpF.cjs';
2
+ import { T as TileMedium, D as Dab, B as BrushSettings, P as PaintTileTarget, S as StrokePoint, f as PaintPixels, C as CoverageMap, d as SmudgeSettings } from './blending-D7jCmjO6.cjs';
3
+
4
+ /**
5
+ * The retouching tools on the shared brush engine: Clone Stamp, Healing Brush, Spot Healing
6
+ * Brush, Smudge, Dodge, Burn and Sponge. Two stroke shapes cover them all:
7
+ *
8
+ * - **Accumulate, then composite** (Clone Stamp, Dodge, Burn; the Healing tools' result): the
9
+ * stroke's dabs build up one `CoverageMap` (flow builds towards the dab's opacity ceiling), and
10
+ * per-pixel paint taken from the pre-stroke state (a cloned source, a toned copy of the pixel,
11
+ * a healed patch) is composited through it with `applyRetouchToTile`. Pixels painted earlier in
12
+ * the stroke are never sampled again, and overlapping dabs never exceed the stroke's opacity.
13
+ * - **Sequential dabs** (Smudge, Sponge): `RetouchDabStroke` runs each dab in order over live
14
+ * tiles, so dab n sees what dab n − 1 did.
15
+ *
16
+ * The Healing Brush clones while it is drawn and, when the stroke ends, replaces the clone with a
17
+ * seamless clone (`healPixels`, Poisson blending): the source's texture with the colour and light
18
+ * of the pixels around the stroke. The Spot Healing Brush finds its own source (`spotHealPixels`).
19
+ *
20
+ * Tone curves (`dodgeBurnCurve`, `dodgeBurn`, `sponge`) are photocraft's, made to behave like
21
+ * Photoshop's tools: the chosen range moves most, black and white stay put for midtones, and
22
+ * Protect Tones works on luma so hue and saturation are kept. Data in, pixels out; no DOM.
23
+ */
24
+
25
+ /** The tonal range Dodge and Burn work on most. */
26
+ type ToneRange = 'shadows' | 'midtones' | 'highlights';
27
+ declare const TONE_RANGES: readonly ToneRange[];
28
+ /** Rec. 601 luma of straight RGB (0..1). */
29
+ declare function luma(r: number, g: number, b: number): number;
30
+ /** The dodge (or burn) transfer of one value `v` (0..1) at exposure `e` (0..1). */
31
+ declare function dodgeBurnCurve(v: number, e: number, range: ToneRange, burn: boolean): number;
32
+ /**
33
+ * Dodge (or burn) straight RGB `c` (0..1, written in place) at exposure `e`. With `protectTones`
34
+ * the curve moves luma and the colour is scaled to it (hue and saturation kept); otherwise each
35
+ * channel goes through the curve.
36
+ */
37
+ declare function dodgeBurn(c: number[], e: number, range: ToneRange, burn: boolean, protectTones: boolean): number[];
38
+ /** HSV saturation (chroma / max) of straight RGB. */
39
+ declare function saturationOf(r: number, g: number, b: number): number;
40
+ /**
41
+ * Sponge: moves straight RGB `c` (written in place) away from (`saturate`) or towards its luma
42
+ * grey by `amount` (0..1). `vibrance` damps it where it would clip: saturating touches already
43
+ * saturated colours less, desaturating touches near-greys less. Gamut-compressed, never clipped.
44
+ */
45
+ declare function sponge(c: number[], amount: number, saturate: boolean, vibrance: boolean): number[];
46
+ /** Per-dab strength so that one pass over a point totals about `e`, whatever the spacing. */
47
+ declare function perDabStrength(e: number, spacing: number): number;
48
+ /** Which pixels the Clone Stamp and the Healing tools sample. */
49
+ type RetouchSample = 'current' | 'current-below' | 'all';
50
+ declare const RETOUCH_SAMPLES: readonly RetouchSample[];
51
+ /** How the Spot Healing Brush finds its source. */
52
+ type SpotHealType = 'proximity-match' | 'content-aware' | 'smooth';
53
+ declare const SPOT_HEAL_TYPES: readonly SpotHealType[];
54
+ /** The retouching tools' options. */
55
+ interface RetouchSettings {
56
+ /** Clone / heal: keep the source offset from stroke to stroke (default true); off, every stroke starts at the source point. */
57
+ aligned: boolean;
58
+ /** Clone / heal / spot heal: what is sampled (default `current`: the target layer). */
59
+ sample: RetouchSample;
60
+ /** 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). */
61
+ spotType: SpotHealType;
62
+ /** Dodge / burn: the tonal range (default `midtones`). */
63
+ range: ToneRange;
64
+ /** Dodge / burn: exposure 0..1 (default 0.5). */
65
+ exposure: number;
66
+ /** Dodge / burn: tone luma only, keeping hue and saturation (default true). */
67
+ protectTones: boolean;
68
+ /** Sponge: `desaturate` (default) or `saturate`. */
69
+ spongeMode: 'desaturate' | 'saturate';
70
+ /** Sponge: damp the change where colours would clip (default true). */
71
+ vibrance: boolean;
72
+ /** Smudge: how strongly the finger carries and lays paint; Blur / Sharpen: how strongly they act, 0..1 (default 0.5). */
73
+ strength: number;
74
+ /** History Brush: how many undo steps back it paints from; 0 (default) is the oldest state history keeps. */
75
+ historySteps: number;
76
+ }
77
+ declare const DEFAULT_RETOUCH: Readonly<RetouchSettings>;
78
+ /** Retouch settings with every field valid (missing or invalid fields from `base`). */
79
+ declare function normalizeRetouch(raw?: Partial<RetouchSettings>, base?: RetouchSettings): RetouchSettings;
80
+ /**
81
+ * Box blur of `data` (`w × h × n` floats) in place, `r` pixels each side, edges clamped. RGBA
82
+ * blurs premultiplied, so transparent pixels lend no colour. Run twice for a near-Gaussian.
83
+ */
84
+ declare function boxBlurPixels(data: Float32Array, w: number, h: number, n: 1 | 4, r: number): void;
85
+ /**
86
+ * The Blur / Sharpen tools' filter over a padded region (`w × h × n` floats on the 0..255 scale,
87
+ * in place): `blur` is two box passes of `radius`; `sharpen` is an unsharp mask of `amount`
88
+ * over a radius-1 blur (colour only; alpha kept). Values stay at or above 0.
89
+ */
90
+ declare function retouchFilter(data: Float32Array, w: number, h: number, n: 1 | 4, kind: 'blur' | 'sharpen', radius?: number, amount?: number): void;
91
+ /** Pixels a retouching stroke samples, by document pixel. */
92
+ interface PixelSource {
93
+ /** Straight RGBA 0..255 of document pixel (x, y) into `out`; false (and `out` zeroed) where there is nothing. */
94
+ sample(x: number, y: number, out: number[]): boolean;
95
+ }
96
+ /**
97
+ * Photoshop's Clone Source transform: the cloned pixels appear scaled by `scaleX` / `scaleY`
98
+ * (negative flips) and rotated by `rotate` degrees (clockwise) about the stroke's start.
99
+ */
100
+ interface CloneTransform {
101
+ scaleX: number;
102
+ scaleY: number;
103
+ rotate: number;
104
+ }
105
+ declare const IDENTITY_CLONE_TRANSFORM: CloneTransform;
106
+ declare function isIdentityCloneTransform(t: CloneTransform): boolean;
107
+ /**
108
+ * Where a clone samples for target point (`x`, `y`): `source + M⁻¹ · (p − anchor)`, with `M` the
109
+ * transform (scale then rotate) and `anchor` the target point that maps onto the source point.
110
+ */
111
+ declare function cloneSamplePoint(t: CloneTransform, source: {
112
+ x: number;
113
+ y: number;
114
+ }, anchor: {
115
+ x: number;
116
+ y: number;
117
+ }, x: number, y: number): [number, number];
118
+ /** A source seen through a map from document points to source points (bilinear, premultiplied). */
119
+ declare class TransformedPixelSource implements PixelSource {
120
+ private readonly inner;
121
+ private readonly map;
122
+ private readonly px;
123
+ constructor(inner: PixelSource, map: (x: number, y: number) => [number, number]);
124
+ sample(x: number, y: number, out: number[]): boolean;
125
+ }
126
+ /**
127
+ * A source over raster tiles (layer pixels, a mask, or composite tiles): `readTile` gives tile
128
+ * (tx, ty) in source space or `null` when it is empty; source pixel = document pixel − `offset`.
129
+ * One-channel tiles read as grey (alpha 255). Tiles are cached, so make one per stroke.
130
+ */
131
+ declare class TilePixelSource implements PixelSource {
132
+ /** A tile's bytes, or a float working tile on the same 0..255 scale (16 / 32 bits). */
133
+ private readonly readTile;
134
+ readonly channels: 1 | 4;
135
+ readonly offset: {
136
+ x: number;
137
+ y: number;
138
+ };
139
+ private readonly cache;
140
+ private lastKey;
141
+ private last;
142
+ constructor(
143
+ /** A tile's bytes, or a float working tile on the same 0..255 scale (16 / 32 bits). */
144
+ readTile: (tx: number, ty: number) => PaintPixels | null, channels: 1 | 4, offset?: {
145
+ x: number;
146
+ y: number;
147
+ });
148
+ sample(x: number, y: number, out: number[]): boolean;
149
+ }
150
+ /** A source over a region of float RGBA pixels (0..255), e.g. a healed patch. */
151
+ declare class RegionPixelSource implements PixelSource {
152
+ readonly rect: RasterRect;
153
+ readonly data: Float32Array;
154
+ constructor(rect: RasterRect, data: Float32Array);
155
+ sample(x: number, y: number, out: number[]): boolean;
156
+ }
157
+ /** `rect` of a source, shifted by (dx, dy), as float RGBA 0..255 (`rect.width × rect.height × 4`). */
158
+ declare function readSourceRegion(source: PixelSource, rect: RasterRect, dx?: number, dy?: number): Float32Array;
159
+ /** What a coverage-based retouching stroke lays through its coverage. */
160
+ type RetouchPaint =
161
+ /** Source pixels at document pixel + (dx, dy) (Clone Stamp, a healed patch). */
162
+ {
163
+ kind: 'source';
164
+ source: PixelSource;
165
+ dx: number;
166
+ dy: number;
167
+ }
168
+ /** The target pixel itself, toned (Dodge, Burn): `tone` edits straight RGB 0..1 in place. */
169
+ | {
170
+ kind: 'tone';
171
+ tone: (rgb: number[]) => void;
172
+ }
173
+ /** A flat colour (the Spot Healing Brush's trail while it is drawn), RGBA 0..255. */
174
+ | {
175
+ kind: 'color';
176
+ color: readonly [number, number, number, number];
177
+ };
178
+ /** How a coverage-based retouching stroke composites. */
179
+ interface RetouchApply {
180
+ /** Stroke opacity 0..1. */
181
+ opacity: number;
182
+ /** Blend mode for source paint (default normal). */
183
+ blend?: BlendMode;
184
+ lockTransparency: boolean;
185
+ /** Selection coverage (document space, canvas-sized), or `null`. */
186
+ selection: {
187
+ width: number;
188
+ height: number;
189
+ data: ArrayLike<number>;
190
+ } | null;
191
+ /** Layer offset (target pixels are in layer space). */
192
+ offset: {
193
+ x: number;
194
+ y: number;
195
+ };
196
+ }
197
+ /**
198
+ * Composites `paint` through the stroke's coverage over `tileData` (target tile (tx, ty) as it was
199
+ * before the stroke, layer space; RGBA, or one byte per pixel for masks) and returns the new tile.
200
+ * With `into` (the previous result) and `region` (document space), only `region` is recomputed.
201
+ * Tone paint keeps alpha; source and colour paint composite with `blend` (source-over by
202
+ * default), keeping alpha on a locked layer.
203
+ */
204
+ declare function applyRetouchToTile<T extends PaintPixels>(coverage: CoverageMap, tileData: T, channels: 1 | 4, tx: number, ty: number, paint: RetouchPaint, s: RetouchApply, options?: {
205
+ into?: T;
206
+ region?: RasterRect;
207
+ }): T;
208
+ /**
209
+ * Dodge / Burn at 16 or 32 bits: `applyRetouchToTile`'s tone paint on a layer tile's deep samples
210
+ * (`Uint16Array` sRGB-encoded, or `Float32Array` linear light, RGBA). The tone curves work on
211
+ * encoded values as at 8 bits, so 32-bit samples are encoded, toned and decoded (colour above 1 is
212
+ * toned at 1 and keeps its excess). Returns the new samples.
213
+ */
214
+ declare function applyToneToDeepTile(coverage: CoverageMap, samples: Uint16Array | Float32Array, tx: number, ty: number, tone: (rgb: number[]) => void, s: RetouchApply, options?: {
215
+ into?: Uint16Array | Float32Array;
216
+ region?: RasterRect;
217
+ }): Uint16Array | Float32Array;
218
+ /** The tone function of a Dodge or Burn stroke. */
219
+ declare function toneOf(tool: 'dodge' | 'burn', settings: Pick<RetouchSettings, 'exposure' | 'range' | 'protectTones'>): (rgb: number[]) => void;
220
+ /**
221
+ * Heals `src` into `dst` over `mask` (all `w × h`, RGBA floats 0..255): a seamless clone solved
222
+ * only over the mask's bounds plus a one-pixel fixed ring; elsewhere the result is `dst`.
223
+ */
224
+ declare function healPixels(w: number, h: number, src: Float32Array, dst: Float32Array, mask: ArrayLike<boolean | number>): Float32Array;
225
+ /**
226
+ * Spot healing: fills `hole` (`w × h`) of `img` (RGBA floats 0..255) with no given source, then
227
+ * heals the fill into its surroundings. `proximity-match` copies the best matching nearby patch
228
+ * (falling back to `smooth` when none fits); `smooth` interpolates the edges inwards. `size` is
229
+ * the brush diameter in pixels. Returns the healed pixels over the whole grid.
230
+ */
231
+ declare function spotHealPixels(w: number, h: number, img: Float32Array, hole: ArrayLike<boolean | number>, size: number, type: SpotHealType,
232
+ /** Told the patch offset it copied (`null`: a smooth fill). */
233
+ onOffset?: (offset: [number, number] | null) => void): Float32Array;
234
+ /** The window a Spot Healing stroke over `bounds` looks for texture in: the bounds grown by their size, on the canvas. */
235
+ declare function spotHealWindow(bounds: RasterRect, canvas: {
236
+ width: number;
237
+ height: number;
238
+ }): RasterRect;
239
+ /**
240
+ * One dab of a sequential retouching stroke: `cov` is the tip's pure coverage over `rect`
241
+ * (document space, 0..1); `d.alpha` is the flow and `d.opacity` the pressure opacity.
242
+ */
243
+ type RetouchDabEffect = (medium: TileMedium, d: Dab, rect: RasterRect, cov: Float32Array) => void;
244
+ /**
245
+ * A stroke whose dabs edit live tiles in order (Smudge, Sponge). Feed points with `push` (any
246
+ * chunks give the same dabs), end with `finish`; each returns the document rectangles it touched.
247
+ */
248
+ declare class RetouchDabStroke {
249
+ readonly brush: BrushSettings;
250
+ private readonly effect;
251
+ dabCount: number;
252
+ bounds: RasterRect | null;
253
+ private readonly walker;
254
+ private readonly medium;
255
+ private smoothPos;
256
+ private buf;
257
+ constructor(brush: BrushSettings, target: PaintTileTarget, effect: RetouchDabEffect);
258
+ push(points: readonly StrokePoint[]): RasterRect[];
259
+ finish(last?: StrokePoint): RasterRect[];
260
+ private smooth;
261
+ private paint;
262
+ }
263
+ /** The Smudge's per-dab effect: a `SmudgeTip` whose strength carries and lays the paint. */
264
+ declare function smudgeEffect(brush: BrushSettings, strength: number, mixing?: SmudgeSettings['mixing']): RetouchDabEffect;
265
+ /** The Sponge's per-dab effect: each dab moves saturation by about half its flow, compounding over passes. */
266
+ declare function spongeEffect(brush: BrushSettings, saturate: boolean, vibrance: boolean): RetouchDabEffect;
267
+ /**
268
+ * An RGBA view of one-channel tiles (masks), for the sequential-dab tools: reads expand grey to
269
+ * opaque RGB, writes land in RGBA copies, and `flush` writes their red channel back through
270
+ * `target.writeTile`. Alpha is locked, so a mask's values are all a dab changes.
271
+ */
272
+ declare function rgbaViewOfGrey(target: PaintTileTarget): {
273
+ target: PaintTileTarget;
274
+ flush(): void;
275
+ };
276
+
277
+ /**
278
+ * The wet-media model: water and pigment on paper, simulated per cell on 256 px tiles.
279
+ *
280
+ * Written from the published literature only (no code, presets or textures from any product):
281
+ *
282
+ * - **Water** is a D2Q9 lattice Boltzmann fluid with BGK collision, τ = 3ν + ½ (any LBM text, e.g.
283
+ * Krüger et al., *The Lattice Boltzmann Method*, 2017). Streaming uses partial bounce-back with a
284
+ * blocking factor from the paper fibres, and the wet front is *pinned* until the water behind it
285
+ * is deep enough: Chu & Tai, "MoXi: Real-Time Ink Dispersion in Absorbent Paper", SIGGRAPH 2005.
286
+ * Tilt and flow maps are a velocity-shift force, with |u| capped at `U_MAX` cells per step.
287
+ * - **Paper** soaks water up until it holds its capacity, and damp paper stops pinning the front
288
+ * (wet-in-wet). Water evaporates faster in a band along the wet front (an "edge" field, 1 at the
289
+ * front and fading over a few cells inwards, as Curtis et al. blur the wet-area mask), so water
290
+ * flows out to the edge and carries pigment there: edge darkening.
291
+ * - **Pigment** (Curtis et al., "Computer-Generated Watercolor", SIGGRAPH 1997) is suspended in the
292
+ * water, advected with a first-order upwind finite-volume flux (it conserves pigment and never
293
+ * goes negative while |u| ≤ ¼), spreads a little between wet cells (bleed), settles into the
294
+ * paper (more in grain valleys: granulation; more at the wet edge: edge darkening), is lifted
295
+ * back by water (more where water arrives: blooms), and settles completely where a cell dries.
296
+ * - **Colour** is Kubelka-Munk: each cell carries pigment as absorption `K` per channel (linear
297
+ * R, G, B) plus one scattering `S`, so mixes are exactly linear (two-constant KM, `K = Σ cᵢKᵢ`,
298
+ * `S = Σ cᵢSᵢ`) and transparent washes multiply like glazes. A colour becomes `K` and `S` with the
299
+ * same two-flux formulas as the shipped pigment model (`K/S = (1 − R)² / 2R`).
300
+ *
301
+ * `stepCellsTs` in `kernel.ts` is the specification of one step, in exact f32 arithmetic. The
302
+ * WebAssembly SIMD kernel (`assembly/wet.ts`) is a lane-for-lane port of it and gives the same bits,
303
+ * so the simulation is deterministic across browsers, kernels and worker counts.
304
+ */
305
+ /** Tile side in cells (the raster tile size). */
306
+ declare const WET_TILE = 256;
307
+ /** A tile slot's side: the tile plus a one-cell halo on each side. */
308
+ declare const SLOT_SIDE = 258;
309
+ /** Floats per plane of a slot. */
310
+ declare const PLANE: number;
311
+ declare const PLANE_BYTES: number;
312
+ /** Planes in each ping-ponged set: f0..f8, depth, velocity, paper water, 4 pigment channels, edge. */
313
+ declare const SET_PLANES = 18;
314
+ declare const P_RHO = 9;
315
+ declare const P_UX = 10;
316
+ declare const P_UY = 11;
317
+ /** Water held in the paper. */
318
+ declare const P_SW = 12;
319
+ /** Suspended pigment: K red, K green, K blue, S. */
320
+ declare const P_P0 = 13;
321
+ /** Edge: 1 at the wet front, fading over a few cells into the wet area (0 where dry). */
322
+ declare const P_EDGE = 17;
323
+ /** Single (not ping-ponged) planes, after the two sets. */
324
+ declare const P_D0: number;
325
+ declare const P_H: number;
326
+ declare const P_FX: number;
327
+ declare const P_FY: number;
328
+ declare const SLOT_PLANES: number;
329
+ declare const SLOT_BYTES: number;
330
+ /** Pigment channels per cell. */
331
+ declare const PIGMENT_CHANNELS = 4;
332
+ /** D2Q9 lattice velocities: 0 rest, 1–4 axis (E, S, W, N in screen space), 5–8 diagonals. */
333
+ declare const EX: readonly [0, 1, 0, -1, 0, 1, -1, -1, 1];
334
+ declare const EY: readonly [0, 0, 1, 0, -1, 1, 1, -1, -1];
335
+ declare const OPP: readonly [0, 3, 4, 1, 2, 7, 8, 5, 6];
336
+ declare const W9: readonly [number, number, number, number, number, number, number, number, number];
337
+ /** Water shallower than this counts as dry. */
338
+ declare const DRY_EPS = 0.001;
339
+ /** Fluid speed cap in cells per step: keeps the LBM low-Mach and the pigment flux CFL-stable. */
340
+ declare const U_MAX = 0.25;
341
+ /** A tile's wet cells within this many cells of an edge wake the neighbouring tile. */
342
+ declare const STRIP = 64;
343
+ /** Steps per batch at most: the wake-up strip stays ahead of anything that moves a cell a step. */
344
+ declare const MAX_BATCH = 32;
345
+ /** Dry tiles are baked and freed only on steps that are multiples of this (so it is deterministic). */
346
+ declare const FREE_INTERVAL = 32;
347
+ /** Per-tile flag bits a step returns. */
348
+ declare const FLAG_ALIVE = 1;
349
+ declare const FLAG_LEFT = 2;
350
+ declare const FLAG_RIGHT = 4;
351
+ declare const FLAG_TOP = 8;
352
+ declare const FLAG_BOTTOM = 16;
353
+ declare const FLAG_WET = 32;
354
+ /** f32 parameters per lane (one per wet layer). */
355
+ declare const PARAM_FLOATS = 32;
356
+ /** Index of each packed parameter (the kernels read these offsets). */
357
+ declare const PARAM: {
358
+ readonly omega: 0;
359
+ readonly tau: 1;
360
+ readonly evap: 2;
361
+ readonly edgeEvap: 3;
362
+ readonly mobility: 4;
363
+ readonly depRate: 5;
364
+ readonly pickup: 6;
365
+ readonly absorption: 7;
366
+ readonly invCap: 8;
367
+ readonly blockScale: 9;
368
+ readonly rough: 10;
369
+ readonly pinT: 11;
370
+ readonly gx: 12;
371
+ readonly gy: 13;
372
+ readonly flowK: 14;
373
+ readonly granulation: 15;
374
+ readonly edgeDep: 16;
375
+ readonly bloom: 17;
376
+ readonly paperEvap: 18;
377
+ readonly hasFlow: 19;
378
+ readonly bleed: 20;
379
+ };
380
+ /**
381
+ * A wet layer's settings, each 0..1 unless noted. The paper's absorption, permeability and
382
+ * roughness come from the paper, not from here.
383
+ */
384
+ interface WetParams {
385
+ /** How thick the water is: 0 runny (ink) .. 1 very thick (oil). Default 0.3 (watercolour). */
386
+ viscosity: number;
387
+ /** How fast the water dries: 0 never .. 1 very fast. Default 0.45. */
388
+ evaporation: number;
389
+ /** Darker, crisper edges where a wash dries: 0 none .. 1 strong. Default 0.5. */
390
+ edgeDarkening: number;
391
+ /** How heavy the pigment is: heavy pigment travels less and settles sooner. Default 0.3. */
392
+ pigmentWeight: number;
393
+ /** How much pigment collects in the paper's valleys. Default 0.4. */
394
+ granulation: number;
395
+ /** How readily water lifts settled pigment back up (re-wetting). Default 0.2. */
396
+ pickup: number;
397
+ /** How fast suspended pigment settles into the paper (staining). Default 0.2. */
398
+ fixture: number;
399
+ /** Blooms (backruns): water flowing into a drying wash pushes its pigment out. Default 0.4. */
400
+ bloom: number;
401
+ /** Colour bleed: pigment spreading between touching wet areas. Default 0.3. */
402
+ bleed: number;
403
+ }
404
+ declare const DEFAULT_WET_PARAMS: Readonly<WetParams>;
405
+ declare const WET_PARAM_KEYS: (keyof WetParams)[];
406
+ /** The paper as wet media read it (all 0..1; from the paint editor's paper settings). */
407
+ interface WetPaper {
408
+ absorption: number;
409
+ permeability: number;
410
+ roughness: number;
411
+ }
412
+ declare const DEFAULT_WET_PAPER: Readonly<WetPaper>;
413
+ /** Canvas tilt (shared by every wet layer): water runs downhill. */
414
+ interface WetTilt {
415
+ /** Degrees from flat, 0 (flat) .. 90 (upright). */
416
+ angle: number;
417
+ /** Which way is downhill, degrees clockwise from canvas down (0 = towards the bottom edge). */
418
+ direction: number;
419
+ }
420
+ declare const FLAT: Readonly<WetTilt>;
421
+ /** Settings with every field in range; missing fields come from `base`. */
422
+ declare function normalizeWetParams(raw?: Partial<WetParams>, base?: WetParams): WetParams;
423
+ declare function normalizeTilt(raw?: Partial<WetTilt>, base?: WetTilt): WetTilt;
424
+ /** Kinematic viscosity (lattice units) for `viscosity` 0..1: 0.02 .. 1.5, exponential. */
425
+ declare function latticeViscosity(v: number): number;
426
+ /** Water a cell of paper holds when saturated, in depth units. */
427
+ declare const PAPER_CAPACITY = 0.08;
428
+ /** Largest tilt acceleration (cells per step²), at 90°. */
429
+ declare const MAX_TILT_ACCEL = 0.025;
430
+ /** Packs a lane's settings into the f32 block the kernels read. `flowStrength` 0 turns the flow map off. */
431
+ declare function packWetParams(params: WetParams, paper: WetPaper, tilt: WetTilt, flowStrength: number, out?: Float32Array<ArrayBuffer>): Float32Array;
432
+ /** Flag bits for a wet (or alive) cell at tile-local (lx, ly): which neighbours it wakes. */
433
+ declare function edgeBits(lx: number, ly: number): number;
434
+
435
+ /**
436
+ * Loading the wet-media WebAssembly kernel and its Web Workers.
437
+ *
438
+ * - The kernel is two small modules (shared memory for workers, ordinary memory for one thread),
439
+ * embedded base64 in `kernel-wasm.ts`, which is only ever imported dynamically, so bundlers split
440
+ * it into its own chunk and nothing loads until the first wet stroke.
441
+ * - Workers run from a Blob URL of `wetWorkerMain`'s source: no extra file for the host to serve
442
+ * and no bundler configuration. They share one `WebAssembly.Memory`, which needs
443
+ * `SharedArrayBuffer`, which browsers only give cross-origin isolated pages (COOP `same-origin`
444
+ * plus COEP `require-corp` or `credentialless`). Without it the kernel runs on one thread.
445
+ * - Everything here is optional: without WebAssembly SIMD the TypeScript kernel runs, with the same
446
+ * results (only slower).
447
+ */
448
+ /** What the wet engine can use here, and why not when it cannot. */
449
+ interface WetSupport {
450
+ /** WebAssembly with SIMD. */
451
+ simd: boolean;
452
+ /** `SharedArrayBuffer` and shared wasm memory (cross-origin isolation in browsers). */
453
+ sharedMemory: boolean;
454
+ /** Web Workers. */
455
+ workers: boolean;
456
+ /** Logical cores reported (`navigator.hardwareConcurrency`, 1 when unknown). */
457
+ cores: number;
458
+ /** A sentence for the user when wet paint will run slower than it could, else `null`. */
459
+ message: string | null;
460
+ }
461
+ /** Compiles the kernel (`shared`: the shared-memory variant). Cached; loads the chunk once. */
462
+ declare function loadWetKernel(shared: boolean): Promise<WebAssembly.Module>;
463
+ /** Whether shared wasm memory can be made here. */
464
+ declare function canShareMemory(): boolean;
465
+ /** Whether this runtime validates WebAssembly SIMD (checked against the kernel itself). */
466
+ declare function wetSimdSupported(): Promise<boolean>;
467
+ /** What the wet engine can use (SIMD is only known after `wetSimdSupported`). */
468
+ declare function wetSupport(simd?: boolean): WetSupport;
469
+ /** The layout numbers a worker needs. */
470
+ interface WetWorkerLayout {
471
+ ctl: number;
472
+ params: number;
473
+ table: number;
474
+ list: number;
475
+ flags: number;
476
+ last: number;
477
+ slots: number;
478
+ slotBytes: number;
479
+ perLane: number;
480
+ tilesX: number;
481
+ tilesY: number;
482
+ keys: number;
483
+ }
484
+ /** A pool of workers stepping one shared memory together. */
485
+ declare class WetWorkerPool {
486
+ private readonly workers;
487
+ private readonly url;
488
+ private constructor();
489
+ get size(): number;
490
+ /** Starts `count` workers on `memory` (shared) with the shared kernel. Rejects if any fails. */
491
+ static create(count: number, module: WebAssembly.Module, memory: WebAssembly.Memory, layout: WetWorkerLayout): Promise<WetWorkerPool>;
492
+ /** Runs `steps` steps over the active list in shared memory; resolves when every worker is done. */
493
+ run(steps: number): Promise<void>;
494
+ destroy(): void;
495
+ }
496
+
497
+ /**
498
+ * Engine B: the wet-media step and display shading on WebGPU, an accelerator for large wet areas.
499
+ *
500
+ * The CPU engine (`WetWorld`, WebAssembly SIMD) stays the authority: it owns the clock, edits,
501
+ * drying, save, export and replay, and gives the same bits everywhere. This engine mirrors the CPU
502
+ * state on the GPU (same slot layout, uploaded region by region as the CPU edits it), steps it and
503
+ * shades it into RGBA8 tiles for display. Its results stay within about 1/255 of the CPU's but are
504
+ * not bit-identical (shader compilers differ), so nothing it computes is ever saved.
505
+ *
506
+ * DOM-free and dependency-free: it takes a `navigator.gpu`-like object (or finds one) and uses
507
+ * only the WebGPU API. Without one, `createWetGpuEngine` says why and the CPU does everything.
508
+ */
509
+ interface GBuffer {
510
+ readonly size: number;
511
+ destroy(): void;
512
+ mapAsync(mode: number, offset?: number, size?: number): Promise<void>;
513
+ getMappedRange(offset?: number, size?: number): ArrayBuffer;
514
+ unmap(): void;
515
+ }
516
+ interface GPass {
517
+ setPipeline(p: unknown): void;
518
+ setBindGroup(i: number, g: unknown): void;
519
+ dispatchWorkgroups(x: number, y?: number, z?: number): void;
520
+ end(): void;
521
+ }
522
+ interface GEncoder {
523
+ beginComputePass(): GPass;
524
+ copyBufferToBuffer(src: GBuffer, so: number, dst: GBuffer, d: number, size: number): void;
525
+ clearBuffer(b: GBuffer, offset?: number, size?: number): void;
526
+ finish(): unknown;
527
+ }
528
+ interface GQueue {
529
+ writeBuffer(b: GBuffer, offset: number, data: ArrayBufferView | ArrayBufferLike, dataOffset?: number, size?: number): void;
530
+ submit(cmds: unknown[]): void;
531
+ onSubmittedWorkDone(): Promise<void>;
532
+ }
533
+ interface GPipeline {
534
+ getBindGroupLayout(i: number): unknown;
535
+ }
536
+ interface GDevice {
537
+ readonly queue: GQueue;
538
+ readonly limits: Record<string, number>;
539
+ readonly lost?: Promise<{
540
+ message?: string;
541
+ reason?: string;
542
+ }>;
543
+ createBuffer(d: {
544
+ size: number;
545
+ usage: number;
546
+ label?: string;
547
+ }): GBuffer;
548
+ createShaderModule(d: {
549
+ code: string;
550
+ label?: string;
551
+ }): unknown;
552
+ createComputePipeline(d: {
553
+ layout: 'auto';
554
+ compute: {
555
+ module: unknown;
556
+ entryPoint: string;
557
+ };
558
+ label?: string;
559
+ }): GPipeline;
560
+ createComputePipelineAsync?(d: {
561
+ layout: 'auto';
562
+ compute: {
563
+ module: unknown;
564
+ entryPoint: string;
565
+ };
566
+ label?: string;
567
+ }): Promise<GPipeline>;
568
+ createBindGroup(d: {
569
+ layout: unknown;
570
+ entries: {
571
+ binding: number;
572
+ resource: {
573
+ buffer: GBuffer;
574
+ offset?: number;
575
+ size?: number;
576
+ };
577
+ }[];
578
+ }): unknown;
579
+ createCommandEncoder(): GEncoder;
580
+ pushErrorScope?(filter: string): void;
581
+ popErrorScope?(): Promise<{
582
+ message: string;
583
+ } | null>;
584
+ destroy(): void;
585
+ }
586
+ /** A `navigator.gpu`-like object. */
587
+ interface WetGpuProvider {
588
+ requestAdapter(options?: {
589
+ powerPreference?: 'high-performance' | 'low-power';
590
+ }): Promise<unknown>;
591
+ }
592
+ /** Whether Engine B can run here, and if not, why. */
593
+ interface WetGpuSupport {
594
+ supported: boolean;
595
+ /** Why not (no WebGPU, no adapter, too little buffer space, the device failed), or `null`. */
596
+ reason: string | null;
597
+ /** The GPU's vendor and architecture, when known. */
598
+ adapter: string | null;
599
+ /** Most wet tiles (256 px, all layers) the GPU can hold. */
600
+ maxSlots: number;
601
+ }
602
+ /** A tile to step or shade: its slot, lane, the slots of its 8 neighbours, and its size on the canvas. */
603
+ interface WetGpuJob {
604
+ slot: number;
605
+ lane: number;
606
+ /** Neighbour slots in the order n, s, w, e, nw, ne, sw, se; −1 for none. */
607
+ neighbours: readonly number[];
608
+ /** Pixels of the tile on the canvas (256 except at the right and bottom edges). */
609
+ width: number;
610
+ height: number;
611
+ }
612
+ /** The `navigator.gpu` of this context, if any. */
613
+ declare function findWebGpu(): WetGpuProvider | null;
614
+ /**
615
+ * Starts Engine B on `gpu` (default `navigator.gpu`). Resolves with the engine, or `null` and the
616
+ * reason it cannot run (no WebGPU, no adapter, too little storage-buffer space, a failed device).
617
+ */
618
+ declare function createWetGpuEngine(gpu?: WetGpuProvider | null): Promise<{
619
+ engine: WetGpuEngine | null;
620
+ support: WetGpuSupport;
621
+ }>;
622
+ /** Whether Engine B can run here (starts and stops a device). */
623
+ declare function wetGpuSupport(gpu?: WetGpuProvider | null): Promise<WetGpuSupport>;
624
+ /** The engine on a started device. Make one with `createWetGpuEngine`. */
625
+ declare class WetGpuEngine {
626
+ private readonly device;
627
+ /** Most tiles the GPU can hold. */
628
+ readonly maxSlots: number;
629
+ /** The GPU, when known. */
630
+ readonly adapter: string | null;
631
+ private readonly stepPipe;
632
+ private readonly haloPipe;
633
+ private readonly shadePipe;
634
+ /** Steps run on the GPU so far (for stats). */
635
+ steps: number;
636
+ /** Set once the device is lost; the engine does nothing after that. */
637
+ lostReason: string | null;
638
+ private state;
639
+ private slots;
640
+ private readonly params;
641
+ private jobs;
642
+ private jobCap;
643
+ private flags;
644
+ private flagsRead;
645
+ private readonly uni;
646
+ private readonly shadeUni;
647
+ private srcTiles;
648
+ private outTiles;
649
+ private outRead;
650
+ private tileCap;
651
+ private groups;
652
+ private constructor();
653
+ /** @internal Compiles the kernels; use `createWetGpuEngine`. */
654
+ static start(device: GDevice, maxSlots: number, adapter: string | null): Promise<WetGpuEngine>;
655
+ /** Bytes of GPU memory in use. */
656
+ memoryBytes(): number;
657
+ /** Slots the state buffer holds now. */
658
+ get capacity(): number;
659
+ /** Grows the state buffer to hold `n` slots (keeping its contents). Returns false past `maxSlots`. */
660
+ ensureSlots(n: number): boolean;
661
+ /** Writes floats into a slot (`offset` floats from the slot's start). */
662
+ writeSlot(slot: number, offset: number, data: Float32Array): void;
663
+ /** Sets a lane's packed parameters (`packWetParams`). */
664
+ setParams(lane: number, prm: Float32Array): void;
665
+ private writeJobs;
666
+ private bindGroups;
667
+ /**
668
+ * Runs `steps` steps of `jobs` from set `parity` (as the CPU's `runHere`: each step, then each
669
+ * tile's halo in the new set). With `readFlags`, resolves with each job's flag bits OR-ed over
670
+ * the steps (`FLAG_*`); otherwise once the work is submitted.
671
+ */
672
+ step(jobs: readonly WetGpuJob[], parity: number, steps: number, readFlags?: boolean): Promise<Uint32Array | null>;
673
+ /** Refills each job's halo in set `parity` from its neighbours (after uploads). */
674
+ refreshHalos(jobs: readonly WetGpuJob[], parity: number): void;
675
+ /**
676
+ * Shades `jobs` (set `parity`) over their base tiles (`src[i]`: RGBA8 256 × 256, or `null` for
677
+ * transparent) on paper `paper` (sRGB bytes). `mode` 'display' darkens wet paper a little;
678
+ * 'dried' shows the paint as it would dry. Resolves with one RGBA8 tile per job.
679
+ */
680
+ shade(jobs: readonly WetGpuJob[], parity: number, src: readonly (Uint8ClampedArray | null)[], paper: readonly [number, number, number], mode?: 'display' | 'dried'): Promise<Uint8ClampedArray[]>;
681
+ /** Reads floats back from a slot (tests and diagnostics). */
682
+ readSlot(slot: number, offset: number, count: number): Promise<Float32Array>;
683
+ /** Waits for submitted work. */
684
+ idle(): Promise<void>;
685
+ destroy(): void;
686
+ }
687
+
688
+ /**
689
+ * `WetWorld`: the wet-media simulation of one canvas. It holds up to `MAX_WET_LANES` wet layers
690
+ * ("lanes"), each a sparse set of 256 px tile slots in one `WebAssembly.Memory`, and steps all of
691
+ * them together.
692
+ *
693
+ * - **Kernels.** It starts on the TypeScript kernel at once and switches to the WebAssembly SIMD
694
+ * kernel (in Web Workers when the page is cross-origin isolated, else on this thread) as soon as
695
+ * that has loaded. The kernels give identical bits, so the switch never changes the painting.
696
+ * - **Determinism.** Time is counted in steps (`clock`), never in wall time. Batches never cross a
697
+ * multiple of `FREE_INTERVAL` and are at most `MAX_BATCH` steps, tiles wake up when anything
698
+ * alive comes within `STRIP` cells of their edge (so earlier or later wake-ups change nothing),
699
+ * and dry tiles are baked and freed only on multiples of `FREE_INTERVAL`, judged by their state
700
+ * at that step. So the result depends only on the edits and the steps at which they were made,
701
+ * not on frame rate, batch sizes, worker counts or the browser.
702
+ * - **Edits** (dabs, water, drying, blowing, blending, smudging, blotting, re-wetting) run on this
703
+ * thread between batches; while a batch runs (`busy`) they throw, so callers queue them.
704
+ * - **Display.** Each lane has a `base` surface: the layer's pixels under the wet paint. `display`
705
+ * gives the base with every live tile shaded (`filmPixel`), as an ordinary RGBA surface for the
706
+ * compositor. Tiles that dry are baked into the base and their slots freed.
707
+ * - **Engine B (WebGPU).** With a GPU engine attached (`attachGpu`) and at least `gpuMinTiles` live
708
+ * tiles, the GPU keeps a mirror of the slots: each edit uploads the rows it changed, each batch
709
+ * runs on the GPU too, and `displayGpu` shades every stale tile there at once. In lead mode
710
+ * (between strokes) the GPU steps a batch frame by frame while the CPU computes it (`gpuLead`).
711
+ * The CPU state stays the authority (edits, reads, drying, snapshots, baking); the GPU's copy
712
+ * only feeds the display.
713
+ */
714
+
715
+ /** Wet layers a world can hold at once. */
716
+ declare const MAX_WET_LANES = 8;
717
+ /** Tile slots at most (about 3.8 GB, inside wasm32's 4 GB). Water reaching a tile past this is lost. */
718
+ declare const MAX_WET_SLOTS = 352;
719
+ /** A flow map: a vector per document pixel, in cells per step² at strength 1. */
720
+ type WetFlowField = (x: number, y: number) => readonly [number, number];
721
+ /** The paper as the simulation reads it. */
722
+ interface WetPaperInput extends WetPaper {
723
+ /** Grain height 0..1 at a document position (the paper's wet height map). */
724
+ heightAt(x: number, y: number): number;
725
+ /** Paper colour, sRGB bytes. */
726
+ color: readonly [number, number, number];
727
+ }
728
+ type WetKernelKind = 'typescript' | 'wasm' | 'wasm-threads';
729
+ interface WetBackendInfo {
730
+ kind: WetKernelKind;
731
+ /** Threads stepping the simulation. */
732
+ threads: number;
733
+ /** Why it runs slower than it could, or `null`. */
734
+ message: string | null;
735
+ }
736
+ interface WetWorldOptions {
737
+ /** Which kernel: `auto` (default: the fastest available), or force `typescript` / `wasm` (one thread). */
738
+ kernel?: 'auto' | 'typescript' | 'wasm';
739
+ /** Worker threads for the wasm kernel: `auto` (default: cores − 1, at most 10) or a number (0: none). */
740
+ threads?: number | 'auto';
741
+ }
742
+ interface WetLaneOptions {
743
+ params?: Partial<WetParams>;
744
+ /** The layer's pixels under the wet paint (default empty). */
745
+ base?: TiledSurface;
746
+ flow?: WetFlowField | null;
747
+ /** Flow map strength (default 1 when a flow map is given). */
748
+ flowStrength?: number;
749
+ }
750
+ /** Totals for one wet layer. */
751
+ interface WetLaneStats {
752
+ /** Tiles being simulated. */
753
+ activeTiles: number;
754
+ /** Cells with surface water (at least `DRY_EPS` deep). */
755
+ wetCells: number;
756
+ /** Water on the surface plus in the paper, in cell-depth units. */
757
+ water: number;
758
+ /** Suspended plus settled pigment mass (sum of the four channels). */
759
+ pigment: number;
760
+ /** Document rectangle of the wet cells, or `null` when nothing is wet. */
761
+ wetBounds: RasterRect | null;
762
+ }
763
+ /** A copy of the whole simulation (for undo). */
764
+ interface WetSnapshot {
765
+ readonly clock: number;
766
+ readonly tilt: WetTilt;
767
+ readonly lanes: ReadonlyArray<{
768
+ id: string;
769
+ index: number;
770
+ params: WetParams;
771
+ flow: WetFlowField | null;
772
+ flowStrength: number;
773
+ base: TiledSurface;
774
+ }>;
775
+ /** Per active key: the current set and the deposit planes; and its last flags. */
776
+ readonly slots: ReadonlyMap<number, {
777
+ planes: Float32Array;
778
+ last: number;
779
+ }>;
780
+ /** Bytes held. */
781
+ readonly bytes: number;
782
+ }
783
+ /** Thrown for edits while a batch is running, and for bad arguments. */
784
+ declare class WetWorldError extends Error {
785
+ constructor(message: string);
786
+ }
787
+ interface Layout extends WetWorkerLayout {
788
+ width: number;
789
+ height: number;
790
+ }
791
+ declare class WetWorld {
792
+ readonly width: number;
793
+ readonly height: number;
794
+ readonly layout: Layout;
795
+ /** Steps simulated so far. */
796
+ clock: number;
797
+ /** Messages for the user (slots ran out, a backend failed). */
798
+ readonly warnings: string[];
799
+ private memory;
800
+ private readonly shared;
801
+ private f32;
802
+ private i32;
803
+ private u32;
804
+ private slotCount;
805
+ private readonly freeSlots;
806
+ private readonly active;
807
+ private parity;
808
+ private readonly lanes;
809
+ private tilt;
810
+ private paper;
811
+ private paperBytes;
812
+ private kind;
813
+ private wasm;
814
+ private pool;
815
+ private busyFlag;
816
+ private destroyed;
817
+ private support;
818
+ private readonly prm;
819
+ private gpu;
820
+ /** Live tiles at which the GPU takes over the display (default 16: a fully wet 1024² canvas). */
821
+ gpuMinTiles: number;
822
+ private gpuLive;
823
+ private gpuFailure;
824
+ /** Slot → rows (1..256, inclusive) to upload; `[0, 257]` means the whole slot. */
825
+ private readonly gpuDirty;
826
+ private gpuParamsDirty;
827
+ /**
828
+ * Lead mode (between strokes): a batch is not sent to the GPU all at once; `gpuLead` steps it
829
+ * frame by frame while the CPU works through it, so the display moves on before the CPU is done.
830
+ */
831
+ gpuLeadMode: boolean;
832
+ /** The batch the GPU is working through in lead mode. */
833
+ private gpuPlan;
834
+ /** Resolves when the fastest available kernel is running (it may stay `typescript`). */
835
+ readonly ready: Promise<WetBackendInfo>;
836
+ constructor(width: number, height: number, paper: WetPaperInput, options?: WetWorldOptions);
837
+ private views;
838
+ private start;
839
+ /** The kernel in use and why it is not faster. */
840
+ backend(): WetBackendInfo;
841
+ /** Whether a batch is running (edits throw until it ends). */
842
+ get busy(): boolean;
843
+ destroy(): void;
844
+ private laneOf;
845
+ hasLane(id: string): boolean;
846
+ laneIds(): string[];
847
+ /** Adds a wet layer (or returns it). Throws when `MAX_WET_LANES` are in use. */
848
+ ensureLane(id: string, options?: WetLaneOptions): void;
849
+ /** Drops a wet layer and its wet paint (nothing is baked). */
850
+ removeLane(id: string): void;
851
+ getParams(id: string): WetParams;
852
+ setParams(id: string, params: Partial<WetParams>): WetParams;
853
+ getTilt(): WetTilt;
854
+ setTilt(tilt: Partial<WetTilt>): WetTilt;
855
+ /** Changes the paper: heights of live tiles are refilled, rates repacked. */
856
+ setPaper(paper: WetPaperInput): void;
857
+ /** Sets a layer's flow map (`null` removes it). */
858
+ setFlow(id: string, flow: WetFlowField | null, strength?: number): void;
859
+ hasFlow(id: string): boolean;
860
+ /** The pixels under a layer's wet paint. */
861
+ getBase(id: string): TiledSurface;
862
+ /** Replaces the pixels under a layer's wet paint. */
863
+ setBase(id: string, base: TiledSurface): void;
864
+ private pack;
865
+ private touchAll;
866
+ private laneIndexOf;
867
+ private slotOf;
868
+ private slotBase;
869
+ private requireIdle;
870
+ /** Writes the active list into memory for the kernels. */
871
+ private syncList;
872
+ private grow;
873
+ /** Activates a tile of a lane. Returns false when no slot can be had. */
874
+ private activate;
875
+ private free;
876
+ private tileXY;
877
+ private tileKeyOf;
878
+ private fillPaper;
879
+ private fillFlow;
880
+ /** Activates every tile of a lane within `STRIP` cells of a document rectangle. */
881
+ private wake;
882
+ /** Fills one slot's halo in the current set from its neighbours (TypeScript). */
883
+ private haloTs;
884
+ private halo;
885
+ /** Refills every live slot's halo in the current set (after edits or wake-ups). */
886
+ private refreshHalos;
887
+ /** Whether anything is being simulated (in `id`, or any layer). */
888
+ isActive(id?: string): boolean;
889
+ /** Steps until the next batch boundary: at most `want`, `MAX_BATCH`, and the next free check. */
890
+ private batchSize;
891
+ /** Runs `n` steps on this thread (TypeScript or wasm kernel). */
892
+ private runHere;
893
+ /** After a batch: wake neighbours from the flags, free dead tiles on a free step, restamp. */
894
+ private afterBatch;
895
+ /**
896
+ * Steps the simulation `steps` times on this thread (the TypeScript or single-thread wasm
897
+ * kernel). Throws while worker threads are in use; use `step` there.
898
+ */
899
+ stepSync(steps: number): number;
900
+ /** Steps the simulation `steps` times (in worker threads when there are any). */
901
+ step(steps: number): Promise<number>;
902
+ /** Steps until nothing is wet or `maxSteps` have run. Returns the steps run. */
903
+ stepUntilDry(maxSteps: number): Promise<number>;
904
+ /**
905
+ * Steps only the live tiles that touch `region` (canvas pixels): a viewport, say, so a huge wet
906
+ * canvas costs what is on screen. The other live tiles hold still (no flow, no drying) until a
907
+ * later step reaches them, and water flowing out of the region stops at their edge; tiles the
908
+ * region's water wakes up are stepped as usual. Holding tiles makes the result depend on the
909
+ * regions, so a replay is only bit-identical with the same regions. Returns the clock.
910
+ */
911
+ stepRegion(steps: number, region: RasterRect): Promise<number>;
912
+ /** `stepRegion` on this thread (throws while worker threads are in use, like `stepSync`). */
913
+ stepRegionSync(steps: number, region: RasterRect): number;
914
+ /**
915
+ * Takes the live tiles that miss `region` out of the active list until the returned release
916
+ * is called. Their state is copied into the other ping-pong set first, so they read the same
917
+ * whichever set is current when they come back.
918
+ */
919
+ private hold;
920
+ /** Float index of a cell's plane-0 value in the current set, or −1 (inactive / off the canvas). */
921
+ private cell;
922
+ /** Index of the same cell in the single (deposit, height, flow) planes, given `cell`. */
923
+ private single;
924
+ /**
925
+ * Runs `fn` for every cell of a footprint (`cov` over `rect`, document cells) on a lane, waking
926
+ * the tiles around it first, then refreshes halos. `fn` gets the cell index and its coverage.
927
+ */
928
+ private footprint;
929
+ private markEdited;
930
+ /** Adds water (depth units) at rest. */
931
+ private addWaterAt;
932
+ /**
933
+ * Lays paint: `water` depth and pigment mass `pigment` (K r, g, b, S) per unit of coverage, or
934
+ * per pixel from `field` (4 values per pixel of `rect`). Pigment without water lands straight in
935
+ * the paper (settled).
936
+ */
937
+ paint(id: string, rect: RasterRect, cov: ArrayLike<number> | null, water: number, pigment: ArrayLike<number> | null, field?: ArrayLike<number> | null): void;
938
+ /** Damp paper: fills the paper's water towards its capacity by `amount` (0..1) × coverage, plus a thin film of surface water. */
939
+ wetPaper(id: string, rect: RasterRect, cov: ArrayLike<number> | null, amount: number): void;
940
+ /**
941
+ * Takes water (and suspended pigment, and settled pigment) away: `water`, `suspended`,
942
+ * `settled` are fractions 0..1 × coverage. A hair dryer takes water only (the pigment stays and
943
+ * settles); blotting takes all three.
944
+ */
945
+ remove(id: string, rect: RasterRect, cov: ArrayLike<number> | null, water: number, suspended: number, settled: number, paperWater?: number): void;
946
+ /** Pushes the water: adds velocity (`ux`, `uy` cells per step, each at most 0.1) × coverage. */
947
+ push(id: string, rect: RasterRect, cov: ArrayLike<number> | null, ux: number, uy: number): void;
948
+ /** Blends wet paint: each wet cell's suspended pigment moves `strength` × coverage towards the footprint's mean. */
949
+ blend(id: string, rect: RasterRect, cov: ArrayLike<number> | null, strength: number): void;
950
+ /**
951
+ * Drags wet paint by (`dx`, `dy`) cells: each footprint cell takes `strength` × coverage of the
952
+ * water and suspended pigment of the cell it came from (a smudge through wet paint).
953
+ */
954
+ drag(id: string, rect: RasterRect, cov: ArrayLike<number> | null, dx: number, dy: number, strength: number): void;
955
+ /**
956
+ * Re-wetting: lifts `amount` × coverage of the dried paint in the layer's pixels (`base`) back into
957
+ * the paper as settled pigment, so water can move it again. Only where there is surface water.
958
+ */
959
+ rewet(id: string, rect: RasterRect, cov: ArrayLike<number> | null, amount: number, paper: readonly [number, number, number]): void;
960
+ /**
961
+ * Dries wet paint at once (everything settles where it is, the water goes): in `rect`, or the
962
+ * whole layer. Whole tiles that end up dry are baked into the base and freed. Returns the
963
+ * document rectangle that changed, or `null`.
964
+ */
965
+ dry(id: string, rect?: RasterRect): RasterRect | null;
966
+ /**
967
+ * Bakes a slot's pigment into its lane's base (settling suspended pigment too when `all`).
968
+ * Returns whether any pixel changed.
969
+ */
970
+ private bakeKey;
971
+ /**
972
+ * Shades one slot over a base tile. `display`: the wet look (suspended + settled, wet paper a
973
+ * little darker); `bake`: settled pigment only; `bake-all`: suspended and settled (dry now).
974
+ * Returns `null` when nothing would change the base.
975
+ */
976
+ private shadeSlot;
977
+ /** The layer's pixels with all its wet paint dried where it is (the bake of "dry on save"); changes nothing. */
978
+ bakedSurface(id: string): TiledSurface;
979
+ /**
980
+ * Sums over a footprint of a lane, weighted by coverage: `weight` (coverage), `water` (surface
981
+ * depth), `wet` (coverage of wet cells), `pigment` (suspended, 4 channels), `settled` (4 channels).
982
+ */
983
+ sample(id: string, rect: RasterRect, cov: ArrayLike<number> | null): {
984
+ weight: number;
985
+ water: number;
986
+ wet: number;
987
+ pigment: number[];
988
+ settled: number[];
989
+ };
990
+ /** Surface water depth at a document cell (0 where nothing is simulated). */
991
+ waterAt(id: string, x: number, y: number): number;
992
+ /** Totals for a layer (scans its live tiles). */
993
+ stats(id: string): WetLaneStats;
994
+ /** Whether a layer (or any) has surface water anywhere. */
995
+ isWet(id?: string): boolean;
996
+ /** Document rectangles of a layer's live tiles. */
997
+ activeRects(id: string): RasterRect[];
998
+ /** A number that changes whenever a layer's display may have changed. */
999
+ stamp(id: string): number;
1000
+ /**
1001
+ * The layer's pixels with its wet paint shaded in: the base with each live tile shaded over it.
1002
+ * At most `maxTiles` stale tiles are reshaded per call (edited tiles first); the rest keep their
1003
+ * last shading until the next call. Returns the surface and the tiles reshaded.
1004
+ */
1005
+ display(id: string, maxTiles?: number): {
1006
+ surface: TiledSurface;
1007
+ changed: RasterRect[];
1008
+ pending: number;
1009
+ };
1010
+ /**
1011
+ * The water as an overlay tile (RGBA, blue, alpha from surface depth and damp paper) for a
1012
+ * document tile, or `null` where nothing is wet.
1013
+ */
1014
+ waterOverlay(id: string, tx: number, ty: number): Uint8ClampedArray | null;
1015
+ /**
1016
+ * Attaches a GPU engine (or `null` to detach). From then on, whenever at least `gpuMinTiles` tiles
1017
+ * are live, the GPU mirrors the slots and `displayGpu` shades on it. The CPU stays the authority.
1018
+ */
1019
+ attachGpu(engine: WetGpuEngine | null): void;
1020
+ /** The attached GPU engine, if any. */
1021
+ get gpuEngine(): WetGpuEngine | null;
1022
+ /** Whether the GPU mirror is in use now (an engine is attached and the wet area is large). */
1023
+ get gpuActive(): boolean;
1024
+ /** Why the GPU stopped (a lost device, out of GPU memory), or `null`. */
1025
+ get gpuError(): string | null;
1026
+ private gpuDirtySlot;
1027
+ private gpuStop;
1028
+ /** Turns the mirror on or off for the current wet area. Returns whether it is on. */
1029
+ private gpuDecide;
1030
+ /** Sends the rest of a lead batch to the GPU (before anything else touches its state). */
1031
+ private gpuFinishPlan;
1032
+ /**
1033
+ * Lead mode: steps the GPU up to `steps` further into the batch the CPU is running (never past
1034
+ * it, so the GPU is never ahead of the CPU's next edit). Returns the steps taken.
1035
+ */
1036
+ gpuLead(steps: number): number;
1037
+ /** Steps the GPU is ahead of the CPU's last finished batch (lead mode), 0 otherwise. */
1038
+ get gpuAhead(): number;
1039
+ /** Whether the GPU can show steps the CPU has not finished (a lead batch is running). */
1040
+ get gpuLeading(): boolean;
1041
+ /** Uploads what the CPU changed since the last upload (the current set and deposits; whole slots on wake-up). */
1042
+ private gpuSync;
1043
+ private gpuJobs;
1044
+ /** Runs the coming CPU batch of `n` steps on the GPU mirror too. */
1045
+ private gpuBatch;
1046
+ /**
1047
+ * Like `display`, but shades every stale tile on the GPU at once (when the mirror is in use;
1048
+ * otherwise, or when the world changed while the GPU worked, it is `display(id, maxCpuTiles)`).
1049
+ * Call between batches.
1050
+ */
1051
+ displayGpu(id: string, maxCpuTiles?: number, lead?: boolean): Promise<{
1052
+ surface: TiledSurface;
1053
+ changed: RasterRect[];
1054
+ pending: number;
1055
+ gpu: boolean;
1056
+ }>;
1057
+ /** A copy of everything (for undo): live slots, layers, clock and tilt. */
1058
+ snapshot(): WetSnapshot;
1059
+ /** Puts a snapshot back exactly (the simulation then continues as it would have). */
1060
+ restore(snap: WetSnapshot): void;
1061
+ /** Bytes of wasm memory held. */
1062
+ memoryBytes(): number;
1063
+ /** Live tiles across all layers. */
1064
+ activeTiles(): number;
1065
+ /** Memory for tests and tools: the raw f32 view (read only, please). */
1066
+ get rawFloats(): Float32Array;
1067
+ /** The current set's parity (tests). */
1068
+ get currentParity(): number;
1069
+ /** Float index of a slot's first plane (tests). */
1070
+ slotIndex(id: string, tx: number, ty: number): number;
1071
+ }
1072
+
1073
+ /**
1074
+ * Flow maps: a push on the water at every cell (cells per step², like tilt), from a few generated
1075
+ * patterns. Every pattern is computed with + − × ÷ only, so it is the same in every engine.
1076
+ */
1077
+
1078
+ type WetFlowKind = 'none' | 'linear' | 'swirl' | 'radial' | 'turbulent';
1079
+ declare const WET_FLOW_KINDS: readonly WetFlowKind[];
1080
+ /** A flow map as settings (saved with the document). */
1081
+ interface WetFlowSpec {
1082
+ kind: WetFlowKind;
1083
+ /** 0..1: how hard it pushes (1 is about as strong as a 25° tilt). */
1084
+ strength: number;
1085
+ /** `linear`: direction in degrees clockwise from canvas down. */
1086
+ angle: number;
1087
+ /** `swirl` / `radial`: centre in document pixels (default the canvas centre). */
1088
+ center?: {
1089
+ x: number;
1090
+ y: number;
1091
+ };
1092
+ /** `swirl`: true turns clockwise. */
1093
+ clockwise: boolean;
1094
+ /** `turbulent`: eddy size in pixels (8..1024). */
1095
+ scale: number;
1096
+ seed: number;
1097
+ }
1098
+ declare const DEFAULT_FLOW: Readonly<WetFlowSpec>;
1099
+ /** sin and cos of an angle in radians, by range reduction and Taylor series (deterministic). */
1100
+ declare function dsincos(a: number): [number, number];
1101
+ /** Settings with every field in range. */
1102
+ declare function normalizeFlow(raw?: Partial<WetFlowSpec>, base?: WetFlowSpec): WetFlowSpec;
1103
+ /** The flow field of a spec on a canvas, or `null` for `none` (strength is applied by the world). */
1104
+ declare function flowField(spec: WetFlowSpec, width: number, height: number): WetFlowField | null;
1105
+
1106
+ /**
1107
+ * Counter-based seeded randomness on 32-bit integer hashes: every value is a pure function of its
1108
+ * inputs, so strokes and papers regenerate identically on every machine. Shared by the paint
1109
+ * engine (`/paint`) and the paint editor (which re-exports it from `noise.ts`).
1110
+ */
1111
+ /** A 32-bit finaliser (murmur3's `fmix32`). */
1112
+ declare function mix32(h: number): number;
1113
+ /** Hash of integer coordinates and a salt to [0, 1). */
1114
+ declare function hash2(x: number, y: number, salt: number): number;
1115
+ /** Uniform in [0, 1) from a seed, an index and a stream (one stream per random quantity). */
1116
+ declare function rand01(seed: number, index: number, stream: number): number;
1117
+
1118
+ export { P_FY as $, FLAG_LEFT as A, FLAG_RIGHT as B, type CloneTransform as C, DEFAULT_FLOW as D, EX as E, FLAG_ALIVE as F, FLAG_TOP as G, FLAG_WET as H, FLAT as I, FREE_INTERVAL as J, IDENTITY_CLONE_TRANSFORM as K, MAX_TILT_ACCEL as L, MAX_BATCH as M, MAX_WET_LANES as N, MAX_WET_SLOTS as O, OPP as P, PAPER_CAPACITY as Q, type RetouchSettings as R, PARAM as S, PARAM_FLOATS as T, PIGMENT_CHANNELS as U, PLANE as V, WetWorld as W, PLANE_BYTES as X, P_D0 as Y, P_EDGE as Z, P_FX as _, type RetouchApply as a, sponge as a$, P_H as a0, P_P0 as a1, P_RHO as a2, P_SW as a3, P_UX as a4, P_UY as a5, type PixelSource as a6, RETOUCH_SAMPLES as a7, RegionPixelSource as a8, type RetouchDabEffect as a9, WetWorldError as aA, applyRetouchToTile as aB, applyToneToDeepTile as aC, boxBlurPixels as aD, canShareMemory as aE, cloneSamplePoint as aF, createWetGpuEngine as aG, dodgeBurn as aH, dodgeBurnCurve as aI, dsincos as aJ, edgeBits as aK, findWebGpu as aL, flowField as aM, healPixels as aN, isIdentityCloneTransform as aO, latticeViscosity as aP, loadWetKernel as aQ, luma as aR, normalizeRetouch as aS, normalizeTilt as aT, packWetParams as aU, perDabStrength as aV, readSourceRegion as aW, retouchFilter as aX, rgbaViewOfGrey as aY, saturationOf as aZ, smudgeEffect as a_, RetouchDabStroke as aa, type RetouchSample as ab, SET_PLANES as ac, SLOT_BYTES as ad, SLOT_PLANES as ae, SLOT_SIDE as af, SPOT_HEAL_TYPES as ag, STRIP as ah, type SpotHealType as ai, TONE_RANGES as aj, TilePixelSource as ak, type ToneRange as al, TransformedPixelSource as am, U_MAX as an, W9 as ao, WET_TILE as ap, type WetFlowField as aq, WetGpuEngine as ar, type WetGpuJob as as, type WetGpuSupport as at, type WetKernelKind as au, type WetLaneOptions as av, type WetLaneStats as aw, type WetPaper as ax, type WetWorkerLayout as ay, WetWorkerPool as az, type RetouchPaint as b, spongeEffect as b0, spotHealPixels as b1, spotHealWindow as b2, toneOf as b3, wetGpuSupport as b4, wetSimdSupported as b5, type WetTilt as c, type WetParams as d, type WetFlowSpec as e, type WetWorldOptions as f, type WetGpuProvider as g, type WetBackendInfo as h, type WetSnapshot as i, type WetPaperInput as j, DEFAULT_WET_PARAMS as k, WET_FLOW_KINDS as l, WET_PARAM_KEYS as m, type WetFlowKind as n, type WetSupport as o, hash2 as p, mix32 as q, normalizeFlow as r, normalizeWetParams as s, rand01 as t, DEFAULT_RETOUCH as u, DEFAULT_WET_PAPER as v, wetSupport as w, DRY_EPS as x, EY as y, FLAG_BOTTOM as z };