@pixerate/editor 0.120.0 → 0.122.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/{chunk-HTAFDTNR.cjs → chunk-4PUPGALM.cjs} +251 -156
- package/dist/chunk-4PUPGALM.cjs.map +1 -0
- package/dist/{chunk-N3467VD6.js → chunk-7QN76ESJ.js} +1 -179
- package/dist/chunk-7QN76ESJ.js.map +1 -0
- package/dist/{chunk-UMVW2Q66.js → chunk-CLTTLLWA.js} +1090 -982
- package/dist/chunk-CLTTLLWA.js.map +1 -0
- package/dist/{chunk-EJVDBITD.cjs → chunk-DCLJNVZS.cjs} +2 -180
- package/dist/chunk-DCLJNVZS.cjs.map +1 -0
- package/dist/{chunk-WMRIG4XQ.cjs → chunk-DFFBJV3U.cjs} +1855 -1747
- package/dist/chunk-DFFBJV3U.cjs.map +1 -0
- package/dist/chunk-GX332W2C.js +181 -0
- package/dist/chunk-GX332W2C.js.map +1 -0
- package/dist/{chunk-BOWUU4LJ.js → chunk-IX3YKUVK.js} +105 -10
- package/dist/chunk-IX3YKUVK.js.map +1 -0
- package/dist/{chunk-TS5IEUW6.cjs → chunk-K7V27T72.cjs} +178 -3
- package/dist/chunk-K7V27T72.cjs.map +1 -0
- package/dist/chunk-MUUXNRU4.cjs +181 -0
- package/dist/chunk-MUUXNRU4.cjs.map +1 -0
- package/dist/{chunk-YHW26LS2.js → chunk-ZZPGTNXJ.js} +177 -2
- package/dist/chunk-ZZPGTNXJ.js.map +1 -0
- package/dist/{render-B05K68_h.d.ts → deep-DLwKZDFr.d.ts} +143 -134
- package/dist/{render-DnAbIKcs.d.cts → deep-QS3ppbMX.d.cts} +143 -134
- package/dist/{export-D2D8hb_u.d.ts → export-VmTofKIk.d.ts} +11 -4
- package/dist/{export-fP2hZYjm.d.cts → export-jKKj8Fg8.d.cts} +11 -4
- package/dist/image-editor/index.cjs +760 -734
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +18 -6
- package/dist/image-editor/index.d.ts +18 -6
- package/dist/image-editor/index.js +37 -11
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint/index.cjs +22 -2
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +159 -1
- package/dist/paint/index.d.ts +159 -1
- package/dist/paint/index.js +21 -1
- package/dist/paint-editor/index.cjs +95 -94
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +2 -2
- package/dist/paint-editor/index.d.ts +2 -2
- package/dist/paint-editor/index.js +12 -11
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +5 -4
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +29 -7
- package/dist/raster/index.d.ts +29 -7
- package/dist/raster/index.js +11 -10
- package/dist/vector/index.cjs +14 -12
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.js +7 -5
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/pixerate-canvas/SKILL.md +23 -1
- package/skills/pixerate-image-editor/SKILL.md +7 -1
- package/skills/pixerate-image-editor/references/photoshop-files.md +4 -3
- package/skills/pixerate-paint-editor/SKILL.md +32 -7
- package/dist/chunk-BOWUU4LJ.js.map +0 -1
- package/dist/chunk-EJVDBITD.cjs.map +0 -1
- package/dist/chunk-HTAFDTNR.cjs.map +0 -1
- package/dist/chunk-N3467VD6.js.map +0 -1
- package/dist/chunk-TS5IEUW6.cjs.map +0 -1
- package/dist/chunk-UMVW2Q66.js.map +0 -1
- package/dist/chunk-WMRIG4XQ.cjs.map +0 -1
- package/dist/chunk-YHW26LS2.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/paint/heal.ts","../src/paint/retouch.ts","../src/paint/path-stroke.ts","../src/paint/brushstroke.ts"],"sourcesContent":["// Portions ported from photocraft/crates/algo/src/poisson.rs and inpaint.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/**\n * Gradient-domain (\"Poisson\") blending for the Healing Brush and the Spot Healing Brush.\n *\n * Method: P. Pérez, M. Gangnet, A. Blake, *Poisson Image Editing*, SIGGRAPH 2003. Seamless cloning\n * looks for `f` over a region Ω with `f = f*` (the destination) on its boundary and `∇f` as close\n * as possible to the source's `∇g`. The answer is `f = g + h`, where `h` is the membrane: the\n * harmonic function (`Δh = 0`) that equals `f* − g` on the boundary. So healed pixels keep the\n * source's texture while their colour and lighting are pulled to the pixels around the stroke.\n *\n * The Laplace equation is solved on the 4-connected grid by successive over-relaxation, started\n * from a recursively solved half-resolution grid (cascadic multigrid). Harmonic functions are\n * smooth, so the coarse guess is close and a few sweeps per level remove the rest.\n *\n * Buffers are interleaved `w × h × ch` floats in any units (the callers use 0..255). Pure maths:\n * no DOM, no framework.\n */\n\n/** Largest per-sweep update below which a solve has converged. */\nconst TOL = 2e-3;\n/** Sweeps (cap) and relaxation on every level above the coarsest. */\nconst FINE_ITERS = 30;\nconst FINE_OMEGA = 1.7;\n\n/**\n * Solves `Δv = 0` on the `unknown` pixels, with the known pixels of `v` as Dirichlet values\n * (neighbours off the grid are ignored: a free boundary). `v` holds the known values on entry and\n * the solution on exit.\n */\nexport function solveMembrane(w: number, h: number, unknown: ArrayLike<boolean | number>, v: Float32Array): void {\n if (unknown.length !== w * h || v.length !== w * h) throw new RangeError(`solveMembrane: expected ${w * h} cells, got ${unknown.length} and ${v.length}.`);\n const u = new Uint8Array(w * h);\n for (let i = 0; i < u.length; i++) u[i] = unknown[i] ? 1 : 0;\n solveLevel(w, h, u, v, 0);\n}\n\nfunction solveLevel(w: number, h: number, unknown: Uint8Array, v: Float32Array, depth: number): void {\n let nUnknown = 0;\n let knownSum = 0;\n let nKnown = 0;\n for (let i = 0; i < unknown.length; i++) {\n if (unknown[i]) nUnknown++;\n else {\n knownSum += v[i];\n nKnown++;\n }\n }\n if (nUnknown === 0 || nKnown === 0) return;\n if (w >= 8 && h >= 8 && nUnknown > 64 && depth < 16) {\n // Restrict: a coarse cell is known when any of its children is (the mean of those).\n const cw = Math.ceil(w / 2);\n const chh = Math.ceil(h / 2);\n const cu = new Uint8Array(cw * chh).fill(1);\n const cv = new Float32Array(cw * chh);\n for (let cy = 0; cy < chh; cy++) {\n for (let cx = 0; cx < cw; cx++) {\n let s = 0;\n let n = 0;\n for (let k = 0; k < 4; k++) {\n const x = cx * 2 + (k & 1);\n const y = cy * 2 + (k >> 1);\n if (x < w && y < h && !unknown[y * w + x]) {\n s += v[y * w + x];\n n++;\n }\n }\n if (n > 0) {\n cu[cy * cw + cx] = 0;\n cv[cy * cw + cx] = s / n;\n }\n }\n }\n solveLevel(cw, chh, cu, cv, depth + 1);\n // Prolong: a bilinear sample of the coarse solution is the starting guess.\n for (let y = 0; y < h; y++) {\n const fy = Math.min(chh - 1, Math.max(0, (y + 0.5) / 2 - 0.5));\n const y0 = Math.floor(fy);\n const y1 = Math.min(chh - 1, y0 + 1);\n const ty = fy - y0;\n for (let x = 0; x < w; x++) {\n if (!unknown[y * w + x]) continue;\n const fx = Math.min(cw - 1, Math.max(0, (x + 0.5) / 2 - 0.5));\n const x0 = Math.floor(fx);\n const x1 = Math.min(cw - 1, x0 + 1);\n const tx = fx - x0;\n const a = cv[y0 * cw + x0] + (cv[y0 * cw + x1] - cv[y0 * cw + x0]) * tx;\n const b = cv[y1 * cw + x0] + (cv[y1 * cw + x1] - cv[y1 * cw + x0]) * tx;\n v[y * w + x] = a + (b - a) * ty;\n }\n }\n sor(w, h, unknown, v, FINE_ITERS, FINE_OMEGA);\n } else {\n const mean = knownSum / nKnown;\n for (let i = 0; i < v.length; i++) if (unknown[i]) v[i] = mean;\n sor(w, h, unknown, v, 2000, 1.85);\n }\n}\n\n/** Over-relaxed Gauss-Seidel sweeps over the unknown cells until the largest update is below `TOL`. */\nfunction sor(w: number, h: number, unknown: Uint8Array, v: Float32Array, maxIter: number, omega: number): void {\n let n = 0;\n for (let i = 0; i < unknown.length; i++) if (unknown[i]) n++;\n const cells = new Uint32Array(n);\n const masks = new Uint8Array(n);\n for (let i = 0, k = 0; i < unknown.length; i++) {\n if (!unknown[i]) continue;\n const x = i % w;\n const y = (i - x) / w;\n cells[k] = i;\n masks[k++] = (x > 0 ? 1 : 0) | (x + 1 < w ? 2 : 0) | (y > 0 ? 4 : 0) | (y + 1 < h ? 8 : 0);\n }\n for (let it = 0; it < maxIter; it++) {\n let maxD = 0;\n for (let k = 0; k < n; k++) {\n const i = cells[k];\n const m = masks[k];\n let s: number;\n let c: number;\n if (m === 15) {\n s = v[i - 1] + v[i + 1] + v[i - w] + v[i + w];\n c = 4;\n } else {\n s = 0;\n c = 0;\n if (m & 1) (s += v[i - 1]), c++;\n if (m & 2) (s += v[i + 1]), c++;\n if (m & 4) (s += v[i - w]), c++;\n if (m & 8) (s += v[i + w]), c++;\n if (c === 0) continue;\n }\n const d = omega * (s / c - v[i]);\n v[i] += d;\n const ad = d < 0 ? -d : d;\n if (ad > maxD) maxD = ad;\n }\n if (maxD < TOL) break;\n }\n}\n\n/**\n * Seamless cloning: `dst` outside `mask`; inside, `src` plus the membrane that removes the\n * mismatch at the mask's edge. So the result keeps the source's texture and takes the\n * destination's colour and light. `src` and `dst` are interleaved `w × h × ch`; leave a one-pixel\n * unmasked margin for a fully fixed boundary.\n */\nexport function seamlessClone(w: number, h: number, ch: number, src: ArrayLike<number>, dst: ArrayLike<number>, mask: ArrayLike<boolean | number>): Float32Array {\n const n = w * h;\n if (src.length !== n * ch || dst.length !== n * ch || mask.length !== n) throw new RangeError('seamlessClone: src, dst and mask must cover w × h pixels.');\n const out = new Float32Array(n * ch);\n const hv = new Float32Array(n);\n for (let c = 0; c < ch; c++) {\n for (let i = 0; i < n; i++) hv[i] = mask[i] ? 0 : dst[i * ch + c] - src[i * ch + c];\n solveMembrane(w, h, mask, hv);\n for (let i = 0; i < n; i++) out[i * ch + c] = mask[i] ? src[i * ch + c] + hv[i] : dst[i * ch + c];\n }\n return out;\n}\n\n/** Membrane fill (no source texture): the known pixels interpolated smoothly into `hole`. */\nexport function membraneFill(w: number, h: number, ch: number, img: ArrayLike<number>, hole: ArrayLike<boolean | number>): Float32Array {\n const n = w * h;\n const out = new Float32Array(n * ch);\n const v = new Float32Array(n);\n for (let c = 0; c < ch; c++) {\n for (let i = 0; i < n; i++) v[i] = hole[i] ? 0 : img[i * ch + c];\n solveMembrane(w, h, hole, v);\n for (let i = 0; i < n; i++) out[i * ch + c] = v[i];\n }\n return out;\n}\n\n/** A mask grown by `r` pixels (square structuring element). */\nexport function dilateMask(w: number, h: number, mask: ArrayLike<boolean | number>, r: number): Uint8Array {\n const tmp = new Uint8Array(w * h);\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n if (!mask[y * w + x]) continue;\n for (let xx = Math.max(0, x - r); xx <= Math.min(w - 1, x + r); xx++) tmp[y * w + xx] = 1;\n }\n }\n const out = new Uint8Array(w * h);\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n if (!tmp[y * w + x]) continue;\n for (let yy = Math.max(0, y - r); yy <= Math.min(h - 1, y + r); yy++) out[yy * w + x] = 1;\n }\n }\n return out;\n}\n\n/** A summed-area table over a mask, for \"how many masked cells in this window\". */\nclass MaskIntegral {\n private readonly s: Uint32Array;\n constructor(\n private readonly w: number,\n private readonly h: number,\n mask: ArrayLike<boolean | number>\n ) {\n const W = w + 1;\n this.s = new Uint32Array(W * (h + 1));\n for (let y = 0; y < h; y++) {\n let row = 0;\n for (let x = 0; x < w; x++) {\n row += mask[y * w + x] ? 1 : 0;\n this.s[(y + 1) * W + x + 1] = this.s[y * W + x + 1] + row;\n }\n }\n }\n /** Masked cells in [x0, x1) × [y0, y1) (clipped to the grid). */\n count(x0: number, y0: number, x1: number, y1: number): number {\n const W = this.w + 1;\n const a = Math.max(0, Math.min(this.w, x0));\n const b = Math.max(0, Math.min(this.h, y0));\n const c = Math.max(0, Math.min(this.w, x1));\n const d = Math.max(0, Math.min(this.h, y1));\n if (c <= a || d <= b) return 0;\n return this.s[d * W + c] - this.s[b * W + c] - this.s[d * W + a] + this.s[b * W + a];\n }\n}\n\n/** Bounding box [x0, y0, x1, y1) of the set cells, or null. */\nexport function maskBounds(w: number, h: number, mask: ArrayLike<boolean | number>): [number, number, number, number] | null {\n let x0 = w;\n let y0 = h;\n let x1 = -1;\n let y1 = -1;\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n if (!mask[y * w + x]) continue;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n }\n }\n return x1 < 0 ? null : [x0, y0, x1 + 1, y1 + 1];\n}\n\n/**\n * Proximity match: the translation `(dx, dy)` (within `maxRadius`) whose pixels best match the\n * ring of `ring` pixels around the hole (sum of squared differences), so `img[p + (dx, dy)]` can\n * fill `p` in the hole. The source window must lie on the grid and hold no hole pixels. Nearer\n * sources win ties. Returns null when no translation fits.\n */\nexport function bestOffset(w: number, h: number, ch: number, img: ArrayLike<number>, hole: ArrayLike<boolean | number>, ring: number, maxRadius: number): [number, number] | null {\n const bb = maskBounds(w, h, hole);\n if (!bb) return null;\n const extent = Math.max(w, h);\n const R = Math.max(0, Math.min(extent, Math.floor(maxRadius)));\n if (R === 0) return null;\n const rg = Math.max(1, Math.min(extent, Math.floor(ring)));\n const hs = new MaskIntegral(w, h, hole);\n const x0 = bb[0] - rg;\n const y0 = bb[1] - rg;\n const x1 = bb[2] + rg;\n const y1 = bb[3] + rg;\n // Sample points: the ring around the hole, subsampled to about 2000.\n const pts: number[] = [];\n for (let y = Math.max(0, y0); y < Math.min(h, y1); y++) {\n for (let x = Math.max(0, x0); x < Math.min(w, x1); x++) {\n if (hole[y * w + x]) continue;\n if (hs.count(x - rg, y - rg, x + rg + 1, y + rg + 1) > 0) pts.push(x, y);\n }\n }\n const stride = Math.max(1, Math.floor(pts.length / 2 / 2000));\n const sx: number[] = [];\n const sy: number[] = [];\n for (let i = 0; i < pts.length; i += 2 * stride) {\n sx.push(pts[i]);\n sy.push(pts[i + 1]);\n }\n const n = Math.max(1, sx.length);\n const evalAt = (dx: number, dy: number, cutoff: number): number | null => {\n if (dx === 0 && dy === 0) return null;\n const hx0 = x0 + dx;\n const hy0 = y0 + dy;\n const hx1 = x1 + dx;\n const hy1 = y1 + dy;\n if (hx0 < 0 || hy0 < 0 || hx1 > w || hy1 > h) return null;\n if (hs.count(hx0, hy0, hx1, hy1) !== 0) return null;\n const penalty = 1 + 1e-3 * (Math.hypot(dx, dy) / R);\n let e = 0;\n for (let i = 0; i < sx.length; i++) {\n const a = (sy[i] * w + sx[i]) * ch;\n const b = ((sy[i] + dy) * w + sx[i] + dx) * ch;\n for (let c = 0; c < ch; c++) {\n const d = img[a + c] - img[b + c];\n e += d * d;\n }\n if ((i & 31) === 31 && (e / n) * penalty >= cutoff) return null;\n }\n return (e / n) * penalty;\n };\n const found: { best: [number, number] | null; e: number } = { best: null, e: Infinity };\n const consider = (dx: number, dy: number) => {\n const e = evalAt(dx, dy, found.e);\n if (e !== null && e < found.e) {\n found.e = e;\n found.best = [dx, dy];\n }\n };\n // A coarse grid over the search window, then every offset around the winner.\n const step = Math.max(1, Math.floor(R / 24));\n for (let dy = -R; dy <= R; dy += step) for (let dx = -R; dx <= R; dx += step) consider(dx, dy);\n if (step > 1 && found.best) {\n const [bx, by] = found.best;\n for (let dy = by - step; dy <= by + step; dy++) for (let dx = bx - step; dx <= bx + step; dx++) consider(dx, dy);\n }\n return found.best;\n}\n","// Portions ported from photocraft/crates/paint/src/retouch.rs, crates/algo/src/retouch.rs and crates/engine/src/retouch_cmds.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/**\n * The retouching tools on the shared brush engine: Clone Stamp, Healing Brush, Spot Healing\n * Brush, Smudge, Dodge, Burn and Sponge. Two stroke shapes cover them all:\n *\n * - **Accumulate, then composite** (Clone Stamp, Dodge, Burn; the Healing tools' result): the\n * stroke's dabs build up one `CoverageMap` (flow builds towards the dab's opacity ceiling), and\n * per-pixel paint taken from the pre-stroke state (a cloned source, a toned copy of the pixel,\n * a healed patch) is composited through it with `applyRetouchToTile`. Pixels painted earlier in\n * the stroke are never sampled again, and overlapping dabs never exceed the stroke's opacity.\n * - **Sequential dabs** (Smudge, Sponge): `RetouchDabStroke` runs each dab in order over live\n * tiles, so dab n sees what dab n − 1 did.\n *\n * The Healing Brush clones while it is drawn and, when the stroke ends, replaces the clone with a\n * seamless clone (`healPixels`, Poisson blending): the source's texture with the colour and light\n * of the pixels around the stroke. The Spot Healing Brush finds its own source (`spotHealPixels`).\n *\n * Tone curves (`dodgeBurnCurve`, `dodgeBurn`, `sponge`) are photocraft's, made to behave like\n * Photoshop's tools: the chosen range moves most, black and white stay put for midtones, and\n * Protect Tones works on luma so hue and saturation are kept. Data in, pixels out; no DOM.\n */\n\nimport { blendPixel, type BlendMode } from '../raster/blend';\nimport type { RasterRect } from '../raster/surface';\nimport { TILE, tileKey } from '../raster/surface';\nimport { srgbDecode, srgbEncode } from '../raster/deep';\nimport { DabWalker, dabRect, rasterizeDab, type BrushSettings, type CoverageMap, type Dab, type StrokePoint } from './brush';\nimport { SmudgeTip, type SmudgeSettings } from './blending';\nimport { TileMedium, put255, type PaintPixels, type PaintTileTarget } from './exchange';\nimport { bestOffset, dilateMask, maskBounds, membraneFill, seamlessClone } from './heal';\n\n// ---------------------------------------------------------------------------\n// Tone kernels\n// ---------------------------------------------------------------------------\n\n/** The tonal range Dodge and Burn work on most. */\nexport type ToneRange = 'shadows' | 'midtones' | 'highlights';\n\nexport const TONE_RANGES: readonly ToneRange[] = ['shadows', 'midtones', 'highlights'];\n\n/** Rec. 601 luma of straight RGB (0..1). */\nexport function luma(r: number, g: number, b: number): number {\n return 0.299 * r + 0.587 * g + 0.114 * b;\n}\n\n/** The dodge (or burn) transfer of one value `v` (0..1) at exposure `e` (0..1). */\nexport function dodgeBurnCurve(v: number, e: number, range: ToneRange, burn: boolean): number {\n const x = Math.max(0, v);\n const k = Math.min(1, Math.max(0, e));\n const c = Math.min(1, x);\n if (range === 'midtones') {\n // A gamma bend weighted to peak at mid-grey: 0 and 1 stay where they are.\n const w = Math.min(1, Math.max(0, 4 * x * (1 - x)));\n return x + w * (Math.pow(c, burn ? 1 + k : 1 / (1 + k)) - x);\n }\n // Shadows / highlights: `v + e·w·(target − v)`, weighted (1 − v)² or v², so that end moves most.\n const w = range === 'shadows' ? (1 - c) * (1 - c) : c * c;\n return burn ? x - k * w * x : x + k * w * (1 - c);\n}\n\n/** Brings RGB back into 0..1 keeping its luma (mixing towards grey) rather than clipping each channel. */\nfunction compressGamut(c: number[], l: number): void {\n const L = Math.min(1, Math.max(0, l));\n const lo = Math.min(c[0], c[1], c[2]);\n const hi = Math.max(c[0], c[1], c[2]);\n let t = 1;\n if (hi > 1 && hi - L > 1e-9) t = Math.min(t, (1 - L) / (hi - L));\n if (lo < 0 && L - lo > 1e-9) t = Math.min(t, L / (L - lo));\n for (let i = 0; i < 3; i++) c[i] = L + (c[i] - L) * t;\n}\n\n/**\n * Dodge (or burn) straight RGB `c` (0..1, written in place) at exposure `e`. With `protectTones`\n * the curve moves luma and the colour is scaled to it (hue and saturation kept); otherwise each\n * channel goes through the curve.\n */\nexport function dodgeBurn(c: number[], e: number, range: ToneRange, burn: boolean, protectTones: boolean): number[] {\n if (!(e > 0)) return c;\n if (protectTones) {\n const l = luma(c[0], c[1], c[2]);\n const nl = dodgeBurnCurve(l, e, range, burn);\n if (l > 1e-6) for (let i = 0; i < 3; i++) c[i] *= nl / l;\n else c[0] = c[1] = c[2] = nl;\n compressGamut(c, nl);\n } else for (let i = 0; i < 3; i++) c[i] = dodgeBurnCurve(c[i], e, range, burn);\n return c;\n}\n\n/** HSV saturation (chroma / max) of straight RGB. */\nexport function saturationOf(r: number, g: number, b: number): number {\n const lo = Math.min(r, g, b);\n const hi = Math.max(r, g, b);\n return hi <= 1e-6 ? 0 : Math.min(1, Math.max(0, (hi - lo) / hi));\n}\n\n/**\n * Sponge: moves straight RGB `c` (written in place) away from (`saturate`) or towards its luma\n * grey by `amount` (0..1). `vibrance` damps it where it would clip: saturating touches already\n * saturated colours less, desaturating touches near-greys less. Gamut-compressed, never clipped.\n */\nexport function sponge(c: number[], amount: number, saturate: boolean, vibrance: boolean): number[] {\n let a = Math.min(1, Math.max(0, amount));\n if (!(a > 0)) return c;\n if (vibrance) {\n const s = saturationOf(c[0], c[1], c[2]);\n a *= saturate ? 1 - s : 0.25 + 0.75 * s;\n }\n const l = luma(c[0], c[1], c[2]);\n const k = saturate ? 1 + a : 1 - a;\n for (let i = 0; i < 3; i++) c[i] = l + (c[i] - l) * k;\n compressGamut(c, l);\n return c;\n}\n\n/** Per-dab strength so that one pass over a point totals about `e`, whatever the spacing. */\nexport function perDabStrength(e: number, spacing: number): number {\n return 1 - Math.pow(1 - Math.min(1, Math.max(0, e)), Math.min(1, Math.max(0.01, spacing)));\n}\n\n// ---------------------------------------------------------------------------\n// Settings\n// ---------------------------------------------------------------------------\n\n/** Which pixels the Clone Stamp and the Healing tools sample. */\nexport type RetouchSample = 'current' | 'current-below' | 'all';\n\nexport const RETOUCH_SAMPLES: readonly RetouchSample[] = ['current', 'current-below', 'all'];\n\n/** How the Spot Healing Brush finds its source. */\nexport type SpotHealType = 'proximity-match' | 'smooth';\n\nexport const SPOT_HEAL_TYPES: readonly SpotHealType[] = ['proximity-match', 'smooth'];\n\n/** The retouching tools' options. */\nexport interface RetouchSettings {\n /** Clone / heal: keep the source offset from stroke to stroke (default true); off, every stroke starts at the source point. */\n aligned: boolean;\n /** Clone / heal / spot heal: what is sampled (default `current`: the target layer). */\n sample: RetouchSample;\n /** Spot heal: `proximity-match` (default: the best matching patch nearby) or `smooth` (a smooth fill from the edges). */\n spotType: SpotHealType;\n /** Dodge / burn: the tonal range (default `midtones`). */\n range: ToneRange;\n /** Dodge / burn: exposure 0..1 (default 0.5). */\n exposure: number;\n /** Dodge / burn: tone luma only, keeping hue and saturation (default true). */\n protectTones: boolean;\n /** Sponge: `desaturate` (default) or `saturate`. */\n spongeMode: 'desaturate' | 'saturate';\n /** Sponge: damp the change where colours would clip (default true). */\n vibrance: boolean;\n /** Smudge: how strongly the finger carries and lays paint, 0..1 (default 0.5). */\n strength: number;\n}\n\nexport const DEFAULT_RETOUCH: Readonly<RetouchSettings> = {\n aligned: true,\n sample: 'current',\n spotType: 'proximity-match',\n range: 'midtones',\n exposure: 0.5,\n protectTones: true,\n spongeMode: 'desaturate',\n vibrance: true,\n strength: 0.5,\n};\n\n/** Retouch settings with every field valid (missing or invalid fields from `base`). */\nexport function normalizeRetouch(raw: Partial<RetouchSettings> = {}, base: RetouchSettings = DEFAULT_RETOUCH): RetouchSettings {\n const unit = (v: unknown, d: number) => (typeof v === 'number' && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d);\n const bool = (v: unknown, d: boolean) => (typeof v === 'boolean' ? v : d);\n const pick = <T extends string>(v: unknown, all: readonly T[], d: T): T => (all.includes(v as T) ? (v as T) : d);\n return {\n aligned: bool(raw.aligned, base.aligned),\n sample: pick(raw.sample, RETOUCH_SAMPLES, base.sample),\n spotType: pick(raw.spotType, SPOT_HEAL_TYPES, base.spotType),\n range: pick(raw.range, TONE_RANGES, base.range),\n exposure: unit(raw.exposure, base.exposure),\n protectTones: bool(raw.protectTones, base.protectTones),\n spongeMode: pick(raw.spongeMode, ['desaturate', 'saturate'] as const, base.spongeMode),\n vibrance: bool(raw.vibrance, base.vibrance),\n strength: unit(raw.strength, base.strength),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pixel sources\n// ---------------------------------------------------------------------------\n\n/** Pixels a retouching stroke samples, by document pixel. */\nexport interface PixelSource {\n /** Straight RGBA 0..255 of document pixel (x, y) into `out`; false (and `out` zeroed) where there is nothing. */\n sample(x: number, y: number, out: number[]): boolean;\n}\n\n/**\n * A source over raster tiles (layer pixels, a mask, or composite tiles): `readTile` gives tile\n * (tx, ty) in source space or `null` when it is empty; source pixel = document pixel − `offset`.\n * One-channel tiles read as grey (alpha 255). Tiles are cached, so make one per stroke.\n */\nexport class TilePixelSource implements PixelSource {\n private readonly cache = new Map<number, PaintPixels | null>();\n private lastKey = NaN;\n private last: PaintPixels | null = null;\n\n constructor(\n /** A tile's bytes, or a float working tile on the same 0..255 scale (16 / 32 bits). */\n private readonly readTile: (tx: number, ty: number) => PaintPixels | null,\n readonly channels: 1 | 4,\n readonly offset: { x: number; y: number } = { x: 0, y: 0 }\n ) {}\n\n sample(x: number, y: number, out: number[]): boolean {\n const lx = x - this.offset.x;\n const ly = y - this.offset.y;\n const tx = Math.floor(lx / TILE);\n const ty = Math.floor(ly / TILE);\n const key = tileKey(tx, ty);\n if (key !== this.lastKey) {\n let t = this.cache.get(key);\n if (t === undefined) {\n t = this.readTile(tx, ty);\n this.cache.set(key, t);\n }\n this.last = t;\n this.lastKey = key;\n }\n const t = this.last;\n if (!t) {\n out[0] = out[1] = out[2] = out[3] = 0;\n return false;\n }\n const p = (ly - ty * TILE) * TILE + (lx - tx * TILE);\n if (this.channels === 1) {\n out[0] = out[1] = out[2] = t[p];\n out[3] = 255;\n return true;\n }\n const i = p * 4;\n out[0] = t[i];\n out[1] = t[i + 1];\n out[2] = t[i + 2];\n out[3] = t[i + 3];\n return out[3] > 0;\n }\n}\n\n/** A source over a region of float RGBA pixels (0..255), e.g. a healed patch. */\nexport class RegionPixelSource implements PixelSource {\n constructor(\n readonly rect: RasterRect,\n readonly data: Float32Array\n ) {}\n\n sample(x: number, y: number, out: number[]): boolean {\n const r = this.rect;\n const lx = x - r.x;\n const ly = y - r.y;\n if (lx < 0 || ly < 0 || lx >= r.width || ly >= r.height) {\n out[0] = out[1] = out[2] = out[3] = 0;\n return false;\n }\n const i = (ly * r.width + lx) * 4;\n out[0] = this.data[i];\n out[1] = this.data[i + 1];\n out[2] = this.data[i + 2];\n out[3] = this.data[i + 3];\n return out[3] > 0;\n }\n}\n\n/** `rect` of a source, shifted by (dx, dy), as float RGBA 0..255 (`rect.width × rect.height × 4`). */\nexport function readSourceRegion(source: PixelSource, rect: RasterRect, dx = 0, dy = 0): Float32Array {\n const out = new Float32Array(rect.width * rect.height * 4);\n const px = [0, 0, 0, 0];\n for (let y = 0; y < rect.height; y++) {\n for (let x = 0; x < rect.width; x++) {\n source.sample(rect.x + x + dx, rect.y + y + dy, px);\n const i = (y * rect.width + x) * 4;\n out[i] = px[0];\n out[i + 1] = px[1];\n out[i + 2] = px[2];\n out[i + 3] = px[3];\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Accumulate, then composite\n// ---------------------------------------------------------------------------\n\n/** What a coverage-based retouching stroke lays through its coverage. */\nexport type RetouchPaint =\n /** Source pixels at document pixel + (dx, dy) (Clone Stamp, a healed patch). */\n | { kind: 'source'; source: PixelSource; dx: number; dy: number }\n /** The target pixel itself, toned (Dodge, Burn): `tone` edits straight RGB 0..1 in place. */\n | { kind: 'tone'; tone: (rgb: number[]) => void }\n /** A flat colour (the Spot Healing Brush's trail while it is drawn), RGBA 0..255. */\n | { kind: 'color'; color: readonly [number, number, number, number] };\n\n/** How a coverage-based retouching stroke composites. */\nexport interface RetouchApply {\n /** Stroke opacity 0..1. */\n opacity: number;\n /** Blend mode for source paint (default normal). */\n blend?: BlendMode;\n lockTransparency: boolean;\n /** Selection coverage (document space, canvas-sized), or `null`. */\n selection: { width: number; height: number; data: ArrayLike<number> } | null;\n /** Layer offset (target pixels are in layer space). */\n offset: { x: number; y: number };\n}\n\n/**\n * Composites `paint` through the stroke's coverage over `tileData` (target tile (tx, ty) as it was\n * before the stroke, layer space; RGBA, or one byte per pixel for masks) and returns the new tile.\n * With `into` (the previous result) and `region` (document space), only `region` is recomputed.\n * Tone paint keeps alpha; source and colour paint composite with `blend` (source-over by\n * default), keeping alpha on a locked layer.\n */\nexport function applyRetouchToTile<T extends PaintPixels>(\n coverage: CoverageMap,\n tileData: T,\n channels: 1 | 4,\n tx: number,\n ty: number,\n paint: RetouchPaint,\n s: RetouchApply,\n options: { into?: T; region?: RasterRect } = {}\n): T {\n // A float working tile (16 / 32 bits) stays float and keeps fractions; bytes round as ever.\n const out = (options.into ?? tileData).slice() as T;\n const ox = tx * TILE + s.offset.x;\n const oy = ty * TILE + s.offset.y;\n let x0 = 0;\n let y0 = 0;\n let x1 = TILE;\n let y1 = TILE;\n if (options.region) {\n const r = options.region;\n x0 = Math.max(0, Math.floor(r.x) - ox);\n y0 = Math.max(0, Math.floor(r.y) - oy);\n x1 = Math.min(TILE, Math.ceil(r.x + r.width) - ox);\n y1 = Math.min(TILE, Math.ceil(r.y + r.height) - oy);\n if (x1 <= x0 || y1 <= y0) return out;\n if (options.into) {\n for (let y = y0; y < y1; y++) out.set(tileData.subarray((y * TILE + x0) * channels, (y * TILE + x1) * channels), (y * TILE + x0) * channels);\n }\n }\n const sel = s.selection;\n const blend = s.blend ?? 'normal';\n const src = [0, 0, 0, 0];\n const rgb = [0, 0, 0];\n const cb = [0, 0, 0, 0];\n const cs = [0, 0, 0, 0];\n // The coverage tile under the pixel, looked up once per tile it crosses.\n let covKey = NaN;\n let covTile: Float32Array | undefined;\n for (let y = y0; y < y1; y++) {\n const dy = oy + y;\n const cty = Math.floor(dy / TILE);\n for (let x = x0; x < x1; x++) {\n const dx = ox + x;\n const ctx = Math.floor(dx / TILE);\n const key = tileKey(ctx, cty);\n if (key !== covKey) {\n covKey = key;\n covTile = coverage.tiles.get(key);\n }\n if (!covTile) continue;\n const c = covTile[(dy - cty * TILE) * TILE + (dx - ctx * TILE)];\n if (c <= 0) continue;\n let k = c * s.opacity;\n if (sel) {\n if (dx < 0 || dy < 0 || dx >= sel.width || dy >= sel.height) continue;\n k *= sel.data[dy * sel.width + dx] / 255;\n }\n if (k > 1) k = 1;\n if (k <= 0) continue;\n const p = y * TILE + x;\n if (channels === 1) {\n // Masks: grey values; the source's grey is its red channel.\n const g = out[p];\n let t: number;\n if (paint.kind === 'tone') {\n rgb[0] = rgb[1] = rgb[2] = g / 255;\n paint.tone(rgb);\n t = luma(rgb[0], rgb[1], rgb[2]) * 255;\n } else if (paint.kind === 'color') t = paint.color[0];\n else {\n if (!paint.source.sample(dx + paint.dx, dy + paint.dy, src)) continue;\n t = src[0];\n k *= src[3] / 255;\n }\n out[p] = Math.round(g + (t - g) * k);\n continue;\n }\n const i = p * 4;\n const a = out[i + 3];\n if (paint.kind === 'tone') {\n if (a === 0) continue;\n rgb[0] = out[i] / 255;\n rgb[1] = out[i + 1] / 255;\n rgb[2] = out[i + 2] / 255;\n const r0 = rgb[0];\n const g0 = rgb[1];\n const b0 = rgb[2];\n paint.tone(rgb);\n put255(out, i, r0 + (rgb[0] - r0) * k);\n put255(out, i + 1, g0 + (rgb[1] - g0) * k);\n put255(out, i + 2, b0 + (rgb[2] - b0) * k);\n continue;\n }\n if (s.lockTransparency && a === 0) continue;\n if (paint.kind === 'color') {\n src[0] = paint.color[0];\n src[1] = paint.color[1];\n src[2] = paint.color[2];\n src[3] = paint.color[3];\n } else if (!paint.source.sample(dx + paint.dx, dy + paint.dy, src)) continue;\n for (let ch = 0; ch < 4; ch++) {\n cb[ch] = out[i + ch] / 255;\n cs[ch] = src[ch] / 255;\n }\n // Source-over, as a brush lays paint: cloning transparency paints nothing.\n const o = blendPixel(blend, cb, cs, k);\n put255(out, i, o[0]);\n put255(out, i + 1, o[1]);\n put255(out, i + 2, o[2]);\n if (!s.lockTransparency) put255(out, i + 3, o[3]);\n }\n }\n return out;\n}\n\n/**\n * Dodge / Burn at 16 or 32 bits: `applyRetouchToTile`'s tone paint on a layer tile's deep samples\n * (`Uint16Array` sRGB-encoded, or `Float32Array` linear light, RGBA). The tone curves work on\n * encoded values as at 8 bits, so 32-bit samples are encoded, toned and decoded (colour above 1 is\n * toned at 1 and keeps its excess). Returns the new samples.\n */\nexport function applyToneToDeepTile(\n coverage: CoverageMap,\n samples: Uint16Array | Float32Array,\n tx: number,\n ty: number,\n tone: (rgb: number[]) => void,\n s: RetouchApply,\n options: { into?: Uint16Array | Float32Array; region?: RasterRect } = {}\n): Uint16Array | Float32Array {\n const linear = samples instanceof Float32Array;\n const out = (options.into ?? samples).slice() as Uint16Array | Float32Array;\n const ox = tx * TILE + s.offset.x;\n const oy = ty * TILE + s.offset.y;\n let x0 = 0;\n let y0 = 0;\n let x1 = TILE;\n let y1 = TILE;\n if (options.region) {\n const r = options.region;\n x0 = Math.max(0, Math.floor(r.x) - ox);\n y0 = Math.max(0, Math.floor(r.y) - oy);\n x1 = Math.min(TILE, Math.ceil(r.x + r.width) - ox);\n y1 = Math.min(TILE, Math.ceil(r.y + r.height) - oy);\n if (x1 <= x0 || y1 <= y0) return out;\n if (options.into) for (let y = y0; y < y1; y++) out.set(samples.subarray((y * TILE + x0) * 4, (y * TILE + x1) * 4), (y * TILE + x0) * 4);\n }\n const sel = s.selection;\n const rgb = [0, 0, 0];\n const before = [0, 0, 0];\n const excess = [0, 0, 0];\n let covKey = NaN;\n let covTile: Float32Array | undefined;\n for (let y = y0; y < y1; y++) {\n const dy = oy + y;\n const cty = Math.floor(dy / TILE);\n for (let x = x0; x < x1; x++) {\n const dx = ox + x;\n const ctx = Math.floor(dx / TILE);\n const key = tileKey(ctx, cty);\n if (key !== covKey) {\n covKey = key;\n covTile = coverage.tiles.get(key);\n }\n if (!covTile) continue;\n const c = covTile[(dy - cty * TILE) * TILE + (dx - ctx * TILE)];\n if (c <= 0) continue;\n let k = c * s.opacity;\n if (sel) {\n if (dx < 0 || dy < 0 || dx >= sel.width || dy >= sel.height) continue;\n k *= sel.data[dy * sel.width + dx] / 255;\n }\n if (k > 1) k = 1;\n if (k <= 0) continue;\n const i = (y * TILE + x) * 4;\n if (out[i + 3] === 0) continue;\n for (let ch = 0; ch < 3; ch++) {\n const v = out[i + ch];\n if (linear) {\n excess[ch] = v > 1 ? v - 1 : 0;\n rgb[ch] = srgbEncode(v > 1 ? 1 : v < 0 ? 0 : v);\n } else rgb[ch] = v / 65535;\n before[ch] = rgb[ch];\n }\n tone(rgb);\n for (let ch = 0; ch < 3; ch++) {\n const e = before[ch] + (rgb[ch] - before[ch]) * k;\n if (linear) out[i + ch] = srgbDecode(e) + excess[ch];\n else out[i + ch] = e <= 0 ? 0 : e >= 1 ? 65535 : Math.round(e * 65535);\n }\n }\n }\n return out;\n}\n\n/** The tone function of a Dodge or Burn stroke. */\nexport function toneOf(tool: 'dodge' | 'burn', settings: Pick<RetouchSettings, 'exposure' | 'range' | 'protectTones'>): (rgb: number[]) => void {\n const burn = tool === 'burn';\n return (rgb) => {\n dodgeBurn(rgb, settings.exposure, settings.range, burn, settings.protectTones);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Healing\n// ---------------------------------------------------------------------------\n\n/**\n * Heals `src` into `dst` over `mask` (all `w × h`, RGBA floats 0..255): a seamless clone solved\n * only over the mask's bounds plus a one-pixel fixed ring; elsewhere the result is `dst`.\n */\nexport function healPixels(w: number, h: number, src: Float32Array, dst: Float32Array, mask: ArrayLike<boolean | number>): Float32Array {\n const out = new Float32Array(dst);\n const bb = maskBounds(w, h, mask);\n if (!bb) return out;\n const x0 = Math.max(0, bb[0] - 1);\n const y0 = Math.max(0, bb[1] - 1);\n const x1 = Math.min(w, bb[2] + 1);\n const y1 = Math.min(h, bb[3] + 1);\n const rw = x1 - x0;\n const rh = y1 - y0;\n const s = new Float32Array(rw * rh * 4);\n const d = new Float32Array(rw * rh * 4);\n const m = new Uint8Array(rw * rh);\n for (let y = 0; y < rh; y++) {\n for (let x = 0; x < rw; x++) {\n const gi = (y + y0) * w + x + x0;\n const li = y * rw + x;\n m[li] = mask[gi] ? 1 : 0;\n for (let c = 0; c < 4; c++) {\n s[li * 4 + c] = src[gi * 4 + c];\n d[li * 4 + c] = dst[gi * 4 + c];\n }\n }\n }\n const healed = seamlessClone(rw, rh, 4, s, d, m);\n for (let y = 0; y < rh; y++) {\n for (let x = 0; x < rw; x++) {\n const gi = ((y + y0) * w + x + x0) * 4;\n const li = (y * rw + x) * 4;\n for (let c = 0; c < 4; c++) out[gi + c] = Math.min(255, Math.max(0, healed[li + c]));\n }\n }\n return out;\n}\n\n/**\n * Spot healing: fills `hole` (`w × h`) of `img` (RGBA floats 0..255) with no given source, then\n * heals the fill into its surroundings. `proximity-match` copies the best matching nearby patch\n * (falling back to `smooth` when none fits); `smooth` interpolates the edges inwards. `size` is\n * the brush diameter in pixels. Returns the healed pixels over the whole grid.\n */\nexport function spotHealPixels(\n w: number,\n h: number,\n img: Float32Array,\n hole: ArrayLike<boolean | number>,\n size: number,\n type: SpotHealType,\n /** Told the patch offset it copied (`null`: a smooth fill). */\n onOffset?: (offset: [number, number] | null) => void\n): Float32Array {\n // Fill a slightly larger domain than the healed area, so the solve's boundary sees filled values\n // that differ from the untouched pixels: that mismatch is what the membrane corrects.\n const domain = dilateMask(w, h, hole, 2);\n const healMask = dilateMask(w, h, hole, 1);\n let filled: Float32Array | null = null;\n let bestFound: [number, number] | null = null;\n if (type === 'proximity-match') {\n const ring = Math.min(16, Math.max(3, Math.floor(size / 8)));\n const off = bestOffset(w, h, 4, img, domain, ring, Math.floor(Math.max(w, h) / 2));\n if (off) {\n bestFound = off;\n // Copy from the pre-healing pixels, never from what this loop already wrote.\n filled = new Float32Array(img);\n const [ddx, ddy] = off;\n for (let i = 0; i < w * h; i++) {\n if (!domain[i]) continue;\n const x = (i % w) + ddx;\n const y = Math.floor(i / w) + ddy;\n const j = (y * w + x) * 4;\n for (let c = 0; c < 4; c++) filled[i * 4 + c] = img[j + c];\n }\n }\n }\n onOffset?.(filled ? (bestFound as [number, number]) : null);\n if (!filled) filled = membraneFill(w, h, 4, img, domain);\n return healPixels(w, h, filled, img, healMask);\n}\n\n/** The window a Spot Healing stroke over `bounds` looks for texture in: the bounds grown by their size, on the canvas. */\nexport function spotHealWindow(bounds: RasterRect, canvas: { width: number; height: number }): RasterRect {\n const g = Math.max(8, Math.ceil(Math.max(bounds.width, bounds.height)));\n const x0 = Math.max(0, bounds.x - g);\n const y0 = Math.max(0, bounds.y - g);\n const x1 = Math.min(canvas.width, bounds.x + bounds.width + g);\n const y1 = Math.min(canvas.height, bounds.y + bounds.height + g);\n return { x: x0, y: y0, width: Math.max(0, x1 - x0), height: Math.max(0, y1 - y0) };\n}\n\n// ---------------------------------------------------------------------------\n// Sequential dabs\n// ---------------------------------------------------------------------------\n\n/**\n * One dab of a sequential retouching stroke: `cov` is the tip's pure coverage over `rect`\n * (document space, 0..1); `d.alpha` is the flow and `d.opacity` the pressure opacity.\n */\nexport type RetouchDabEffect = (medium: TileMedium, d: Dab, rect: RasterRect, cov: Float32Array) => void;\n\n/**\n * A stroke whose dabs edit live tiles in order (Smudge, Sponge). Feed points with `push` (any\n * chunks give the same dabs), end with `finish`; each returns the document rectangles it touched.\n */\nexport class RetouchDabStroke {\n dabCount = 0;\n bounds: RasterRect | null = null;\n private readonly walker: DabWalker;\n private readonly medium: TileMedium;\n private smoothPos: StrokePoint | null = null;\n private buf = new Float32Array(0);\n\n constructor(\n readonly brush: BrushSettings,\n target: PaintTileTarget,\n private readonly effect: RetouchDabEffect\n ) {\n this.walker = new DabWalker(brush);\n this.medium = new TileMedium(target);\n }\n\n push(points: readonly StrokePoint[]): RasterRect[] {\n const dabs: Dab[] = [];\n for (const p of points) this.walker.push(this.smooth(p), dabs);\n return this.paint(dabs);\n }\n\n finish(last?: StrokePoint): RasterRect[] {\n const dabs: Dab[] = [];\n if (last && this.smoothPos && (this.smoothPos.x !== last.x || this.smoothPos.y !== last.y)) this.walker.push(last, dabs);\n this.walker.finish(dabs);\n return this.paint(dabs);\n }\n\n private smooth(p: StrokePoint): StrokePoint {\n const a = 1 - Math.min(0.95, Math.max(0, this.brush.smoothing));\n const cur = this.smoothPos;\n const n = cur && a < 1 ? { ...p, x: cur.x + (p.x - cur.x) * a, y: cur.y + (p.y - cur.y) * a } : p;\n this.smoothPos = n;\n return n;\n }\n\n private paint(dabs: Dab[]): RasterRect[] {\n const rects: RasterRect[] = [];\n for (const d of dabs) {\n const rect = dabRect(d, this.brush.hardness);\n const n = rect.width * rect.height;\n if (this.buf.length < n) this.buf = new Float32Array(n);\n const cov = this.buf.subarray(0, n);\n rasterizeDab({ ...d, alpha: 1 }, this.brush.hardness, rect, cov);\n this.medium.reset();\n this.effect(this.medium, d, rect, cov);\n rects.push(rect);\n this.bounds = unionRect(this.bounds, rect);\n this.dabCount++;\n }\n return rects;\n }\n}\n\n/** The Smudge's per-dab effect: a `SmudgeTip` whose strength carries and lays the paint. */\nexport function smudgeEffect(brush: BrushSettings, strength: number, mixing: SmudgeSettings['mixing'] = 'rgb'): RetouchDabEffect {\n const s = Math.min(1, Math.max(0, strength));\n const tip = new SmudgeTip({ strength: s, length: s, mixing }, brush.size);\n return (medium, d, rect, cov) => tip.dab(medium, { x: d.x, y: d.y, radius: d.radius, opacity: d.opacity * d.alpha }, rect, cov);\n}\n\n/** The Sponge's per-dab effect: each dab moves saturation by about half its flow, compounding over passes. */\nexport function spongeEffect(brush: BrushSettings, saturate: boolean, vibrance: boolean): RetouchDabEffect {\n const rgb = [0, 0, 0];\n return (medium, d, rect, cov) => {\n const w = rect.width;\n for (let yy = 0; yy < rect.height; yy++) {\n for (let xx = 0; xx < w; xx++) {\n const cv = cov[yy * w + xx];\n if (cv <= 0) continue;\n const dx = rect.x + xx;\n const dy = rect.y + yy;\n const k = perDabStrength(cv * d.alpha * d.opacity * 0.5, brush.spacing) * medium.selectionAt(dx, dy);\n if (k <= 0) continue;\n const ri = medium.locate(dx, dy, false);\n if (ri < 0 || medium.pixels[ri + 3] === 0) continue;\n const i = medium.locate(dx, dy, true);\n const px = medium.pixels;\n rgb[0] = px[i] / 255;\n rgb[1] = px[i + 1] / 255;\n rgb[2] = px[i + 2] / 255;\n sponge(rgb, k, saturate, vibrance);\n put255(px, i, rgb[0]);\n put255(px, i + 1, rgb[1]);\n put255(px, i + 2, rgb[2]);\n }\n }\n };\n}\n\n/**\n * An RGBA view of one-channel tiles (masks), for the sequential-dab tools: reads expand grey to\n * opaque RGB, writes land in RGBA copies, and `flush` writes their red channel back through\n * `target.writeTile`. Alpha is locked, so a mask's values are all a dab changes.\n */\nexport function rgbaViewOfGrey(target: PaintTileTarget): { target: PaintTileTarget; flush(): void } {\n const views = new Map<number, Uint8ClampedArray>();\n const dirty = new Map<number, { tx: number; ty: number; data: Uint8ClampedArray }>();\n const expand = (g: Uint8ClampedArray) => {\n const out = new Uint8ClampedArray(g.length * 4);\n for (let i = 0; i < g.length; i++) {\n out[i * 4] = out[i * 4 + 1] = out[i * 4 + 2] = g[i];\n out[i * 4 + 3] = 255;\n }\n return out;\n };\n const view: PaintTileTarget = {\n offset: target.offset,\n lockTransparency: true,\n selection: target.selection,\n readTile: (tx, ty) => {\n const key = tileKey(tx, ty);\n let v = views.get(key);\n if (!v) {\n // An empty mask tile is still values (0), not transparency: always give a tile.\n v = expand((target.readTile(tx, ty) as Uint8ClampedArray | null) ?? new Uint8ClampedArray(TILE * TILE));\n views.set(key, v);\n }\n return v;\n },\n writeTile: (tx, ty) => {\n const key = tileKey(tx, ty);\n let v = views.get(key);\n if (!v) {\n v = expand((target.readTile(tx, ty) as Uint8ClampedArray | null) ?? new Uint8ClampedArray(TILE * TILE));\n views.set(key, v);\n }\n dirty.set(key, { tx, ty, data: v });\n return v;\n },\n };\n return {\n target: view,\n flush: () => {\n for (const { tx, ty, data } of dirty.values()) {\n const g = target.writeTile(tx, ty);\n for (let i = 0; i < g.length; i++) g[i] = data[i * 4];\n }\n dirty.clear();\n },\n };\n}\n\nfunction unionRect(a: RasterRect | null, b: RasterRect): RasterRect {\n if (!a) return { ...b };\n const x = Math.min(a.x, b.x);\n const y = Math.min(a.y, b.y);\n return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };\n}\n","/**\n * A brush stroke along a path: polyline samples, each with an optional pressure and pen tilt, are\n * walked at the brush's spacing (pressure and tilt interpolated between samples) into dabs, built\n * up in a coverage map and composited onto a layer. This is what a vector editor calls to draw a\n * path with a natural-media brush (\"brushstrokes on vector paths\"): the path is the data, the\n * pixels are rendered from it.\n *\n * - Pressure scales size and / or opacity as the brush says.\n * - Tilt (`tiltX` / `tiltY` in degrees, or `altitude` / `azimuth` in radians, as Pointer Events give\n * them) widens the tip (`tilt.size`), flattens it along the lean (`tilt.roundness`), and with\n * `tilt.followAzimuth` turns the tip to the lean, as a pen laid on its side does.\n * - With `followPath` the tip turns with the path (a calligraphic nib along a curve).\n *\n * Deterministic and DOM-free: the same samples and brush give the same pixels everywhere.\n */\n\nimport { applyStrokeToTile, CoverageMap, dabRect, normalizeBrush, rasterizeDab, type BrushSettings, type Dab } from './brush';\nimport { TiledSurface, finishTile, tileKey, tilesInRect, TILE, type RasterRect } from '../raster/surface';\nimport type { BlendMode } from '../raster/blend';\n\n/** One sample of the path (document pixels). */\nexport interface PathSample {\n x: number;\n y: number;\n /** 0..1 (default 1). */\n pressure?: number;\n /** Pointer Events tilt in degrees (−90..90). */\n tiltX?: number;\n tiltY?: number;\n /** Radians above the surface (π/2 upright); overrides tiltX / tiltY. */\n altitude?: number;\n /** Radians clockwise from +x: where the pen leans (with altitude). */\n azimuth?: number;\n}\n\nexport interface PathStrokeOptions {\n brush: Partial<BrushSettings>;\n /** How tilt shapes the tip (each 0..1; default none). */\n tilt?: { size?: number; roundness?: number; followAzimuth?: boolean };\n /** Turn the tip with the direction of the path (added to the brush angle). */\n followPath?: boolean;\n /** Size at zero pressure as a fraction of the full size, when pressure controls size (default 0). */\n minSize?: number;\n /** Join the last sample back to the first. */\n closed?: boolean;\n}\n\n/** A pen's lean from a sample: 0 upright .. 1 flat, and the direction it leans (radians, clockwise from +x). */\nfunction lean(s: PathSample): { amount: number; azimuth: number } | null {\n if (typeof s.altitude === 'number' && Number.isFinite(s.altitude)) {\n return { amount: 1 - Math.min(1, Math.max(0, s.altitude / (Math.PI / 2))), azimuth: s.azimuth ?? 0 };\n }\n if (typeof s.tiltX === 'number' || typeof s.tiltY === 'number') {\n const tx = ((s.tiltX ?? 0) * Math.PI) / 180;\n const ty = ((s.tiltY ?? 0) * Math.PI) / 180;\n if (tx === 0 && ty === 0) return { amount: 0, azimuth: 0 };\n const az = Math.atan2(Math.tan(ty), Math.tan(tx));\n const alt = Math.atan(1 / Math.hypot(Math.tan(tx), Math.tan(ty)));\n return { amount: 1 - alt / (Math.PI / 2), azimuth: az };\n }\n return null;\n}\n\n/** The dabs of a brush stroke along `samples`, with pressure and tilt interpolated between them. */\nexport function pathStrokeDabs(samples: readonly PathSample[], options: PathStrokeOptions): Dab[] {\n const brush = normalizeBrush(options.brush);\n const pts = options.closed && samples.length > 2 ? [...samples, samples[0]] : [...samples];\n if (!pts.length) return [];\n const tilt = options.tilt ?? {};\n const minSize = Math.min(1, Math.max(0, options.minSize ?? 0));\n const dab = (x: number, y: number, p: number, l: { amount: number; azimuth: number } | null, dir: number): Dab => {\n const pr = Math.min(1, Math.max(0, p));\n const lean0 = l?.amount ?? 0;\n const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * pr : 1) * (1 + 1.5 * (tilt.size ?? 0) * lean0);\n let angle = (brush.angle * Math.PI) / 180;\n if (options.followPath) angle += dir;\n if (tilt.followAzimuth && l) angle = -l.azimuth;\n const roundness = Math.max(0.05, brush.roundness * (1 - 0.85 * (tilt.roundness ?? 0) * lean0));\n const op = brush.pressureOpacity ? pr : 1;\n return { x, y, radius: Math.max(0.5, size / 2), alpha: brush.flow, opacity: brush.opacity * op, angle, roundness };\n };\n const out: Dab[] = [];\n const pressure = (s: PathSample) => (typeof s.pressure === 'number' && Number.isFinite(s.pressure) ? s.pressure : 1);\n if (pts.length === 1) {\n out.push(dab(pts[0].x, pts[0].y, pressure(pts[0]), lean(pts[0]), 0));\n return out;\n }\n let carry = 0;\n out.push(dab(pts[0].x, pts[0].y, pressure(pts[0]), lean(pts[0]), Math.atan2(-(pts[1].y - pts[0].y), pts[1].x - pts[0].x)));\n for (let i = 0; i + 1 < pts.length; i++) {\n const a = pts[i];\n const b = pts[i + 1];\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len = Math.hypot(dx, dy);\n if (len === 0) continue;\n // Dab angles are counter-clockwise with y up, as the shared rasterizer reads them.\n const dir = Math.atan2(-dy, dx);\n const la = lean(a);\n const lb = lean(b);\n let t = carry;\n for (;;) {\n const f = t / len;\n if (f > 1) break;\n const p = pressure(a) + (pressure(b) - pressure(a)) * f;\n const l = la && lb ? { amount: la.amount + (lb.amount - la.amount) * f, azimuth: la.azimuth + (lb.azimuth - la.azimuth) * f } : (la ?? lb);\n const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * p : 1);\n const step = Math.max(0.5, size * brush.spacing);\n if (t > 0 || i > 0) out.push(dab(a.x + dx * f, a.y + dy * f, p, l, dir));\n t += step;\n }\n carry = t - len;\n }\n return out;\n}\n\n/** The coverage of a brush stroke along `samples` (build-up towards each dab's opacity) and its bounds. */\nexport function pathStrokeCoverage(samples: readonly PathSample[], options: PathStrokeOptions): { coverage: CoverageMap; bounds: RasterRect | null; dabs: number } {\n const brush = normalizeBrush(options.brush);\n const coverage = new CoverageMap();\n const dabs = pathStrokeDabs(samples, options);\n let buf = new Float32Array(0);\n let bounds: RasterRect | null = null;\n for (const d of dabs) {\n const rect = dabRect(d, brush.hardness);\n const n = rect.width * rect.height;\n if (n <= 0) continue;\n if (buf.length < n) buf = new Float32Array(n);\n const vals = buf.subarray(0, n);\n rasterizeDab(d, brush.hardness, rect, vals);\n coverage.accumulate(rect, vals, d.opacity);\n if (!bounds) bounds = { ...rect };\n else {\n const x = Math.min(bounds.x, rect.x);\n const y = Math.min(bounds.y, rect.y);\n bounds = { x, y, width: Math.max(bounds.x + bounds.width, rect.x + rect.width) - x, height: Math.max(bounds.y + bounds.height, rect.y + rect.height) - y };\n }\n }\n return { coverage, bounds, dabs: dabs.length };\n}\n\nexport interface PaintPathOptions extends PathStrokeOptions {\n /** RGBA 0..255 of the paint. */\n color: readonly [number, number, number, number];\n /** `erase` removes paint instead (default `paint`). */\n mode?: 'paint' | 'erase';\n /** Blend mode (default the brush's). */\n blend?: BlendMode;\n /** Layer offset: layer pixel = document pixel − offset (default 0, 0). */\n offset?: { x: number; y: number };\n lockTransparency?: boolean;\n /** Where paint may land (canvas-sized coverage 0..255), or none. */\n selection?: { width: number; height: number; data: ArrayLike<number> } | null;\n}\n\n/**\n * Paints a brush stroke along `samples` onto an RGBA layer surface and returns the new surface\n * (the input is not changed; untouched tiles are shared) with the bounds it touched.\n */\nexport function paintPathStroke(surface: TiledSurface, samples: readonly PathSample[], options: PaintPathOptions): { surface: TiledSurface; bounds: RasterRect | null; dabs: number } {\n if (surface.channels !== 4) throw new Error('paintPathStroke paints RGBA surfaces (4 channels).');\n const brush = normalizeBrush(options.brush);\n const { coverage, bounds, dabs } = pathStrokeCoverage(samples, options);\n if (!bounds) return { surface, bounds: null, dabs };\n const offset = options.offset ?? { x: 0, y: 0 };\n const apply = {\n mode: options.mode ?? 'paint',\n color: options.color,\n background: [0, 0, 0, 0] as const,\n opacity: brush.opacity,\n blend: options.blend ?? brush.blend,\n lockTransparency: options.lockTransparency ?? false,\n selection: options.selection ?? null,\n offset,\n };\n const tiles = new Map(surface.tiles);\n for (const { tx, ty } of tilesInRect({ x: bounds.x - offset.x, y: bounds.y - offset.y, width: bounds.width, height: bounds.height })) {\n const key = tileKey(tx, ty);\n const src = surface.tiles.get(key)?.data;\n const base = src && src.length === TILE * TILE * 4 ? src : new Uint8ClampedArray(TILE * TILE * 4);\n const t = finishTile(applyStrokeToTile(coverage, base, 4, tx, ty, apply), 4, surface.defaultValue);\n if (t) tiles.set(key, t);\n else tiles.delete(key);\n }\n return { surface: new TiledSurface(4, tiles, surface.defaultValue), bounds, dabs };\n}\n","/**\n * A brushstroke rendered on its own: a path (polyline samples with pressure, tilt and time, or\n * points plus pressure / tilt profiles along the length) and a dry brush in, an RGBA image and its\n * bounds out. The canvas renders brushed freehand strokes with it, and the vector editor\n * brushstrokes on vector paths; both keep the path as the data and the pixels as a cache.\n *\n * - Built on {@link paintPathStroke}: the same dabs, coverage and build-up as everywhere else.\n * - Rendered at `scale` image pixels per path unit (both the samples and the brush size scale), so\n * a stroke looks the same at any zoom with more or fewer pixels. Snap zooms to\n * {@link brushstrokeScaleBucket} so panning and small zoom changes reuse the same image.\n * - Dry media only. A wet brush needs an area to flow in (the paint editor's wet layer); mixer,\n * blender and smudge brushes need paint under them. Those are refused with an error.\n * - Deterministic and DOM-free: the image is plain RGBA bytes. Turn it into a PNG data URL with\n * {@link brushstrokeDataUrl}, or into a canvas through an injected factory\n * ({@link brushstrokeToCanvas}).\n */\n\nimport { normalizeBrush, type BrushSettings } from './brush';\nimport { paintPathStroke, type PathSample, type PathStrokeOptions } from './path-stroke';\nimport { emptySurface, readRegion, type RasterRect } from '../raster/surface';\nimport { pngDataUrl } from '../raster/png';\n\n/** One sample of a brushstroke: a {@link PathSample} plus an optional time stamp (milliseconds). */\nexport interface BrushstrokeSample extends PathSample {\n t?: number;\n}\n\n/**\n * A path plus pen profiles along it. Each profile is a list of values spread evenly over the\n * path's length (first at the start, last at the end) and interpolated in between; one value is\n * constant. Missing profiles leave the samples upright at full pressure.\n */\nexport interface BrushstrokeProfilePath {\n points: ReadonlyArray<{ x: number; y: number }>;\n /** Pressure 0..1 along the length. */\n pressure?: readonly number[];\n /** Pointer Events tilt in degrees (−90..90) along the length. */\n tiltX?: readonly number[];\n tiltY?: readonly number[];\n}\n\nexport type BrushstrokeInput = readonly BrushstrokeSample[] | BrushstrokeProfilePath;\n\n/**\n * A brush for a brushstroke: plain tip settings, or a paint-editor brush (`PaintBrush` matches this\n * shape structurally). Only dab-engine brushes render; `rotation: 'stroke'` turns the tip with the\n * path, and `dynamics` give the size at zero pressure and how tilt shapes the tip. Grain, jitter and\n * velocity dynamics are not applied (there is no paper, and the result must be deterministic).\n */\nexport type BrushstrokeBrush =\n | Partial<BrushSettings>\n | {\n id?: string;\n name?: string;\n engine?: string;\n tip: Partial<BrushSettings>;\n rotation?: string;\n dynamics?: { minSize?: number; tiltSize?: number; tiltRoundness?: number };\n };\n\n/**\n * What the stroke is painted with. A solid RGBA colour today; when the paint editor's gradient\n * paint (`PaintSource`) lands it is accepted here too, so callers already pass `paint` rather than a\n * bare colour.\n */\nexport type BrushstrokePaint = { color: readonly [number, number, number, number] };\n\nexport interface BrushstrokeOptions {\n /** Image pixels per path unit (default 1). Use {@link brushstrokeScaleBucket} for a zoom. */\n scale?: number;\n /** RGBA 0..255 (default opaque black). Shorthand for `paint: { color }`. */\n color?: readonly [number, number, number, number];\n /** What to paint with (wins over `color`). */\n paint?: BrushstrokePaint;\n /** Join the last sample back to the first. */\n closed?: boolean;\n /**\n * The largest image to render, in pixels (default 16 777 216, 4096²). A stroke that would be\n * bigger at `scale` renders at a lower scale instead; the result's `scale` says which.\n */\n maxPixels?: number;\n}\n\n/** A rendered brushstroke. */\nexport interface BrushstrokeImage {\n /** Straight-alpha RGBA bytes, `width × height × 4` (the same layout as `ImageData`). */\n image: { width: number; height: number; data: Uint8ClampedArray };\n /** Where the image goes, in path units: draw `image` stretched over this rectangle. Null for an empty stroke. */\n bounds: { x: number; y: number; width: number; height: number } | null;\n /** Image pixels per path unit it was rendered at. */\n scale: number;\n /** How many dabs the stroke laid down. */\n dabs: number;\n}\n\nconst DEFAULT_MAX_PIXELS = 4096 * 4096;\nconst MIN_SCALE = 1 / 16;\nconst MAX_SCALE = 16;\n\nconst isProfilePath = (input: BrushstrokeInput): input is BrushstrokeProfilePath => !Array.isArray(input) && typeof input === 'object' && input !== null && 'points' in input;\n\n/** A profile's value at `f` (0..1 along the length), or undefined without one. */\nfunction profileAt(profile: readonly number[] | undefined, f: number): number | undefined {\n if (!profile || !profile.length) return undefined;\n if (profile.length === 1) return profile[0];\n const u = Math.min(1, Math.max(0, f)) * (profile.length - 1);\n const i = Math.min(profile.length - 2, Math.floor(u));\n return profile[i] + (profile[i + 1] - profile[i]) * (u - i);\n}\n\n/** The samples of a brushstroke input: profile paths are sampled at their points by arc length. */\nexport function brushstrokeSamples(input: BrushstrokeInput): BrushstrokeSample[] {\n if (!isProfilePath(input)) return input.map((s) => ({ ...s }));\n const pts = input.points;\n const lengths = [0];\n for (let i = 1; i < pts.length; i++) lengths.push(lengths[i - 1] + Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y));\n const total = lengths.at(-1) ?? 0;\n return pts.map((p, i) => {\n const f = total > 0 ? lengths[i] / total : pts.length > 1 ? i / (pts.length - 1) : 0;\n const s: BrushstrokeSample = { x: p.x, y: p.y };\n const pressure = profileAt(input.pressure, f);\n const tiltX = profileAt(input.tiltX, f);\n const tiltY = profileAt(input.tiltY, f);\n if (pressure !== undefined) s.pressure = pressure;\n if (tiltX !== undefined) s.tiltX = tiltX;\n if (tiltY !== undefined) s.tiltY = tiltY;\n return s;\n });\n}\n\n/** Engines that cannot render a brushstroke on its own, and why. */\nconst REFUSED_ENGINES: Record<string, string> = {\n wet: 'is a wet brush, and wet paint needs an area to flow in (paint it on a wet layer in the paint editor)',\n mixer: 'is a Mixer Brush, which picks up paint from under the tip and has none on a standalone stroke',\n blender: 'is a blender, which only moves paint that is already there',\n smudge: 'is a smudge brush, which only moves paint that is already there',\n eraser: 'is an eraser, which has nothing to remove on a standalone stroke',\n};\n\n/**\n * The tip settings and path-stroke options of a brushstroke brush. Throws for brushes that are not\n * dry dab brushes (wet, mixer, blender, smudge, eraser, impasto and the procedural engines).\n */\nexport function brushstrokeStrokeOptions(brush: BrushstrokeBrush): PathStrokeOptions {\n if (!brush || typeof brush !== 'object') throw new Error('A brushstroke needs a brush: tip settings ({ size, hardness, … }) or a paint brush preset.');\n if (!('tip' in brush) || !brush.tip) return { brush: normalizeBrush(brush as Partial<BrushSettings>) };\n const engine = brush.engine ?? 'dab';\n if (engine !== 'dab') {\n const label = brush.name ?? brush.id ?? 'This brush';\n const why = REFUSED_ENGINES[engine] ?? `uses the '${engine}' engine, which renderBrushstroke does not draw`;\n throw new Error(`Brushstrokes render dry media only: '${label}' ${why}. Pick a dry brush (a dab-engine preset such as a pencil, pastel, charcoal, marker, ink or airbrush) or plain tip settings.`);\n }\n const d = brush.dynamics ?? {};\n const tilt = { size: d.tiltSize ?? 0, roundness: d.tiltRoundness ?? 0 };\n return {\n brush: normalizeBrush(brush.tip),\n followPath: brush.rotation === 'stroke',\n minSize: d.minSize ?? 0,\n ...(tilt.size || tilt.roundness ? { tilt } : {}),\n };\n}\n\n/** Whether a brush renders as a brushstroke (a dry dab brush, or plain tip settings). */\nexport function isBrushstrokeBrush(brush: BrushstrokeBrush): boolean {\n return !('tip' in brush) || !brush.tip || (brush.engine ?? 'dab') === 'dab';\n}\n\n/**\n * The scale to render at for a zoom (and device pixel ratio): the smallest power of √2 at or above\n * it, clamped to 1/16..16. Every zoom in a bucket shares one cached image and never draws it\n * enlarged by more than √2.\n */\nexport function brushstrokeScaleBucket(zoom: number, devicePixelRatio = 1): number {\n const s = zoom * devicePixelRatio;\n if (!Number.isFinite(s) || s <= 0) return 1;\n const b = Math.pow(Math.SQRT2, Math.ceil(Math.log2(Math.min(MAX_SCALE, Math.max(MIN_SCALE, s))) * 2 - 1e-9));\n return Math.min(MAX_SCALE, Math.max(MIN_SCALE, Math.round(b * 1e9) / 1e9));\n}\n\nconst finite = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);\n\n/**\n * Renders a dry brushstroke along `input` with `brush` to an RGBA image. The same input, brush and\n * options always give the same bytes. Throws for a wet (or other non-dry) brush and for samples\n * that are not finite.\n */\nexport function renderBrushstroke(input: BrushstrokeInput, brush: BrushstrokeBrush, options: BrushstrokeOptions = {}): BrushstrokeImage {\n const strokeOptions = brushstrokeStrokeOptions(brush);\n const samples = brushstrokeSamples(input);\n for (const s of samples) if (!finite(s.x) || !finite(s.y)) throw new Error('Brushstroke samples need finite x and y.');\n const color = options.paint?.color ?? options.color ?? [0, 0, 0, 255];\n let scale = finite(options.scale) && options.scale > 0 ? Math.min(MAX_SCALE, Math.max(MIN_SCALE, options.scale)) : 1;\n if (!samples.length) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs: 0 };\n\n // Keep the image under maxPixels: estimate it from the samples and the widest the tip can get.\n const tip = strokeOptions.brush as BrushSettings;\n const reach = tip.size * (1 + 1.5 * (strokeOptions.tilt?.size ?? 0)) + 4;\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n for (const s of samples) {\n x0 = Math.min(x0, s.x); y0 = Math.min(y0, s.y); x1 = Math.max(x1, s.x); y1 = Math.max(y1, s.y);\n }\n const maxPixels = finite(options.maxPixels) && options.maxPixels > 0 ? options.maxPixels : DEFAULT_MAX_PIXELS;\n const area = (x1 - x0 + reach) * (y1 - y0 + reach);\n if (area * scale * scale > maxPixels) scale = Math.max(MIN_SCALE, Math.sqrt(maxPixels / area));\n\n const scaled = samples.map((s) => ({ ...s, x: s.x * scale, y: s.y * scale }));\n const { surface, bounds, dabs } = paintPathStroke(emptySurface(4), scaled, {\n ...strokeOptions,\n brush: { ...tip, size: tip.size * scale },\n closed: options.closed ?? strokeOptions.closed,\n color,\n });\n if (!bounds) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs };\n const rect: RasterRect = bounds;\n const data = readRegion(surface, rect);\n return {\n image: { width: rect.width, height: rect.height, data },\n bounds: { x: rect.x / scale, y: rect.y / scale, width: rect.width / scale, height: rect.height / scale },\n scale,\n dabs,\n };\n}\n\n/** A rendered brushstroke as a PNG data URL (for an SVG `<image>`, an `<img>` or an export). Empty strokes give ''. */\nexport function brushstrokeDataUrl(stroke: BrushstrokeImage): string {\n const { width, height, data } = stroke.image;\n return width && height ? pngDataUrl(width, height, data) : '';\n}\n\n/** The 2D-canvas surface {@link brushstrokeToCanvas} draws on (an `HTMLCanvasElement` or `OffscreenCanvas`). */\nexport interface BrushstrokeCanvasLike {\n getContext(type: '2d'): { createImageData(w: number, h: number): { data: Uint8ClampedArray }; putImageData(img: never, x: number, y: number): void } | null;\n}\n\n/**\n * A rendered brushstroke on a canvas made by `createCanvas(width, height)` (injected, so this\n * module stays DOM-free). Returns null for an empty stroke or a canvas without a 2D context.\n */\nexport function brushstrokeToCanvas<C extends BrushstrokeCanvasLike>(stroke: BrushstrokeImage, createCanvas: (width: number, height: number) => C): C | null {\n const { width, height, data } = stroke.image;\n if (!width || !height) return null;\n const canvas = createCanvas(width, height);\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n const img = ctx.createImageData(width, height);\n img.data.set(data);\n ctx.putImageData(img as never, 0, 0);\n return canvas;\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\n/** FNV-1a over a string, as 8 hex digits. */\nfunction hashString(s: string, h = 0x811c9dc5): number {\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h;\n}\n\n/**\n * A key for a brushstroke: the path (samples or profiles), the brush, the colour and the scale.\n * Two calls with equal arguments give equal keys; any change to them changes the key.\n */\nexport function brushstrokeKey(input: BrushstrokeInput, brush: BrushstrokeBrush, options: BrushstrokeOptions = {}): string {\n const path = JSON.stringify(input);\n const { brush: tip, ...rest } = brushstrokeStrokeOptions(brush);\n const rest2 = JSON.stringify([tip, rest, options.paint?.color ?? options.color ?? null, options.closed ?? false, options.maxPixels ?? null]);\n const scale = finite(options.scale) ? options.scale : 1;\n return `${hashString(path).toString(16)}:${path.length}:${hashString(rest2).toString(16)}:${scale}`;\n}\n\n/**\n * A small least-recently-used cache of rendered brushstrokes keyed by {@link brushstrokeKey}: path,\n * brush, colour and scale. Pass a bucketed scale ({@link brushstrokeScaleBucket}) so zooming inside\n * a bucket hits the cache, and a changed path, brush or bucket re-renders.\n */\nexport class BrushstrokeCache {\n private readonly entries = new Map<string, BrushstrokeImage>();\n /** Renders that missed the cache (for tests and stats). */\n misses = 0;\n\n constructor(readonly capacity = 64) {}\n\n get size(): number {\n return this.entries.size;\n }\n\n /** The cached stroke for these arguments, rendering (and caching) it on a miss. */\n render(input: BrushstrokeInput, brush: BrushstrokeBrush, options: BrushstrokeOptions = {}): BrushstrokeImage {\n const key = brushstrokeKey(input, brush, options);\n const hit = this.entries.get(key);\n if (hit) {\n this.entries.delete(key);\n this.entries.set(key, hit);\n return hit;\n }\n this.misses++;\n const out = renderBrushstroke(input, brush, options);\n this.entries.set(key, out);\n while (this.entries.size > Math.max(1, this.capacity)) this.entries.delete(this.entries.keys().next().value as string);\n return out;\n }\n\n clear(): void {\n this.entries.clear();\n }\n}\n"],"mappings":"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","names":["w","t"]}
|
|
@@ -558,6 +558,143 @@ declare function isLayerLocked(layer: {
|
|
|
558
558
|
locks?: LayerLocks;
|
|
559
559
|
}, what: keyof LayerLocks): boolean;
|
|
560
560
|
|
|
561
|
+
/**
|
|
562
|
+
* CPU renderer for layer effects.
|
|
563
|
+
*
|
|
564
|
+
* Render order, bottom to top (Photoshop's layer-style stack read upwards): drop shadows, outer
|
|
565
|
+
* glows, then the layer itself (content at fill opacity) with pattern, gradient and colour
|
|
566
|
+
* overlays, satin, inner glows, inner shadows, the inside part of strokes and inner bevels on
|
|
567
|
+
* top. The outside part of strokes and outer bevels go beneath the layer, onto the backdrop.
|
|
568
|
+
* Exterior effects blend straight into the backdrop with their own modes; the layer blends with
|
|
569
|
+
* its mode and, in a mode other than Normal, is merged into the backdrop before the interior
|
|
570
|
+
* effects paint over it (Photoshop's "Blend Interior Effects as Group" off). Emboss and pillow
|
|
571
|
+
* emboss then shade the composited result. Layer opacity applies to the whole stack; fill
|
|
572
|
+
* opacity only to the layer's own pixels.
|
|
573
|
+
*
|
|
574
|
+
* Every map depends only on the layer's shape (its alpha after the mask), the effect settings
|
|
575
|
+
* and the global light, never on the backdrop. {@link buildEffectMaps} computes them once over the
|
|
576
|
+
* layer's whole region; {@link compositeLayerEffects} paints any part of that region (a tile) from
|
|
577
|
+
* them, pixel by pixel, so a tiled render equals a whole-image render.
|
|
578
|
+
*
|
|
579
|
+
* Distances: a stroke's outer edge uses the 5 × 5 chamfer below 20 px and an exact Euclidean
|
|
580
|
+
* transform from there (the chamfer's octagonal level sets facet wide strokes); inside strokes and
|
|
581
|
+
* spread / choke keep the chamfer; precise glows and chiselled bevels use the exact transform.
|
|
582
|
+
* Soft falloffs are two box blurs (a tent).
|
|
583
|
+
*/
|
|
584
|
+
|
|
585
|
+
interface EffectRect {
|
|
586
|
+
x: number;
|
|
587
|
+
y: number;
|
|
588
|
+
width: number;
|
|
589
|
+
height: number;
|
|
590
|
+
}
|
|
591
|
+
/** Outside stroke width from which the outer edge uses the exact Euclidean transform. */
|
|
592
|
+
declare const STROKE_CHAMFER_MAX = 20;
|
|
593
|
+
/** A contour as a 0..1 LUT (corner points split the spline), or `null` for linear. */
|
|
594
|
+
declare function contourLut(c: EffectContour | undefined): Float32Array | null;
|
|
595
|
+
/** Where a bevel paints: inside the shape, outside it, or both (emboss styles). */
|
|
596
|
+
type BevelPaint = 'inner' | 'outer' | 'both';
|
|
597
|
+
interface EffectContext {
|
|
598
|
+
light: GlobalLight;
|
|
599
|
+
/** A pattern tile by id, or `null` when missing (pattern effects then paint nothing). */
|
|
600
|
+
pattern(id: string): PatternTile | null;
|
|
601
|
+
}
|
|
602
|
+
/** Effect maps of one layer over its region: see {@link buildEffectMaps}. */
|
|
603
|
+
interface EffectMaps {
|
|
604
|
+
/** The region the maps cover (document space). */
|
|
605
|
+
rect: EffectRect;
|
|
606
|
+
/** The enabled effects, Scale Effects applied, in list order. */
|
|
607
|
+
items: LayerEffect[];
|
|
608
|
+
/** Pixel bounds of the shape (alpha above half a step), or `null` when the shape is empty. */
|
|
609
|
+
bounds: EffectRect | null;
|
|
610
|
+
shape: Float32Array;
|
|
611
|
+
/** Per item: the maps it paints through. */
|
|
612
|
+
per: Float32Array[][];
|
|
613
|
+
bevelPaint: (BevelPaint | null)[];
|
|
614
|
+
din: Float32Array | null;
|
|
615
|
+
dout: Float32Array | null;
|
|
616
|
+
doutExact: Float32Array | null;
|
|
617
|
+
/** Frames of gradient strokes by item index. */
|
|
618
|
+
frames: Map<number, EffectRect>;
|
|
619
|
+
/** Approximate size in bytes (cache budgeting). */
|
|
620
|
+
bytes: number;
|
|
621
|
+
}
|
|
622
|
+
/** The enabled effects of `effects` with Scale Effects applied (empty when the master switch is off). */
|
|
623
|
+
declare function activeEffectItems(effects: LayerEffects | undefined | null): LayerEffect[];
|
|
624
|
+
/**
|
|
625
|
+
* Whether every map pixel of `items` depends only on the shape within the effects' margin, so a
|
|
626
|
+
* tile is unchanged while the layer is unchanged within its margin. Not local: anything laid out
|
|
627
|
+
* over the layer's bounds (aligned gradient overlays, gradient strokes, linked patterns) and noise
|
|
628
|
+
* (it speckles any coverage above zero, which picks up the blur's float noise from far away).
|
|
629
|
+
*/
|
|
630
|
+
declare function effectsAreLocal(items: readonly LayerEffect[]): boolean;
|
|
631
|
+
/**
|
|
632
|
+
* Builds every map `items` paints through from the layer's alpha `shape` (0..1) over `rect`.
|
|
633
|
+
* `rect` should cover the layer's shape plus the effects' margin.
|
|
634
|
+
*/
|
|
635
|
+
declare function buildEffectMaps(items: LayerEffect[], shape: Float32Array, rect: EffectRect, ctx: EffectContext): EffectMaps;
|
|
636
|
+
/**
|
|
637
|
+
* `prev` brought up to date for a shape over `rect` that differs from the one it was built from only
|
|
638
|
+
* inside `changed` (document rects), or `null` when a full {@link buildEffectMaps} is the better
|
|
639
|
+
* deal (the change covers much of the region) or the effects are not local
|
|
640
|
+
* ({@link effectsAreLocal}). `items` must be the ones `prev` was built with, reaching at most
|
|
641
|
+
* `margin` px, and `shapeOver(r)` returns the new shape over `r`.
|
|
642
|
+
*
|
|
643
|
+
* Every map pixel within `margin` of a change, or outside `prev.rect`, is rebuilt from the shape
|
|
644
|
+
* within `margin` of it (a window of the change grown by twice the margin) and pasted in; the rest
|
|
645
|
+
* is kept. A local map pixel depends only on the shape within the margin and the blur sums exactly,
|
|
646
|
+
* so the result paints exactly what a full build over `rect` would. `prev`'s buffers are reused
|
|
647
|
+
* when `rect` is unchanged.
|
|
648
|
+
*/
|
|
649
|
+
declare function patchEffectMaps(prev: EffectMaps, items: LayerEffect[], rect: EffectRect, changed: readonly EffectRect[], margin: number, shapeOver: (r: EffectRect) => Float32Array, ctx: EffectContext): EffectMaps | null;
|
|
650
|
+
/** Gradient parameter `t` (0..1) at pixel centre `(x, y)` of a gradient laid out in `frame`. */
|
|
651
|
+
declare function gradientT(style: GradientStyle, angle: number, scale: number, reverse: boolean, offset: {
|
|
652
|
+
x: number;
|
|
653
|
+
y: number;
|
|
654
|
+
}, frame: EffectRect, x: number, y: number): number;
|
|
655
|
+
/**
|
|
656
|
+
* The colours an effect fill paints over `rect`, as straight float RGBA (0..1, `width × height`
|
|
657
|
+
* pixels, row by row): a solid colour, a gradient laid out in `frame` (when aligned with the layer)
|
|
658
|
+
* or `canvas`, or a pattern tiled from `anchor` (when linked). Shape layers paint with it, so their
|
|
659
|
+
* fills and strokes match the overlays of layer effects.
|
|
660
|
+
*/
|
|
661
|
+
declare function effectFillColors(fill: EffectFill, rect: EffectRect, frame: EffectRect, canvas: EffectRect, anchor: {
|
|
662
|
+
x: number;
|
|
663
|
+
y: number;
|
|
664
|
+
}, ctx: EffectContext): Float32Array;
|
|
665
|
+
interface EffectCompositeInput {
|
|
666
|
+
/**
|
|
667
|
+
* The accumulator (straight RGBA over `rect`); receives the layer and its effects. Bytes, or
|
|
668
|
+
* (high bit depth) floats 0..1 sRGB-encoded, written back unrounded.
|
|
669
|
+
*/
|
|
670
|
+
backdrop: Uint8ClampedArray | Float32Array;
|
|
671
|
+
/** The layer's pixels over `rect` (straight RGBA, mask applied to alpha, clipped layers applied); bytes or floats 0..1 as `backdrop`. */
|
|
672
|
+
content: Uint8ClampedArray | Float32Array;
|
|
673
|
+
rect: EffectRect;
|
|
674
|
+
maps: EffectMaps;
|
|
675
|
+
blend: BlendMode;
|
|
676
|
+
/** Layer opacity: the whole stack. */
|
|
677
|
+
opacity: number;
|
|
678
|
+
/** Fill opacity: the layer's own pixels only. */
|
|
679
|
+
fillOpacity: number;
|
|
680
|
+
/** The canvas (unaligned gradients span it). */
|
|
681
|
+
canvas: EffectRect;
|
|
682
|
+
ctx: EffectContext;
|
|
683
|
+
}
|
|
684
|
+
/** Composites a layer's content and its effects onto `backdrop` in place (see the module comment for the order). */
|
|
685
|
+
declare function compositeLayerEffects(input: EffectCompositeInput): void;
|
|
686
|
+
/**
|
|
687
|
+
* Renders one layer with its effects over a whole region in one pass (no tiles): the reference
|
|
688
|
+
* the per-tile compositor is checked against, and a convenience for one-off renders.
|
|
689
|
+
*/
|
|
690
|
+
declare function renderLayerEffects(effects: LayerEffects, layer: {
|
|
691
|
+
content: Uint8ClampedArray;
|
|
692
|
+
rect: EffectRect;
|
|
693
|
+
blend: BlendMode;
|
|
694
|
+
opacity: number;
|
|
695
|
+
fillOpacity?: number;
|
|
696
|
+
}, backdrop: Uint8ClampedArray, canvas: EffectRect, ctx: EffectContext): void;
|
|
697
|
+
|
|
561
698
|
/**
|
|
562
699
|
* The per-tile layer compositor engine. Composites a layer stack (pixel layers and groups with
|
|
563
700
|
* masks, clipping, blend modes, opacity, fill and pass-through) tile by tile in document space,
|
|
@@ -733,6 +870,12 @@ declare class RasterCompositor<L extends CompositorLayer = CompositorLayer, S ex
|
|
|
733
870
|
* over `r` in document space, or `null` when it reveals all of `r`.
|
|
734
871
|
*/
|
|
735
872
|
maskCoverage(layer: L, r: RasterRect, sources: S): Float32Array | null;
|
|
873
|
+
/**
|
|
874
|
+
* For the float compositor: everything `compositeLayerEffects` needs for `layer` over `r` but
|
|
875
|
+
* the pixels (the area its effects reach inside `r`, the effect maps built from its 8-bit
|
|
876
|
+
* shape, blend, opacities, canvas and context), or `null` when nothing reaches `r`.
|
|
877
|
+
*/
|
|
878
|
+
effectsInput(layer: L, r: RasterRect, sources: S): Omit<EffectCompositeInput, 'backdrop' | 'content'> | null;
|
|
736
879
|
/** Pixel layers, and the layer kinds the profile composites like them. */
|
|
737
880
|
private isPixel;
|
|
738
881
|
private isFilter;
|
|
@@ -925,138 +1068,4 @@ declare function deepBytes(surface: TiledSurface): number;
|
|
|
925
1068
|
*/
|
|
926
1069
|
declare function keepDeepSamples(before: TiledSurface, after: TiledSurface): TiledSurface;
|
|
927
1070
|
|
|
928
|
-
/**
|
|
929
|
-
* CPU renderer for layer effects.
|
|
930
|
-
*
|
|
931
|
-
* Render order, bottom to top (Photoshop's layer-style stack read upwards): drop shadows, outer
|
|
932
|
-
* glows, then the layer itself (content at fill opacity) with pattern, gradient and colour
|
|
933
|
-
* overlays, satin, inner glows, inner shadows, the inside part of strokes and inner bevels on
|
|
934
|
-
* top. The outside part of strokes and outer bevels go beneath the layer, onto the backdrop.
|
|
935
|
-
* Exterior effects blend straight into the backdrop with their own modes; the layer blends with
|
|
936
|
-
* its mode and, in a mode other than Normal, is merged into the backdrop before the interior
|
|
937
|
-
* effects paint over it (Photoshop's "Blend Interior Effects as Group" off). Emboss and pillow
|
|
938
|
-
* emboss then shade the composited result. Layer opacity applies to the whole stack; fill
|
|
939
|
-
* opacity only to the layer's own pixels.
|
|
940
|
-
*
|
|
941
|
-
* Every map depends only on the layer's shape (its alpha after the mask), the effect settings
|
|
942
|
-
* and the global light, never on the backdrop. {@link buildEffectMaps} computes them once over the
|
|
943
|
-
* layer's whole region; {@link compositeLayerEffects} paints any part of that region (a tile) from
|
|
944
|
-
* them, pixel by pixel, so a tiled render equals a whole-image render.
|
|
945
|
-
*
|
|
946
|
-
* Distances: a stroke's outer edge uses the 5 × 5 chamfer below 20 px and an exact Euclidean
|
|
947
|
-
* transform from there (the chamfer's octagonal level sets facet wide strokes); inside strokes and
|
|
948
|
-
* spread / choke keep the chamfer; precise glows and chiselled bevels use the exact transform.
|
|
949
|
-
* Soft falloffs are two box blurs (a tent).
|
|
950
|
-
*/
|
|
951
|
-
|
|
952
|
-
interface EffectRect {
|
|
953
|
-
x: number;
|
|
954
|
-
y: number;
|
|
955
|
-
width: number;
|
|
956
|
-
height: number;
|
|
957
|
-
}
|
|
958
|
-
/** Outside stroke width from which the outer edge uses the exact Euclidean transform. */
|
|
959
|
-
declare const STROKE_CHAMFER_MAX = 20;
|
|
960
|
-
/** A contour as a 0..1 LUT (corner points split the spline), or `null` for linear. */
|
|
961
|
-
declare function contourLut(c: EffectContour | undefined): Float32Array | null;
|
|
962
|
-
/** Where a bevel paints: inside the shape, outside it, or both (emboss styles). */
|
|
963
|
-
type BevelPaint = 'inner' | 'outer' | 'both';
|
|
964
|
-
interface EffectContext {
|
|
965
|
-
light: GlobalLight;
|
|
966
|
-
/** A pattern tile by id, or `null` when missing (pattern effects then paint nothing). */
|
|
967
|
-
pattern(id: string): PatternTile | null;
|
|
968
|
-
}
|
|
969
|
-
/** Effect maps of one layer over its region: see {@link buildEffectMaps}. */
|
|
970
|
-
interface EffectMaps {
|
|
971
|
-
/** The region the maps cover (document space). */
|
|
972
|
-
rect: EffectRect;
|
|
973
|
-
/** The enabled effects, Scale Effects applied, in list order. */
|
|
974
|
-
items: LayerEffect[];
|
|
975
|
-
/** Pixel bounds of the shape (alpha above half a step), or `null` when the shape is empty. */
|
|
976
|
-
bounds: EffectRect | null;
|
|
977
|
-
shape: Float32Array;
|
|
978
|
-
/** Per item: the maps it paints through. */
|
|
979
|
-
per: Float32Array[][];
|
|
980
|
-
bevelPaint: (BevelPaint | null)[];
|
|
981
|
-
din: Float32Array | null;
|
|
982
|
-
dout: Float32Array | null;
|
|
983
|
-
doutExact: Float32Array | null;
|
|
984
|
-
/** Frames of gradient strokes by item index. */
|
|
985
|
-
frames: Map<number, EffectRect>;
|
|
986
|
-
/** Approximate size in bytes (cache budgeting). */
|
|
987
|
-
bytes: number;
|
|
988
|
-
}
|
|
989
|
-
/** The enabled effects of `effects` with Scale Effects applied (empty when the master switch is off). */
|
|
990
|
-
declare function activeEffectItems(effects: LayerEffects | undefined | null): LayerEffect[];
|
|
991
|
-
/**
|
|
992
|
-
* Whether every map pixel of `items` depends only on the shape within the effects' margin, so a
|
|
993
|
-
* tile is unchanged while the layer is unchanged within its margin. Not local: anything laid out
|
|
994
|
-
* over the layer's bounds (aligned gradient overlays, gradient strokes, linked patterns) and noise
|
|
995
|
-
* (it speckles any coverage above zero, which picks up the blur's float noise from far away).
|
|
996
|
-
*/
|
|
997
|
-
declare function effectsAreLocal(items: readonly LayerEffect[]): boolean;
|
|
998
|
-
/**
|
|
999
|
-
* Builds every map `items` paints through from the layer's alpha `shape` (0..1) over `rect`.
|
|
1000
|
-
* `rect` should cover the layer's shape plus the effects' margin.
|
|
1001
|
-
*/
|
|
1002
|
-
declare function buildEffectMaps(items: LayerEffect[], shape: Float32Array, rect: EffectRect, ctx: EffectContext): EffectMaps;
|
|
1003
|
-
/**
|
|
1004
|
-
* `prev` brought up to date for a shape over `rect` that differs from the one it was built from only
|
|
1005
|
-
* inside `changed` (document rects), or `null` when a full {@link buildEffectMaps} is the better
|
|
1006
|
-
* deal (the change covers much of the region) or the effects are not local
|
|
1007
|
-
* ({@link effectsAreLocal}). `items` must be the ones `prev` was built with, reaching at most
|
|
1008
|
-
* `margin` px, and `shapeOver(r)` returns the new shape over `r`.
|
|
1009
|
-
*
|
|
1010
|
-
* Every map pixel within `margin` of a change, or outside `prev.rect`, is rebuilt from the shape
|
|
1011
|
-
* within `margin` of it (a window of the change grown by twice the margin) and pasted in; the rest
|
|
1012
|
-
* is kept. A local map pixel depends only on the shape within the margin and the blur sums exactly,
|
|
1013
|
-
* so the result paints exactly what a full build over `rect` would. `prev`'s buffers are reused
|
|
1014
|
-
* when `rect` is unchanged.
|
|
1015
|
-
*/
|
|
1016
|
-
declare function patchEffectMaps(prev: EffectMaps, items: LayerEffect[], rect: EffectRect, changed: readonly EffectRect[], margin: number, shapeOver: (r: EffectRect) => Float32Array, ctx: EffectContext): EffectMaps | null;
|
|
1017
|
-
/** Gradient parameter `t` (0..1) at pixel centre `(x, y)` of a gradient laid out in `frame`. */
|
|
1018
|
-
declare function gradientT(style: GradientStyle, angle: number, scale: number, reverse: boolean, offset: {
|
|
1019
|
-
x: number;
|
|
1020
|
-
y: number;
|
|
1021
|
-
}, frame: EffectRect, x: number, y: number): number;
|
|
1022
|
-
/**
|
|
1023
|
-
* The colours an effect fill paints over `rect`, as straight float RGBA (0..1, `width × height`
|
|
1024
|
-
* pixels, row by row): a solid colour, a gradient laid out in `frame` (when aligned with the layer)
|
|
1025
|
-
* or `canvas`, or a pattern tiled from `anchor` (when linked). Shape layers paint with it, so their
|
|
1026
|
-
* fills and strokes match the overlays of layer effects.
|
|
1027
|
-
*/
|
|
1028
|
-
declare function effectFillColors(fill: EffectFill, rect: EffectRect, frame: EffectRect, canvas: EffectRect, anchor: {
|
|
1029
|
-
x: number;
|
|
1030
|
-
y: number;
|
|
1031
|
-
}, ctx: EffectContext): Float32Array;
|
|
1032
|
-
interface EffectCompositeInput {
|
|
1033
|
-
/** The accumulator (straight RGBA over `rect`); receives the layer and its effects. */
|
|
1034
|
-
backdrop: Uint8ClampedArray;
|
|
1035
|
-
/** The layer's pixels over `rect` (straight RGBA, mask applied to alpha, clipped layers applied). */
|
|
1036
|
-
content: Uint8ClampedArray;
|
|
1037
|
-
rect: EffectRect;
|
|
1038
|
-
maps: EffectMaps;
|
|
1039
|
-
blend: BlendMode;
|
|
1040
|
-
/** Layer opacity: the whole stack. */
|
|
1041
|
-
opacity: number;
|
|
1042
|
-
/** Fill opacity: the layer's own pixels only. */
|
|
1043
|
-
fillOpacity: number;
|
|
1044
|
-
/** The canvas (unaligned gradients span it). */
|
|
1045
|
-
canvas: EffectRect;
|
|
1046
|
-
ctx: EffectContext;
|
|
1047
|
-
}
|
|
1048
|
-
/** Composites a layer's content and its effects onto `backdrop` in place (see the module comment for the order). */
|
|
1049
|
-
declare function compositeLayerEffects(input: EffectCompositeInput): void;
|
|
1050
|
-
/**
|
|
1051
|
-
* Renders one layer with its effects over a whole region in one pass (no tiles): the reference
|
|
1052
|
-
* the per-tile compositor is checked against, and a convenience for one-off renders.
|
|
1053
|
-
*/
|
|
1054
|
-
declare function renderLayerEffects(effects: LayerEffects, layer: {
|
|
1055
|
-
content: Uint8ClampedArray;
|
|
1056
|
-
rect: EffectRect;
|
|
1057
|
-
blend: BlendMode;
|
|
1058
|
-
opacity: number;
|
|
1059
|
-
fillOpacity?: number;
|
|
1060
|
-
}, backdrop: Uint8ClampedArray, canvas: EffectRect, ctx: EffectContext): void;
|
|
1061
|
-
|
|
1062
1071
|
export { type PixelFilterSpec as $, type GradientOverlayEffect as A, BIT_DEPTHS as B, type CompositorLayer as C, DEFAULT_GLOBAL_LIGHT as D, type EffectCompositeInput as E, type GradientStyle as F, type GlobalLight as G, type GroupBlendMode as H, type GroupIn as I, INTERPOLATIONS as J, type InnerGlowEffect as K, type InnerShadowEffect as L, type Interpolation as M, LAYER_EFFECT_KINDS as N, type LayerEffect as O, LayerEffectError as P, type LayerEffectKind as Q, type RasterSources as R, type LayerEffects as S, type LayerLocation as T, type LayerLocks as U, MAX_EFFECT_REACH as V, MAX_TRANSFORM_PIXELS as W, type OuterGlowEffect as X, type PatternOverlayEffect as Y, PatternPlacement as Z, PatternTile as _, RasterCompositor as a, tileSamples as a$, type RasterGroupLayer as a0, type RasterLayer as a1, type RasterLayerBase as a2, type RasterMask as a3, type RasterNode as a4, type RasterPixelLayer as a5, type ResampleOptions as a6, STROKE_CHAMFER_MAX as a7, type SatinEffect as a8, type StrokeEffect as a9, gradientT as aA, hasActiveEffects as aB, hashNoise as aC, insertLayer as aD, isBitDepth as aE, isInterpolation as aF, isLayerEffectKind as aG, isLayerLocked as aH, keepDeepSamples as aI, keysWeight as aJ, layerEffectFields as aK, layerEffectLabel as aL, mapLayer as aM, mergeLayerEffect as aN, newEffectId as aO, normalizeLayerEffects as aP, parseEffectColor as aQ, patchEffectMaps as aR, proxyOf as aS, readDeepRegion as aT, reduceSurface as aU, renderLayerEffects as aV, scaleLayerEffect as aW, srgbDecode as aX, srgbEncode as aY, surfaceDepth as aZ, surfaceFromDeepPixels as a_, type StrokePosition as aa, TransformTooLargeError as ab, activeEffectItems as ac, blendTile as ad, buildEffectMaps as ae, builtinPattern as af, builtinPatternIds as ag, changedTiles as ah, compositeLayerEffects as ai, compositeSurfaceOver as aj, contourLut as ak, convertSurfaceDepth as al, deepBytes as am, deepTile as an, defaultEffectGradient as ao, defaultLayerEffect as ap, depthOf as aq, effectColorHex as ar, effectFillColors as as, effectReach as at, effectsAreLocal as au, effectsFingerprint as av, effectsMargin as aw, effectsScale as ax, findLayer as ay, flattenLayers as az, type CompositorProfile as b, tileSamples16 as b0, tileSamples32 as b1, to16From32 as b2, to16From8 as b3, to32From16 as b4, to32From8 as b5, to8From16 as b6, to8From32 as b7, transformSurface as b8, transformedBounds as b9, validateEffectGradient as ba, validateLayerEffect as bb, validateLayerEffects as bc, warpSurface as bd, writeDeepRegion as be, type BevelContour as c, type BevelEmbossEffect as d, type BevelStyle as e, type BevelTechnique as f, type BevelTexture as g, type BitDepth as h, type ColorOverlayEffect as i, type CompositeCallOptions as j, type CompositeStats as k, type CompositorOptions as l, type ContourPoint as m, type DeepSamples as n, type DropShadowEffect as o, type EffectContext as p, type EffectContour as q, type EffectFill as r, type EffectGradient as s, type EffectMaps as t, type EffectPattern as u, type EffectPatternRef as v, type EffectRect as w, type GlowTechnique as x, type GradientColorStop as y, type GradientOpacityStop as z };
|