@pixerate/editor 0.88.0 → 0.89.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 +8 -2
- package/dist/agent/index.cjs +28 -28
- package/dist/agent/index.js +3 -3
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.js +16 -16
- package/dist/{chunk-TYOAJDFD.cjs → chunk-66IMF6OD.cjs} +3 -3
- package/dist/{chunk-TYOAJDFD.cjs.map → chunk-66IMF6OD.cjs.map} +1 -1
- package/dist/chunk-D4ZCU72J.js +604 -0
- package/dist/chunk-D4ZCU72J.js.map +1 -0
- package/dist/{chunk-IMSX4IVG.js → chunk-FWEO3HSL.js} +4 -4
- package/dist/chunk-H543GD6T.cjs +1696 -0
- package/dist/chunk-H543GD6T.cjs.map +1 -0
- package/dist/{chunk-ROXFP2LQ.cjs → chunk-HNDSPZ5T.cjs} +4 -4
- package/dist/{chunk-ROXFP2LQ.cjs.map → chunk-HNDSPZ5T.cjs.map} +1 -1
- package/dist/{chunk-MXF4DP2C.cjs → chunk-J3B45HTA.cjs} +74 -74
- package/dist/{chunk-MXF4DP2C.cjs.map → chunk-J3B45HTA.cjs.map} +1 -1
- package/dist/{chunk-GNO5PEMH.js → chunk-OK7T2LHK.js} +2 -2
- package/dist/chunk-P5TPUNBZ.js +1696 -0
- package/dist/chunk-P5TPUNBZ.js.map +1 -0
- package/dist/chunk-PDCITYBT.cjs +416 -0
- package/dist/chunk-PDCITYBT.cjs.map +1 -0
- package/dist/{chunk-RKJDBXTY.cjs → chunk-T4XWVPJK.cjs} +3 -3
- package/dist/{chunk-RKJDBXTY.cjs.map → chunk-T4XWVPJK.cjs.map} +1 -1
- package/dist/{chunk-ZQNDSUZO.js → chunk-UKI64KUD.js} +6 -6
- package/dist/{chunk-SVW2UNUC.js → chunk-WON34HKM.js} +2 -2
- package/dist/chunk-YLB2WVNA.js +416 -0
- package/dist/chunk-YLB2WVNA.js.map +1 -0
- package/dist/chunk-ZOOEX3WT.cjs +604 -0
- package/dist/chunk-ZOOEX3WT.cjs.map +1 -0
- package/dist/extensions/index.cjs +6 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +5 -5
- package/dist/image-editor/index.cjs +4387 -690
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +1677 -590
- package/dist/image-editor/index.d.ts +1677 -590
- package/dist/image-editor/index.js +4273 -576
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index.cjs +27 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/paint/index.cjs +38 -0
- package/dist/paint/index.cjs.map +1 -0
- package/dist/paint/index.d.cts +167 -0
- package/dist/paint/index.d.ts +167 -0
- package/dist/paint/index.js +38 -0
- package/dist/paint/index.js.map +1 -0
- package/dist/raster/index.cjs +121 -0
- package/dist/raster/index.cjs.map +1 -0
- package/dist/raster/index.d.cts +365 -0
- package/dist/raster/index.d.ts +365 -0
- package/dist/raster/index.js +121 -0
- package/dist/raster/index.js.map +1 -0
- package/dist/serializers/index.cjs +4 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.js +3 -3
- package/dist/surface-fJhkmNQn.d.cts +181 -0
- package/dist/surface-fJhkmNQn.d.ts +181 -0
- package/package.json +13 -2
- package/skills/pixerate-image-editor/SKILL.md +111 -11
- /package/dist/{chunk-IMSX4IVG.js.map → chunk-FWEO3HSL.js.map} +0 -0
- /package/dist/{chunk-GNO5PEMH.js.map → chunk-OK7T2LHK.js.map} +0 -0
- /package/dist/{chunk-ZQNDSUZO.js.map → chunk-UKI64KUD.js.map} +0 -0
- /package/dist/{chunk-SVW2UNUC.js.map → chunk-WON34HKM.js.map} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BLOCK_CHANGE_ATTRIBUTE,
|
|
3
|
-
MOVE_ID_ATTRIBUTE,
|
|
4
|
-
resolveTrackedChanges
|
|
5
|
-
} from "./chunk-SWE3NPOU.js";
|
|
6
1
|
import {
|
|
7
2
|
MENTION_REGEX,
|
|
8
3
|
NODE_ROLE_MARK,
|
|
9
4
|
formatMentionHandle,
|
|
10
5
|
readRoleSuffix
|
|
11
6
|
} from "./chunk-O5RYH2PG.js";
|
|
7
|
+
import {
|
|
8
|
+
BLOCK_CHANGE_ATTRIBUTE,
|
|
9
|
+
MOVE_ID_ATTRIBUTE,
|
|
10
|
+
resolveTrackedChanges
|
|
11
|
+
} from "./chunk-SWE3NPOU.js";
|
|
12
12
|
|
|
13
13
|
// src/serializers/plain-text.ts
|
|
14
14
|
import { DOMSerializer, Slice, Fragment, Node as ProsemirrorNode } from "@tiptap/pm/model";
|
|
@@ -844,4 +844,4 @@ export {
|
|
|
844
844
|
markdownToTipTapHtml,
|
|
845
845
|
getEditorMarkdown
|
|
846
846
|
};
|
|
847
|
-
//# sourceMappingURL=chunk-
|
|
847
|
+
//# sourceMappingURL=chunk-UKI64KUD.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
escapeHtml,
|
|
3
3
|
sanitizeHtml
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UKI64KUD.js";
|
|
5
5
|
|
|
6
6
|
// src/serializers/html.ts
|
|
7
7
|
var DROP_WITH_CONTENT_REGEX = /<(script|style|template|noscript|iframe|object|embed|head|title|svg|math|textarea|select|button)\b[^>]*>[\s\S]*?<\/\1\s*>/gi;
|
|
@@ -296,4 +296,4 @@ export {
|
|
|
296
296
|
getClipboardData,
|
|
297
297
|
readRichClipboard
|
|
298
298
|
};
|
|
299
|
-
//# sourceMappingURL=chunk-
|
|
299
|
+
//# sourceMappingURL=chunk-WON34HKM.js.map
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TILE,
|
|
3
|
+
blendPixel,
|
|
4
|
+
tileCoord,
|
|
5
|
+
tileKey
|
|
6
|
+
} from "./chunk-D4ZCU72J.js";
|
|
7
|
+
|
|
8
|
+
// src/paint/brush.ts
|
|
9
|
+
var DEFAULT_BRUSH = {
|
|
10
|
+
size: 20,
|
|
11
|
+
hardness: 1,
|
|
12
|
+
spacing: 0.1,
|
|
13
|
+
angle: 0,
|
|
14
|
+
roundness: 1,
|
|
15
|
+
opacity: 1,
|
|
16
|
+
flow: 1,
|
|
17
|
+
smoothing: 0,
|
|
18
|
+
pressureSize: true,
|
|
19
|
+
pressureOpacity: false,
|
|
20
|
+
blend: "normal"
|
|
21
|
+
};
|
|
22
|
+
var MAX_BRUSH_SIZE = 2500;
|
|
23
|
+
function normalizeBrush(raw = {}) {
|
|
24
|
+
const num = (v, lo, hi, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : d;
|
|
25
|
+
return {
|
|
26
|
+
size: num(raw.size, 1, MAX_BRUSH_SIZE, DEFAULT_BRUSH.size),
|
|
27
|
+
hardness: num(raw.hardness, 0, 1, DEFAULT_BRUSH.hardness),
|
|
28
|
+
spacing: num(raw.spacing, 0.01, 10, DEFAULT_BRUSH.spacing),
|
|
29
|
+
angle: num(raw.angle, -360, 360, DEFAULT_BRUSH.angle),
|
|
30
|
+
roundness: num(raw.roundness, 0.01, 1, DEFAULT_BRUSH.roundness),
|
|
31
|
+
opacity: num(raw.opacity, 0, 1, DEFAULT_BRUSH.opacity),
|
|
32
|
+
flow: num(raw.flow, 0, 1, DEFAULT_BRUSH.flow),
|
|
33
|
+
smoothing: num(raw.smoothing, 0, 0.95, DEFAULT_BRUSH.smoothing),
|
|
34
|
+
pressureSize: typeof raw.pressureSize === "boolean" ? raw.pressureSize : DEFAULT_BRUSH.pressureSize,
|
|
35
|
+
pressureOpacity: typeof raw.pressureOpacity === "boolean" ? raw.pressureOpacity : DEFAULT_BRUSH.pressureOpacity,
|
|
36
|
+
blend: raw.blend ?? "normal"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
var TIP_SHAPE = [
|
|
40
|
+
[0, 0.664],
|
|
41
|
+
[0.14, 0.58],
|
|
42
|
+
[0.28, 0.5],
|
|
43
|
+
[0.4, 0.44],
|
|
44
|
+
[0.52, 0.36],
|
|
45
|
+
[0.63, 0.29],
|
|
46
|
+
[0.7, 0.24],
|
|
47
|
+
[0.78, 0.19],
|
|
48
|
+
[0.84, 0.15],
|
|
49
|
+
[0.89, 0.11],
|
|
50
|
+
[1, 0]
|
|
51
|
+
];
|
|
52
|
+
var TIP_EDGE_PX = 1.2;
|
|
53
|
+
var SMALL_TIP_RADIUS = 3;
|
|
54
|
+
var SUBPIXEL = [-0.375, -0.125, 0.125, 0.375];
|
|
55
|
+
function tipShape(radius, hardness) {
|
|
56
|
+
const h = (Number.isNaN(hardness) ? 0 : Math.min(1, Math.max(0, hardness))) * 10;
|
|
57
|
+
const i = Math.min(9, Math.floor(h));
|
|
58
|
+
const t = h - i;
|
|
59
|
+
const a = TIP_SHAPE[i];
|
|
60
|
+
const b = TIP_SHAPE[i + 1];
|
|
61
|
+
const r0 = a[0] + (b[0] - a[0]) * t;
|
|
62
|
+
const s = a[1] + (b[1] - a[1]) * t;
|
|
63
|
+
const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
|
|
64
|
+
const edge = Math.max(0.25, Math.min(TIP_EDGE_PX, 0.3 * r));
|
|
65
|
+
return [Math.max(0, Math.min(r0 * r, r - edge)), Math.max(1e-3, s * r, edge)];
|
|
66
|
+
}
|
|
67
|
+
function softExp(u) {
|
|
68
|
+
let g = Math.min(1, Math.max(0, 1 - u / 64));
|
|
69
|
+
for (let k = 0; k < 6; k++) g *= g;
|
|
70
|
+
return g;
|
|
71
|
+
}
|
|
72
|
+
function tipFalloff(d, radius, hardness) {
|
|
73
|
+
const [r0, s] = tipShape(radius, hardness);
|
|
74
|
+
if (d <= r0) return 1;
|
|
75
|
+
const x = (d - r0) / s;
|
|
76
|
+
return softExp(x * x);
|
|
77
|
+
}
|
|
78
|
+
function tipReach(radius, hardness) {
|
|
79
|
+
const [r0, s] = tipShape(radius, hardness);
|
|
80
|
+
return r0 + 2.6 * s;
|
|
81
|
+
}
|
|
82
|
+
function dabCoverage(d, radius, hardness) {
|
|
83
|
+
if (Number.isNaN(d) || d >= tipReach(radius, hardness) + 0.5) return 0;
|
|
84
|
+
return tipFalloff(d, radius, hardness);
|
|
85
|
+
}
|
|
86
|
+
function smoothPoints(points, amount) {
|
|
87
|
+
const a = 1 - Math.min(0.95, Math.max(0, amount));
|
|
88
|
+
if (a >= 1 || points.length < 3) return points.slice();
|
|
89
|
+
const out = [];
|
|
90
|
+
let cur = null;
|
|
91
|
+
for (const p of points) {
|
|
92
|
+
const n = cur ? { ...p, x: cur.x + (p.x - cur.x) * a, y: cur.y + (p.y - cur.y) * a } : p;
|
|
93
|
+
out.push(n);
|
|
94
|
+
cur = n;
|
|
95
|
+
}
|
|
96
|
+
out[out.length - 1] = points[points.length - 1];
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
var pressureOf = (p) => typeof p.pressure === "number" && Number.isFinite(p.pressure) ? Math.min(1, Math.max(0, p.pressure)) : 1;
|
|
100
|
+
var DabWalker = class {
|
|
101
|
+
constructor(brush) {
|
|
102
|
+
this.brush = brush;
|
|
103
|
+
}
|
|
104
|
+
brush;
|
|
105
|
+
last = null;
|
|
106
|
+
nextAt = 0;
|
|
107
|
+
pendingFirst = null;
|
|
108
|
+
diameter(p) {
|
|
109
|
+
return this.brush.pressureSize ? this.brush.size * pressureOf(p) : this.brush.size;
|
|
110
|
+
}
|
|
111
|
+
stepLen(p) {
|
|
112
|
+
return Math.max(0.5, Math.max(1, this.diameter(p)) * Math.max(0.01, this.brush.spacing));
|
|
113
|
+
}
|
|
114
|
+
dab(p) {
|
|
115
|
+
const b = this.brush;
|
|
116
|
+
return {
|
|
117
|
+
x: p.x,
|
|
118
|
+
y: p.y,
|
|
119
|
+
radius: Math.max(0.5, this.diameter(p) / 2),
|
|
120
|
+
alpha: Math.min(1, Math.max(0, b.flow)),
|
|
121
|
+
opacity: b.pressureOpacity ? pressureOf(p) : 1,
|
|
122
|
+
angle: b.angle * Math.PI / 180,
|
|
123
|
+
roundness: Math.min(1, Math.max(0.01, b.roundness))
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
push(p, out) {
|
|
127
|
+
const a = this.last;
|
|
128
|
+
if (!a) {
|
|
129
|
+
this.last = p;
|
|
130
|
+
this.pendingFirst = p;
|
|
131
|
+
this.nextAt = this.stepLen(p);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (this.pendingFirst) {
|
|
135
|
+
out.push(this.dab(this.pendingFirst));
|
|
136
|
+
this.pendingFirst = null;
|
|
137
|
+
}
|
|
138
|
+
const dx = p.x - a.x;
|
|
139
|
+
const dy = p.y - a.y;
|
|
140
|
+
const len = Math.hypot(dx, dy);
|
|
141
|
+
if (len > 1e-9) {
|
|
142
|
+
while (this.nextAt <= len + 1e-9) {
|
|
143
|
+
const f = Math.min(1, this.nextAt / len);
|
|
144
|
+
const q = {
|
|
145
|
+
x: a.x + dx * f,
|
|
146
|
+
y: a.y + dy * f,
|
|
147
|
+
pressure: pressureOf(a) + (pressureOf(p) - pressureOf(a)) * f
|
|
148
|
+
};
|
|
149
|
+
out.push(this.dab(q));
|
|
150
|
+
this.nextAt += this.stepLen(q);
|
|
151
|
+
}
|
|
152
|
+
this.nextAt -= len;
|
|
153
|
+
}
|
|
154
|
+
this.last = p;
|
|
155
|
+
}
|
|
156
|
+
finish(out) {
|
|
157
|
+
if (this.pendingFirst) {
|
|
158
|
+
out.push(this.dab(this.pendingFirst));
|
|
159
|
+
this.pendingFirst = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
function walkDabs(points, brush) {
|
|
164
|
+
const w = new DabWalker(brush);
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const p of smoothPoints(points, brush.smoothing)) w.push(p, out);
|
|
167
|
+
w.finish(out);
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
function dabRect(d, hardness) {
|
|
171
|
+
const ro = Math.min(1, Math.max(0.5 / d.radius, d.roundness));
|
|
172
|
+
const rm = Math.max(0.5, d.radius * ro);
|
|
173
|
+
const r = Math.max(rm, tipReach(rm, hardness)) / ro + 1.5;
|
|
174
|
+
const x0 = Math.floor(d.x - r);
|
|
175
|
+
const y0 = Math.floor(d.y - r);
|
|
176
|
+
return { x: x0, y: y0, width: Math.ceil(d.x + r) - x0, height: Math.ceil(d.y + r) - y0 };
|
|
177
|
+
}
|
|
178
|
+
function rasterizeDab(d, hardness, rect, out) {
|
|
179
|
+
const w = rect.width;
|
|
180
|
+
const h = rect.height;
|
|
181
|
+
out.fill(0, 0, w * h);
|
|
182
|
+
const r = d.radius;
|
|
183
|
+
const ro = Math.min(1, Math.max(0.5 / r, d.roundness));
|
|
184
|
+
const rm = Math.max(0.5, r * ro);
|
|
185
|
+
const reach = Math.max(rm, tipReach(rm, hardness));
|
|
186
|
+
const sn = Math.sin(d.angle);
|
|
187
|
+
const cs = Math.cos(d.angle);
|
|
188
|
+
const toTip = (dx, dy) => {
|
|
189
|
+
const ux = dx;
|
|
190
|
+
const uy = -dy;
|
|
191
|
+
return [ux * cs + uy * sn, -ux * sn + uy * cs];
|
|
192
|
+
};
|
|
193
|
+
const small = rm < SMALL_TIP_RADIUS;
|
|
194
|
+
const reach2 = (reach + (small ? 0.75 : 0.5)) ** 2;
|
|
195
|
+
for (let yy = 0; yy < h; yy++) {
|
|
196
|
+
const dy = rect.y + yy + 0.5 - d.y;
|
|
197
|
+
for (let xx = 0; xx < w; xx++) {
|
|
198
|
+
const dx = rect.x + xx + 0.5 - d.x;
|
|
199
|
+
const [u, v] = toTip(dx, dy);
|
|
200
|
+
const d2 = (u * ro) ** 2 + v * v;
|
|
201
|
+
if (d2 >= reach2) continue;
|
|
202
|
+
let val;
|
|
203
|
+
if (small) {
|
|
204
|
+
let sum = 0;
|
|
205
|
+
for (const sy of SUBPIXEL) {
|
|
206
|
+
for (const sx of SUBPIXEL) {
|
|
207
|
+
const [su, sv] = toTip(dx + sx, dy + sy);
|
|
208
|
+
sum += tipFalloff(Math.sqrt((su * ro) ** 2 + sv * sv), rm, hardness);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
val = sum / 16;
|
|
212
|
+
} else {
|
|
213
|
+
val = dabCoverage(Math.sqrt(d2), rm, hardness);
|
|
214
|
+
}
|
|
215
|
+
if (val > 0) out[yy * w + xx] = val * d.alpha;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
var TILE_PIXELS = TILE * TILE;
|
|
220
|
+
var CoverageMap = class {
|
|
221
|
+
tiles = /* @__PURE__ */ new Map();
|
|
222
|
+
bounds = null;
|
|
223
|
+
dirty = /* @__PURE__ */ new Set();
|
|
224
|
+
get(x, y) {
|
|
225
|
+
const tx = Math.floor(x / TILE);
|
|
226
|
+
const ty = Math.floor(y / TILE);
|
|
227
|
+
const t = this.tiles.get(tileKey(tx, ty));
|
|
228
|
+
return t ? t[(y - ty * TILE) * TILE + (x - tx * TILE)] : 0;
|
|
229
|
+
}
|
|
230
|
+
/** Builds up `vals` (over `rect`) with flow towards `ceil`: `c ← c + v·(ceil − c)`. */
|
|
231
|
+
accumulate(rect, vals, ceil) {
|
|
232
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
233
|
+
const w = rect.width;
|
|
234
|
+
const tx0 = Math.floor(rect.x / TILE);
|
|
235
|
+
const ty0 = Math.floor(rect.y / TILE);
|
|
236
|
+
const tx1 = Math.floor((rect.x + rect.width - 1) / TILE);
|
|
237
|
+
const ty1 = Math.floor((rect.y + rect.height - 1) / TILE);
|
|
238
|
+
let touched = false;
|
|
239
|
+
for (let ty = ty0; ty <= ty1; ty++) {
|
|
240
|
+
for (let tx = tx0; tx <= tx1; tx++) {
|
|
241
|
+
const x0 = Math.max(rect.x, tx * TILE);
|
|
242
|
+
const y0 = Math.max(rect.y, ty * TILE);
|
|
243
|
+
const x1 = Math.min(rect.x + rect.width, tx * TILE + TILE);
|
|
244
|
+
const y1 = Math.min(rect.y + rect.height, ty * TILE + TILE);
|
|
245
|
+
let tile;
|
|
246
|
+
for (let y = y0; y < y1; y++) {
|
|
247
|
+
const row = (y - rect.y) * w;
|
|
248
|
+
const trow = (y - ty * TILE) * TILE;
|
|
249
|
+
for (let x = x0; x < x1; x++) {
|
|
250
|
+
const v = vals[row + (x - rect.x)];
|
|
251
|
+
if (v <= 0) continue;
|
|
252
|
+
if (!tile) {
|
|
253
|
+
const key = tileKey(tx, ty);
|
|
254
|
+
tile = this.tiles.get(key);
|
|
255
|
+
if (!tile) {
|
|
256
|
+
tile = new Float32Array(TILE_PIXELS);
|
|
257
|
+
this.tiles.set(key, tile);
|
|
258
|
+
}
|
|
259
|
+
this.dirty.add(key);
|
|
260
|
+
touched = true;
|
|
261
|
+
}
|
|
262
|
+
const ti = trow + (x - tx * TILE);
|
|
263
|
+
const c = tile[ti];
|
|
264
|
+
if (ceil > c) tile[ti] = c + v * (ceil - c);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (touched) this.bounds = unionRect(this.bounds, rect);
|
|
270
|
+
}
|
|
271
|
+
/** Tiles changed since the last call. */
|
|
272
|
+
takeDirty() {
|
|
273
|
+
const out = [...this.dirty];
|
|
274
|
+
this.dirty.clear();
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
function unionRect(a, b) {
|
|
279
|
+
if (!a) return { ...b };
|
|
280
|
+
const x = Math.min(a.x, b.x);
|
|
281
|
+
const y = Math.min(a.y, b.y);
|
|
282
|
+
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 };
|
|
283
|
+
}
|
|
284
|
+
var StrokeCoverage = class {
|
|
285
|
+
constructor(brush) {
|
|
286
|
+
this.brush = brush;
|
|
287
|
+
this.walker = new DabWalker(brush);
|
|
288
|
+
this.smoothing = brush.smoothing;
|
|
289
|
+
}
|
|
290
|
+
brush;
|
|
291
|
+
coverage = new CoverageMap();
|
|
292
|
+
walker;
|
|
293
|
+
smoothing;
|
|
294
|
+
smoothPos = null;
|
|
295
|
+
buf = new Float32Array(0);
|
|
296
|
+
dabCount = 0;
|
|
297
|
+
/** Feeds input points; returns the document rectangles the new dabs touched. */
|
|
298
|
+
push(points) {
|
|
299
|
+
const dabs = [];
|
|
300
|
+
for (const p of points) this.walker.push(this.smooth(p), dabs);
|
|
301
|
+
return this.paint(dabs);
|
|
302
|
+
}
|
|
303
|
+
/** Ends the stroke (a single click still paints one dab). */
|
|
304
|
+
finish(last) {
|
|
305
|
+
const dabs = [];
|
|
306
|
+
if (last && this.smoothPos && (this.smoothPos.x !== last.x || this.smoothPos.y !== last.y)) this.walker.push(last, dabs);
|
|
307
|
+
this.walker.finish(dabs);
|
|
308
|
+
return this.paint(dabs);
|
|
309
|
+
}
|
|
310
|
+
smooth(p) {
|
|
311
|
+
const a = 1 - Math.min(0.95, Math.max(0, this.smoothing));
|
|
312
|
+
const cur = this.smoothPos;
|
|
313
|
+
const n = cur && a < 1 ? { ...p, x: cur.x + (p.x - cur.x) * a, y: cur.y + (p.y - cur.y) * a } : p;
|
|
314
|
+
this.smoothPos = n;
|
|
315
|
+
return n;
|
|
316
|
+
}
|
|
317
|
+
paint(dabs) {
|
|
318
|
+
const rects = [];
|
|
319
|
+
for (const d of dabs) {
|
|
320
|
+
const rect = dabRect(d, this.brush.hardness);
|
|
321
|
+
const n = rect.width * rect.height;
|
|
322
|
+
if (this.buf.length < n) this.buf = new Float32Array(n);
|
|
323
|
+
const vals = this.buf.subarray(0, n);
|
|
324
|
+
rasterizeDab(d, this.brush.hardness, rect, vals);
|
|
325
|
+
this.coverage.accumulate(rect, vals, d.opacity);
|
|
326
|
+
rects.push(rect);
|
|
327
|
+
this.dabCount++;
|
|
328
|
+
}
|
|
329
|
+
return rects;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
function applyStrokeToTile(coverage, tileData, channels, tx, ty, s, options = {}) {
|
|
333
|
+
const out = options.into ? new Uint8ClampedArray(options.into) : new Uint8ClampedArray(tileData);
|
|
334
|
+
const ox = tx * TILE + s.offset.x;
|
|
335
|
+
const oy = ty * TILE + s.offset.y;
|
|
336
|
+
let x0 = 0;
|
|
337
|
+
let y0 = 0;
|
|
338
|
+
let x1 = TILE;
|
|
339
|
+
let y1 = TILE;
|
|
340
|
+
if (options.region) {
|
|
341
|
+
const r = options.region;
|
|
342
|
+
x0 = Math.max(0, Math.floor(r.x) - ox);
|
|
343
|
+
y0 = Math.max(0, Math.floor(r.y) - oy);
|
|
344
|
+
x1 = Math.min(TILE, Math.ceil(r.x + r.width) - ox);
|
|
345
|
+
y1 = Math.min(TILE, Math.ceil(r.y + r.height) - oy);
|
|
346
|
+
if (x1 <= x0 || y1 <= y0) return out;
|
|
347
|
+
if (options.into) {
|
|
348
|
+
for (let y = y0; y < y1; y++) out.set(tileData.subarray((y * TILE + x0) * channels, (y * TILE + x1) * channels), (y * TILE + x0) * channels);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const sel = s.selection;
|
|
352
|
+
const cb = [0, 0, 0, 0];
|
|
353
|
+
const cs = [s.color[0] / 255, s.color[1] / 255, s.color[2] / 255, s.color[3] / 255];
|
|
354
|
+
const bg = [s.background[0] / 255, s.background[1] / 255, s.background[2] / 255, s.background[3] / 255];
|
|
355
|
+
const aligned = s.offset.x % TILE === 0 && s.offset.y % TILE === 0;
|
|
356
|
+
const covTile = aligned ? coverage.tiles.get(tileKey(Math.floor(ox / TILE), Math.floor(oy / TILE))) : void 0;
|
|
357
|
+
if (aligned && !covTile) return out;
|
|
358
|
+
for (let y = y0; y < y1; y++) {
|
|
359
|
+
const dy = oy + y;
|
|
360
|
+
for (let x = x0; x < x1; x++) {
|
|
361
|
+
const dx = ox + x;
|
|
362
|
+
const c = covTile ? covTile[y * TILE + x] : coverage.get(dx, dy);
|
|
363
|
+
if (c <= 0) continue;
|
|
364
|
+
let k = c * s.opacity;
|
|
365
|
+
if (sel) {
|
|
366
|
+
if (dx < 0 || dy < 0 || dx >= sel.width || dy >= sel.height) continue;
|
|
367
|
+
k *= sel.data[dy * sel.width + dx] / 255;
|
|
368
|
+
}
|
|
369
|
+
if (k > 1) k = 1;
|
|
370
|
+
if (k <= 0) continue;
|
|
371
|
+
const p = y * TILE + x;
|
|
372
|
+
if (channels === 1) {
|
|
373
|
+
const target = s.mode === "erase" ? s.background[0] : s.color[0];
|
|
374
|
+
out[p] = Math.round(out[p] + (target - out[p]) * k);
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const i = p * 4;
|
|
378
|
+
if (s.mode === "erase" && !s.lockTransparency) {
|
|
379
|
+
out[i + 3] = Math.round(out[i + 3] * (1 - k));
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (s.lockTransparency && out[i + 3] === 0) continue;
|
|
383
|
+
const src = s.mode === "erase" ? bg : cs;
|
|
384
|
+
for (let ch = 0; ch < 4; ch++) cb[ch] = out[i + ch] / 255;
|
|
385
|
+
const o = blendPixel(s.blend, cb, src, k);
|
|
386
|
+
out[i] = Math.round(o[0] * 255);
|
|
387
|
+
out[i + 1] = Math.round(o[1] * 255);
|
|
388
|
+
out[i + 2] = Math.round(o[2] * 255);
|
|
389
|
+
if (!s.lockTransparency) out[i + 3] = Math.round(o[3] * 255);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
function coverageTileCoord(key) {
|
|
395
|
+
return tileCoord(key);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export {
|
|
399
|
+
DEFAULT_BRUSH,
|
|
400
|
+
MAX_BRUSH_SIZE,
|
|
401
|
+
normalizeBrush,
|
|
402
|
+
tipShape,
|
|
403
|
+
tipFalloff,
|
|
404
|
+
tipReach,
|
|
405
|
+
dabCoverage,
|
|
406
|
+
smoothPoints,
|
|
407
|
+
DabWalker,
|
|
408
|
+
walkDabs,
|
|
409
|
+
dabRect,
|
|
410
|
+
rasterizeDab,
|
|
411
|
+
CoverageMap,
|
|
412
|
+
StrokeCoverage,
|
|
413
|
+
applyStrokeToTile,
|
|
414
|
+
coverageTileCoord
|
|
415
|
+
};
|
|
416
|
+
//# sourceMappingURL=chunk-YLB2WVNA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/paint/brush.ts"],"sourcesContent":["// Portions ported from photocraft/crates/paint/src/lib.rs, dynamics.rs and render.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/**\n * The brush engine (a subset of photocraft's `crates/paint`): a computed round tip with size,\n * hardness, angle, roundness and spacing; pressure to size and opacity; smoothing; and Photoshop's\n * opacity-versus-flow build-up. Strokes are data, so one replays the same way for undo, agents\n * and tests.\n *\n * - Points are walked into dabs at `spacing × diameter` (`walkDabs`).\n * - Each dab's coverage (`dabCoverage`, photocraft's measured Photoshop profile) builds up in a\n * stroke coverage map with flow towards the dab's opacity ceiling: `c ← c + v·(ceil − c)`.\n * - The stroke composites once at the stroke opacity over the layer as it was before the stroke,\n * so overlapping dabs never exceed the opacity.\n */\n\nimport { blendPixel, type BlendMode } from '../raster/blend';\nimport type { RasterRect as CropBox } from '../raster/surface';\nimport { TILE, tileKey, tileCoord, type TileKey } from '../raster/surface';\n\n/** One input sample. Missing pressure is a mouse (1). */\nexport interface StrokePoint {\n x: number;\n y: number;\n /** 0..1 (default 1). */\n pressure?: number;\n /** Milliseconds (optional; kept for replays). */\n t?: number;\n}\n\nexport interface BrushSettings {\n /** Diameter in pixels (1..2500, default 20). */\n size: number;\n /** 0 (soft) .. 1 (hard, default). */\n hardness: number;\n /** Dab spacing as a fraction of the diameter (0.01..10, default 0.1). */\n spacing: number;\n /** Tip angle in degrees (default 0). */\n angle: number;\n /** Tip roundness 0.01..1 (default 1). */\n roundness: number;\n /** Stroke opacity ceiling 0..1 (default 1). */\n opacity: number;\n /** Build-up per dab 0..1 (default 1). */\n flow: number;\n /** Input smoothing 0..0.95 (default 0). */\n smoothing: number;\n /** Pressure controls size (default true). */\n pressureSize: boolean;\n /** Pressure controls opacity (default false). */\n pressureOpacity: boolean;\n /** How the stroke paints onto the layer (default normal). */\n blend: BlendMode;\n}\n\nexport const DEFAULT_BRUSH: Readonly<BrushSettings> = {\n size: 20,\n hardness: 1,\n spacing: 0.1,\n angle: 0,\n roundness: 1,\n opacity: 1,\n flow: 1,\n smoothing: 0,\n pressureSize: true,\n pressureOpacity: false,\n blend: 'normal',\n};\n\nexport const MAX_BRUSH_SIZE = 2500;\n\n/** A brush with every field in range (missing fields from `DEFAULT_BRUSH`). */\nexport function normalizeBrush(raw: Partial<BrushSettings> = {}): BrushSettings {\n const num = (v: unknown, lo: number, hi: number, d: number) => (typeof v === 'number' && Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : d);\n return {\n size: num(raw.size, 1, MAX_BRUSH_SIZE, DEFAULT_BRUSH.size),\n hardness: num(raw.hardness, 0, 1, DEFAULT_BRUSH.hardness),\n spacing: num(raw.spacing, 0.01, 10, DEFAULT_BRUSH.spacing),\n angle: num(raw.angle, -360, 360, DEFAULT_BRUSH.angle),\n roundness: num(raw.roundness, 0.01, 1, DEFAULT_BRUSH.roundness),\n opacity: num(raw.opacity, 0, 1, DEFAULT_BRUSH.opacity),\n flow: num(raw.flow, 0, 1, DEFAULT_BRUSH.flow),\n smoothing: num(raw.smoothing, 0, 0.95, DEFAULT_BRUSH.smoothing),\n pressureSize: typeof raw.pressureSize === 'boolean' ? raw.pressureSize : DEFAULT_BRUSH.pressureSize,\n pressureOpacity: typeof raw.pressureOpacity === 'boolean' ? raw.pressureOpacity : DEFAULT_BRUSH.pressureOpacity,\n blend: raw.blend ?? 'normal',\n };\n}\n\n// ---------------------------------------------------------------------------\n// The round tip (photocraft's measured Photoshop profile)\n// ---------------------------------------------------------------------------\n\n/**\n * Photoshop's round tip, measured by photocraft: a solid core of radius `r0 · R`, then a Gaussian\n * shoulder `exp(-((d − r0) / s)²)` of width `s · R`, both by hardness (0, 0.1, … 1).\n */\nconst TIP_SHAPE: ReadonlyArray<[number, number]> = [\n [0.0, 0.664], [0.14, 0.58], [0.28, 0.5], [0.4, 0.44], [0.52, 0.36], [0.63, 0.29],\n [0.7, 0.24], [0.78, 0.19], [0.84, 0.15], [0.89, 0.11], [1.0, 0.0],\n];\n/** The anti-aliased edge: the shoulder never narrows below this many pixels. */\nconst TIP_EDGE_PX = 1.2;\nconst SMALL_TIP_RADIUS = 3;\nconst SUBPIXEL = [-0.375, -0.125, 0.125, 0.375];\n\n/** The core radius and shoulder width (pixels) of a round tip of `radius` and `hardness`. */\nexport function tipShape(radius: number, hardness: number): [number, number] {\n const h = (Number.isNaN(hardness) ? 0 : Math.min(1, Math.max(0, hardness))) * 10;\n const i = Math.min(9, Math.floor(h));\n const t = h - i;\n const a = TIP_SHAPE[i];\n const b = TIP_SHAPE[i + 1];\n const r0 = a[0] + (b[0] - a[0]) * t;\n const s = a[1] + (b[1] - a[1]) * t;\n const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;\n const edge = Math.max(0.25, Math.min(TIP_EDGE_PX, 0.3 * r));\n return [Math.max(0, Math.min(r0 * r, r - edge)), Math.max(1e-3, s * r, edge)];\n}\n\n/** `exp(-u)` as `(1 − u/64)^64` by six squarings: exactly 0 from u = 64. */\nfunction softExp(u: number): number {\n let g = Math.min(1, Math.max(0, 1 - u / 64));\n for (let k = 0; k < 6; k++) g *= g;\n return g;\n}\n\n/** Opacity of a round tip at distance `d` from its centre. */\nexport function tipFalloff(d: number, radius: number, hardness: number): number {\n const [r0, s] = tipShape(radius, hardness);\n if (d <= r0) return 1;\n const x = (d - r0) / s;\n return softExp(x * x);\n}\n\n/** How far from its centre a round tip still paints. */\nexport function tipReach(radius: number, hardness: number): number {\n const [r0, s] = tipShape(radius, hardness);\n return r0 + 2.6 * s;\n}\n\n/** Coverage of a round dab at distance `d` from its centre. */\nexport function dabCoverage(d: number, radius: number, hardness: number): number {\n if (Number.isNaN(d) || d >= tipReach(radius, hardness) + 0.5) return 0;\n return tipFalloff(d, radius, hardness);\n}\n\n// ---------------------------------------------------------------------------\n// Points → dabs\n// ---------------------------------------------------------------------------\n\nexport interface Dab {\n x: number;\n y: number;\n radius: number;\n /** Flow (per-dab build-up). */\n alpha: number;\n /** Opacity ceiling this dab builds up to. */\n opacity: number;\n /** Radians. */\n angle: number;\n roundness: number;\n}\n\n/** Exponential smoothing of input points; the first and last point are kept. */\nexport function smoothPoints(points: readonly StrokePoint[], amount: number): StrokePoint[] {\n const a = 1 - Math.min(0.95, Math.max(0, amount));\n if (a >= 1 || points.length < 3) return points.slice();\n const out: StrokePoint[] = [];\n let cur: StrokePoint | null = null;\n for (const p of points) {\n const n: StrokePoint = cur ? { ...p, x: cur.x + (p.x - cur.x) * a, y: cur.y + (p.y - cur.y) * a } : p;\n out.push(n);\n cur = n;\n }\n out[out.length - 1] = points[points.length - 1];\n return out;\n}\n\nconst pressureOf = (p: StrokePoint) => (typeof p.pressure === 'number' && Number.isFinite(p.pressure) ? Math.min(1, Math.max(0, p.pressure)) : 1);\n\n/**\n * Walks points into dabs (incrementally: feed points in any chunks and get the same dabs). The\n * first point is a dab; then one every `spacing × diameter` along the path.\n */\nexport class DabWalker {\n private last: StrokePoint | null = null;\n private nextAt = 0;\n private pendingFirst: StrokePoint | null = null;\n\n constructor(readonly brush: BrushSettings) {}\n\n private diameter(p: StrokePoint): number {\n return this.brush.pressureSize ? this.brush.size * pressureOf(p) : this.brush.size;\n }\n\n private stepLen(p: StrokePoint): number {\n return Math.max(0.5, Math.max(1, this.diameter(p)) * Math.max(0.01, this.brush.spacing));\n }\n\n private dab(p: StrokePoint): Dab {\n const b = this.brush;\n return {\n x: p.x,\n y: p.y,\n radius: Math.max(0.5, this.diameter(p) / 2),\n alpha: Math.min(1, Math.max(0, b.flow)),\n opacity: b.pressureOpacity ? pressureOf(p) : 1,\n angle: (b.angle * Math.PI) / 180,\n roundness: Math.min(1, Math.max(0.01, b.roundness)),\n };\n }\n\n push(p: StrokePoint, out: Dab[]): void {\n const a = this.last;\n if (!a) {\n this.last = p;\n this.pendingFirst = p;\n this.nextAt = this.stepLen(p);\n return;\n }\n if (this.pendingFirst) {\n out.push(this.dab(this.pendingFirst));\n this.pendingFirst = null;\n }\n const dx = p.x - a.x;\n const dy = p.y - a.y;\n const len = Math.hypot(dx, dy);\n if (len > 1e-9) {\n while (this.nextAt <= len + 1e-9) {\n const f = Math.min(1, this.nextAt / len);\n const q: StrokePoint = {\n x: a.x + dx * f,\n y: a.y + dy * f,\n pressure: pressureOf(a) + (pressureOf(p) - pressureOf(a)) * f,\n };\n out.push(this.dab(q));\n this.nextAt += this.stepLen(q);\n }\n this.nextAt -= len;\n }\n this.last = p;\n }\n\n finish(out: Dab[]): void {\n if (this.pendingFirst) {\n out.push(this.dab(this.pendingFirst));\n this.pendingFirst = null;\n }\n }\n}\n\n/** All dabs of a point list (smoothing applied). */\nexport function walkDabs(points: readonly StrokePoint[], brush: BrushSettings): Dab[] {\n const w = new DabWalker(brush);\n const out: Dab[] = [];\n for (const p of smoothPoints(points, brush.smoothing)) w.push(p, out);\n w.finish(out);\n return out;\n}\n\n/** The pixel rectangle a dab can touch. */\nexport function dabRect(d: Dab, hardness: number): CropBox {\n // As `rasterizeDab`: the tip reaches `reach` across its minor axis and `reach / roundness` along it.\n const ro = Math.min(1, Math.max(0.5 / d.radius, d.roundness));\n const rm = Math.max(0.5, d.radius * ro);\n const r = Math.max(rm, tipReach(rm, hardness)) / ro + 1.5;\n const x0 = Math.floor(d.x - r);\n const y0 = Math.floor(d.y - r);\n return { x: x0, y: y0, width: Math.ceil(d.x + r) - x0, height: Math.ceil(d.y + r) - y0 };\n}\n\n/**\n * Rasterizes one dab over `rect` into `out` (rect.width × rect.height values, coverage × flow).\n * Small tips (radius below 3 px) take each pixel's covered area from a 4×4 grid.\n */\nexport function rasterizeDab(d: Dab, hardness: number, rect: CropBox, out: Float32Array): void {\n const w = rect.width;\n const h = rect.height;\n out.fill(0, 0, w * h);\n const r = d.radius;\n const ro = Math.min(1, Math.max(0.5 / r, d.roundness));\n const rm = Math.max(0.5, r * ro);\n const reach = Math.max(rm, tipReach(rm, hardness));\n const sn = Math.sin(d.angle);\n const cs = Math.cos(d.angle);\n const toTip = (dx: number, dy: number): [number, number] => {\n const ux = dx;\n const uy = -dy;\n return [ux * cs + uy * sn, -ux * sn + uy * cs];\n };\n const small = rm < SMALL_TIP_RADIUS;\n const reach2 = (reach + (small ? 0.75 : 0.5)) ** 2;\n for (let yy = 0; yy < h; yy++) {\n const dy = rect.y + yy + 0.5 - d.y;\n for (let xx = 0; xx < w; xx++) {\n const dx = rect.x + xx + 0.5 - d.x;\n const [u, v] = toTip(dx, dy);\n const d2 = (u * ro) ** 2 + v * v;\n if (d2 >= reach2) continue;\n let val: number;\n if (small) {\n let sum = 0;\n for (const sy of SUBPIXEL) {\n for (const sx of SUBPIXEL) {\n const [su, sv] = toTip(dx + sx, dy + sy);\n sum += tipFalloff(Math.sqrt((su * ro) ** 2 + sv * sv), rm, hardness);\n }\n }\n val = sum / 16;\n } else {\n val = dabCoverage(Math.sqrt(d2), rm, hardness);\n }\n if (val > 0) out[yy * w + xx] = val * d.alpha;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stroke coverage\n// ---------------------------------------------------------------------------\n\nconst TILE_PIXELS = TILE * TILE;\n\n/** A stroke's accumulated coverage, in 256 px tiles over document space. */\nexport class CoverageMap {\n readonly tiles = new Map<TileKey, Float32Array>();\n bounds: CropBox | null = null;\n private dirty = new Set<TileKey>();\n\n get(x: number, y: number): number {\n const tx = Math.floor(x / TILE);\n const ty = Math.floor(y / TILE);\n const t = this.tiles.get(tileKey(tx, ty));\n return t ? t[(y - ty * TILE) * TILE + (x - tx * TILE)] : 0;\n }\n\n /** Builds up `vals` (over `rect`) with flow towards `ceil`: `c ← c + v·(ceil − c)`. */\n accumulate(rect: CropBox, vals: Float32Array, ceil: number): void {\n if (rect.width <= 0 || rect.height <= 0) return;\n const w = rect.width;\n const tx0 = Math.floor(rect.x / TILE);\n const ty0 = Math.floor(rect.y / TILE);\n const tx1 = Math.floor((rect.x + rect.width - 1) / TILE);\n const ty1 = Math.floor((rect.y + rect.height - 1) / TILE);\n let touched = false;\n for (let ty = ty0; ty <= ty1; ty++) {\n for (let tx = tx0; tx <= tx1; tx++) {\n const x0 = Math.max(rect.x, tx * TILE);\n const y0 = Math.max(rect.y, ty * TILE);\n const x1 = Math.min(rect.x + rect.width, tx * TILE + TILE);\n const y1 = Math.min(rect.y + rect.height, ty * TILE + TILE);\n let tile: Float32Array | undefined;\n for (let y = y0; y < y1; y++) {\n const row = (y - rect.y) * w;\n const trow = (y - ty * TILE) * TILE;\n for (let x = x0; x < x1; x++) {\n const v = vals[row + (x - rect.x)];\n if (v <= 0) continue;\n if (!tile) {\n const key = tileKey(tx, ty);\n tile = this.tiles.get(key);\n if (!tile) {\n tile = new Float32Array(TILE_PIXELS);\n this.tiles.set(key, tile);\n }\n this.dirty.add(key);\n touched = true;\n }\n const ti = trow + (x - tx * TILE);\n const c = tile[ti];\n if (ceil > c) tile[ti] = c + v * (ceil - c);\n }\n }\n }\n }\n if (touched) this.bounds = unionRect(this.bounds, rect);\n }\n\n /** Tiles changed since the last call. */\n takeDirty(): TileKey[] {\n const out = [...this.dirty];\n this.dirty.clear();\n return out;\n }\n}\n\nfunction unionRect(a: CropBox | null, b: CropBox): CropBox {\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/** Paints a stroke's dabs into a coverage map, incrementally. */\nexport class StrokeCoverage {\n readonly coverage = new CoverageMap();\n private walker: DabWalker;\n private smoothing: number;\n private smoothPos: StrokePoint | null = null;\n private buf = new Float32Array(0);\n dabCount = 0;\n\n constructor(readonly brush: BrushSettings) {\n this.walker = new DabWalker(brush);\n this.smoothing = brush.smoothing;\n }\n\n /** Feeds input points; returns the document rectangles the new dabs touched. */\n push(points: readonly StrokePoint[]): CropBox[] {\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 /** Ends the stroke (a single click still paints one dab). */\n finish(last?: StrokePoint): CropBox[] {\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.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[]): CropBox[] {\n const rects: CropBox[] = [];\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 vals = this.buf.subarray(0, n);\n rasterizeDab(d, this.brush.hardness, rect, vals);\n this.coverage.accumulate(rect, vals, d.opacity);\n rects.push(rect);\n this.dabCount++;\n }\n return rects;\n }\n}\n\n/** How a stroke changes pixels. */\nexport interface StrokeApply {\n /** `paint` puts colour down; `erase` removes alpha (or, with transparency locked, paints `background`). */\n mode: 'paint' | 'erase';\n /** RGBA 0..255 (paint colour, or the mask grey in `r`). */\n color: readonly [number, number, number, number];\n /** RGBA 0..255 the eraser paints on a transparency-locked layer (and on masks). */\n background: readonly [number, number, number, number];\n opacity: number;\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 the stroke's coverage over `tileData` (the target's tile (tx, ty) as it was before\n * the stroke, in layer space; RGBA or one byte for masks) and returns the new tile data. With\n * `into` (the tile's previous result) and `region` (document space), only `region` is recomputed\n * and the rest of `into` is kept.\n */\nexport function applyStrokeToTile(\n coverage: CoverageMap,\n tileData: Uint8ClampedArray,\n channels: 1 | 4,\n tx: number,\n ty: number,\n s: StrokeApply,\n options: { into?: Uint8ClampedArray; region?: CropBox } = {}\n): Uint8ClampedArray {\n const out = options.into ? new Uint8ClampedArray(options.into) : new Uint8ClampedArray(tileData);\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 // Start the region from the pre-stroke pixels: coverage is cumulative.\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 cb = [0, 0, 0, 0];\n const cs = [s.color[0] / 255, s.color[1] / 255, s.color[2] / 255, s.color[3] / 255];\n const bg = [s.background[0] / 255, s.background[1] / 255, s.background[2] / 255, s.background[3] / 255];\n // With a tile-aligned offset the layer tile is one coverage tile: read it directly.\n const aligned = s.offset.x % TILE === 0 && s.offset.y % TILE === 0;\n const covTile = aligned ? coverage.tiles.get(tileKey(Math.floor(ox / TILE), Math.floor(oy / TILE))) : undefined;\n if (aligned && !covTile) return out;\n for (let y = y0; y < y1; y++) {\n const dy = oy + y;\n for (let x = x0; x < x1; x++) {\n const dx = ox + x;\n const c = covTile ? covTile[y * TILE + x] : coverage.get(dx, dy);\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 const target = s.mode === 'erase' ? s.background[0] : s.color[0];\n out[p] = Math.round(out[p] + (target - out[p]) * k);\n continue;\n }\n const i = p * 4;\n if (s.mode === 'erase' && !s.lockTransparency) {\n out[i + 3] = Math.round(out[i + 3] * (1 - k));\n continue;\n }\n if (s.lockTransparency && out[i + 3] === 0) continue;\n const src = s.mode === 'erase' ? bg : cs;\n for (let ch = 0; ch < 4; ch++) cb[ch] = out[i + ch] / 255;\n const o = blendPixel(s.blend, cb, src, k);\n out[i] = Math.round(o[0] * 255);\n out[i + 1] = Math.round(o[1] * 255);\n out[i + 2] = Math.round(o[2] * 255);\n if (!s.lockTransparency) out[i + 3] = Math.round(o[3] * 255);\n }\n }\n return out;\n}\n\n/** Tile coordinates of coverage tile keys (for callers that track dirty tiles). */\nexport function coverageTileCoord(key: TileKey): { tx: number; ty: number } {\n return tileCoord(key);\n}\n"],"mappings":"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","names":[]}
|