@pixerate/editor 0.92.0 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/THIRD_PARTY_NOTICES.md +2 -0
  2. package/dist/{chunk-PDCITYBT.cjs → chunk-4F3YDIWW.cjs} +367 -2
  3. package/dist/chunk-4F3YDIWW.cjs.map +1 -0
  4. package/dist/chunk-73L6TROJ.cjs +3685 -0
  5. package/dist/chunk-73L6TROJ.cjs.map +1 -0
  6. package/dist/chunk-747CGOT2.js +3685 -0
  7. package/dist/chunk-747CGOT2.js.map +1 -0
  8. package/dist/{chunk-YLB2WVNA.js → chunk-ISDCJHTO.js} +367 -2
  9. package/dist/chunk-ISDCJHTO.js.map +1 -0
  10. package/dist/image-editor/index.cjs +787 -302
  11. package/dist/image-editor/index.cjs.map +1 -1
  12. package/dist/image-editor/index.d.cts +84 -18
  13. package/dist/image-editor/index.d.ts +84 -18
  14. package/dist/image-editor/index.js +560 -75
  15. package/dist/image-editor/index.js.map +1 -1
  16. package/dist/paint/index.cjs +26 -2
  17. package/dist/paint/index.cjs.map +1 -1
  18. package/dist/paint/index.d.cts +96 -2
  19. package/dist/paint/index.d.ts +96 -2
  20. package/dist/paint/index.js +25 -1
  21. package/dist/raster/index.cjs +78 -2
  22. package/dist/raster/index.cjs.map +1 -1
  23. package/dist/raster/index.d.cts +99 -348
  24. package/dist/raster/index.d.ts +99 -348
  25. package/dist/raster/index.js +78 -2
  26. package/dist/{surface-fJhkmNQn.d.cts → surface-CuBg2c6k.d.cts} +1 -1
  27. package/dist/{surface-fJhkmNQn.d.ts → surface-CuBg2c6k.d.ts} +1 -1
  28. package/dist/zlib-BJ6U7LuB.d.cts +795 -0
  29. package/dist/zlib-BjQkpMFi.d.ts +795 -0
  30. package/package.json +3 -3
  31. package/skills/pixerate-image-editor/SKILL.md +63 -4
  32. package/dist/chunk-H543GD6T.cjs +0 -1696
  33. package/dist/chunk-H543GD6T.cjs.map +0 -1
  34. package/dist/chunk-P5TPUNBZ.js +0 -1696
  35. package/dist/chunk-P5TPUNBZ.js.map +0 -1
  36. package/dist/chunk-PDCITYBT.cjs.map +0 -1
  37. package/dist/chunk-YLB2WVNA.js.map +0 -1
@@ -0,0 +1,3685 @@
1
+ import {
2
+ TILE,
3
+ blendChannel,
4
+ blendRgb,
5
+ emptySurface,
6
+ isBlendMode,
7
+ isSeparableBlendMode,
8
+ readRegion,
9
+ surfaceFromPixels,
10
+ tileKey,
11
+ tilesInRect
12
+ } from "./chunk-D4ZCU72J.js";
13
+
14
+ // src/raster/blur.ts
15
+ function gaussianKernel(sigma) {
16
+ const r = Math.ceil(sigma * 3);
17
+ const k = new Float32Array(2 * r + 1);
18
+ let s = 0;
19
+ for (let i = -r; i <= r; i++) {
20
+ const v = Math.exp(-(i * i) / (2 * sigma * sigma));
21
+ k[i + r] = v;
22
+ s += v;
23
+ }
24
+ for (let i = 0; i < k.length; i++) k[i] /= s;
25
+ return k;
26
+ }
27
+ var DIRECT_GAUSSIAN_MAX_SIGMA = 8;
28
+ function boxRadii(sigma) {
29
+ const n = 3;
30
+ const wIdeal = Math.sqrt(12 * sigma * sigma / n + 1);
31
+ let wl = Math.floor(wIdeal);
32
+ if (wl % 2 === 0) wl--;
33
+ const wu = wl + 2;
34
+ const mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
35
+ const m = Math.round(mIdeal);
36
+ const sizes = [0, 1, 2].map((i) => i < m ? wl : wu);
37
+ return sizes.map((s) => (s - 1) / 2);
38
+ }
39
+ function convolveLine(src, off, stride, len, k, edge, dst) {
40
+ const r = (k.length - 1) / 2;
41
+ for (let i = 0; i < len; i++) {
42
+ let a = 0;
43
+ const lo = i - r;
44
+ for (let j = 0; j < k.length; j++) {
45
+ let x = lo + j;
46
+ if (x < 0 || x >= len) {
47
+ if (edge === "zero") continue;
48
+ x = x < 0 ? 0 : len - 1;
49
+ }
50
+ a += src[off + x * stride] * k[j];
51
+ }
52
+ dst[i] = a;
53
+ }
54
+ }
55
+ function boxesLine(src, off, stride, len, radii, edge, a, b, dst) {
56
+ const pad = radii.reduce((s, r) => s + r, 0);
57
+ const plen = len + 2 * pad;
58
+ for (let i = 0; i < plen; i++) {
59
+ const j = i - pad;
60
+ a[i] = j < 0 ? edge === "zero" ? 0 : src[off] : j >= len ? edge === "zero" ? 0 : src[off + (len - 1) * stride] : src[off + j * stride];
61
+ }
62
+ let from = a;
63
+ let to = b;
64
+ for (const r of radii) {
65
+ let sum = 0;
66
+ for (let i = 0; i <= Math.min(r, plen - 1); i++) sum += from[i];
67
+ const k = 2 * r + 1;
68
+ for (let i = 0; i < plen; i++) {
69
+ to[i] = sum / k;
70
+ const add = i + r + 1;
71
+ const drop = i - r;
72
+ if (add < plen) sum += from[add];
73
+ if (drop >= 0) sum -= from[drop];
74
+ }
75
+ const t = from;
76
+ from = to;
77
+ to = t;
78
+ }
79
+ for (let i = 0; i < len; i++) dst[i] = from[i + pad];
80
+ }
81
+ function gaussianBlur(src, w, h, sigma, edge = "zero") {
82
+ const out = Float32Array.from(src);
83
+ if (!(sigma >= 0.1) || w <= 0 || h <= 0) return out;
84
+ const s = Math.min(sigma, 4 * Math.max(w, h));
85
+ const line = new Float32Array(Math.max(w, h));
86
+ const passes = [];
87
+ if (s <= DIRECT_GAUSSIAN_MAX_SIGMA) {
88
+ const k = gaussianKernel(s);
89
+ passes.push((buf, off, stride, len) => convolveLine(buf, off, stride, len, k, edge, line));
90
+ } else {
91
+ const radii = boxRadii(s);
92
+ const plen = Math.max(w, h) + 2 * radii.reduce((a2, r) => a2 + r, 0);
93
+ const a = new Float32Array(plen);
94
+ const b = new Float32Array(plen);
95
+ passes.push((buf, off, stride, len) => boxesLine(buf, off, stride, len, radii, edge, a, b, line));
96
+ }
97
+ for (const pass of passes) {
98
+ for (let y = 0; y < h; y++) {
99
+ pass(out, y * w, 1, w);
100
+ out.set(line.subarray(0, w), y * w);
101
+ }
102
+ }
103
+ for (const pass of passes) {
104
+ for (let x = 0; x < w; x++) {
105
+ pass(out, x, w, h);
106
+ for (let y = 0; y < h; y++) out[y * w + x] = line[y];
107
+ }
108
+ }
109
+ return out;
110
+ }
111
+
112
+ // src/raster/layers.ts
113
+ var childrenOf = (l) => l.type === "group" ? l.children ?? null : null;
114
+ function flattenLayers(layers) {
115
+ const out = [];
116
+ const walk = (list) => {
117
+ for (const l of list) {
118
+ out.push(l);
119
+ const c = childrenOf(l);
120
+ if (c) walk(c);
121
+ }
122
+ };
123
+ walk(layers);
124
+ return out;
125
+ }
126
+ function findLayer(layers, id) {
127
+ const walk = (list, parent, path) => {
128
+ for (let i = 0; i < list.length; i++) {
129
+ const l = list[i];
130
+ if (l.id === id) return { layer: l, parent, index: i, path };
131
+ const c = childrenOf(l);
132
+ if (c) {
133
+ const g = l;
134
+ const hit = walk(c, g, [...path, g]);
135
+ if (hit) return hit;
136
+ }
137
+ }
138
+ return null;
139
+ };
140
+ return walk(layers, null, []);
141
+ }
142
+ function mapLayer(layers, id, fn) {
143
+ let changed = false;
144
+ const walk = (list) => {
145
+ let out = null;
146
+ for (let i = 0; i < list.length; i++) {
147
+ const l = list[i];
148
+ let next = l;
149
+ if (l.id === id) {
150
+ next = fn(l);
151
+ changed = true;
152
+ } else {
153
+ const c = childrenOf(l);
154
+ if (c) {
155
+ const children = walk(c);
156
+ if (children !== c) next = { ...l, children };
157
+ }
158
+ }
159
+ if (next !== l) {
160
+ if (!out) out = list.slice(0, i);
161
+ if (next) out.push(next);
162
+ } else if (out) out.push(l);
163
+ }
164
+ return out ?? list;
165
+ };
166
+ const result = walk(layers);
167
+ return changed ? result : layers;
168
+ }
169
+ function insertLayer(layers, layer, parentId, index) {
170
+ const put = (list) => {
171
+ const at = index === void 0 ? list.length : Math.max(0, Math.min(list.length, index));
172
+ const next = list.slice();
173
+ next.splice(at, 0, layer);
174
+ return next;
175
+ };
176
+ if (!parentId) return put(layers);
177
+ return mapLayer(layers, parentId, (g) => {
178
+ const c = childrenOf(g);
179
+ return c ? { ...g, children: put(c) } : g;
180
+ });
181
+ }
182
+ function isLayerLocked(layer, what) {
183
+ return Boolean(layer.locks?.all || layer.locks?.[what]);
184
+ }
185
+
186
+ // src/raster/effects/model.ts
187
+ var DEFAULT_GLOBAL_LIGHT = { angle: 120, altitude: 30 };
188
+ var LAYER_EFFECT_KINDS = [
189
+ "bevel-emboss",
190
+ "stroke",
191
+ "inner-shadow",
192
+ "inner-glow",
193
+ "satin",
194
+ "color-overlay",
195
+ "gradient-overlay",
196
+ "pattern-overlay",
197
+ "outer-glow",
198
+ "drop-shadow"
199
+ ];
200
+ var LABELS = {
201
+ "drop-shadow": "Drop Shadow",
202
+ "inner-shadow": "Inner Shadow",
203
+ "outer-glow": "Outer Glow",
204
+ "inner-glow": "Inner Glow",
205
+ "bevel-emboss": "Bevel & Emboss",
206
+ satin: "Satin",
207
+ "color-overlay": "Color Overlay",
208
+ "gradient-overlay": "Gradient Overlay",
209
+ "pattern-overlay": "Pattern Overlay",
210
+ stroke: "Stroke"
211
+ };
212
+ function layerEffectLabel(kind) {
213
+ return LABELS[kind];
214
+ }
215
+ function isLayerEffectKind(value) {
216
+ return typeof value === "string" && value in LABELS;
217
+ }
218
+ function defaultEffectGradient() {
219
+ return {
220
+ stops: [
221
+ { position: 0, color: "#000000" },
222
+ { position: 1, color: "#ffffff" }
223
+ ],
224
+ style: "linear",
225
+ angle: 90,
226
+ scale: 1,
227
+ reverse: false,
228
+ align: true,
229
+ offset: { x: 0, y: 0 }
230
+ };
231
+ }
232
+ var fxCounter = 0;
233
+ function newEffectId() {
234
+ return `fx_${Date.now().toString(36)}_${(++fxCounter).toString(36)}`;
235
+ }
236
+ function defaultLayerEffect(kind, id = newEffectId()) {
237
+ const base = { id, enabled: true };
238
+ switch (kind) {
239
+ case "drop-shadow":
240
+ return { ...base, kind, blend: "multiply", opacity: 0.75, color: "#000000", angle: 120, useGlobalLight: true, distance: 5, spread: 0, size: 5, antiAlias: false, noise: 0, knocksOut: true };
241
+ case "inner-shadow":
242
+ return { ...base, kind, blend: "multiply", opacity: 0.75, color: "#000000", angle: 120, useGlobalLight: true, distance: 5, choke: 0, size: 5, antiAlias: false, noise: 0 };
243
+ case "outer-glow":
244
+ return { ...base, kind, blend: "screen", opacity: 0.75, fill: { type: "color", color: "#ffffbe" }, technique: "softer", spread: 0, size: 5, antiAlias: false, range: 0.5, jitter: 0, noise: 0 };
245
+ case "inner-glow":
246
+ return { ...base, kind, blend: "screen", opacity: 0.75, fill: { type: "color", color: "#ffffbe" }, technique: "softer", choke: 0, size: 5, antiAlias: false, range: 0.5, jitter: 0, noise: 0, source: "edge" };
247
+ case "bevel-emboss":
248
+ return {
249
+ ...base,
250
+ kind,
251
+ style: "inner-bevel",
252
+ technique: "smooth",
253
+ depth: 1,
254
+ direction: "up",
255
+ size: 5,
256
+ soften: 0,
257
+ angle: 120,
258
+ altitude: 30,
259
+ useGlobalLight: true,
260
+ highlightColor: "#ffffff",
261
+ highlightBlend: "screen",
262
+ highlightOpacity: 0.75,
263
+ shadowColor: "#000000",
264
+ shadowBlend: "multiply",
265
+ shadowOpacity: 0.75
266
+ };
267
+ case "satin":
268
+ return { ...base, kind, blend: "multiply", opacity: 0.5, color: "#000000", angle: 19, distance: 11, size: 14, antiAlias: true, invert: true };
269
+ case "color-overlay":
270
+ return { ...base, kind, blend: "normal", opacity: 1, color: "#ff0000" };
271
+ case "gradient-overlay":
272
+ return { ...base, kind, blend: "normal", opacity: 1, gradient: defaultEffectGradient(), dither: false };
273
+ case "pattern-overlay":
274
+ return { ...base, kind, blend: "normal", opacity: 1, patternId: "checker", name: "Checker", scale: 1, angle: 0, link: true, phase: { x: 0, y: 0 } };
275
+ case "stroke":
276
+ return { ...base, kind, blend: "normal", opacity: 1, size: 3, position: "outside", fill: { type: "color", color: "#000000" } };
277
+ }
278
+ }
279
+ function hasActiveEffects(effects) {
280
+ return Boolean(effects && effects.enabled && effects.items.some((e) => e.enabled));
281
+ }
282
+ var MAX_EFFECT_REACH = 512;
283
+ function effectReach(e) {
284
+ switch (e.kind) {
285
+ case "drop-shadow":
286
+ case "inner-shadow":
287
+ return e.distance + e.size;
288
+ case "outer-glow":
289
+ case "inner-glow":
290
+ return e.size + 1;
291
+ case "stroke":
292
+ return e.size;
293
+ case "satin":
294
+ return Math.max(1, e.distance) + e.size;
295
+ case "bevel-emboss":
296
+ return e.size + e.soften + 1;
297
+ default:
298
+ return 0;
299
+ }
300
+ }
301
+ function effectsMargin(effects) {
302
+ if (!hasActiveEffects(effects)) return 0;
303
+ const scale = effectsScale(effects);
304
+ let m = 0;
305
+ for (const e of effects.items) {
306
+ if (!e.enabled) continue;
307
+ const r = effectReach(e) * scale;
308
+ if (Number.isFinite(r)) m = Math.max(m, r);
309
+ }
310
+ return Math.ceil(Math.min(Math.max(m, 0), MAX_EFFECT_REACH)) + 2;
311
+ }
312
+ function effectsScale(effects) {
313
+ const s = effects.scale;
314
+ return typeof s === "number" && Number.isFinite(s) && s > 0 ? s : 1;
315
+ }
316
+ function scaleLayerEffect(e, s) {
317
+ if (s === 1) return e;
318
+ const pattern = (p) => ({ ...p, scale: p.scale * s });
319
+ const fill2 = (f) => f.type === "pattern" ? pattern(f) : f;
320
+ switch (e.kind) {
321
+ case "drop-shadow":
322
+ case "inner-shadow":
323
+ case "satin":
324
+ return { ...e, distance: e.distance * s, size: e.size * s };
325
+ case "outer-glow":
326
+ case "inner-glow":
327
+ return { ...e, size: e.size * s };
328
+ case "stroke":
329
+ return { ...e, size: e.size * s, fill: fill2(e.fill) };
330
+ case "pattern-overlay":
331
+ return pattern(e);
332
+ case "bevel-emboss":
333
+ return { ...e, size: e.size * s, soften: e.soften * s, texture: e.texture ? pattern(e.texture) : void 0 };
334
+ default:
335
+ return e;
336
+ }
337
+ }
338
+ function parseEffectColor(value) {
339
+ if (typeof value !== "string") return null;
340
+ let h = value.trim().replace(/^#/, "");
341
+ if (/^[0-9a-f]{3}$/i.test(h)) h = h.replace(/./g, (c) => c + c);
342
+ if (!/^[0-9a-f]{6}$/i.test(h)) return null;
343
+ return [parseInt(h.slice(0, 2), 16) / 255, parseInt(h.slice(2, 4), 16) / 255, parseInt(h.slice(4, 6), 16) / 255];
344
+ }
345
+ function effectColorHex(rgb) {
346
+ const c = (v) => Math.round(Math.max(0, Math.min(1, v)) * 255).toString(16).padStart(2, "0");
347
+ return `#${c(rgb[0])}${c(rgb[1])}${c(rgb[2])}`;
348
+ }
349
+ var LayerEffectError = class extends Error {
350
+ constructor(message) {
351
+ super(message);
352
+ this.name = "LayerEffectError";
353
+ }
354
+ };
355
+ function fail(path, msg) {
356
+ throw new LayerEffectError(`${path}: ${msg}`);
357
+ }
358
+ function num(r, key, path, min, max) {
359
+ const v = r[key];
360
+ if (typeof v !== "number" || !Number.isFinite(v)) fail(`${path}.${key}`, `must be a number from ${min} to ${max}.`);
361
+ if (v < min || v > max) fail(`${path}.${key}`, `${v} is out of range (${min} to ${max}).`);
362
+ }
363
+ function bool(r, key, path) {
364
+ if (typeof r[key] !== "boolean") fail(`${path}.${key}`, "must be true or false.");
365
+ }
366
+ function color(r, key, path) {
367
+ if (!parseEffectColor(r[key])) fail(`${path}.${key}`, `must be a colour like "#ff8800" (got ${JSON.stringify(r[key])}).`);
368
+ }
369
+ function oneOf(r, key, path, values) {
370
+ if (typeof r[key] !== "string" || !values.includes(r[key])) fail(`${path}.${key}`, `must be one of ${values.join(", ")}.`);
371
+ }
372
+ function blend(r, key, path) {
373
+ if (!isBlendMode(r[key])) fail(`${path}.${key}`, `must be a blend mode such as normal, multiply or screen (got ${JSON.stringify(r[key])}).`);
374
+ }
375
+ function contour(v, path) {
376
+ if (v === void 0) return;
377
+ if (!v || typeof v !== "object") fail(path, "must be an object { name, points }.");
378
+ const c = v;
379
+ if (typeof c.name !== "string") fail(`${path}.name`, "must be a string.");
380
+ if (!Array.isArray(c.points) || c.points.length < 2) fail(`${path}.points`, "needs at least 2 points { input, output } from 0 to 1.");
381
+ c.points.forEach((p, i) => {
382
+ if (!p || typeof p !== "object") fail(`${path}.points[${i}]`, "must be { input, output }.");
383
+ num(p, "input", `${path}.points[${i}]`, 0, 1);
384
+ num(p, "output", `${path}.points[${i}]`, 0, 1);
385
+ if (p.corner !== void 0) bool(p, "corner", `${path}.points[${i}]`);
386
+ });
387
+ }
388
+ function validateEffectGradient(v, path) {
389
+ if (!v || typeof v !== "object") fail(path, "must be a gradient object.");
390
+ const g = v;
391
+ if (!Array.isArray(g.stops) || g.stops.length < 1) fail(`${path}.stops`, "needs at least one stop { position, color }.");
392
+ g.stops.forEach((s, i) => {
393
+ num(s, "position", `${path}.stops[${i}]`, 0, 1);
394
+ color(s, "color", `${path}.stops[${i}]`);
395
+ });
396
+ if (g.opacityStops !== void 0) {
397
+ if (!Array.isArray(g.opacityStops)) fail(`${path}.opacityStops`, "must be a list of { position, opacity }.");
398
+ g.opacityStops.forEach((s, i) => {
399
+ num(s, "position", `${path}.opacityStops[${i}]`, 0, 1);
400
+ num(s, "opacity", `${path}.opacityStops[${i}]`, 0, 1);
401
+ });
402
+ }
403
+ oneOf(g, "style", path, ["linear", "radial", "angle", "reflected", "diamond"]);
404
+ num(g, "angle", path, -360, 360);
405
+ num(g, "scale", path, 0.1, 1.5);
406
+ bool(g, "reverse", path);
407
+ bool(g, "align", path);
408
+ const o = g.offset;
409
+ if (!o || typeof o !== "object") fail(`${path}.offset`, "must be { x, y } (fractions of the bounds).");
410
+ num(o, "x", `${path}.offset`, -1, 1);
411
+ num(o, "y", `${path}.offset`, -1, 1);
412
+ }
413
+ function patternRef(r, path) {
414
+ if (typeof r.patternId !== "string" || !r.patternId) fail(`${path}.patternId`, 'must be a pattern id (a built-in pattern such as "checker", or one from list_layer_effects.patterns).');
415
+ num(r, "scale", path, 0.01, 10);
416
+ if (r.angle !== void 0) num(r, "angle", path, -360, 360);
417
+ if (r.link !== void 0) bool(r, "link", path);
418
+ if (r.phase !== void 0) {
419
+ const p = r.phase;
420
+ if (!p || typeof p !== "object") fail(`${path}.phase`, "must be { x, y } in pixels.");
421
+ num(p, "x", `${path}.phase`, -1e6, 1e6);
422
+ num(p, "y", `${path}.phase`, -1e6, 1e6);
423
+ }
424
+ }
425
+ function fill(v, path, allowPattern) {
426
+ if (!v || typeof v !== "object") fail(path, 'must be { type: "color", color } or { type: "gradient", gradient }' + (allowPattern ? ' or { type: "pattern", patternId, scale }.' : "."));
427
+ const f = v;
428
+ if (f.type === "color") color(f, "color", path);
429
+ else if (f.type === "gradient") validateEffectGradient(f.gradient, `${path}.gradient`);
430
+ else if (f.type === "pattern" && allowPattern) patternRef(f, path);
431
+ else fail(`${path}.type`, allowPattern ? "must be color, gradient or pattern." : "must be color or gradient (glows cannot use a pattern).");
432
+ }
433
+ var OPTIONAL_FIELDS = {
434
+ "drop-shadow": ["contour"],
435
+ "inner-shadow": ["contour"],
436
+ "outer-glow": ["contour"],
437
+ "inner-glow": ["contour"],
438
+ satin: ["contour"],
439
+ "bevel-emboss": ["glossContour", "contourElement", "texture"]
440
+ };
441
+ function layerEffectFields(kind) {
442
+ return [...Object.keys(defaultLayerEffect(kind, "x")), ...OPTIONAL_FIELDS[kind] ?? []];
443
+ }
444
+ function validateLayerEffect(v, path = "effect") {
445
+ if (!v || typeof v !== "object") fail(path, "must be an object.");
446
+ const e = v;
447
+ if (!isLayerEffectKind(e.kind)) fail(`${path}.kind`, `must be one of ${LAYER_EFFECT_KINDS.join(", ")}.`);
448
+ if (typeof e.id !== "string" || !e.id) fail(`${path}.id`, "must be a non-empty string.");
449
+ bool(e, "enabled", path);
450
+ const kind = e.kind;
451
+ const allowed = layerEffectFields(kind);
452
+ for (const k of Object.keys(e)) {
453
+ if (e[k] !== void 0 && !allowed.includes(k)) fail(`${path}.${k}`, `not a setting of ${kind}. Its settings are ${allowed.filter((f) => f !== "id" && f !== "kind").join(", ")}.`);
454
+ }
455
+ if (kind !== "bevel-emboss") {
456
+ blend(e, "blend", path);
457
+ num(e, "opacity", path, 0, 1);
458
+ }
459
+ switch (kind) {
460
+ case "drop-shadow":
461
+ case "inner-shadow":
462
+ color(e, "color", path);
463
+ num(e, "angle", path, -360, 360);
464
+ bool(e, "useGlobalLight", path);
465
+ num(e, "distance", path, 0, 3e4);
466
+ num(e, kind === "drop-shadow" ? "spread" : "choke", path, 0, 1);
467
+ num(e, "size", path, 0, 250);
468
+ contour(e.contour, `${path}.contour`);
469
+ bool(e, "antiAlias", path);
470
+ num(e, "noise", path, 0, 1);
471
+ if (kind === "drop-shadow") bool(e, "knocksOut", path);
472
+ break;
473
+ case "outer-glow":
474
+ case "inner-glow":
475
+ fill(e.fill, `${path}.fill`, false);
476
+ oneOf(e, "technique", path, ["softer", "precise"]);
477
+ num(e, kind === "outer-glow" ? "spread" : "choke", path, 0, 1);
478
+ num(e, "size", path, 0, 250);
479
+ contour(e.contour, `${path}.contour`);
480
+ bool(e, "antiAlias", path);
481
+ num(e, "range", path, 0.01, 1);
482
+ num(e, "jitter", path, 0, 1);
483
+ num(e, "noise", path, 0, 1);
484
+ if (kind === "inner-glow") oneOf(e, "source", path, ["edge", "center"]);
485
+ break;
486
+ case "bevel-emboss":
487
+ oneOf(e, "style", path, ["inner-bevel", "outer-bevel", "emboss", "pillow-emboss", "stroke-emboss"]);
488
+ oneOf(e, "technique", path, ["smooth", "chisel-hard", "chisel-soft"]);
489
+ num(e, "depth", path, 0, 10);
490
+ oneOf(e, "direction", path, ["up", "down"]);
491
+ num(e, "size", path, 0, 250);
492
+ num(e, "soften", path, 0, 16);
493
+ num(e, "angle", path, -360, 360);
494
+ num(e, "altitude", path, 0, 90);
495
+ bool(e, "useGlobalLight", path);
496
+ contour(e.glossContour, `${path}.glossContour`);
497
+ color(e, "highlightColor", path);
498
+ blend(e, "highlightBlend", path);
499
+ num(e, "highlightOpacity", path, 0, 1);
500
+ color(e, "shadowColor", path);
501
+ blend(e, "shadowBlend", path);
502
+ num(e, "shadowOpacity", path, 0, 1);
503
+ if (e.contourElement !== void 0) {
504
+ const c = e.contourElement;
505
+ if (!c || typeof c !== "object") fail(`${path}.contourElement`, "must be { contour, range, antiAlias } or left out.");
506
+ contour(c.contour, `${path}.contourElement.contour`);
507
+ num(c, "range", `${path}.contourElement`, 0, 1);
508
+ bool(c, "antiAlias", `${path}.contourElement`);
509
+ }
510
+ if (e.texture !== void 0) {
511
+ const t = e.texture;
512
+ if (!t || typeof t !== "object") fail(`${path}.texture`, "must be { patternId, scale, depth, invert } or left out.");
513
+ patternRef(t, `${path}.texture`);
514
+ num(t, "depth", `${path}.texture`, -10, 10);
515
+ bool(t, "invert", `${path}.texture`);
516
+ }
517
+ break;
518
+ case "satin":
519
+ color(e, "color", path);
520
+ num(e, "angle", path, -360, 360);
521
+ num(e, "distance", path, 0, 250);
522
+ num(e, "size", path, 0, 250);
523
+ contour(e.contour, `${path}.contour`);
524
+ bool(e, "antiAlias", path);
525
+ bool(e, "invert", path);
526
+ break;
527
+ case "color-overlay":
528
+ color(e, "color", path);
529
+ break;
530
+ case "gradient-overlay":
531
+ validateEffectGradient(e.gradient, `${path}.gradient`);
532
+ bool(e, "dither", path);
533
+ break;
534
+ case "pattern-overlay":
535
+ patternRef(e, path);
536
+ break;
537
+ case "stroke":
538
+ num(e, "size", path, 0, 250);
539
+ oneOf(e, "position", path, ["outside", "inside", "center"]);
540
+ fill(e.fill, `${path}.fill`, true);
541
+ break;
542
+ }
543
+ }
544
+ function mergeLayerEffect(effect, changes) {
545
+ if ("kind" in changes && changes.kind !== effect.kind) throw new LayerEffectError(`kind cannot change (${effect.kind}); remove the effect and add a new one.`);
546
+ if ("id" in changes && changes.id !== effect.id) throw new LayerEffectError("id cannot change.");
547
+ const next = { ...effect };
548
+ for (const [k, v] of Object.entries(changes)) {
549
+ if (v === null) delete next[k];
550
+ else next[k] = v;
551
+ }
552
+ validateLayerEffect(next);
553
+ return next;
554
+ }
555
+ function validateLayerEffects(v) {
556
+ if (!v || typeof v !== "object") fail("effects", "must be { enabled, items }.");
557
+ const r = v;
558
+ bool(r, "enabled", "effects");
559
+ if (r.scale !== void 0) num(r, "scale", "effects", 0.01, 10);
560
+ if (!Array.isArray(r.items)) fail("effects.items", "must be a list.");
561
+ const ids = /* @__PURE__ */ new Set();
562
+ r.items.forEach((e, i) => {
563
+ validateLayerEffect(e, `effects.items[${i}]`);
564
+ if (ids.has(e.id)) fail(`effects.items[${i}].id`, `duplicate id ${e.id}.`);
565
+ ids.add(e.id);
566
+ });
567
+ }
568
+ function normalizeLayerEffects(raw) {
569
+ if (!raw || typeof raw !== "object") return void 0;
570
+ const r = raw;
571
+ const items = [];
572
+ const ids = /* @__PURE__ */ new Set();
573
+ if (Array.isArray(r.items)) {
574
+ for (const it of r.items) {
575
+ if (!it || typeof it !== "object" || !isLayerEffectKind(it.kind)) continue;
576
+ const kind = it.kind;
577
+ const merged = { ...defaultLayerEffect(kind), ...it };
578
+ if (typeof merged.id !== "string" || !merged.id || ids.has(merged.id)) merged.id = newEffectId();
579
+ try {
580
+ validateLayerEffect(merged);
581
+ } catch {
582
+ continue;
583
+ }
584
+ ids.add(merged.id);
585
+ items.push(merged);
586
+ }
587
+ }
588
+ const out = { enabled: r.enabled !== false, items };
589
+ if (typeof r.scale === "number" && Number.isFinite(r.scale) && r.scale > 0 && r.scale !== 1) out.scale = r.scale;
590
+ if (r.psd && typeof r.psd === "object") {
591
+ const p = r.psd;
592
+ if (typeof p.key === "string" && p.data instanceof Uint8Array) out.psd = { key: p.key, data: p.data };
593
+ }
594
+ return items.length || out.psd ? out : void 0;
595
+ }
596
+ function effectsFingerprint(effects) {
597
+ if (!effects) return "";
598
+ const items = effects.items.map((e) => {
599
+ const { id: _id, ...rest } = e;
600
+ return rest;
601
+ });
602
+ return stableStringify([effects.enabled, effectsScale(effects), items]);
603
+ }
604
+ function stableStringify(v) {
605
+ if (Array.isArray(v)) return `[${v.map(stableStringify).join(",")}]`;
606
+ if (v && typeof v === "object") {
607
+ const keys = Object.keys(v).filter((k) => v[k] !== void 0).sort();
608
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(v[k])}`).join(",")}}`;
609
+ }
610
+ return JSON.stringify(v);
611
+ }
612
+
613
+ // src/raster/effects/patterns.ts
614
+ var PatternTile = class {
615
+ w;
616
+ h;
617
+ px;
618
+ constructor(p) {
619
+ this.w = Math.max(1, p.width | 0);
620
+ this.h = Math.max(1, p.height | 0);
621
+ const n = this.w * this.h;
622
+ this.px = new Float32Array(n * 4);
623
+ for (let i = 0; i < n; i++) {
624
+ const a = (p.data[i * 4 + 3] ?? 0) / 255;
625
+ this.px[i * 4] = (p.data[i * 4] ?? 0) / 255 * a;
626
+ this.px[i * 4 + 1] = (p.data[i * 4 + 1] ?? 0) / 255 * a;
627
+ this.px[i * 4 + 2] = (p.data[i * 4 + 2] ?? 0) / 255 * a;
628
+ this.px[i * 4 + 3] = a;
629
+ }
630
+ }
631
+ /** Bilinear wrapping sample at tile coordinates (pixel centres at `i + 0.5`), straight alpha into `out`. */
632
+ sample(u, v, out) {
633
+ u -= 0.5;
634
+ v -= 0.5;
635
+ const x0 = Math.floor(u);
636
+ const y0 = Math.floor(v);
637
+ const fx = u - x0;
638
+ const fy = v - y0;
639
+ let r = 0;
640
+ let g = 0;
641
+ let b = 0;
642
+ let a = 0;
643
+ for (let dy = 0; dy < 2; dy++) {
644
+ const wy = dy ? fy : 1 - fy;
645
+ if (wy === 0) continue;
646
+ const yi = ((y0 + dy) % this.h + this.h) % this.h;
647
+ for (let dx = 0; dx < 2; dx++) {
648
+ const wx = dx ? fx : 1 - fx;
649
+ if (wx === 0) continue;
650
+ const xi = ((x0 + dx) % this.w + this.w) % this.w;
651
+ const k = wx * wy;
652
+ const i = (yi * this.w + xi) * 4;
653
+ r += this.px[i] * k;
654
+ g += this.px[i + 1] * k;
655
+ b += this.px[i + 2] * k;
656
+ a += this.px[i + 3] * k;
657
+ }
658
+ }
659
+ if (a <= 0) {
660
+ out[0] = out[1] = out[2] = out[3] = 0;
661
+ return;
662
+ }
663
+ out[0] = r / a;
664
+ out[1] = g / a;
665
+ out[2] = b / a;
666
+ out[3] = Math.min(1, a);
667
+ }
668
+ };
669
+ var PatternPlacement = class {
670
+ ox;
671
+ oy;
672
+ c;
673
+ s;
674
+ inv;
675
+ /** `anchor`: the layer's top-left (used when `link`); `phase` in px; `scale` a fraction; `angle` degrees counter-clockwise. */
676
+ constructor(anchor, link, phase, scale, angle) {
677
+ const bx = link ? anchor.x : 0;
678
+ const by = link ? anchor.y : 0;
679
+ this.ox = bx + phase.x;
680
+ this.oy = by + phase.y;
681
+ this.inv = Number.isFinite(scale) && scale > 1e-3 ? 1 / scale : 1;
682
+ const a = (Number.isFinite(angle) ? angle : 0) * Math.PI / 180;
683
+ this.c = Math.cos(a);
684
+ this.s = Math.sin(a);
685
+ }
686
+ /** Tile coordinates of the document point `(x, y)` into `out[0..1]`. */
687
+ map(x, y, out) {
688
+ const dx = x - this.ox;
689
+ const dy = y - this.oy;
690
+ out[0] = (dx * this.c - dy * this.s) * this.inv;
691
+ out[1] = (dx * this.s + dy * this.c) * this.inv;
692
+ }
693
+ };
694
+ var BUILTINS = {
695
+ checker: { name: "Checker", size: 16, paint: (x, y) => x < 8 !== y < 8 ? [204, 204, 204, 255] : [255, 255, 255, 255] },
696
+ stripes: { name: "Diagonal Stripes", size: 16, paint: (x, y) => (x + y) % 16 < 8 ? [40, 40, 40, 255] : [235, 235, 235, 255] },
697
+ dots: {
698
+ name: "Dots",
699
+ size: 12,
700
+ paint: (x, y) => {
701
+ const d = Math.hypot(x + 0.5 - 6, y + 0.5 - 6);
702
+ const a = Math.max(0, Math.min(1, 3.5 - d));
703
+ return [Math.round(255 - a * 215), Math.round(255 - a * 215), Math.round(255 - a * 215), 255];
704
+ }
705
+ },
706
+ grid: { name: "Grid", size: 16, paint: (x, y) => x === 0 || y === 0 ? [90, 90, 90, 255] : [250, 250, 250, 255] },
707
+ noise: {
708
+ name: "Noise",
709
+ size: 64,
710
+ paint: (x, y) => {
711
+ const v = Math.round(hashNoise(x, y) * 255);
712
+ return [v, v, v, 255];
713
+ }
714
+ }
715
+ };
716
+ function builtinPatternIds() {
717
+ return Object.keys(BUILTINS);
718
+ }
719
+ var builtinCache = /* @__PURE__ */ new Map();
720
+ function builtinPattern(id) {
721
+ const def = BUILTINS[id];
722
+ if (!def) return null;
723
+ let p = builtinCache.get(id);
724
+ if (!p) {
725
+ const data = new Uint8ClampedArray(def.size * def.size * 4);
726
+ for (let y = 0; y < def.size; y++) {
727
+ for (let x = 0; x < def.size; x++) data.set(def.paint(x, y), (y * def.size + x) * 4);
728
+ }
729
+ p = { id, name: def.name, width: def.size, height: def.size, data };
730
+ builtinCache.set(id, p);
731
+ }
732
+ return p;
733
+ }
734
+ function hashNoise(x, y) {
735
+ let h = Math.imul(x | 0, 2376512323) ^ Math.imul(y | 0, 3625334849) ^ 2654435769;
736
+ h ^= h >>> 15;
737
+ h = Math.imul(h, 739982445);
738
+ h ^= h >>> 12;
739
+ return (h & 65535) / 65536;
740
+ }
741
+
742
+ // src/raster/curve.ts
743
+ var CURVE_LUT_SIZE = 4096;
744
+ var clamp01 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
745
+ function curveLut(points, size = CURVE_LUT_SIZE) {
746
+ const pts = points.filter((p) => Number.isFinite(p.input) && Number.isFinite(p.output)).map((p) => [p.input, p.output]).sort((a, b) => a[0] - b[0]);
747
+ const dedup = [];
748
+ for (const p of pts) {
749
+ if (dedup.length && Math.abs(dedup[dedup.length - 1][0] - p[0]) < 1e-6) continue;
750
+ dedup.push(p);
751
+ }
752
+ const out = new Float32Array(size);
753
+ const n = dedup.length;
754
+ if (n < 2) {
755
+ for (let k = 0; k < size; k++) out[k] = k / (size - 1);
756
+ return out;
757
+ }
758
+ const x = dedup.map((p) => p[0]);
759
+ const y = dedup.map((p) => p[1]);
760
+ const m2 = new Float64Array(n);
761
+ if (n > 2) {
762
+ const c = new Float64Array(n);
763
+ const d = new Float64Array(n);
764
+ for (let i = 1; i < n - 1; i++) {
765
+ const h0 = x[i] - x[i - 1];
766
+ const h1 = x[i + 1] - x[i];
767
+ const a = h0 / 6;
768
+ const b = (h0 + h1) / 3;
769
+ const cc = h1 / 6;
770
+ const r = (y[i + 1] - y[i]) / h1 - (y[i] - y[i - 1]) / h0;
771
+ const denom = b - a * c[i - 1];
772
+ c[i] = cc / denom;
773
+ d[i] = (r - a * d[i - 1]) / denom;
774
+ }
775
+ for (let i = n - 2; i >= 1; i--) m2[i] = d[i] - c[i] * m2[i + 1];
776
+ }
777
+ let seg = 0;
778
+ for (let k = 0; k < size; k++) {
779
+ const xv = k / (size - 1);
780
+ if (xv <= x[0]) {
781
+ out[k] = clamp01(y[0]);
782
+ continue;
783
+ }
784
+ if (xv >= x[n - 1]) {
785
+ out[k] = clamp01(y[n - 1]);
786
+ continue;
787
+ }
788
+ while (seg < n - 2 && xv > x[seg + 1]) seg++;
789
+ const x0 = x[seg], x1 = x[seg + 1];
790
+ const h = x1 - x0;
791
+ const a = (x1 - xv) / h;
792
+ const b = (xv - x0) / h;
793
+ const v = a * y[seg] + b * y[seg + 1] + ((a * a * a - a) * m2[seg] + (b * b * b - b) * m2[seg + 1]) * (h * h) / 6;
794
+ out[k] = clamp01(v);
795
+ }
796
+ return out;
797
+ }
798
+
799
+ // src/raster/effects/render.ts
800
+ var INSIDE_EPS = 0.5 / 255;
801
+ var INF = 1e20;
802
+ var FAR = 1e9;
803
+ var STROKE_CHAMFER_MAX = 20;
804
+ var BEVEL_H_EPS = 1e-5;
805
+ var fmap = (w, h, fill2 = 0) => {
806
+ const v = new Float32Array(w * h);
807
+ if (fill2) v.fill(fill2);
808
+ return { w, h, v };
809
+ };
810
+ var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
811
+ var clamp012 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
812
+ function mapGet(m, x, y) {
813
+ return x < 0 || y < 0 || x >= m.w || y >= m.h ? 0 : m.v[y * m.w + x];
814
+ }
815
+ function shifted(m, dx, dy, outside) {
816
+ const reach = (d) => Number.isFinite(d) ? clamp(d, -1e9, 1e9) : 0;
817
+ dx = reach(dx);
818
+ dy = reach(dy);
819
+ const fx = Math.floor(dx);
820
+ const fy = Math.floor(dy);
821
+ const ax = dx - fx;
822
+ const ay = dy - fy;
823
+ const { w, h, v } = m;
824
+ const s = (x, y) => x < 0 || y < 0 || x >= w || y >= h ? outside : v[y * w + x];
825
+ const out = fmap(w, h);
826
+ for (let y = 0; y < h; y++) {
827
+ const sy = y - fy;
828
+ for (let x = 0; x < w; x++) {
829
+ const sx = x - fx;
830
+ const top = s(sx, sy) * (1 - ax) + s(sx - 1, sy) * ax;
831
+ const bot = s(sx, sy - 1) * (1 - ax) + s(sx - 1, sy - 1) * ax;
832
+ out.v[y * w + x] = top * (1 - ay) + bot * ay;
833
+ }
834
+ }
835
+ return out;
836
+ }
837
+ function mapped(m, f) {
838
+ const out = fmap(m.w, m.h);
839
+ for (let i = 0; i < m.v.length; i++) out.v[i] = f(m.v[i]);
840
+ return out;
841
+ }
842
+ function dt1(f, n, out, near, v, z) {
843
+ let k = 0;
844
+ v[0] = 0;
845
+ z[0] = -Infinity;
846
+ z[1] = Infinity;
847
+ for (let q = 1; q < n; q++) {
848
+ for (; ; ) {
849
+ const p = v[k];
850
+ const s = (f[q] + q * q - (f[p] + p * p)) / (2 * q - 2 * p);
851
+ if (s <= z[k] && k > 0) {
852
+ k--;
853
+ continue;
854
+ }
855
+ if (s <= z[k]) {
856
+ v[0] = q;
857
+ z[0] = -Infinity;
858
+ z[1] = Infinity;
859
+ break;
860
+ }
861
+ k++;
862
+ v[k] = q;
863
+ z[k] = s;
864
+ z[k + 1] = Infinity;
865
+ break;
866
+ }
867
+ }
868
+ k = 0;
869
+ for (let q = 0; q < n; q++) {
870
+ while (z[k + 1] < q) k++;
871
+ const p = v[k];
872
+ const d = q - p;
873
+ out[q] = d * d + f[p];
874
+ near[q] = p;
875
+ }
876
+ }
877
+ function edtNearest(inside, w, h) {
878
+ const total = w * h;
879
+ const n = Math.max(w, h);
880
+ const f = new Float64Array(n);
881
+ const o = new Float64Array(n);
882
+ const nr = new Int32Array(n);
883
+ const v = new Int32Array(n);
884
+ const z = new Float64Array(n + 1);
885
+ const gc = new Float64Array(total);
886
+ const nc = new Int32Array(total);
887
+ for (let x = 0; x < w; x++) {
888
+ for (let y = 0; y < h; y++) f[y] = inside[y * w + x] ? 0 : INF;
889
+ dt1(f, h, o, nr, v, z);
890
+ for (let y = 0; y < h; y++) {
891
+ gc[x * h + y] = o[y];
892
+ nc[x * h + y] = nr[y];
893
+ }
894
+ }
895
+ const d = new Float32Array(total);
896
+ const near = new Int32Array(total);
897
+ for (let y = 0; y < h; y++) {
898
+ for (let x = 0; x < w; x++) f[x] = gc[x * h + y];
899
+ dt1(f, w, o, nr, v, z);
900
+ for (let x = 0; x < w; x++) {
901
+ d[y * w + x] = Math.sqrt(o[x]);
902
+ const px = nr[x];
903
+ near[y * w + x] = nc[px * h + y] * w + px;
904
+ }
905
+ }
906
+ return { d, near };
907
+ }
908
+ var S2 = Math.SQRT2;
909
+ var S5 = 2.236068;
910
+ var FWD = [
911
+ [-1, 0, 1],
912
+ [-1, -1, S2],
913
+ [0, -1, 1],
914
+ [1, -1, S2],
915
+ [-1, -2, S5],
916
+ [1, -2, S5],
917
+ [-2, -1, S5],
918
+ [2, -1, S5]
919
+ ];
920
+ function chamferFrom(d, w, h) {
921
+ const ox = FWD.map((f) => f[0]);
922
+ const oy = FWD.map((f) => f[1]);
923
+ const wt = FWD.map((f) => f[2]);
924
+ const k = FWD.length;
925
+ for (let pass = 0; pass < 2; pass++) {
926
+ const sign = pass === 0 ? 1 : -1;
927
+ for (let yy = 0; yy < h; yy++) {
928
+ const y = pass === 0 ? yy : h - 1 - yy;
929
+ for (let xx = 0; xx < w; xx++) {
930
+ const x = pass === 0 ? xx : w - 1 - xx;
931
+ const i = y * w + x;
932
+ let best = d[i];
933
+ for (let j = 0; j < k; j++) {
934
+ const nx = x + ox[j] * sign;
935
+ const ny = y + oy[j] * sign;
936
+ if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
937
+ const c = d[ny * w + nx] + wt[j];
938
+ if (c < best) best = c;
939
+ }
940
+ d[i] = best;
941
+ }
942
+ }
943
+ }
944
+ return d;
945
+ }
946
+ function distOutside(s, chamfer) {
947
+ const { w, h, v } = s;
948
+ const n = v.length;
949
+ const out = new Float32Array(n);
950
+ if (chamfer) {
951
+ const start = new Float32Array(n);
952
+ for (let i = 0; i < n; i++) start[i] = v[i] > INSIDE_EPS ? 1 - Math.min(v[i], 1) : INF;
953
+ const d2 = chamferFrom(start, w, h);
954
+ for (let i = 0; i < n; i++) out[i] = v[i] > INSIDE_EPS ? -0.5 : d2[i] - 0.5;
955
+ return out;
956
+ }
957
+ const inside = new Uint8Array(n);
958
+ for (let i = 0; i < n; i++) inside[i] = v[i] > INSIDE_EPS ? 1 : 0;
959
+ const { d, near } = edtNearest(inside, w, h);
960
+ for (let i = 0; i < n; i++) out[i] = inside[i] ? -0.5 : d[i] - 0.5 + (1 - Math.min(v[near[i]], 1));
961
+ return out;
962
+ }
963
+ function localCoverage(s) {
964
+ const out = { w: s.w, h: s.h, v: new Float32Array(s.v) };
965
+ for (let y = 0; y < s.h; y++) {
966
+ for (let x = 0; x < s.w; x++) {
967
+ const a = s.v[y * s.w + x];
968
+ if (a <= INSIDE_EPS) continue;
969
+ let mx = a;
970
+ for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) mx = Math.max(mx, mapGet(s, x + dx, y + dy));
971
+ out.v[y * s.w + x] = Math.min(a / mx, 1);
972
+ }
973
+ }
974
+ return out;
975
+ }
976
+ function distInside(s, chamfer) {
977
+ const { w, h, v } = s;
978
+ const n = v.length;
979
+ const out = new Float32Array(n);
980
+ if (chamfer) {
981
+ const cov = localCoverage(s);
982
+ const start = new Float32Array(n);
983
+ for (let i = 0; i < n; i++) start[i] = v[i] <= INSIDE_EPS ? 0 : cov.v[i] < 1 - INSIDE_EPS ? cov.v[i] : INF;
984
+ const d2 = chamferFrom(start, w, h);
985
+ for (let i = 0; i < n; i++) out[i] = v[i] <= INSIDE_EPS ? -0.5 : d2[i] - 0.5;
986
+ return out;
987
+ }
988
+ const outside = new Uint8Array(n);
989
+ for (let i = 0; i < n; i++) outside[i] = v[i] <= INSIDE_EPS ? 1 : 0;
990
+ const { d } = edtNearest(outside, w, h);
991
+ for (let i = 0; i < n; i++) out[i] = outside[i] ? -0.5 : d[i] - 0.5;
992
+ return out;
993
+ }
994
+ function edgeDistances(s, reach) {
995
+ const { w, h, v } = s;
996
+ const n = v.length;
997
+ const edge = new Uint8Array(n);
998
+ const inside = new Uint8Array(n);
999
+ for (let y = 0; y < h; y++) {
1000
+ for (let x = 0; x < w; x++) {
1001
+ const i = y * w + x;
1002
+ const a = v[i];
1003
+ if (a <= INSIDE_EPS) continue;
1004
+ inside[i] = 1;
1005
+ if (a < 1 - INSIDE_EPS || mapGet(s, x - 1, y) <= INSIDE_EPS || mapGet(s, x + 1, y) <= INSIDE_EPS || mapGet(s, x, y - 1) <= INSIDE_EPS || mapGet(s, x, y + 1) <= INSIDE_EPS) edge[i] = 1;
1006
+ }
1007
+ }
1008
+ const { d, near } = edtNearest(edge, w, h);
1009
+ const din = new Float32Array(n);
1010
+ const dout = new Float32Array(n);
1011
+ for (let y = 0; y < h; y++) {
1012
+ for (let x = 0; x < w; x++) {
1013
+ const i = y * w + x;
1014
+ const q = near[i];
1015
+ let sd;
1016
+ if (d[i] > reach || d[i] >= INF / 2) {
1017
+ sd = inside[i] ? -(d[i] + 0.5) : d[i] - 0.5;
1018
+ } else {
1019
+ const qx = q % w;
1020
+ const qy = (q - qx) / w;
1021
+ const g = (dx, dy) => mapGet(s, qx + dx, qy + dy);
1022
+ let nx = -(g(1, -1) + 2 * g(1, 0) + g(1, 1) - g(-1, -1) - 2 * g(-1, 0) - g(-1, 1));
1023
+ let ny = -(g(-1, 1) + 2 * g(0, 1) + g(1, 1) - g(-1, -1) - 2 * g(0, -1) - g(1, -1));
1024
+ const len = Math.hypot(nx, ny);
1025
+ if (len > 1e-6) {
1026
+ nx /= len;
1027
+ ny /= len;
1028
+ } else if (d[i] > 0) {
1029
+ nx = (x - qx) / d[i];
1030
+ ny = (y - qy) / d[i];
1031
+ } else {
1032
+ nx = 1;
1033
+ ny = 0;
1034
+ }
1035
+ sd = (x - qx) * nx + (y - qy) * ny - (Math.min(v[q], 1) - 0.5);
1036
+ }
1037
+ din[i] = inside[i] ? Math.max(-sd, inside[i] && !edge[i] ? 0.5 : -0.5) : -0.5;
1038
+ dout[i] = inside[i] && !edge[i] ? -0.5 : Math.max(sd, -0.5);
1039
+ }
1040
+ }
1041
+ return { din, dout };
1042
+ }
1043
+ function dilate(s, r) {
1044
+ if (!(r > 0)) return s;
1045
+ const d = distOutside(s, true);
1046
+ const out = { w: s.w, h: s.h, v: new Float32Array(s.v) };
1047
+ for (let i = 0; i < d.length; i++) {
1048
+ const o = out.v[i];
1049
+ out.v[i] = d[i] < 0 ? Math.min(o + r, 1) : Math.max(o, clamp012(r + 0.5 - d[i]));
1050
+ }
1051
+ return out;
1052
+ }
1053
+ function boxWidth(w) {
1054
+ return Number.isNaN(w) ? 1 : clamp(w, 1, MAX_EFFECT_REACH);
1055
+ }
1056
+ function boxGeom(bw) {
1057
+ bw = boxWidth(bw);
1058
+ const half = bw / 2;
1059
+ const r = Math.max(0, Math.ceil(half - 0.5));
1060
+ const f = clamp012(half - (r - 0.5));
1061
+ return [r, f, 1 / bw];
1062
+ }
1063
+ function boxLine(src, x0, dst, bw) {
1064
+ const n = src.length;
1065
+ const [r, f, norm] = boxGeom(bw);
1066
+ const at = (i) => i >= 0 && i < n ? src[i] : 0;
1067
+ if (r === 0) {
1068
+ for (let k = 0; k < dst.length; k++) dst[k] = at(x0 + k);
1069
+ return;
1070
+ }
1071
+ let inner = 0;
1072
+ for (let i = x0 - (r - 1); i <= x0 + (r - 1); i++) inner += at(i);
1073
+ for (let k = 0; k < dst.length; k++) {
1074
+ const x = x0 + k;
1075
+ dst[k] = (inner + f * (at(x - r) + at(x + r))) * norm;
1076
+ inner += at(x + r) - at(x - r + 1);
1077
+ }
1078
+ }
1079
+ var BLUR_EPS = 1e-7;
1080
+ function tentLine(src, dst, bw, mid) {
1081
+ const [r] = boxGeom(bw);
1082
+ boxLine(src, -r, mid.subarray(0, src.length + 2 * r), bw);
1083
+ boxLine(mid.subarray(0, src.length + 2 * r), r, dst, bw);
1084
+ for (let i = 0; i < dst.length; i++) if (Math.abs(dst[i]) < BLUR_EPS) dst[i] = 0;
1085
+ }
1086
+ function tent(m, w) {
1087
+ if (!Number.isFinite(w) || m.v.length === 0) return;
1088
+ if (boxGeom(w)[0] === 0) return;
1089
+ const { w: mw, h: mh } = m;
1090
+ const r = boxGeom(w)[0];
1091
+ const mid = new Float64Array(Math.max(mw, mh) + 2 * r);
1092
+ const row = new Float64Array(mw);
1093
+ const rowOut = new Float64Array(mw);
1094
+ for (let y = 0; y < mh; y++) {
1095
+ for (let x = 0; x < mw; x++) row[x] = m.v[y * mw + x];
1096
+ tentLine(row, rowOut, w, mid);
1097
+ for (let x = 0; x < mw; x++) m.v[y * mw + x] = rowOut[x];
1098
+ }
1099
+ const col = new Float64Array(mh);
1100
+ const colOut = new Float64Array(mh);
1101
+ for (let x = 0; x < mw; x++) {
1102
+ for (let y = 0; y < mh; y++) col[y] = m.v[y * mw + x];
1103
+ tentLine(col, colOut, w, mid);
1104
+ for (let y = 0; y < mh; y++) m.v[y * mw + x] = colOut[y];
1105
+ }
1106
+ }
1107
+ function blur(m, size) {
1108
+ if (Number.isFinite(size)) tent(m, size);
1109
+ }
1110
+ var lutCache = /* @__PURE__ */ new WeakMap();
1111
+ function contourLut(c) {
1112
+ if (!c) return null;
1113
+ const cached = lutCache.get(c);
1114
+ if (cached !== void 0) return cached;
1115
+ const pts = c.points;
1116
+ const linear = pts.length === 2 && pts[0].input === 0 && pts[0].output === 0 && pts[1].input === 1 && pts[1].output === 1;
1117
+ let lut = null;
1118
+ if (pts.length >= 2 && !linear) {
1119
+ if (!pts.some((p) => p.corner)) lut = curveLut(pts);
1120
+ else {
1121
+ const sorted = [...pts].sort((a, b) => a.input - b.input);
1122
+ let start = 0;
1123
+ for (let i = 1; i < sorted.length; i++) {
1124
+ if (!(sorted[i].corner || i === sorted.length - 1)) continue;
1125
+ const seg = sorted.slice(start, i + 1);
1126
+ const from = seg[0].input;
1127
+ const part = curveLut(seg);
1128
+ if (!lut) lut = part;
1129
+ else for (let k = 0; k < part.length; k++) if (k / (part.length - 1) >= from) lut[k] = part[k];
1130
+ start = i;
1131
+ }
1132
+ lut ??= curveLut(pts);
1133
+ }
1134
+ }
1135
+ lutCache.set(c, lut);
1136
+ return lut;
1137
+ }
1138
+ function lutAt(l, v) {
1139
+ const x = clamp012(v) * (l.length - 1);
1140
+ const i = Math.floor(x);
1141
+ const j = Math.min(i + 1, l.length - 1);
1142
+ return l[i] + (l[j] - l[i]) * (x - i);
1143
+ }
1144
+ function applyLut(m, l) {
1145
+ return l ? mapped(m, (v) => lutAt(l, v)) : m;
1146
+ }
1147
+ function rangedLut(c, range) {
1148
+ const base = contourLut(c);
1149
+ const r = Number.isFinite(range) ? clamp(range, 0.01, 1) : 1;
1150
+ if (r >= 0.999) return base;
1151
+ const n = 4096;
1152
+ const out = new Float32Array(n);
1153
+ for (let k = 0; k < n; k++) {
1154
+ const v = Math.min(k / (n - 1) / r, 1);
1155
+ out[k] = base ? lutAt(base, v) : v;
1156
+ }
1157
+ return out;
1158
+ }
1159
+ function satinLut(c) {
1160
+ const lut = contourLut(c);
1161
+ if (!lut) return null;
1162
+ const end = lut[lut.length - 1];
1163
+ const out = new Float32Array(lut.length);
1164
+ for (let k = 0; k < lut.length; k++) out[k] = Math.abs(lut[lut.length - 1 - k] - end);
1165
+ return out;
1166
+ }
1167
+ function noise(m, amount, x0, y0) {
1168
+ if (!Number.isFinite(amount) || amount <= 0) return;
1169
+ for (let y = 0; y < m.h; y++) {
1170
+ for (let x = 0; x < m.w; x++) {
1171
+ const i = y * m.w + x;
1172
+ if (m.v[i] <= BLUR_EPS) continue;
1173
+ m.v[i] = clamp012(m.v[i] + (hashNoise(x0 + x, y0 + y) - 0.5) * 2 * amount);
1174
+ }
1175
+ }
1176
+ }
1177
+ function lightOffset(angle, distance) {
1178
+ const a = angle * Math.PI / 180;
1179
+ return [Math.round(-Math.cos(a) * distance), Math.round(Math.sin(a) * distance)];
1180
+ }
1181
+ function spreadSplit(size, spread) {
1182
+ return [Math.round(size * spread), size * (1 - spread)];
1183
+ }
1184
+ function shadowMap(shape, s, spread, light, inner, ox, oy) {
1185
+ const angle = s.useGlobalLight ? light.angle : s.angle;
1186
+ const [dx, dy] = lightOffset(angle, s.distance);
1187
+ const src = inner ? mapped(shape, (a) => 1 - a) : shape;
1188
+ let m = shifted(src, dx, dy, inner ? 1 : 0);
1189
+ const [r, bw] = spreadSplit(s.size, spread);
1190
+ m = dilate(m, r);
1191
+ blur(m, bw);
1192
+ m = applyLut(m, contourLut(s.contour));
1193
+ noise(m, s.noise, ox, oy);
1194
+ if (inner) for (let i = 0; i < m.v.length; i++) m.v[i] *= shape.v[i];
1195
+ return m;
1196
+ }
1197
+ function glowMap(shape, g, ox, oy) {
1198
+ const inner = g.kind === "inner-glow";
1199
+ const spread = inner ? g.choke : g.spread;
1200
+ const src = inner ? mapped(shape, (a) => 1 - a) : shape;
1201
+ let m;
1202
+ if (g.technique === "precise") {
1203
+ const d = inner ? distInside(shape, false) : distOutside(src, false);
1204
+ const solid = g.size * spread;
1205
+ const soft = Math.max(g.size + 1 - solid, 1e-3);
1206
+ m = fmap(shape.w, shape.h);
1207
+ for (let i = 0; i < d.length; i++) {
1208
+ const di = d[i] + 0.5;
1209
+ m.v[i] = di <= solid ? 1 : 0.5 * clamp012(1 - (di - solid) / soft);
1210
+ }
1211
+ } else {
1212
+ const [r, bw] = spreadSplit(g.size, spread);
1213
+ m = dilate(src, r);
1214
+ if (m === src || m === shape) m = { w: m.w, h: m.h, v: new Float32Array(m.v) };
1215
+ blur(m, bw);
1216
+ }
1217
+ m = applyLut(m, rangedLut(g.contour, g.range));
1218
+ if (inner && g.source === "center") m = mapped(m, (v) => 1 - v);
1219
+ noise(m, g.noise, ox, oy);
1220
+ if (inner) for (let i = 0; i < m.v.length; i++) m.v[i] *= shape.v[i];
1221
+ return m;
1222
+ }
1223
+ function satinMap(shape, s) {
1224
+ const [dx, dy] = lightOffset(s.angle, Math.max(1, s.distance));
1225
+ const a = shifted(shape, dx, dy, 0);
1226
+ const b = shifted(shape, -dx, -dy, 0);
1227
+ tent(a, s.size);
1228
+ tent(b, s.size);
1229
+ let m = fmap(shape.w, shape.h);
1230
+ for (let i = 0; i < m.v.length; i++) m.v[i] = Math.abs(a.v[i] - b.v[i]);
1231
+ if (s.invert) m = mapped(m, (v) => 1 - v);
1232
+ m = applyLut(m, satinLut(s.contour));
1233
+ for (let i = 0; i < m.v.length; i++) m.v[i] *= shape.v[i];
1234
+ return m;
1235
+ }
1236
+ function bevelGeom(b) {
1237
+ const size = Math.max(b.size, 1);
1238
+ const emboss = b.style === "emboss" || b.style === "pillow-emboss";
1239
+ const paint2 = b.style === "outer-bevel" ? "outer" : emboss ? "both" : "inner";
1240
+ const width = emboss ? Math.ceil(size / 2) : size;
1241
+ const smooth = b.technique === "smooth";
1242
+ const sign = b.direction === "up" ? 1 : -1;
1243
+ return {
1244
+ paint: paint2,
1245
+ width,
1246
+ depth: b.depth * (smooth ? width : size / 2) * sign,
1247
+ pillow: b.style === "pillow-emboss",
1248
+ chiselSoft: b.technique === "chisel-soft" ? Math.max(size / 4, 1) : 0
1249
+ };
1250
+ }
1251
+ function bevelChiselH(paint2, size, din, dout, soft) {
1252
+ const ramp = size + 1;
1253
+ if (paint2 === "outer" && !soft) {
1254
+ const top = (size + 2) / ramp;
1255
+ return dout > 0 ? clamp((size + 1.5 - dout) / ramp, 0, top) : top;
1256
+ }
1257
+ if (paint2 === "outer") return dout > 0 ? 1 - clamp012((dout + 0.5) / ramp) : (size + 2) / ramp;
1258
+ if (paint2 === "both") {
1259
+ const half = size / 2 + 0.5;
1260
+ return din > 0 ? 0.5 + 0.5 * clamp012(din / half) : 0.5 - 0.5 * clamp012(dout / half);
1261
+ }
1262
+ if (soft) return din > 0 ? clamp((din + 1.5) / ramp, 0, (size + 2) / ramp) : 0;
1263
+ return din > 0 ? clamp012((din + 0.5) / ramp) : 0;
1264
+ }
1265
+ function bevelHeight(shape, b, g, rect, anchor, ctx) {
1266
+ const size = Math.max(b.size, 1);
1267
+ let h;
1268
+ if (b.technique === "smooth") {
1269
+ h = { w: shape.w, h: shape.h, v: new Float32Array(shape.v) };
1270
+ tent(h, g.width);
1271
+ } else {
1272
+ const { din, dout } = edgeDistances(shape, size + 2);
1273
+ h = fmap(shape.w, shape.h);
1274
+ const soft = g.chiselSoft > 0;
1275
+ for (let i = 0; i < h.v.length; i++) h.v[i] = bevelChiselH(g.paint, size, din[i], dout[i], soft);
1276
+ if (soft && g.paint === "both") tent(h, g.chiselSoft);
1277
+ }
1278
+ if (b.contourElement) h = applyLut(h, contourLut(b.contourElement.contour) ?? identityLut());
1279
+ const t = b.texture;
1280
+ const tile = t ? ctx.pattern(t.patternId) : null;
1281
+ if (t && tile) {
1282
+ const unit = Math.abs(b.depth) > 1e-6 ? Math.max(Math.abs(g.depth / b.depth), 1e-3) : Math.max(g.width, 1);
1283
+ const place = new PatternPlacement(anchor, t.link ?? true, t.phase ?? { x: 0, y: 0 }, t.scale, 0);
1284
+ const k = t.depth / unit;
1285
+ const uv = [0, 0];
1286
+ const p = [0, 0, 0, 0];
1287
+ for (let y = 0; y < h.h; y++) {
1288
+ for (let x = 0; x < h.w; x++) {
1289
+ place.map(rect.x + x + 0.5, rect.y + y + 0.5, uv);
1290
+ tile.sample(uv[0], uv[1], p);
1291
+ const l = (0.299 * p[0] + 0.587 * p[1] + 0.114 * p[2]) * p[3];
1292
+ h.v[y * h.w + x] += k * (t.invert ? 1 - l : l);
1293
+ }
1294
+ }
1295
+ }
1296
+ if (b.soften >= 1) tent(h, b.soften);
1297
+ return h;
1298
+ }
1299
+ var identity = null;
1300
+ function identityLut() {
1301
+ if (!identity) {
1302
+ identity = new Float32Array(4096);
1303
+ for (let k = 0; k < 4096; k++) identity[k] = k / 4095;
1304
+ }
1305
+ return identity;
1306
+ }
1307
+ function bevelMaps(shape, b, rect, anchor, ctx) {
1308
+ const g = bevelGeom(b);
1309
+ const hmap = bevelHeight(shape, b, g, rect, anchor, ctx);
1310
+ const angle = b.useGlobalLight ? ctx.light.angle : b.angle;
1311
+ const altitude = b.useGlobalLight ? ctx.light.altitude : b.altitude;
1312
+ const sa = Math.sin(angle * Math.PI / 180);
1313
+ const ca = Math.cos(angle * Math.PI / 180);
1314
+ const se = Math.sin(altitude * Math.PI / 180);
1315
+ const ce = Math.cos(altitude * Math.PI / 180);
1316
+ const lx = ca * ce;
1317
+ const ly = -sa * ce;
1318
+ const lz = se;
1319
+ const reachPast = g.paint === "both" || g.paint === "outer" && b.technique !== "smooth";
1320
+ const onBevel = (x, y) => mapGet(hmap, x, y) > BEVEL_H_EPS || reachPast && (mapGet(hmap, x + 1, y) > BEVEL_H_EPS || mapGet(hmap, x - 1, y) > BEVEL_H_EPS || mapGet(hmap, x, y + 1) > BEVEL_H_EPS || mapGet(hmap, x, y - 1) > BEVEL_H_EPS);
1321
+ const gloss = contourLut(b.glossContour);
1322
+ const shadeInto = (depth, outside) => {
1323
+ const hi2 = fmap(shape.w, shape.h);
1324
+ const sh2 = fmap(shape.w, shape.h);
1325
+ for (let y = 0; y < shape.h; y++) {
1326
+ for (let x = 0; x < shape.w; x++) {
1327
+ const i = y * shape.w + x;
1328
+ const gx = (mapGet(hmap, x + 1, y) - mapGet(hmap, x - 1, y)) * 0.5 * depth;
1329
+ const gy = (mapGet(hmap, x, y + 1) - mapGet(hmap, x, y - 1)) * 0.5 * depth;
1330
+ const len = Math.sqrt(gx * gx + gy * gy + 1);
1331
+ const shade = (-gx * lx - gy * ly + lz) / len;
1332
+ const region = outside ? shape.v[i] < 1 - INSIDE_EPS && onBevel(x, y) ? 1 : 0 : shape.v[i];
1333
+ const k = shade - se;
1334
+ if (k > 0) hi2.v[i] = clamp012(k / Math.max(1 - se, 1e-3)) * region;
1335
+ else sh2.v[i] = clamp012(-k / Math.max(se, 1e-3)) * region;
1336
+ }
1337
+ }
1338
+ return [applyLut(hi2, gloss), applyLut(sh2, gloss)];
1339
+ };
1340
+ if (g.paint !== "both") {
1341
+ const [hi2, sh2] = shadeInto(g.depth, g.paint === "outer");
1342
+ return { maps: [hi2, sh2], paint: g.paint };
1343
+ }
1344
+ const [hi, sh] = shadeInto(g.depth, false);
1345
+ const [ho, so] = shadeInto(g.pillow ? -g.depth : g.depth, true);
1346
+ const mix = (a, o) => {
1347
+ const out = fmap(a.w, a.h);
1348
+ for (let i = 0; i < out.v.length; i++) {
1349
+ const s = shape.v[i];
1350
+ out.v[i] = (s > INSIDE_EPS ? a.v[i] : 0) + o.v[i] * (1 - Math.min(s, 1));
1351
+ }
1352
+ return out;
1353
+ };
1354
+ return { maps: [mix(hi, ho), mix(sh, so)], paint: "both" };
1355
+ }
1356
+ function strokeWidths(s) {
1357
+ return s.position === "outside" ? [0, s.size] : s.position === "inside" ? [s.size, 0] : [s.size / 2, s.size / 2];
1358
+ }
1359
+ function activeEffectItems(effects) {
1360
+ if (!hasActiveEffects(effects)) return [];
1361
+ const s = effectsScale(effects);
1362
+ return effects.items.filter((e) => e.enabled).map((e) => scaleLayerEffect(e, s));
1363
+ }
1364
+ function effectsAreLocal(items) {
1365
+ const linked = (p) => p.link !== false;
1366
+ return items.every((e) => {
1367
+ switch (e.kind) {
1368
+ case "gradient-overlay":
1369
+ return !e.gradient.align;
1370
+ case "pattern-overlay":
1371
+ return !linked(e);
1372
+ case "stroke":
1373
+ return e.fill.type === "color" || e.fill.type === "pattern" && !linked(e.fill);
1374
+ case "drop-shadow":
1375
+ case "inner-shadow":
1376
+ case "outer-glow":
1377
+ case "inner-glow":
1378
+ return e.noise <= 0;
1379
+ case "bevel-emboss":
1380
+ return !e.texture || !linked(e.texture);
1381
+ default:
1382
+ return true;
1383
+ }
1384
+ });
1385
+ }
1386
+ function buildEffectMaps(items, shape, rect, ctx) {
1387
+ const w = rect.width;
1388
+ const h = rect.height;
1389
+ const s = { w, h, v: shape };
1390
+ let x0 = Infinity;
1391
+ let y0 = Infinity;
1392
+ let x1 = -Infinity;
1393
+ let y1 = -Infinity;
1394
+ for (let y = 0; y < h; y++) {
1395
+ for (let x = 0; x < w; x++) {
1396
+ if (shape[y * w + x] > INSIDE_EPS) {
1397
+ if (x < x0) x0 = x;
1398
+ if (x > x1) x1 = x;
1399
+ if (y < y0) y0 = y;
1400
+ if (y > y1) y1 = y;
1401
+ }
1402
+ }
1403
+ }
1404
+ const bounds = x0 === Infinity ? null : { x: rect.x + x0, y: rect.y + y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
1405
+ const anchor = bounds ? { x: bounds.x, y: bounds.y } : { x: rect.x, y: rect.y };
1406
+ const per = [];
1407
+ const bevelPaint = [];
1408
+ const frames = /* @__PURE__ */ new Map();
1409
+ let bytes = shape.byteLength;
1410
+ items.forEach((e, i) => {
1411
+ let maps = [];
1412
+ let paint2 = null;
1413
+ switch (e.kind) {
1414
+ case "drop-shadow":
1415
+ maps = [shadowMap(s, e, e.spread, ctx.light, false, rect.x, rect.y)];
1416
+ break;
1417
+ case "inner-shadow":
1418
+ maps = [shadowMap(s, e, e.choke, ctx.light, true, rect.x, rect.y)];
1419
+ break;
1420
+ case "outer-glow":
1421
+ case "inner-glow":
1422
+ maps = [glowMap(s, e, rect.x, rect.y)];
1423
+ break;
1424
+ case "satin":
1425
+ maps = [satinMap(s, e)];
1426
+ break;
1427
+ case "bevel-emboss": {
1428
+ const r = bevelMaps(s, e, rect, anchor, ctx);
1429
+ maps = r.maps;
1430
+ paint2 = r.paint;
1431
+ break;
1432
+ }
1433
+ case "stroke":
1434
+ if (e.fill.type === "gradient" && bounds) {
1435
+ const grow = Math.max(0, Math.ceil(Math.min(strokeWidths(e)[1], MAX_EFFECT_REACH)) - 1);
1436
+ frames.set(i, { x: bounds.x - grow, y: bounds.y - grow, width: bounds.width + 2 * grow, height: bounds.height + 2 * grow });
1437
+ }
1438
+ break;
1439
+ }
1440
+ per.push(maps.map((m) => m.v));
1441
+ bevelPaint.push(paint2);
1442
+ for (const m of maps) bytes += m.v.byteLength;
1443
+ });
1444
+ const strokes = items.filter((e) => e.kind === "stroke");
1445
+ const outW = strokes.map((st) => strokeWidths(st)[1]).filter((x) => x > 0);
1446
+ const din = strokes.length ? distInside(s, true) : null;
1447
+ const dout = outW.some((x) => x < STROKE_CHAMFER_MAX) ? distOutside(s, true) : null;
1448
+ const doutExact = outW.some((x) => x >= STROKE_CHAMFER_MAX) ? distOutside(s, false) : null;
1449
+ for (const d of [din, dout, doutExact]) if (d) bytes += d.byteLength;
1450
+ return { rect, items, bounds, shape, per, bevelPaint, din, dout, doutExact, frames, bytes };
1451
+ }
1452
+ var rgbScratchB = [0, 0, 0];
1453
+ var rgbScratchS = [0, 0, 0];
1454
+ function compositeAt(dst, i, mode, separable, sr, sg, sb, sa) {
1455
+ if (!(sa > 0)) return;
1456
+ const ab = dst[i + 3];
1457
+ const ao = sa + ab * (1 - sa);
1458
+ if (ao <= 0) {
1459
+ dst[i] = dst[i + 1] = dst[i + 2] = dst[i + 3] = 0;
1460
+ return;
1461
+ }
1462
+ const b0 = dst[i];
1463
+ const b1 = dst[i + 1];
1464
+ const b2 = dst[i + 2];
1465
+ let m0;
1466
+ let m1;
1467
+ let m2;
1468
+ if (mode === "normal") {
1469
+ m0 = sr;
1470
+ m1 = sg;
1471
+ m2 = sb;
1472
+ } else if (separable) {
1473
+ m0 = blendChannel(mode, b0, sr);
1474
+ m1 = blendChannel(mode, b1, sg);
1475
+ m2 = blendChannel(mode, b2, sb);
1476
+ } else {
1477
+ rgbScratchB[0] = b0;
1478
+ rgbScratchB[1] = b1;
1479
+ rgbScratchB[2] = b2;
1480
+ rgbScratchS[0] = sr;
1481
+ rgbScratchS[1] = sg;
1482
+ rgbScratchS[2] = sb;
1483
+ const m = blendRgb(mode, rgbScratchB, rgbScratchS);
1484
+ m0 = m[0];
1485
+ m1 = m[1];
1486
+ m2 = m[2];
1487
+ }
1488
+ const wb = (1 - sa) * ab;
1489
+ const ws = (1 - ab) * sa;
1490
+ const wm = sa * ab;
1491
+ dst[i] = (wb * b0 + ws * sr + wm * m0) / ao;
1492
+ dst[i + 1] = (wb * b1 + ws * sg + wm * m1) / ao;
1493
+ dst[i + 2] = (wb * b2 + ws * sb + wm * m2) / ao;
1494
+ dst[i + 3] = ao;
1495
+ }
1496
+ function paint(dst, m, color2, mode, opacity) {
1497
+ const sep = mode !== "normal" && isSeparableBlendMode(mode);
1498
+ const c = [0, 0, 0, 1];
1499
+ const n = m.length;
1500
+ for (let i = 0; i < n; i++) {
1501
+ const a = m[i] * opacity;
1502
+ if (!(a > 0)) continue;
1503
+ color2(i, c);
1504
+ compositeAt(dst, i * 4, mode, sep, c[0], c[1], c[2], c[3] * Math.min(a, 1));
1505
+ }
1506
+ }
1507
+ function rgbOf(hex) {
1508
+ return parseEffectColor(hex) ?? [0, 0, 0];
1509
+ }
1510
+ function paintColor(dst, m, hex, mode, opacity) {
1511
+ const [r, g, b] = rgbOf(hex);
1512
+ paint(
1513
+ dst,
1514
+ m,
1515
+ (_i, c) => {
1516
+ c[0] = r;
1517
+ c[1] = g;
1518
+ c[2] = b;
1519
+ c[3] = 1;
1520
+ },
1521
+ mode,
1522
+ opacity
1523
+ );
1524
+ }
1525
+ var PreparedGradient = class {
1526
+ color;
1527
+ alpha;
1528
+ constructor(g) {
1529
+ this.color = [...g.stops].sort((a, b) => a.position - b.position).map((s) => [s.position, ...rgbOf(s.color)]);
1530
+ this.alpha = [...g.opacityStops ?? []].sort((a, b) => a.position - b.position).map((s) => [s.position, s.opacity]);
1531
+ }
1532
+ sample(t, out) {
1533
+ const c = this.color;
1534
+ if (!c.length) {
1535
+ out[0] = out[1] = out[2] = t;
1536
+ } else if (c.length === 1 || t <= c[0][0]) {
1537
+ out[0] = c[0][1];
1538
+ out[1] = c[0][2];
1539
+ out[2] = c[0][3];
1540
+ } else {
1541
+ let done = false;
1542
+ for (let k = 0; k < c.length - 1; k++) {
1543
+ const a2 = c[k];
1544
+ const b = c[k + 1];
1545
+ if (t <= b[0]) {
1546
+ const f = b[0] > a2[0] ? (t - a2[0]) / (b[0] - a2[0]) : 0;
1547
+ out[0] = a2[1] + (b[1] - a2[1]) * f;
1548
+ out[1] = a2[2] + (b[2] - a2[2]) * f;
1549
+ out[2] = a2[3] + (b[3] - a2[3]) * f;
1550
+ done = true;
1551
+ break;
1552
+ }
1553
+ }
1554
+ if (!done) {
1555
+ const l = c[c.length - 1];
1556
+ out[0] = l[1];
1557
+ out[1] = l[2];
1558
+ out[2] = l[3];
1559
+ }
1560
+ }
1561
+ const a = this.alpha;
1562
+ if (!a.length) out[3] = 1;
1563
+ else if (a.length === 1 || t <= a[0][0]) out[3] = a[0][1];
1564
+ else {
1565
+ out[3] = a[a.length - 1][1];
1566
+ for (let k = 0; k < a.length - 1; k++) {
1567
+ if (t <= a[k + 1][0]) {
1568
+ const f = a[k + 1][0] > a[k][0] ? (t - a[k][0]) / (a[k + 1][0] - a[k][0]) : 0;
1569
+ out[3] = a[k][1] + (a[k + 1][1] - a[k][1]) * f;
1570
+ break;
1571
+ }
1572
+ }
1573
+ }
1574
+ }
1575
+ };
1576
+ function gradientUnits(angle, w, h) {
1577
+ const a = angle * Math.PI / 180;
1578
+ const s = Math.sin(a);
1579
+ const c = Math.cos(a);
1580
+ const len = Math.max(Math.sqrt((c * w) ** 2 + (s * h) ** 2), 1);
1581
+ const chord = Math.max(Math.min(w / Math.max(Math.abs(c), 1e-6), h / Math.max(Math.abs(s), 1e-6)), 1);
1582
+ return [chord, len];
1583
+ }
1584
+ function gradientT(style, angle, scale, reverse, offset, frame, x, y) {
1585
+ const w = Math.max(frame.width, 1);
1586
+ const h = Math.max(frame.height, 1);
1587
+ const cx = frame.x + w / 2 + offset.x * w;
1588
+ const cy = frame.y + h / 2 + offset.y * h;
1589
+ const a = angle * Math.PI / 180;
1590
+ const s = Math.sin(a);
1591
+ const c = Math.cos(a);
1592
+ const dx = x - cx;
1593
+ const dy = y - cy;
1594
+ const along = dx * c - dy * s;
1595
+ const across = dx * s + dy * c;
1596
+ let [chord, len] = gradientUnits(angle, w, h);
1597
+ chord *= Math.max(scale, 1e-3);
1598
+ len *= Math.max(scale, 1e-3);
1599
+ const corner = 0.5 * (c - s);
1600
+ let t;
1601
+ switch (style) {
1602
+ case "linear":
1603
+ t = (along - corner) / chord + 0.5;
1604
+ break;
1605
+ case "reflected":
1606
+ t = Math.abs((along - corner) / (chord / 2));
1607
+ break;
1608
+ case "radial":
1609
+ t = Math.sqrt(dx * dx + dy * dy) / (len / 2);
1610
+ break;
1611
+ case "diamond":
1612
+ t = (Math.abs(along) + Math.abs(across)) / (len / 2);
1613
+ break;
1614
+ case "angle": {
1615
+ const turn = (a - Math.atan2(-dy, dx)) / (2 * Math.PI);
1616
+ t = turn - Math.floor(turn);
1617
+ break;
1618
+ }
1619
+ }
1620
+ t = clamp012(t);
1621
+ return reverse ? 1 - t : t;
1622
+ }
1623
+ function gradientLayout(style, angle, scale, offset, frame) {
1624
+ const unchanged = [angle, scale, offset];
1625
+ if (style !== "linear" && style !== "reflected" || ![angle, scale, offset.x, offset.y].every(Number.isFinite)) return unchanged;
1626
+ const w = Math.max(frame.width, 1);
1627
+ const h = Math.max(frame.height, 1);
1628
+ const cx = frame.x + w / 2 + offset.x * w;
1629
+ const cy = frame.y + h / 2 + offset.y * h;
1630
+ const chordOf = (r) => Math.max(Math.min(w / Math.max(Math.abs(Math.cos(r)), 1e-6), h / Math.max(Math.abs(Math.sin(r)), 1e-6)), 1);
1631
+ const a = angle * Math.PI / 180;
1632
+ const half = chordOf(a) * Math.max(scale, 1e-3) / 2;
1633
+ const dx = Math.cos(a) * half;
1634
+ const dy = -Math.sin(a) * half;
1635
+ const snap = (v2) => Math.floor(v2 + 1e-4);
1636
+ const end = [snap(cx + dx), snap(cy + dy)];
1637
+ let start;
1638
+ let mid;
1639
+ if (style === "reflected") {
1640
+ start = [cx, cy];
1641
+ mid = [cx, cy];
1642
+ } else {
1643
+ start = [snap(cx - dx), snap(cy - dy)];
1644
+ mid = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];
1645
+ }
1646
+ const v = [end[0] - start[0], end[1] - start[1]];
1647
+ const len = Math.hypot(v[0], v[1]);
1648
+ if (len < 0.5) return unchanged;
1649
+ const a2 = Math.atan2(-v[1], v[0]);
1650
+ const span = style === "reflected" ? 2 * len : len;
1651
+ return [a2 * 180 / Math.PI, span / chordOf(a2), { x: offset.x + (mid[0] - cx) / w, y: offset.y + (mid[1] - cy) / h }];
1652
+ }
1653
+ function paintGradient(dst, m, g, frame, env, mode, opacity) {
1654
+ const prepared = new PreparedGradient(g);
1655
+ const [angle, scale, offset] = gradientLayout(g.style, g.angle, g.scale, g.offset, frame);
1656
+ const { x: rx, y: ry, width: w } = env.rect;
1657
+ paint(dst, m, (i, c) => prepared.sample(gradientT(g.style, angle, scale, g.reverse, offset, frame, rx + i % w + 0.5, ry + Math.floor(i / w) + 0.5), c), mode, opacity);
1658
+ }
1659
+ function paintPattern(dst, m, p, env, mode, opacity) {
1660
+ const tile = env.ctx.pattern(p.patternId);
1661
+ if (!tile) return;
1662
+ const place = new PatternPlacement(env.anchor, p.link ?? true, p.phase ?? { x: 0, y: 0 }, p.scale, p.angle ?? 0);
1663
+ const { x: rx, y: ry, width: w } = env.rect;
1664
+ const uv = [0, 0];
1665
+ paint(
1666
+ dst,
1667
+ m,
1668
+ (i, c) => {
1669
+ place.map(rx + i % w + 0.5, ry + Math.floor(i / w) + 0.5, uv);
1670
+ tile.sample(uv[0], uv[1], c);
1671
+ },
1672
+ mode,
1673
+ opacity
1674
+ );
1675
+ }
1676
+ function paintFill(dst, m, f, frame, env, mode, opacity) {
1677
+ if (f.type === "color") paintColor(dst, m, f.color, mode, opacity);
1678
+ else if (f.type === "gradient") paintGradient(dst, m, f.gradient, f.gradient.align ? frame : env.canvas, env, mode, opacity);
1679
+ else paintPattern(dst, m, f, env, mode, opacity);
1680
+ }
1681
+ function paintGlow(dst, m, g, env) {
1682
+ if (g.fill.type === "color") return paintColor(dst, m, g.fill.color, g.blend, g.opacity);
1683
+ const prepared = new PreparedGradient(g.fill.gradient);
1684
+ const r = Number.isFinite(g.range) ? clamp(g.range, 0.01, 1) : 1;
1685
+ const gain = 1 / (r * r);
1686
+ const strength = new Float32Array(m.length);
1687
+ for (let i = 0; i < m.length; i++) strength[i] = Math.min(m[i] * gain, 1);
1688
+ paint(dst, strength, (i, c) => prepared.sample(1 - clamp012(m[i]), c), g.blend, g.opacity);
1689
+ }
1690
+ function crop(v, from, to, fill2) {
1691
+ const out = new Float32Array(to.width * to.height);
1692
+ if (fill2) out.fill(fill2);
1693
+ const ix0 = Math.max(from.x, to.x);
1694
+ const iy0 = Math.max(from.y, to.y);
1695
+ const ix1 = Math.min(from.x + from.width, to.x + to.width);
1696
+ const iy1 = Math.min(from.y + from.height, to.y + to.height);
1697
+ if (ix1 <= ix0 || iy1 <= iy0) return out;
1698
+ const n = ix1 - ix0;
1699
+ for (let y = iy0; y < iy1; y++) {
1700
+ const s = (y - from.y) * from.width + (ix0 - from.x);
1701
+ out.set(v.subarray(s, s + n), (y - to.y) * to.width + (ix0 - to.x));
1702
+ }
1703
+ return out;
1704
+ }
1705
+ function compositeLayerEffects(input) {
1706
+ const { backdrop, content, rect, maps, ctx } = input;
1707
+ const items = maps.items;
1708
+ const n = rect.width * rect.height;
1709
+ const shape = new Float32Array(n);
1710
+ for (let i = 0; i < n; i++) shape[i] = content[i * 4 + 3] / 255;
1711
+ const before = new Float32Array(n * 4);
1712
+ for (let i = 0; i < n * 4; i++) before[i] = backdrop[i] / 255;
1713
+ const work = new Float32Array(before);
1714
+ const fx = (i, k) => crop(maps.per[i][k], maps.rect, rect, 0);
1715
+ const bounds = maps.bounds ?? rect;
1716
+ const env = { rect, bounds, canvas: input.canvas, anchor: { x: bounds.x, y: bounds.y }, ctx };
1717
+ const fill2 = clamp012(input.fillOpacity);
1718
+ const inside = (a) => a > INSIDE_EPS;
1719
+ const rev = [];
1720
+ for (let i = items.length - 1; i >= 0; i--) rev.push(i);
1721
+ const knockOut = (m, fillOpacity) => {
1722
+ const seeThrough = 1 - clamp012(fillOpacity);
1723
+ for (let i = 0; i < n; i++) m[i] *= 1 - shape[i] * seeThrough;
1724
+ };
1725
+ const normalLike = input.blend === "normal";
1726
+ for (const i of rev) {
1727
+ const e = items[i];
1728
+ if (e.kind !== "drop-shadow") continue;
1729
+ const m = fx(i, 0);
1730
+ if (e.knocksOut) knockOut(m, normalLike ? fill2 : 0);
1731
+ paintColor(work, m, e.color, e.blend, e.opacity);
1732
+ }
1733
+ for (const i of rev) {
1734
+ const e = items[i];
1735
+ if (e.kind !== "outer-glow") continue;
1736
+ const m = fx(i, 0);
1737
+ knockOut(m, fill2);
1738
+ paintGlow(work, m, e, env);
1739
+ }
1740
+ const lay = new Float32Array(n * 4);
1741
+ for (let i = 0; i < n; i++) {
1742
+ lay[i * 4] = content[i * 4] / 255;
1743
+ lay[i * 4 + 1] = content[i * 4 + 1] / 255;
1744
+ lay[i * 4 + 2] = content[i * 4 + 2] / 255;
1745
+ lay[i * 4 + 3] = inside(shape[i]) ? fill2 : 0;
1746
+ }
1747
+ const premerge = !normalLike;
1748
+ if (premerge) {
1749
+ const sep2 = isSeparableBlendMode(input.blend);
1750
+ for (let i = 0; i < n; i++) {
1751
+ const p = i * 4;
1752
+ const sr = lay[p];
1753
+ const sg = lay[p + 1];
1754
+ const sb = lay[p + 2];
1755
+ const sa = lay[p + 3];
1756
+ lay[p] = work[p];
1757
+ lay[p + 1] = work[p + 1];
1758
+ lay[p + 2] = work[p + 2];
1759
+ lay[p + 3] = work[p + 3];
1760
+ compositeAt(lay, p, input.blend, sep2, sr, sg, sb, sa);
1761
+ }
1762
+ }
1763
+ const rel = (m) => {
1764
+ for (let i = 0; i < n; i++) m[i] = inside(shape[i]) ? Math.min(m[i] / shape[i], 1) : 0;
1765
+ return m;
1766
+ };
1767
+ const full = new Float32Array(n);
1768
+ for (let i = 0; i < n; i++) full[i] = inside(shape[i]) ? 1 : 0;
1769
+ for (const i of rev) {
1770
+ const e = items[i];
1771
+ if (e.kind === "pattern-overlay") paintPattern(lay, full, e, env, e.blend, e.opacity);
1772
+ }
1773
+ for (const i of rev) {
1774
+ const e = items[i];
1775
+ if (e.kind === "gradient-overlay") paintGradient(lay, full, e.gradient, e.gradient.align ? bounds : input.canvas, env, e.blend, e.opacity);
1776
+ }
1777
+ for (const i of rev) {
1778
+ const e = items[i];
1779
+ if (e.kind === "color-overlay") paintColor(lay, full, e.color, e.blend, e.opacity);
1780
+ }
1781
+ for (const i of rev) {
1782
+ const e = items[i];
1783
+ if (e.kind === "satin") paintColor(lay, rel(fx(i, 0)), e.color, e.blend, e.opacity);
1784
+ }
1785
+ for (const i of rev) {
1786
+ const e = items[i];
1787
+ if (e.kind === "inner-glow") paintGlow(lay, rel(fx(i, 0)), e, env);
1788
+ }
1789
+ for (const i of rev) {
1790
+ const e = items[i];
1791
+ if (e.kind === "inner-shadow") paintColor(lay, rel(fx(i, 0)), e.color, e.blend, e.opacity);
1792
+ }
1793
+ const strokeIdx = items.map((e, i) => e.kind === "stroke" ? i : -1).filter((i) => i >= 0);
1794
+ const din = strokeIdx.length && maps.din ? crop(maps.din, maps.rect, rect, 0) : null;
1795
+ const frameOf = (i) => maps.frames.get(i) ?? bounds;
1796
+ for (let k = strokeIdx.length - 1; k >= 0; k--) {
1797
+ const st = items[strokeIdx[k]];
1798
+ const [inW] = strokeWidths(st);
1799
+ if (!(inW > 0) || !din) continue;
1800
+ const m = new Float32Array(n);
1801
+ for (let i = 0; i < n; i++) m[i] = inside(shape[i]) ? clamp012(inW + 0.5 - din[i]) : 0;
1802
+ paintFill(lay, m, st.fill, frameOf(strokeIdx[k]), env, st.blend, st.opacity);
1803
+ }
1804
+ for (const i of rev) {
1805
+ const e = items[i];
1806
+ if (e.kind !== "bevel-emboss" || maps.bevelPaint[i] !== "inner") continue;
1807
+ paintColor(lay, rel(fx(i, 0)), e.highlightColor, e.highlightBlend, e.highlightOpacity);
1808
+ paintColor(lay, rel(fx(i, 1)), e.shadowColor, e.shadowBlend, e.shadowOpacity);
1809
+ }
1810
+ for (let i = 0; i < n; i++) lay[i * 4 + 3] *= Math.min(shape[i], 1);
1811
+ const outside = strokeIdx.filter((i) => strokeWidths(items[i])[1] > 0);
1812
+ if (outside.length) {
1813
+ const dout = maps.dout ? crop(maps.dout, maps.rect, rect, FAR) : null;
1814
+ const doutExact = maps.doutExact ? crop(maps.doutExact, maps.rect, rect, FAR) : null;
1815
+ const base = new Float32Array(work);
1816
+ const acc = new Float32Array(n * 4);
1817
+ const cover = new Float32Array(n);
1818
+ for (const si of outside) {
1819
+ const st = items[si];
1820
+ const outW = strokeWidths(st)[1];
1821
+ const d = (outW >= STROKE_CHAMFER_MAX ? doutExact : dout) ?? new Float32Array(n).fill(FAR);
1822
+ const share = new Float32Array(n);
1823
+ const ones = new Float32Array(n);
1824
+ for (let i = 0; i < n; i++) {
1825
+ const band = clamp012(outW + 0.5 - d[i]);
1826
+ let k;
1827
+ if (inside(shape[i])) {
1828
+ const c = clamp012(lay[i * 4 + 3]);
1829
+ k = c >= 1 ? 1 : clamp012((1 - clamp012(shape[i])) / (1 - c));
1830
+ } else k = band;
1831
+ share[i] = k * st.opacity * (1 - cover[i]);
1832
+ cover[i] += share[i];
1833
+ ones[i] = share[i] > 0 ? 1 : 0;
1834
+ }
1835
+ const blended = new Float32Array(base);
1836
+ paintFill(blended, ones, st.fill, frameOf(si), env, st.blend, 1);
1837
+ for (let i = 0; i < n; i++) {
1838
+ const s = share[i];
1839
+ if (!(s > 0)) continue;
1840
+ const p = i * 4;
1841
+ const ba = blended[p + 3];
1842
+ acc[p] += s * blended[p] * ba;
1843
+ acc[p + 1] += s * blended[p + 1] * ba;
1844
+ acc[p + 2] += s * blended[p + 2] * ba;
1845
+ acc[p + 3] += s * ba;
1846
+ }
1847
+ }
1848
+ for (let i = 0; i < n; i++) {
1849
+ const c = cover[i];
1850
+ if (c <= 0) continue;
1851
+ const p = i * 4;
1852
+ const k = 1 - Math.min(c, 1);
1853
+ const a = work[p + 3] * k + acc[p + 3];
1854
+ if (a > 0) {
1855
+ work[p] = (work[p] * work[p + 3] * k + acc[p]) / a;
1856
+ work[p + 1] = (work[p + 1] * work[p + 3] * k + acc[p + 1]) / a;
1857
+ work[p + 2] = (work[p + 2] * work[p + 3] * k + acc[p + 2]) / a;
1858
+ work[p + 3] = Math.min(a, 1);
1859
+ }
1860
+ }
1861
+ }
1862
+ for (const i of rev) {
1863
+ const e = items[i];
1864
+ if (e.kind !== "bevel-emboss" || maps.bevelPaint[i] !== "outer") continue;
1865
+ paintColor(work, fx(i, 0), e.highlightColor, e.highlightBlend, e.highlightOpacity);
1866
+ paintColor(work, fx(i, 1), e.shadowColor, e.shadowBlend, e.shadowOpacity);
1867
+ }
1868
+ const mode = premerge ? "normal" : input.blend;
1869
+ const sep = isSeparableBlendMode(mode);
1870
+ for (let i = 0; i < n; i++) {
1871
+ const p = i * 4;
1872
+ if (lay[p + 3] > 0) compositeAt(work, p, mode, sep, lay[p], lay[p + 1], lay[p + 2], lay[p + 3]);
1873
+ }
1874
+ for (const i of rev) {
1875
+ const e = items[i];
1876
+ if (e.kind !== "bevel-emboss" || maps.bevelPaint[i] !== "both") continue;
1877
+ paintColor(work, fx(i, 0), e.highlightColor, e.highlightBlend, e.highlightOpacity);
1878
+ paintColor(work, fx(i, 1), e.shadowColor, e.shadowBlend, e.shadowOpacity);
1879
+ }
1880
+ const op = clamp012(input.opacity);
1881
+ for (let i = 0; i < n; i++) {
1882
+ const p = i * 4;
1883
+ const aa = before[p + 3];
1884
+ const ba = work[p + 3];
1885
+ const alpha = aa + (ba - aa) * op;
1886
+ if (alpha <= 0) {
1887
+ backdrop[p] = backdrop[p + 1] = backdrop[p + 2] = backdrop[p + 3] = 0;
1888
+ continue;
1889
+ }
1890
+ for (let c = 0; c < 3; c++) backdrop[p + c] = Math.round((before[p + c] * aa + (work[p + c] * ba - before[p + c] * aa) * op) / alpha * 255);
1891
+ backdrop[p + 3] = Math.round(alpha * 255);
1892
+ }
1893
+ }
1894
+ function renderLayerEffects(effects, layer, backdrop, canvas, ctx) {
1895
+ const items = activeEffectItems(effects);
1896
+ const n = layer.rect.width * layer.rect.height;
1897
+ const shape = new Float32Array(n);
1898
+ for (let i = 0; i < n; i++) shape[i] = layer.content[i * 4 + 3] / 255;
1899
+ const maps = buildEffectMaps(items, shape, layer.rect, ctx);
1900
+ compositeLayerEffects({
1901
+ backdrop,
1902
+ content: layer.content,
1903
+ rect: layer.rect,
1904
+ maps,
1905
+ blend: layer.blend,
1906
+ opacity: layer.opacity,
1907
+ fillOpacity: layer.fillOpacity ?? 1,
1908
+ canvas,
1909
+ ctx
1910
+ });
1911
+ }
1912
+
1913
+ // src/raster/compositor.ts
1914
+ var TILE_PIXELS = TILE * TILE;
1915
+ var TILE_BYTES = TILE_PIXELS * 4;
1916
+ function sameSig(a, b) {
1917
+ if (!a || a.key !== b.key || a.refs.length !== b.refs.length) return false;
1918
+ for (let i = 0; i < a.refs.length; i++) if (a.refs[i] !== b.refs[i]) return false;
1919
+ return true;
1920
+ }
1921
+ function dirtyInTile(rects, tx, ty) {
1922
+ let x0 = Infinity;
1923
+ let y0 = Infinity;
1924
+ let x1 = -Infinity;
1925
+ let y1 = -Infinity;
1926
+ const ox = tx * TILE;
1927
+ const oy = ty * TILE;
1928
+ for (const r of rects) {
1929
+ const a = Math.max(ox, Math.floor(r.x));
1930
+ const b = Math.max(oy, Math.floor(r.y));
1931
+ const c = Math.min(ox + TILE, Math.ceil(r.x + r.width));
1932
+ const d = Math.min(oy + TILE, Math.ceil(r.y + r.height));
1933
+ if (c <= a || d <= b) continue;
1934
+ x0 = Math.min(x0, a);
1935
+ y0 = Math.min(y0, b);
1936
+ x1 = Math.max(x1, c);
1937
+ y1 = Math.max(y1, d);
1938
+ }
1939
+ return x0 === Infinity ? null : { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
1940
+ }
1941
+ function blendTile(dst, src, mode, alpha, coverage) {
1942
+ if (!(alpha > 0)) return;
1943
+ const normal = mode === "normal";
1944
+ const lut = !normal && isSeparableBlendMode(mode) ? separableLut(mode) : null;
1945
+ const cb = [0, 0, 0];
1946
+ const cs = [0, 0, 0];
1947
+ const n = dst.length;
1948
+ for (let i = 0, p = 0; i < n; i += 4, p++) {
1949
+ const sa = src[i + 3];
1950
+ if (sa === 0) continue;
1951
+ const k = coverage ? alpha * coverage[p] : alpha;
1952
+ if (!(k > 0)) continue;
1953
+ if (normal && sa === 255 && k >= 1) {
1954
+ dst[i] = src[i];
1955
+ dst[i + 1] = src[i + 1];
1956
+ dst[i + 2] = src[i + 2];
1957
+ dst[i + 3] = 255;
1958
+ continue;
1959
+ }
1960
+ const as = sa / 255 * k;
1961
+ const ab = dst[i + 3] / 255;
1962
+ const ao = as + ab * (1 - as);
1963
+ if (ao <= 0) {
1964
+ dst[i] = dst[i + 1] = dst[i + 2] = dst[i + 3] = 0;
1965
+ continue;
1966
+ }
1967
+ const d0 = dst[i];
1968
+ const d1 = dst[i + 1];
1969
+ const d2 = dst[i + 2];
1970
+ const b0 = d0 / 255;
1971
+ const b1 = d1 / 255;
1972
+ const b2 = d2 / 255;
1973
+ const s0 = src[i] / 255;
1974
+ const s1 = src[i + 1] / 255;
1975
+ const s2 = src[i + 2] / 255;
1976
+ let m0;
1977
+ let m1;
1978
+ let m2;
1979
+ if (normal) {
1980
+ m0 = s0;
1981
+ m1 = s1;
1982
+ m2 = s2;
1983
+ } else if (lut) {
1984
+ m0 = lut[d0 << 8 | src[i]];
1985
+ m1 = lut[d1 << 8 | src[i + 1]];
1986
+ m2 = lut[d2 << 8 | src[i + 2]];
1987
+ } else {
1988
+ cb[0] = b0;
1989
+ cb[1] = b1;
1990
+ cb[2] = b2;
1991
+ cs[0] = s0;
1992
+ cs[1] = s1;
1993
+ cs[2] = s2;
1994
+ const m = blendRgb(mode, cb, cs);
1995
+ m0 = m[0];
1996
+ m1 = m[1];
1997
+ m2 = m[2];
1998
+ }
1999
+ const wb = (1 - as) * ab;
2000
+ const ws = (1 - ab) * as;
2001
+ const wm = as * ab;
2002
+ dst[i] = Math.round((wb * b0 + ws * s0 + wm * m0) / ao * 255);
2003
+ dst[i + 1] = Math.round((wb * b1 + ws * s1 + wm * m1) / ao * 255);
2004
+ dst[i + 2] = Math.round((wb * b2 + ws * s2 + wm * m2) / ao * 255);
2005
+ dst[i + 3] = Math.round(ao * 255);
2006
+ }
2007
+ }
2008
+ var separableLuts = /* @__PURE__ */ new Map();
2009
+ function separableLut(mode) {
2010
+ let lut = separableLuts.get(mode);
2011
+ if (!lut) {
2012
+ lut = new Float64Array(65536);
2013
+ for (let b = 0; b < 256; b++) for (let c = 0; c < 256; c++) lut[b << 8 | c] = blendChannel(mode, b / 255, c / 255);
2014
+ separableLuts.set(mode, lut);
2015
+ }
2016
+ return lut;
2017
+ }
2018
+ function blendAtop(dst, src, mode, alpha, coverage) {
2019
+ const keep = new Uint8ClampedArray(dst.length >> 2);
2020
+ for (let i = 3, p = 0; i < dst.length; i += 4, p++) keep[p] = dst[i];
2021
+ blendTile(dst, src, mode, alpha, coverage);
2022
+ for (let i = 3, p = 0; i < dst.length; i += 4, p++) dst[i] = keep[p];
2023
+ }
2024
+ function lerpTile(dst, before, alpha, coverage) {
2025
+ for (let i = 0, p = 0; i < dst.length; i += 4, p++) {
2026
+ const k = coverage ? alpha * coverage[p] : alpha;
2027
+ if (k >= 1) continue;
2028
+ for (let c = 0; c < 4; c++) dst[i + c] = Math.round(before[i + c] + (dst[i + c] - before[i + c]) * k);
2029
+ }
2030
+ }
2031
+ var ZERO_TILE = new Uint8ClampedArray(TILE_BYTES);
2032
+ var isGroup = (l) => l.type === "group";
2033
+ var isPixel = (l) => l.type === "pixel";
2034
+ var RasterCompositor = class {
2035
+ constructor(profile = {}, options = {}) {
2036
+ this.profile = profile;
2037
+ this.maxCacheBytes = options.maxCacheBytes ?? 256 * 1024 * 1024;
2038
+ }
2039
+ profile;
2040
+ tiles = /* @__PURE__ */ new Map();
2041
+ maxCacheBytes;
2042
+ cacheBytes = 0;
2043
+ stamp = 0;
2044
+ sources = { width: 1, height: 1 };
2045
+ sourcesVersion = void 0;
2046
+ filteredCache = /* @__PURE__ */ new WeakMap();
2047
+ planeCache = /* @__PURE__ */ new Map();
2048
+ planeIds = /* @__PURE__ */ new WeakMap();
2049
+ nextPlaneId = 1;
2050
+ contentCache = /* @__PURE__ */ new WeakMap();
2051
+ stepsCache = /* @__PURE__ */ new WeakMap();
2052
+ propsKeyCache = /* @__PURE__ */ new WeakMap();
2053
+ /** Effect maps by layer id (see the module comment), least recently used first. */
2054
+ effectCache = /* @__PURE__ */ new Map();
2055
+ /** Effect maps already validated in this call, by layer object. */
2056
+ effectMemo = /* @__PURE__ */ new WeakMap();
2057
+ patternTiles = /* @__PURE__ */ new WeakMap();
2058
+ reachCache = /* @__PURE__ */ new WeakMap();
2059
+ fxContext = null;
2060
+ lastStats = { tiles: 0, cached: 0, stepsRun: 0 };
2061
+ hint = -1;
2062
+ /** Drops every cached tile and plane. */
2063
+ clear() {
2064
+ this.tiles.clear();
2065
+ this.cacheBytes = 0;
2066
+ this.planeCache.clear();
2067
+ this.filteredCache = /* @__PURE__ */ new WeakMap();
2068
+ this.contentCache = /* @__PURE__ */ new WeakMap();
2069
+ this.effectCache.clear();
2070
+ this.effectMemo = /* @__PURE__ */ new WeakMap();
2071
+ }
2072
+ setSources(sources) {
2073
+ const version = `${sources.width}x${sources.height}:${sources.version ?? ""}`;
2074
+ if (version !== this.sourcesVersion) {
2075
+ this.clear();
2076
+ this.sourcesVersion = version;
2077
+ }
2078
+ this.sources = sources;
2079
+ this.fxContext = null;
2080
+ }
2081
+ /** The composite of `layers` over `rect` as `rect.width × rect.height` RGBA (straight alpha). */
2082
+ composite(layers, rect, sources, options = {}) {
2083
+ const out = new Uint8ClampedArray(Math.max(0, rect.width * rect.height * 4));
2084
+ this.compositeTiles(layers, rect, sources, (tx, ty, data) => {
2085
+ const ox = tx * TILE;
2086
+ const oy = ty * TILE;
2087
+ const x0 = Math.max(rect.x, ox);
2088
+ const y0 = Math.max(rect.y, oy);
2089
+ const x1 = Math.min(rect.x + rect.width, ox + TILE);
2090
+ const y1 = Math.min(rect.y + rect.height, oy + TILE);
2091
+ const span = (x1 - x0) * 4;
2092
+ for (let y = y0; y < y1; y++) {
2093
+ const s = ((y - oy) * TILE + (x0 - ox)) * 4;
2094
+ out.set(data.subarray(s, s + span), ((y - rect.y) * rect.width + (x0 - rect.x)) * 4);
2095
+ }
2096
+ }, options);
2097
+ return out;
2098
+ }
2099
+ /**
2100
+ * Composites every tile intersecting `rect` and calls `onTile` with each tile's RGBA buffer
2101
+ * (TILE × TILE). A tile whose inputs did not change since the last call returns the same buffer
2102
+ * object; never mutate it.
2103
+ */
2104
+ compositeTiles(layers, rect, sources, onTile, options = {}) {
2105
+ this.setSources(sources);
2106
+ const steps = this.buildSteps(layers);
2107
+ this.hint = options.checkpointLayerId ? this.stepIndexOf(steps, options.checkpointLayerId) : -1;
2108
+ this.stamp++;
2109
+ const stats = { tiles: 0, cached: 0, stepsRun: 0 };
2110
+ const grow = options.dirty ? this.effectsReach(layers) : 0;
2111
+ const dirty = grow > 0 ? options.dirty.map((d) => inflateRect(d, grow)) : options.dirty;
2112
+ for (const { tx, ty } of tilesInRect(rect)) {
2113
+ stats.tiles++;
2114
+ const before = this.tiles.get(tileKey(tx, ty))?.result;
2115
+ const data = this.compositeTile(layers, steps, tx, ty, stats, dirty ? dirtyInTile(dirty, tx, ty) : null);
2116
+ onTile(tx, ty, data, data !== before);
2117
+ }
2118
+ this.lastStats = stats;
2119
+ this.evict();
2120
+ }
2121
+ /** The composite of one tile (cached; do not mutate the result). */
2122
+ tile(layers, tx, ty, sources) {
2123
+ this.setSources(sources);
2124
+ this.stamp++;
2125
+ const stats = { tiles: 1, cached: 0, stepsRun: 0 };
2126
+ this.hint = -1;
2127
+ const data = this.compositeTile(layers, this.buildSteps(layers), tx, ty, stats);
2128
+ this.lastStats = stats;
2129
+ this.evict();
2130
+ return data;
2131
+ }
2132
+ /**
2133
+ * A layer's effective mask coverage (0..1, after density, feather or parametric evaluation)
2134
+ * over `r` in document space, or `null` when it reveals all of `r`.
2135
+ */
2136
+ maskCoverage(layer, r, sources) {
2137
+ this.setSources(sources);
2138
+ return this.maskTile(layer, r);
2139
+ }
2140
+ // --- Steps -------------------------------------------------------------------
2141
+ isFilter(l) {
2142
+ return this.profile.isFilterLayer?.(l) ?? false;
2143
+ }
2144
+ /**
2145
+ * Groups a layer list into compositing steps: clipped layers join the nearest unclipped
2146
+ * content or group layer below; consecutive unclipped filter layers form one run.
2147
+ */
2148
+ buildSteps(layers) {
2149
+ const cached = this.stepsCache.get(layers);
2150
+ if (cached) return cached;
2151
+ const steps = [];
2152
+ for (const layer of layers) {
2153
+ const last = steps[steps.length - 1];
2154
+ if (layer.clipped && last && (last.kind === "content" || last.kind === "group")) {
2155
+ last.clipped.push(layer);
2156
+ continue;
2157
+ }
2158
+ if (this.isFilter(layer)) {
2159
+ if (last && last.kind === "adjust") last.layers.push(layer);
2160
+ else steps.push({ kind: "adjust", layers: [layer] });
2161
+ } else if (isGroup(layer)) {
2162
+ steps.push({ kind: "group", layer, clipped: [], steps: this.buildSteps(layer.children ?? []) });
2163
+ } else {
2164
+ steps.push({ kind: "content", layer, clipped: [] });
2165
+ }
2166
+ }
2167
+ this.stepsCache.set(layers, steps);
2168
+ return steps;
2169
+ }
2170
+ /** The index of the top-level step that holds layer `id` (searching groups and clipped layers), or -1. */
2171
+ stepIndexOf(steps, id) {
2172
+ const holds = (l) => l.id === id || isGroup(l) && (l.children ?? []).some(holds);
2173
+ return steps.findIndex((s) => s.kind === "adjust" ? s.layers.some(holds) : holds(s.layer) || s.clipped.some(holds));
2174
+ }
2175
+ /** Properties of a layer that change pixels, minus its surfaces (memoised per object). */
2176
+ propsKey(layer) {
2177
+ let key = this.propsKeyCache.get(layer);
2178
+ if (key !== void 0) return key;
2179
+ const mask = layer.mask ? [layer.mask.enabled !== false, layer.mask.density ?? 1, layer.mask.feather ?? 0, layer.mask.defaultValue, layer.mask.source ?? null] : null;
2180
+ const base = [layer.type, layer.id, layer.visible, layer.opacity, layer.blend, Boolean(layer.clipped), mask];
2181
+ if (isPixel(layer)) base.push(layer.offset?.x ?? 0, layer.offset?.y ?? 0, layer.fillOpacity ?? 1);
2182
+ if (this.hasEffects(layer)) base.push(effectsFingerprint(layer.effects));
2183
+ const extra = this.profile.layerKey?.(layer);
2184
+ if (extra !== void 0) base.push(extra);
2185
+ key = JSON.stringify(base);
2186
+ this.propsKeyCache.set(layer, key);
2187
+ return key;
2188
+ }
2189
+ compositeTile(root, steps, tx, ty, stats, dirty = null) {
2190
+ const key = tileKey(tx, ty);
2191
+ const entry = this.tiles.get(key);
2192
+ const sigs = steps.map((s) => this.stepSig(s, tx, ty));
2193
+ let first = 0;
2194
+ if (entry) {
2195
+ while (first < sigs.length && first < entry.sigs.length && sameSig(entry.sigs[first], sigs[first])) first++;
2196
+ if (first === sigs.length && first === entry.sigs.length) {
2197
+ entry.stamp = this.stamp;
2198
+ stats.cached++;
2199
+ return entry.result;
2200
+ }
2201
+ }
2202
+ let start = 0;
2203
+ let base = null;
2204
+ if (entry) {
2205
+ for (const [index, cp] of entry.checkpoints) {
2206
+ if (index <= first && index > start) {
2207
+ start = index;
2208
+ base = cp;
2209
+ }
2210
+ }
2211
+ }
2212
+ const tileRect = { x: tx * TILE, y: ty * TILE, width: TILE, height: TILE };
2213
+ const hint = this.hint;
2214
+ const checkpoints = /* @__PURE__ */ new Map();
2215
+ if (entry) {
2216
+ for (const [index, cp] of entry.checkpoints) if (index <= start) checkpoints.set(index, cp);
2217
+ }
2218
+ let result;
2219
+ if (entry && dirty && (dirty.width < TILE || dirty.height < TILE)) {
2220
+ const r = dirty;
2221
+ const sub = (data) => {
2222
+ const out = new Uint8ClampedArray(r.width * r.height * 4);
2223
+ if (!data) return out;
2224
+ for (let y = 0; y < r.height; y++) {
2225
+ const s = ((r.y - tileRect.y + y) * TILE + (r.x - tileRect.x)) * 4;
2226
+ out.set(data.subarray(s, s + r.width * 4), y * r.width * 4);
2227
+ }
2228
+ return out;
2229
+ };
2230
+ const acc = sub(base ? base.data : null);
2231
+ let empty = base ? base.empty : true;
2232
+ for (let i = start; i < steps.length; i++) {
2233
+ empty = this.runStep(steps[i], acc, empty, r);
2234
+ stats.stepsRun++;
2235
+ }
2236
+ result = new Uint8ClampedArray(entry.result);
2237
+ for (let y = 0; y < r.height; y++) {
2238
+ result.set(acc.subarray(y * r.width * 4, (y + 1) * r.width * 4), ((r.y - tileRect.y + y) * TILE + (r.x - tileRect.x)) * 4);
2239
+ }
2240
+ } else {
2241
+ const acc = base ? new Uint8ClampedArray(base.data) : new Uint8ClampedArray(TILE_BYTES);
2242
+ let empty = base ? base.empty : true;
2243
+ for (let i = start; i < steps.length; i++) {
2244
+ if ((i === first || i === hint) && i > 0 && !checkpoints.has(i)) checkpoints.set(i, { data: new Uint8ClampedArray(acc), empty });
2245
+ empty = this.runStep(steps[i], acc, empty, tileRect);
2246
+ stats.stepsRun++;
2247
+ }
2248
+ result = empty ? ZERO_TILE : acc;
2249
+ }
2250
+ if (checkpoints.size > 2) {
2251
+ const keys = [...checkpoints.keys()].sort((a, b) => a - b);
2252
+ const keep = /* @__PURE__ */ new Set([keys[keys.length - 1], first, hint]);
2253
+ for (const k of keys) if (!keep.has(k)) checkpoints.delete(k);
2254
+ }
2255
+ const prevBytes = entry ? this.entryBytes(entry) : 0;
2256
+ const next = { sigs, checkpoints, result, root, stamp: this.stamp };
2257
+ this.tiles.set(key, next);
2258
+ this.cacheBytes += this.entryBytes(next) - prevBytes;
2259
+ return result;
2260
+ }
2261
+ entryBytes(e) {
2262
+ let n = e.result === ZERO_TILE ? 0 : TILE_BYTES;
2263
+ for (const cp of e.checkpoints.values()) n += cp.data.byteLength;
2264
+ return n;
2265
+ }
2266
+ evict() {
2267
+ if (this.cacheBytes <= this.maxCacheBytes) return;
2268
+ const entries = [...this.tiles.entries()].sort((a, b) => a[1].stamp - b[1].stamp);
2269
+ for (const [, e] of entries) {
2270
+ if (this.cacheBytes <= this.maxCacheBytes) return;
2271
+ for (const cp of e.checkpoints.values()) this.cacheBytes -= cp.data.byteLength;
2272
+ e.checkpoints.clear();
2273
+ }
2274
+ for (const [k, e] of entries) {
2275
+ if (this.cacheBytes <= this.maxCacheBytes) return;
2276
+ if (e.stamp === this.stamp) continue;
2277
+ this.cacheBytes -= this.entryBytes(e);
2278
+ this.tiles.delete(k);
2279
+ }
2280
+ }
2281
+ // --- Signatures -----------------------------------------------------------
2282
+ stepSig(step, tx, ty) {
2283
+ if (step.kind === "adjust") {
2284
+ const refs2 = [];
2285
+ let key2 = "A";
2286
+ for (const l of step.layers) {
2287
+ key2 += this.propsKey(l);
2288
+ refs2.push(...this.maskRefs(l, tx, ty, 0, 0));
2289
+ }
2290
+ return { key: key2, refs: refs2 };
2291
+ }
2292
+ const own = step.kind === "content" ? this.contentSig(step.layer, tx, ty) : this.groupSig(step.layer, step.steps, tx, ty);
2293
+ if (!step.clipped.length) return own;
2294
+ const refs = [...own.refs];
2295
+ let key = own.key;
2296
+ for (const c of step.clipped) {
2297
+ const s = this.layerSig(c, tx, ty);
2298
+ key += "|" + s.key;
2299
+ refs.push(...s.refs);
2300
+ }
2301
+ return { key, refs };
2302
+ }
2303
+ layerSig(layer, tx, ty) {
2304
+ if (this.isFilter(layer)) return { key: this.propsKey(layer), refs: this.maskRefs(layer, tx, ty, 0, 0) };
2305
+ if (isGroup(layer)) return this.groupSig(layer, this.buildSteps(layer.children ?? []), tx, ty);
2306
+ return this.contentSig(layer, tx, ty);
2307
+ }
2308
+ groupSig(group, steps, tx, ty) {
2309
+ if (group.visible && this.hasEffects(group)) return this.effectSig(group, tx, ty);
2310
+ return this.plainGroupSig(group, steps, tx, ty);
2311
+ }
2312
+ plainGroupSig(group, steps, tx, ty) {
2313
+ let key = "G" + this.propsKey(group);
2314
+ const refs = this.maskRefs(group, tx, ty, 0, 0);
2315
+ if (!group.visible) return { key, refs };
2316
+ for (const s of steps) {
2317
+ const sig = this.stepSig(s, tx, ty);
2318
+ key += "[" + sig.key + "]";
2319
+ refs.push(...sig.refs);
2320
+ }
2321
+ return { key, refs };
2322
+ }
2323
+ contentSig(layer, tx, ty) {
2324
+ if (layer.visible && this.hasEffects(layer)) return this.effectSig(layer, tx, ty);
2325
+ return this.plainContentSig(layer, tx, ty);
2326
+ }
2327
+ plainContentSig(layer, tx, ty) {
2328
+ const key = "C" + this.propsKey(layer);
2329
+ if (!layer.visible) return { key, refs: [] };
2330
+ if (!isPixel(layer)) return { key, refs: this.maskRefs(layer, tx, ty, 0, 0) };
2331
+ const { surface, dx, dy } = this.layerPixels(layer);
2332
+ const refs = [surface.defaultValue];
2333
+ const r = { x: tx * TILE - dx, y: ty * TILE - dy, width: TILE, height: TILE };
2334
+ for (const t of tilesInRect(r)) refs.push(surface.getTile(t.tx, t.ty) ?? null);
2335
+ refs.push(...this.maskRefs(layer, tx, ty, layer.offset?.x ?? 0, layer.offset?.y ?? 0));
2336
+ return { key, refs };
2337
+ }
2338
+ // --- Layer effects ------------------------------------------------------------
2339
+ hasEffects(layer) {
2340
+ return hasActiveEffects(layer.effects) && !this.isFilter(layer);
2341
+ }
2342
+ /** The largest effects margin of any visible layer in the stack (memoised per root). */
2343
+ effectsReach(layers) {
2344
+ let m = this.reachCache.get(layers);
2345
+ if (m !== void 0) return m;
2346
+ m = 0;
2347
+ const walk = (list) => {
2348
+ for (const l of list) {
2349
+ if (!l.visible) continue;
2350
+ if (this.hasEffects(l)) m = Math.max(m, effectsMargin(l.effects));
2351
+ if (l.children) walk(l.children);
2352
+ }
2353
+ };
2354
+ walk(layers);
2355
+ this.reachCache.set(layers, m);
2356
+ return m;
2357
+ }
2358
+ effectContext() {
2359
+ if (this.fxContext) return this.fxContext;
2360
+ const sources = this.sources;
2361
+ const tiles = this.patternTiles;
2362
+ this.fxContext = {
2363
+ light: sources.globalLight ?? DEFAULT_GLOBAL_LIGHT,
2364
+ pattern: (id) => {
2365
+ const p = sources.pattern?.(id) ?? builtinPattern(id);
2366
+ if (!p || !(p.width > 0 && p.height > 0) || p.data.length < p.width * p.height * 4) return null;
2367
+ let t = tiles.get(p);
2368
+ if (!t) {
2369
+ t = new PatternTile(p);
2370
+ tiles.set(p, t);
2371
+ }
2372
+ return t;
2373
+ }
2374
+ };
2375
+ return this.fxContext;
2376
+ }
2377
+ /**
2378
+ * Where a layer's effect maps are built: its bounds (pixel layers; the canvas otherwise) grown by
2379
+ * the effects' margin, within the canvas grown the same.
2380
+ */
2381
+ effectRegion(layer) {
2382
+ const m = effectsMargin(layer.effects);
2383
+ const canvas = { x: -m, y: -m, width: this.sources.width + 2 * m, height: this.sources.height + 2 * m };
2384
+ if (!isPixel(layer)) return canvas;
2385
+ const { surface, dx, dy } = this.layerPixels(layer);
2386
+ if (surface.defaultValue !== 0) return canvas;
2387
+ const b = surface.bounds;
2388
+ if (!b) return { x: 0, y: 0, width: 0, height: 0 };
2389
+ return intersectRect({ x: b.x + dx - m, y: b.y + dy - m, width: b.width + 2 * m, height: b.height + 2 * m }, canvas);
2390
+ }
2391
+ /** The surface tiles (and mask refs) a layer's shape reads over `region`. */
2392
+ shapeRefs(layer, region) {
2393
+ const refs = [];
2394
+ if (region.width <= 0 || region.height <= 0) return refs;
2395
+ for (const t of tilesInRect(region)) {
2396
+ const sig = isGroup(layer) ? this.plainGroupSig(layer, this.buildSteps(layer.children ?? []), t.tx, t.ty) : this.plainContentSig(layer, t.tx, t.ty);
2397
+ for (const r of sig.refs) refs.push(r);
2398
+ }
2399
+ return refs;
2400
+ }
2401
+ lightKey() {
2402
+ const l = this.effectContext().light;
2403
+ return `${l.angle},${l.altitude}`;
2404
+ }
2405
+ /**
2406
+ * The signature of a layer with effects for tile (tx, ty): its properties and the tiles its shape
2407
+ * reads within the effects' margin around the tile, or over its whole region when an effect is
2408
+ * laid out over the layer's bounds.
2409
+ */
2410
+ effectSig(layer, tx, ty) {
2411
+ const key = (isGroup(layer) ? "Gfx" : "Cfx") + this.propsKey(layer) + this.lightKey();
2412
+ const region = this.effectRegion(layer);
2413
+ const m = effectsMargin(layer.effects);
2414
+ if (effectsAreLocal(activeEffectItems(layer.effects))) {
2415
+ return { key, refs: this.shapeRefs(layer, intersectRect({ x: tx * TILE - m, y: ty * TILE - m, width: TILE + 2 * m, height: TILE + 2 * m }, region)) };
2416
+ }
2417
+ return { key, refs: [`${region.x},${region.y},${region.width},${region.height}`, ...this.shapeRefs(layer, region)] };
2418
+ }
2419
+ /** A layer's effect maps over its region (cached; see the module comment). */
2420
+ effectMapsOf(layer) {
2421
+ const memo = this.effectMemo.get(layer);
2422
+ if (memo && memo.stamp === this.stamp) return memo.maps;
2423
+ const region = this.effectRegion(layer);
2424
+ const key = `${this.propsKey(layer)}${this.lightKey()}|${region.x},${region.y},${region.width},${region.height}`;
2425
+ const refs = this.shapeRefs(layer, region);
2426
+ const hit = this.effectCache.get(layer.id);
2427
+ let maps;
2428
+ if (hit && hit.key === key && sameSig({ key, refs: hit.refs }, { key, refs })) {
2429
+ this.effectCache.delete(layer.id);
2430
+ this.effectCache.set(layer.id, hit);
2431
+ maps = hit.maps;
2432
+ } else {
2433
+ maps = buildEffectMaps(activeEffectItems(layer.effects), this.layerShape(layer, region), region, this.effectContext());
2434
+ this.effectCache.delete(layer.id);
2435
+ this.effectCache.set(layer.id, { key, refs, maps });
2436
+ let bytes = 0;
2437
+ for (const e of this.effectCache.values()) bytes += e.maps.bytes;
2438
+ while (this.effectCache.size > 1 && (this.effectCache.size > 32 || bytes > this.maxCacheBytes / 2)) {
2439
+ const [oldest, e] = this.effectCache.entries().next().value;
2440
+ bytes -= e.maps.bytes;
2441
+ this.effectCache.delete(oldest);
2442
+ }
2443
+ }
2444
+ this.effectMemo.set(layer, { stamp: this.stamp, maps });
2445
+ return maps;
2446
+ }
2447
+ /** A layer's alpha over `r` after its mask (0..1, quantised to bytes as tiles see it). */
2448
+ layerShape(layer, r) {
2449
+ const n = r.width * r.height;
2450
+ const shape = new Float32Array(n);
2451
+ if (n <= 0) return shape;
2452
+ const rgba = this.ownPixels(layer, r);
2453
+ if (!rgba) return shape;
2454
+ for (let i = 0; i < n; i++) shape[i] = rgba[i * 4 + 3] / 255;
2455
+ return shape;
2456
+ }
2457
+ /** A content or group layer's own pixels over `r` with its mask applied to alpha, or `null` when empty. */
2458
+ ownPixels(layer, r) {
2459
+ let rgba;
2460
+ if (isGroup(layer)) {
2461
+ const buf = new Uint8ClampedArray(r.width * r.height * 4);
2462
+ rgba = this.runSteps(this.buildSteps(layer.children ?? []), buf, true, r) ? null : buf;
2463
+ } else {
2464
+ const content = this.contentTile(layer, r);
2465
+ rgba = content ? new Uint8ClampedArray(content) : null;
2466
+ }
2467
+ if (!rgba) return null;
2468
+ const cov = this.maskTile(layer, r);
2469
+ if (cov) for (let i = 0, p = 3; i < cov.length; i++, p += 4) rgba[p] = Math.round(rgba[p] * cov[i]);
2470
+ return rgba;
2471
+ }
2472
+ /**
2473
+ * The part of `r` a layer with effects can change: its shape's bounds grown by the effects'
2474
+ * margin. Beyond it the layer and every effect are transparent, so the backdrop stays as it is.
2475
+ */
2476
+ effectArea(layer, maps, r) {
2477
+ if (!maps.bounds) return null;
2478
+ const a = intersectRect(inflateRect(maps.bounds, effectsMargin(layer.effects)), r);
2479
+ return a.width > 0 && a.height > 0 ? a : null;
2480
+ }
2481
+ /**
2482
+ * Composites `layer`'s pixels (with `clipped` applied) and its effects onto `acc` (RGBA over `r`),
2483
+ * only where they can reach. `keepAlpha`: clipped layers keep the base's alpha.
2484
+ */
2485
+ compositeEffects(layer, clipped, acc, r, keepAlpha) {
2486
+ const maps = this.effectMapsOf(layer);
2487
+ const a = this.effectArea(layer, maps, r);
2488
+ if (!a) return false;
2489
+ const whole = a.width === r.width && a.height === r.height;
2490
+ const sub = whole ? acc : copyRect(acc, r, a);
2491
+ const content = this.ownPixels(layer, a) ?? new Uint8ClampedArray(a.width * a.height * 4);
2492
+ if (clipped.length) this.applyClipped(clipped, content, a);
2493
+ const keep = keepAlpha ? sub.filter((_, i) => (i & 3) === 3) : null;
2494
+ const blend2 = isGroup(layer) ? layer.blend === "pass-through" ? "normal" : layer.blend : this.contentBlend(layer);
2495
+ compositeLayerEffects({
2496
+ backdrop: sub,
2497
+ content,
2498
+ rect: a,
2499
+ maps,
2500
+ blend: blend2,
2501
+ opacity: layer.opacity,
2502
+ fillOpacity: isPixel(layer) ? layer.fillOpacity ?? 1 : 1,
2503
+ canvas: { x: 0, y: 0, width: this.sources.width, height: this.sources.height },
2504
+ ctx: this.effectContext()
2505
+ });
2506
+ if (keep) for (let i = 0; i < keep.length; i++) sub[i * 4 + 3] = keep[i];
2507
+ if (!whole) pasteRect(acc, r, sub, a);
2508
+ return true;
2509
+ }
2510
+ /** Composites a layer with active effects onto `acc` over `r`; returns whether `acc` is still empty. */
2511
+ runEffectStep(layer, clipped, acc, empty, r) {
2512
+ return this.compositeEffects(layer, clipped, acc, r, false) ? false : empty;
2513
+ }
2514
+ maskRefs(layer, tx, ty, ox, oy) {
2515
+ const mask = layer.mask;
2516
+ if (!mask || mask.enabled === false) return [];
2517
+ if (mask.source || (mask.feather ?? 0) > 0) return [this.planeFor(mask, ox, oy)];
2518
+ const refs = [];
2519
+ for (const t of tilesInRect({ x: tx * TILE - ox, y: ty * TILE - oy, width: TILE, height: TILE })) {
2520
+ refs.push(mask.surface.getTile(t.tx, t.ty) ?? null);
2521
+ }
2522
+ return refs;
2523
+ }
2524
+ // --- Layer pixels ------------------------------------------------------------
2525
+ /**
2526
+ * A pixel layer's pixels after spatial filters: the surface and the offset to apply. Layers
2527
+ * with a spatial filter are filtered once over the canvas (in document space, offset 0).
2528
+ */
2529
+ layerPixels(layer) {
2530
+ const raw = this.sources.surfaceOf ? this.sources.surfaceOf(layer) : layer.surface;
2531
+ const ox = layer.offset?.x ?? 0;
2532
+ const oy = layer.offset?.y ?? 0;
2533
+ const filters = this.profile.pixelFilters?.(layer) ?? null;
2534
+ if (!filters?.spatial) return { surface: raw, dx: ox, dy: oy, perPixel: filters };
2535
+ const fkey = `${ox},${oy}:${filters.key}`;
2536
+ let byKey = this.filteredCache.get(raw);
2537
+ if (!byKey) {
2538
+ byKey = /* @__PURE__ */ new Map();
2539
+ this.filteredCache.set(raw, byKey);
2540
+ }
2541
+ let filtered = byKey.get(fkey);
2542
+ if (!filtered) {
2543
+ filtered = filterOverCanvas(raw, ox, oy, filters, this.sources.width, this.sources.height);
2544
+ byKey.clear();
2545
+ byKey.set(fkey, filtered);
2546
+ }
2547
+ return { surface: filtered, dx: 0, dy: 0, perPixel: null };
2548
+ }
2549
+ /** RGBA of a content layer over `r` (document space, inside one tile), or `null` when it has nothing there. */
2550
+ contentTile(layer, r) {
2551
+ if (!isPixel(layer)) return this.profile.contentTile?.(layer, r, this.sources) ?? null;
2552
+ const { surface, dx, dy, perPixel } = this.layerPixels(layer);
2553
+ const src = { x: r.x - dx, y: r.y - dy, width: r.width, height: r.height };
2554
+ const covered = tilesInRect(src).map((t) => surface.getTile(t.tx, t.ty));
2555
+ if (surface.defaultValue === 0 && covered.every((t) => !t)) return null;
2556
+ const aligned = r.width === TILE && r.height === TILE && src.x % TILE === 0 && src.y % TILE === 0;
2557
+ if (!perPixel) {
2558
+ if (aligned && covered[0]) return covered[0].data;
2559
+ return readRegion(surface, src);
2560
+ }
2561
+ const fkey = `${dx},${dy}:${perPixel.key}`;
2562
+ const anchor = covered.find(Boolean) ?? surface;
2563
+ let byKey = this.contentCache.get(anchor);
2564
+ const refsKey = `${r.x},${r.y}:${fkey}`;
2565
+ if (aligned) {
2566
+ const hit = byKey?.get(refsKey);
2567
+ if (hit) return hit;
2568
+ }
2569
+ const data = readRegion(surface, src);
2570
+ perPixel.apply(data, r.width, r.height);
2571
+ if (aligned) {
2572
+ if (!byKey) {
2573
+ byKey = /* @__PURE__ */ new Map();
2574
+ this.contentCache.set(anchor, byKey);
2575
+ }
2576
+ byKey.set(refsKey, data);
2577
+ }
2578
+ return data;
2579
+ }
2580
+ // --- Masks -------------------------------------------------------------------
2581
+ planeKey(mask, ox, oy) {
2582
+ const W = this.sources.width;
2583
+ const H = this.sources.height;
2584
+ let id = this.planeIds.get(mask.surface);
2585
+ if (id === void 0) {
2586
+ id = this.nextPlaneId++;
2587
+ this.planeIds.set(mask.surface, id);
2588
+ }
2589
+ return `${id}:${ox},${oy}:${W}x${H}:${mask.feather ?? 0}:${mask.density ?? 1}:${mask.source ? JSON.stringify(mask.source) : ""}`;
2590
+ }
2591
+ /** Canvas-sized coverage of a feathered or parametric mask (cached). */
2592
+ planeFor(mask, ox, oy) {
2593
+ const key = this.planeKey(mask, ox, oy);
2594
+ let plane = this.planeCache.get(key);
2595
+ if (plane) {
2596
+ this.planeCache.delete(key);
2597
+ this.planeCache.set(key, plane);
2598
+ return plane;
2599
+ }
2600
+ const W = this.sources.width;
2601
+ const H = this.sources.height;
2602
+ let v = new Float32Array(W * H);
2603
+ if (mask.source) {
2604
+ const generated = this.profile.maskSourcePlane?.(mask.source, W, H, this.sources) ?? null;
2605
+ if (!generated || generated.length !== W * H) v.fill(1);
2606
+ else v.set(generated);
2607
+ } else {
2608
+ const bytes = readRegion(mask.surface, { x: -ox, y: -oy, width: W, height: H });
2609
+ for (let i = 0; i < v.length; i++) v[i] = bytes[i] / 255;
2610
+ }
2611
+ const feather = mask.feather ?? 0;
2612
+ if (feather > 0) v = gaussianBlur(v, W, H, feather, "clamp");
2613
+ const density = mask.density ?? 1;
2614
+ if (density !== 1) for (let i = 0; i < v.length; i++) v[i] = 1 - density * (1 - v[i]);
2615
+ plane = { data: v, width: W, height: H };
2616
+ this.planeCache.set(key, plane);
2617
+ while (this.planeCache.size > 8) this.planeCache.delete(this.planeCache.keys().next().value);
2618
+ return plane;
2619
+ }
2620
+ maskTile(layer, r) {
2621
+ const mask = layer.mask;
2622
+ if (!mask || mask.enabled === false) return null;
2623
+ const ox = isPixel(layer) ? layer.offset?.x ?? 0 : 0;
2624
+ const oy = isPixel(layer) ? layer.offset?.y ?? 0 : 0;
2625
+ const n = r.width * r.height;
2626
+ const out = new Float32Array(n);
2627
+ const density = mask.density ?? 1;
2628
+ if (mask.source || (mask.feather ?? 0) > 0) {
2629
+ const plane = this.planeFor(mask, ox, oy);
2630
+ const W = plane.width;
2631
+ const H = plane.height;
2632
+ const outside = mask.source ? 1 : 1 - density * (1 - mask.defaultValue / 255);
2633
+ out.fill(outside);
2634
+ const x0 = Math.max(0, r.x);
2635
+ const y0 = Math.max(0, r.y);
2636
+ const x1 = Math.min(W, r.x + r.width);
2637
+ const y1 = Math.min(H, r.y + r.height);
2638
+ for (let y = y0; y < y1; y++) {
2639
+ const s = y * W;
2640
+ const d = (y - r.y) * r.width - r.x;
2641
+ for (let x = x0; x < x1; x++) out[d + x] = plane.data[s + x];
2642
+ }
2643
+ } else {
2644
+ const bytes = readRegion(mask.surface, { x: r.x - ox, y: r.y - oy, width: r.width, height: r.height });
2645
+ let all = true;
2646
+ for (let i = 0; i < n; i++) {
2647
+ out[i] = bytes[i] / 255;
2648
+ if (bytes[i] !== 255) all = false;
2649
+ }
2650
+ if (all) return null;
2651
+ if (density !== 1) for (let i = 0; i < n; i++) out[i] = 1 - density * (1 - out[i]);
2652
+ }
2653
+ return out;
2654
+ }
2655
+ // --- Running steps ---------------------------------------------------------
2656
+ /** Runs one step onto `acc`; returns whether `acc` is still empty. */
2657
+ runStep(step, acc, empty, r) {
2658
+ if (step.kind === "adjust") {
2659
+ if (empty) return true;
2660
+ this.applyFilterRun(step.layers, acc, r);
2661
+ return false;
2662
+ }
2663
+ const layer = step.layer;
2664
+ if (!layer.visible) return empty;
2665
+ const clipped = step.clipped.filter((l) => l.visible);
2666
+ if (this.hasEffects(layer)) return this.runEffectStep(layer, clipped, acc, empty, r);
2667
+ const coverage = this.maskTile(layer, r);
2668
+ if (coverage && coverage.every((v) => v <= 0)) return empty;
2669
+ if (step.kind === "group") {
2670
+ if (layer.blend === "pass-through" && !clipped.length) {
2671
+ if (layer.opacity >= 1 && !coverage) return this.runSteps(step.steps, acc, empty, r);
2672
+ const before = new Uint8ClampedArray(acc);
2673
+ const stillEmpty = this.runSteps(step.steps, acc, empty, r);
2674
+ lerpTile(acc, before, layer.opacity, coverage);
2675
+ return empty && stillEmpty;
2676
+ }
2677
+ const buf2 = new Uint8ClampedArray(acc.length);
2678
+ if (this.runSteps(step.steps, buf2, true, r)) return empty;
2679
+ this.applyClipped(clipped, buf2, r);
2680
+ const mode = layer.blend === "pass-through" ? "normal" : layer.blend;
2681
+ return this.blendOnto(acc, buf2, empty, mode, layer.opacity, coverage);
2682
+ }
2683
+ const content = this.contentTile(step.layer, r);
2684
+ if (!content) return empty;
2685
+ const fill2 = isPixel(step.layer) ? step.layer.fillOpacity ?? 1 : 1;
2686
+ const blend2 = this.contentBlend(step.layer);
2687
+ if (!clipped.length) return this.blendOnto(acc, content, empty, blend2, layer.opacity * fill2, coverage);
2688
+ const buf = new Uint8ClampedArray(acc.length);
2689
+ blendTile(buf, content, "normal", fill2, coverage);
2690
+ this.applyClipped(clipped, buf, r);
2691
+ return this.blendOnto(acc, buf, empty, blend2, layer.opacity, null);
2692
+ }
2693
+ runSteps(steps, acc, empty, r) {
2694
+ for (const s of steps) empty = this.runStep(s, acc, empty, r);
2695
+ return empty;
2696
+ }
2697
+ contentBlend(layer) {
2698
+ return this.profile.contentBlend?.(layer) ?? layer.blend;
2699
+ }
2700
+ blendOnto(acc, src, empty, mode, alpha, coverage) {
2701
+ if (!(alpha > 0)) return empty;
2702
+ if (empty && mode === "normal" && alpha >= 1 && !coverage) {
2703
+ acc.set(src);
2704
+ return false;
2705
+ }
2706
+ blendTile(acc, src, mode, alpha, coverage);
2707
+ return false;
2708
+ }
2709
+ applyClipped(clipped, buf, r) {
2710
+ let run = [];
2711
+ const flush = () => {
2712
+ if (run.length) this.applyFilterRun(run, buf, r);
2713
+ run = [];
2714
+ };
2715
+ for (const c of clipped) {
2716
+ if (this.isFilter(c)) {
2717
+ run.push(c);
2718
+ continue;
2719
+ }
2720
+ flush();
2721
+ if (this.hasEffects(c) && !isGroup(c)) {
2722
+ this.compositeEffects(c, [], buf, r, true);
2723
+ continue;
2724
+ }
2725
+ const coverage = this.maskTile(c, r);
2726
+ if (isGroup(c)) {
2727
+ const inner = new Uint8ClampedArray(buf.length);
2728
+ if (this.runSteps(this.buildSteps(c.children ?? []), inner, true, r)) continue;
2729
+ const mode = c.blend === "pass-through" ? "normal" : c.blend;
2730
+ blendAtop(buf, inner, mode, c.opacity, coverage);
2731
+ continue;
2732
+ }
2733
+ const content = this.contentTile(c, r);
2734
+ if (!content) continue;
2735
+ const fill2 = isPixel(c) ? c.fillOpacity ?? 1 : 1;
2736
+ blendAtop(buf, content, this.contentBlend(c), c.opacity * fill2, coverage);
2737
+ }
2738
+ flush();
2739
+ }
2740
+ applyFilterRun(layers, acc, r) {
2741
+ const visible = [];
2742
+ let masks;
2743
+ for (const node of layers) {
2744
+ if (!node.visible) continue;
2745
+ const cov = this.maskTile(node, r);
2746
+ if (cov) {
2747
+ if (cov.every((v) => v <= 0)) continue;
2748
+ (masks ??= /* @__PURE__ */ new Map()).set(node.id, cov);
2749
+ }
2750
+ visible.push(node);
2751
+ }
2752
+ if (visible.length) this.profile.applyFilterRun?.(visible, acc, r, masks, this.sources);
2753
+ }
2754
+ };
2755
+ function filterOverCanvas(surface, ox, oy, filters, width, height) {
2756
+ if (width <= 0 || height <= 0) return emptySurface(4);
2757
+ const data = readRegion(surface, { x: -ox, y: -oy, width, height });
2758
+ filters.apply(data, width, height);
2759
+ return surfaceFromPixels({ width, height, data }, 4, 0, 0);
2760
+ }
2761
+ function copyRect(data, r, a) {
2762
+ const out = new Uint8ClampedArray(a.width * a.height * 4);
2763
+ for (let y = 0; y < a.height; y++) {
2764
+ const s = ((a.y - r.y + y) * r.width + (a.x - r.x)) * 4;
2765
+ out.set(data.subarray(s, s + a.width * 4), y * a.width * 4);
2766
+ }
2767
+ return out;
2768
+ }
2769
+ function pasteRect(data, r, part, a) {
2770
+ for (let y = 0; y < a.height; y++) data.set(part.subarray(y * a.width * 4, (y + 1) * a.width * 4), ((a.y - r.y + y) * r.width + (a.x - r.x)) * 4);
2771
+ }
2772
+ function inflateRect(r, m) {
2773
+ return { x: r.x - m, y: r.y - m, width: r.width + 2 * m, height: r.height + 2 * m };
2774
+ }
2775
+ function intersectRect(a, b) {
2776
+ const x0 = Math.max(a.x, b.x);
2777
+ const y0 = Math.max(a.y, b.y);
2778
+ const x1 = Math.min(a.x + a.width, b.x + b.width);
2779
+ const y1 = Math.min(a.y + a.height, b.y + b.height);
2780
+ return x1 <= x0 || y1 <= y0 ? { x: x0, y: y0, width: 0, height: 0 } : { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
2781
+ }
2782
+ function changedTiles(a, b) {
2783
+ const out = [];
2784
+ for (const [k, t] of b.tiles) if (a.tiles.get(k) !== t) out.push(k);
2785
+ for (const k of a.tiles.keys()) if (!b.tiles.has(k)) out.push(k);
2786
+ return out;
2787
+ }
2788
+
2789
+ // src/raster/zlib.ts
2790
+ var LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
2791
+ var LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
2792
+ var DIST_BASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
2793
+ var DIST_EXTRA = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
2794
+ var CL_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
2795
+ var ZlibError = class extends Error {
2796
+ constructor(message) {
2797
+ super(message);
2798
+ this.name = "ZlibError";
2799
+ }
2800
+ };
2801
+ function buildHuffman(lengths, n) {
2802
+ const counts = new Uint16Array(16);
2803
+ for (let i = 0; i < n; i++) counts[lengths[i]]++;
2804
+ counts[0] = 0;
2805
+ const offs = new Uint16Array(16);
2806
+ for (let i = 1; i < 16; i++) offs[i] = offs[i - 1] + counts[i - 1];
2807
+ const symbols = new Uint16Array(n);
2808
+ for (let i = 0; i < n; i++) if (lengths[i]) symbols[offs[lengths[i]]++] = i;
2809
+ return { counts, symbols };
2810
+ }
2811
+ var fixedLit = null;
2812
+ var fixedDist = null;
2813
+ function fixedTables() {
2814
+ if (!fixedLit || !fixedDist) {
2815
+ const l = new Uint8Array(288);
2816
+ for (let i = 0; i < 144; i++) l[i] = 8;
2817
+ for (let i = 144; i < 256; i++) l[i] = 9;
2818
+ for (let i = 256; i < 280; i++) l[i] = 7;
2819
+ for (let i = 280; i < 288; i++) l[i] = 8;
2820
+ fixedLit = buildHuffman(l, 288);
2821
+ const d = new Uint8Array(30).fill(5);
2822
+ fixedDist = buildHuffman(d, 30);
2823
+ }
2824
+ return [fixedLit, fixedDist];
2825
+ }
2826
+ var BitReader = class {
2827
+ constructor(data) {
2828
+ this.data = data;
2829
+ }
2830
+ data;
2831
+ pos = 0;
2832
+ bit = 0;
2833
+ bits = 0;
2834
+ need(n) {
2835
+ while (this.bits < n) {
2836
+ if (this.pos >= this.data.length) throw new ZlibError("Unexpected end of compressed data");
2837
+ this.bit |= this.data[this.pos++] << this.bits;
2838
+ this.bits += 8;
2839
+ }
2840
+ const v = this.bit & (1 << n) - 1;
2841
+ this.bit >>>= n;
2842
+ this.bits -= n;
2843
+ return v;
2844
+ }
2845
+ decode(h) {
2846
+ let code = 0;
2847
+ let first = 0;
2848
+ let index = 0;
2849
+ for (let len = 1; len < 16; len++) {
2850
+ code |= this.need(1);
2851
+ const count = h.counts[len];
2852
+ if (code - count < first) return h.symbols[index + (code - first)];
2853
+ index += count;
2854
+ first += count;
2855
+ first <<= 1;
2856
+ code <<= 1;
2857
+ }
2858
+ throw new ZlibError("Invalid Huffman code");
2859
+ }
2860
+ align() {
2861
+ this.bit = 0;
2862
+ this.bits = 0;
2863
+ }
2864
+ };
2865
+ var Output = class {
2866
+ constructor(hint, max) {
2867
+ this.max = max;
2868
+ this.buf = new Uint8Array(Math.max(1024, Math.min(hint, max)));
2869
+ }
2870
+ max;
2871
+ buf;
2872
+ len = 0;
2873
+ ensure(n) {
2874
+ if (this.len + n > this.max) throw new ZlibError(`Decompressed data exceeds ${this.max} bytes`);
2875
+ if (this.len + n <= this.buf.length) return;
2876
+ let size = this.buf.length * 2;
2877
+ while (size < this.len + n) size *= 2;
2878
+ const next = new Uint8Array(Math.min(size, Math.max(this.max, this.len + n)));
2879
+ next.set(this.buf.subarray(0, this.len));
2880
+ this.buf = next;
2881
+ }
2882
+ };
2883
+ function inflateRaw(data, options = {}) {
2884
+ const max = options.maxOutput ?? 512 * 1024 * 1024;
2885
+ const out = new Output(options.sizeHint ?? data.length * 4, max);
2886
+ const br = new BitReader(data);
2887
+ let final = 0;
2888
+ while (!final) {
2889
+ final = br.need(1);
2890
+ const type = br.need(2);
2891
+ if (type === 0) {
2892
+ br.align();
2893
+ if (br.pos + 4 > data.length) throw new ZlibError("Unexpected end of compressed data");
2894
+ const len = data[br.pos] | data[br.pos + 1] << 8;
2895
+ const nlen = data[br.pos + 2] | data[br.pos + 3] << 8;
2896
+ br.pos += 4;
2897
+ if ((len ^ 65535) !== nlen) throw new ZlibError("Stored block length mismatch");
2898
+ if (br.pos + len > data.length) throw new ZlibError("Unexpected end of compressed data");
2899
+ out.ensure(len);
2900
+ out.buf.set(data.subarray(br.pos, br.pos + len), out.len);
2901
+ out.len += len;
2902
+ br.pos += len;
2903
+ continue;
2904
+ }
2905
+ let lit;
2906
+ let dist;
2907
+ if (type === 1) [lit, dist] = fixedTables();
2908
+ else if (type === 2) {
2909
+ const hlit = br.need(5) + 257;
2910
+ const hdist = br.need(5) + 1;
2911
+ const hclen = br.need(4) + 4;
2912
+ const cl = new Uint8Array(19);
2913
+ for (let i2 = 0; i2 < hclen; i2++) cl[CL_ORDER[i2]] = br.need(3);
2914
+ const clh = buildHuffman(cl, 19);
2915
+ const lengths = new Uint8Array(hlit + hdist);
2916
+ let i = 0;
2917
+ while (i < hlit + hdist) {
2918
+ const sym = br.decode(clh);
2919
+ if (sym < 16) lengths[i++] = sym;
2920
+ else {
2921
+ let rep = 0;
2922
+ let val = 0;
2923
+ if (sym === 16) {
2924
+ if (i === 0) throw new ZlibError("Repeat with no previous length");
2925
+ val = lengths[i - 1];
2926
+ rep = 3 + br.need(2);
2927
+ } else if (sym === 17) rep = 3 + br.need(3);
2928
+ else rep = 11 + br.need(7);
2929
+ if (i + rep > hlit + hdist) throw new ZlibError("Too many code lengths");
2930
+ while (rep--) lengths[i++] = val;
2931
+ }
2932
+ }
2933
+ lit = buildHuffman(lengths.subarray(0, hlit), hlit);
2934
+ dist = buildHuffman(lengths.subarray(hlit), hdist);
2935
+ } else throw new ZlibError("Invalid block type");
2936
+ for (; ; ) {
2937
+ const sym = br.decode(lit);
2938
+ if (sym < 256) {
2939
+ out.ensure(1);
2940
+ out.buf[out.len++] = sym;
2941
+ } else if (sym === 256) break;
2942
+ else {
2943
+ const li = sym - 257;
2944
+ if (li >= 29) throw new ZlibError("Invalid length symbol");
2945
+ const len = LENGTH_BASE[li] + br.need(LENGTH_EXTRA[li]);
2946
+ const di = br.decode(dist);
2947
+ if (di >= 30) throw new ZlibError("Invalid distance symbol");
2948
+ const d = DIST_BASE[di] + br.need(DIST_EXTRA[di]);
2949
+ if (d > out.len) throw new ZlibError("Distance too far back");
2950
+ out.ensure(len);
2951
+ const buf = out.buf;
2952
+ let p = out.len;
2953
+ for (let k = 0; k < len; k++, p++) buf[p] = buf[p - d];
2954
+ out.len = p;
2955
+ }
2956
+ }
2957
+ }
2958
+ return out.buf.slice(0, out.len);
2959
+ }
2960
+ function adler32(data) {
2961
+ let a = 1;
2962
+ let b = 0;
2963
+ const n = data.length;
2964
+ let i = 0;
2965
+ while (i < n) {
2966
+ const end = Math.min(n, i + 3800);
2967
+ for (; i < end; i++) {
2968
+ a += data[i];
2969
+ b += a;
2970
+ }
2971
+ a %= 65521;
2972
+ b %= 65521;
2973
+ }
2974
+ return (b << 16 | a) >>> 0;
2975
+ }
2976
+ function inflate(data, options = {}) {
2977
+ if (data.length < 6) throw new ZlibError("Truncated zlib stream");
2978
+ const cmf = data[0];
2979
+ const flg = data[1];
2980
+ if ((cmf & 15) !== 8 || (cmf << 8 | flg) % 31 !== 0) throw new ZlibError("Not a zlib stream");
2981
+ if (flg & 32) throw new ZlibError("Preset dictionaries are not supported");
2982
+ const out = inflateRaw(data.subarray(2), options);
2983
+ if (options.verify !== false && data.length >= 6) {
2984
+ const t = data.length - 4;
2985
+ const want = (data[t] << 24 | data[t + 1] << 16 | data[t + 2] << 8 | data[t + 3]) >>> 0;
2986
+ if (adler32(out) !== want) throw new ZlibError("Adler-32 mismatch");
2987
+ }
2988
+ return out;
2989
+ }
2990
+ var BitWriter = class {
2991
+ buf;
2992
+ len = 0;
2993
+ bit = 0;
2994
+ bits = 0;
2995
+ constructor(hint) {
2996
+ this.buf = new Uint8Array(Math.max(1024, hint));
2997
+ }
2998
+ grow() {
2999
+ const next = new Uint8Array(this.buf.length * 2);
3000
+ next.set(this.buf);
3001
+ this.buf = next;
3002
+ }
3003
+ write(value, n) {
3004
+ this.bit |= value << this.bits;
3005
+ this.bits += n;
3006
+ while (this.bits >= 8) {
3007
+ if (this.len >= this.buf.length) this.grow();
3008
+ this.buf[this.len++] = this.bit & 255;
3009
+ this.bit >>>= 8;
3010
+ this.bits -= 8;
3011
+ }
3012
+ }
3013
+ flush() {
3014
+ if (this.bits > 0) {
3015
+ if (this.len >= this.buf.length) this.grow();
3016
+ this.buf[this.len++] = this.bit & 255;
3017
+ }
3018
+ this.bit = 0;
3019
+ this.bits = 0;
3020
+ }
3021
+ bytes() {
3022
+ return this.buf.slice(0, this.len);
3023
+ }
3024
+ };
3025
+ function codeLengths(freqs, maxBits) {
3026
+ const n = freqs.length;
3027
+ const lengths = new Uint8Array(n);
3028
+ const f = Float64Array.from(freqs);
3029
+ for (; ; ) {
3030
+ const nodes = [];
3031
+ for (let i = 0; i < n; i++) if (f[i] > 0) nodes.push({ w: f[i], sym: i });
3032
+ lengths.fill(0);
3033
+ if (nodes.length === 0) return lengths;
3034
+ if (nodes.length === 1) {
3035
+ lengths[nodes[0].sym] = 1;
3036
+ return lengths;
3037
+ }
3038
+ const heap = nodes.map((_, i) => i);
3039
+ const less = (a, b) => nodes[a].w < nodes[b].w || nodes[a].w === nodes[b].w && a < b;
3040
+ const down = (i) => {
3041
+ for (; ; ) {
3042
+ const l = 2 * i + 1;
3043
+ const r = l + 1;
3044
+ let m = i;
3045
+ if (l < heap.length && less(heap[l], heap[m])) m = l;
3046
+ if (r < heap.length && less(heap[r], heap[m])) m = r;
3047
+ if (m === i) return;
3048
+ [heap[i], heap[m]] = [heap[m], heap[i]];
3049
+ i = m;
3050
+ }
3051
+ };
3052
+ const up = (i) => {
3053
+ while (i > 0) {
3054
+ const p = i - 1 >> 1;
3055
+ if (!less(heap[i], heap[p])) return;
3056
+ [heap[i], heap[p]] = [heap[p], heap[i]];
3057
+ i = p;
3058
+ }
3059
+ };
3060
+ for (let i = (heap.length >> 1) - 1; i >= 0; i--) down(i);
3061
+ const pop = () => {
3062
+ const top = heap[0];
3063
+ const last = heap.pop();
3064
+ if (heap.length) {
3065
+ heap[0] = last;
3066
+ down(0);
3067
+ }
3068
+ return top;
3069
+ };
3070
+ while (heap.length > 1) {
3071
+ const a = pop();
3072
+ const b = pop();
3073
+ nodes.push({ w: nodes[a].w + nodes[b].w, sym: -1, left: a, right: b });
3074
+ heap.push(nodes.length - 1);
3075
+ up(heap.length - 1);
3076
+ }
3077
+ let maxLen = 0;
3078
+ const stack = [[heap[0], 0]];
3079
+ while (stack.length) {
3080
+ const [i, d] = stack.pop();
3081
+ const node = nodes[i];
3082
+ if (node.sym >= 0) {
3083
+ lengths[node.sym] = d;
3084
+ if (d > maxLen) maxLen = d;
3085
+ } else {
3086
+ stack.push([node.left, d + 1], [node.right, d + 1]);
3087
+ }
3088
+ }
3089
+ if (maxLen <= maxBits) return lengths;
3090
+ for (let i = 0; i < n; i++) if (f[i] > 0) f[i] = Math.floor(f[i] / 2) + 1;
3091
+ }
3092
+ }
3093
+ function canonicalCodes(lengths) {
3094
+ const blCount = new Uint16Array(16);
3095
+ for (const l of lengths) blCount[l]++;
3096
+ blCount[0] = 0;
3097
+ const next = new Uint16Array(16);
3098
+ let code = 0;
3099
+ for (let bits = 1; bits < 16; bits++) {
3100
+ code = code + blCount[bits - 1] << 1;
3101
+ next[bits] = code;
3102
+ }
3103
+ const codes = new Uint16Array(lengths.length);
3104
+ for (let i = 0; i < lengths.length; i++) {
3105
+ const len = lengths[i];
3106
+ if (!len) continue;
3107
+ let c = next[len]++;
3108
+ let r = 0;
3109
+ for (let k = 0; k < len; k++) {
3110
+ r = r << 1 | c & 1;
3111
+ c >>= 1;
3112
+ }
3113
+ codes[i] = r;
3114
+ }
3115
+ return codes;
3116
+ }
3117
+ function lengthSymbol(len) {
3118
+ let i = 28;
3119
+ while (LENGTH_BASE[i] > len) i--;
3120
+ return i;
3121
+ }
3122
+ function distSymbol(d) {
3123
+ let i = 29;
3124
+ while (DIST_BASE[i] > d) i--;
3125
+ return i;
3126
+ }
3127
+ var WINDOW = 32768;
3128
+ var HASH_BITS = 15;
3129
+ var HASH_SIZE = 1 << HASH_BITS;
3130
+ var MAX_CHAIN = 24;
3131
+ var MAX_MATCH = 258;
3132
+ var BLOCK_SYMBOLS = 65536;
3133
+ function deflateRaw(data, level = 1) {
3134
+ const w = new BitWriter(Math.ceil(data.length / 2) + 64);
3135
+ const n = data.length;
3136
+ if (level === 0 || n === 0) {
3137
+ let pos = 0;
3138
+ do {
3139
+ const len = Math.min(65535, n - pos);
3140
+ const final = pos + len >= n ? 1 : 0;
3141
+ w.write(final, 1);
3142
+ w.write(0, 2);
3143
+ w.flush();
3144
+ w.write(len & 65535, 16);
3145
+ w.write(~len & 65535, 16);
3146
+ for (let i2 = 0; i2 < len; i2++) w.write(data[pos + i2], 8);
3147
+ pos += len;
3148
+ } while (pos < n);
3149
+ return w.bytes();
3150
+ }
3151
+ const head = new Int32Array(HASH_SIZE).fill(-1);
3152
+ const prev = new Int32Array(WINDOW);
3153
+ const litLen = new Uint16Array(BLOCK_SYMBOLS);
3154
+ const dists = new Uint16Array(BLOCK_SYMBOLS);
3155
+ let count = 0;
3156
+ const hash = (i2) => (data[i2] << 10 ^ data[i2 + 1] << 5 ^ data[i2 + 2]) * 2654435761 >>> 32 - HASH_BITS;
3157
+ const insert = (i2) => {
3158
+ if (i2 + 2 >= n) return;
3159
+ const h = hash(i2);
3160
+ prev[i2 & WINDOW - 1] = head[h];
3161
+ head[h] = i2;
3162
+ };
3163
+ const flushBlock = (final) => {
3164
+ const litFreq = new Uint32Array(286);
3165
+ const distFreq = new Uint32Array(30);
3166
+ for (let k = 0; k < count; k++) {
3167
+ if (dists[k] === 0) litFreq[litLen[k]]++;
3168
+ else {
3169
+ litFreq[257 + lengthSymbol(litLen[k])]++;
3170
+ distFreq[distSymbol(dists[k])]++;
3171
+ }
3172
+ }
3173
+ litFreq[256] = 1;
3174
+ let anyDist = false;
3175
+ for (const v of distFreq) if (v) anyDist = true;
3176
+ if (!anyDist) distFreq[0] = 1;
3177
+ const litLengths = codeLengths(litFreq, 15);
3178
+ const distLengths = codeLengths(distFreq, 15);
3179
+ let hlit = 286;
3180
+ while (hlit > 257 && !litLengths[hlit - 1]) hlit--;
3181
+ let hdist = 30;
3182
+ while (hdist > 1 && !distLengths[hdist - 1]) hdist--;
3183
+ const all = new Uint8Array(hlit + hdist);
3184
+ all.set(litLengths.subarray(0, hlit));
3185
+ all.set(distLengths.subarray(0, hdist), hlit);
3186
+ const clSyms = [];
3187
+ const clExtra = [];
3188
+ for (let i2 = 0; i2 < all.length; ) {
3189
+ const v = all[i2];
3190
+ let run = 1;
3191
+ while (i2 + run < all.length && all[i2 + run] === v) run++;
3192
+ if (v === 0 && run >= 3) {
3193
+ const r = Math.min(run, 138);
3194
+ if (r >= 11) {
3195
+ clSyms.push(18);
3196
+ clExtra.push(r - 11);
3197
+ } else {
3198
+ clSyms.push(17);
3199
+ clExtra.push(r - 3);
3200
+ }
3201
+ i2 += r;
3202
+ } else if (v !== 0 && run >= 4) {
3203
+ clSyms.push(v);
3204
+ clExtra.push(0);
3205
+ const r = Math.min(run - 1, 6);
3206
+ clSyms.push(16);
3207
+ clExtra.push(r - 3);
3208
+ i2 += r + 1;
3209
+ } else {
3210
+ clSyms.push(v);
3211
+ clExtra.push(0);
3212
+ i2++;
3213
+ }
3214
+ }
3215
+ const clFreq = new Uint32Array(19);
3216
+ for (const s of clSyms) clFreq[s]++;
3217
+ const clLengths = codeLengths(clFreq, 7);
3218
+ let hclen = 19;
3219
+ while (hclen > 4 && !clLengths[CL_ORDER[hclen - 1]]) hclen--;
3220
+ const clCodes = canonicalCodes(clLengths);
3221
+ const litCodes = canonicalCodes(litLengths);
3222
+ const distCodes = canonicalCodes(distLengths);
3223
+ w.write(final ? 1 : 0, 1);
3224
+ w.write(2, 2);
3225
+ w.write(hlit - 257, 5);
3226
+ w.write(hdist - 1, 5);
3227
+ w.write(hclen - 4, 4);
3228
+ for (let i2 = 0; i2 < hclen; i2++) w.write(clLengths[CL_ORDER[i2]], 3);
3229
+ for (let i2 = 0; i2 < clSyms.length; i2++) {
3230
+ const s = clSyms[i2];
3231
+ w.write(clCodes[s], clLengths[s]);
3232
+ if (s === 16) w.write(clExtra[i2], 2);
3233
+ else if (s === 17) w.write(clExtra[i2], 3);
3234
+ else if (s === 18) w.write(clExtra[i2], 7);
3235
+ }
3236
+ for (let k = 0; k < count; k++) {
3237
+ const d = dists[k];
3238
+ if (d === 0) {
3239
+ const s = litLen[k];
3240
+ w.write(litCodes[s], litLengths[s]);
3241
+ } else {
3242
+ const len = litLen[k];
3243
+ const li = lengthSymbol(len);
3244
+ w.write(litCodes[257 + li], litLengths[257 + li]);
3245
+ if (LENGTH_EXTRA[li]) w.write(len - LENGTH_BASE[li], LENGTH_EXTRA[li]);
3246
+ const di = distSymbol(d);
3247
+ w.write(distCodes[di], distLengths[di]);
3248
+ if (DIST_EXTRA[di]) w.write(d - DIST_BASE[di], DIST_EXTRA[di]);
3249
+ }
3250
+ }
3251
+ w.write(litCodes[256], litLengths[256]);
3252
+ count = 0;
3253
+ };
3254
+ let i = 0;
3255
+ while (i < n) {
3256
+ let bestLen = 0;
3257
+ let bestDist = 0;
3258
+ if (i + 2 < n) {
3259
+ let cand = head[hash(i)];
3260
+ let chain = MAX_CHAIN;
3261
+ const limit = Math.min(MAX_MATCH, n - i);
3262
+ while (cand >= 0 && i - cand <= WINDOW - 1 && chain-- > 0) {
3263
+ if (data[cand + bestLen] === data[i + bestLen]) {
3264
+ let l = 0;
3265
+ while (l < limit && data[cand + l] === data[i + l]) l++;
3266
+ if (l > bestLen) {
3267
+ bestLen = l;
3268
+ bestDist = i - cand;
3269
+ if (l === limit) break;
3270
+ }
3271
+ }
3272
+ const next = prev[cand & WINDOW - 1];
3273
+ if (next >= cand) break;
3274
+ cand = next;
3275
+ }
3276
+ }
3277
+ if (bestLen >= 3) {
3278
+ litLen[count] = bestLen;
3279
+ dists[count] = bestDist;
3280
+ count++;
3281
+ const end = i + bestLen;
3282
+ const insertEnd = bestLen > 64 ? i + 64 : end;
3283
+ for (let k = i; k < insertEnd; k++) insert(k);
3284
+ for (let k = Math.max(insertEnd, end - 3); k < end; k++) insert(k);
3285
+ i = end;
3286
+ } else {
3287
+ litLen[count] = data[i];
3288
+ dists[count] = 0;
3289
+ count++;
3290
+ insert(i);
3291
+ i++;
3292
+ }
3293
+ if (count >= BLOCK_SYMBOLS - 1 && i < n) flushBlock(false);
3294
+ }
3295
+ flushBlock(true);
3296
+ w.flush();
3297
+ return w.bytes();
3298
+ }
3299
+ function deflate(data, level = 1) {
3300
+ const raw = deflateRaw(data, level);
3301
+ const out = new Uint8Array(raw.length + 6);
3302
+ out[0] = 120;
3303
+ out[1] = 1;
3304
+ out.set(raw, 2);
3305
+ const a = adler32(data);
3306
+ const t = out.length - 4;
3307
+ out[t] = a >>> 24;
3308
+ out[t + 1] = a >>> 16 & 255;
3309
+ out[t + 2] = a >>> 8 & 255;
3310
+ out[t + 3] = a & 255;
3311
+ return out;
3312
+ }
3313
+
3314
+ // src/raster/png.ts
3315
+ var SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
3316
+ var crcTable = null;
3317
+ function crc32(bytes, start, end) {
3318
+ if (!crcTable) {
3319
+ crcTable = new Uint32Array(256);
3320
+ for (let n = 0; n < 256; n++) {
3321
+ let c2 = n;
3322
+ for (let k = 0; k < 8; k++) c2 = c2 & 1 ? 3988292384 ^ c2 >>> 1 : c2 >>> 1;
3323
+ crcTable[n] = c2 >>> 0;
3324
+ }
3325
+ }
3326
+ let c = 4294967295;
3327
+ for (let i = start; i < end; i++) c = crcTable[(c ^ bytes[i]) & 255] ^ c >>> 8;
3328
+ return (c ^ 4294967295) >>> 0;
3329
+ }
3330
+ function paeth(a, b, c) {
3331
+ const p = a + b - c;
3332
+ const pa = Math.abs(p - a);
3333
+ const pb = Math.abs(p - b);
3334
+ const pc = Math.abs(p - c);
3335
+ return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
3336
+ }
3337
+ function encodePng(width, height, data, channels = 4) {
3338
+ const stride = width * channels;
3339
+ const raw = new Uint8Array((stride + 1) * height);
3340
+ const bpp = channels;
3341
+ const cand = [new Uint8Array(stride), new Uint8Array(stride), new Uint8Array(stride), new Uint8Array(stride), new Uint8Array(stride)];
3342
+ for (let y = 0; y < height; y++) {
3343
+ const row = y * stride;
3344
+ const prevRow = (y - 1) * stride;
3345
+ let best = 0;
3346
+ let bestScore = Infinity;
3347
+ for (let f = 0; f < 5; f++) {
3348
+ const out2 = cand[f];
3349
+ let score = 0;
3350
+ for (let x = 0; x < stride; x++) {
3351
+ const v = data[row + x];
3352
+ const a = x >= bpp ? data[row + x - bpp] : 0;
3353
+ const b = y > 0 ? data[prevRow + x] : 0;
3354
+ const c = x >= bpp && y > 0 ? data[prevRow + x - bpp] : 0;
3355
+ let r;
3356
+ switch (f) {
3357
+ case 0:
3358
+ r = v;
3359
+ break;
3360
+ case 1:
3361
+ r = v - a;
3362
+ break;
3363
+ case 2:
3364
+ r = v - b;
3365
+ break;
3366
+ case 3:
3367
+ r = v - (a + b >> 1);
3368
+ break;
3369
+ default:
3370
+ r = v - paeth(a, b, c);
3371
+ }
3372
+ r &= 255;
3373
+ out2[x] = r;
3374
+ score += r < 128 ? r : 256 - r;
3375
+ if (score >= bestScore) break;
3376
+ }
3377
+ if (score < bestScore) {
3378
+ bestScore = score;
3379
+ best = f;
3380
+ }
3381
+ }
3382
+ const o = y * (stride + 1);
3383
+ raw[o] = best;
3384
+ raw.set(cand[best], o + 1);
3385
+ }
3386
+ const idat = deflate(raw);
3387
+ const chunks = [];
3388
+ const ihdr = new Uint8Array(13);
3389
+ writeU32(ihdr, 0, width);
3390
+ writeU32(ihdr, 4, height);
3391
+ ihdr[8] = 8;
3392
+ ihdr[9] = channels === 1 ? 0 : 6;
3393
+ chunks.push(["IHDR", ihdr], ["IDAT", idat], ["IEND", new Uint8Array(0)]);
3394
+ let size = 8;
3395
+ for (const [, d] of chunks) size += 12 + d.length;
3396
+ const out = new Uint8Array(size);
3397
+ out.set(SIGNATURE, 0);
3398
+ let p = 8;
3399
+ for (const [type, d] of chunks) {
3400
+ writeU32(out, p, d.length);
3401
+ for (let i = 0; i < 4; i++) out[p + 4 + i] = type.charCodeAt(i);
3402
+ out.set(d, p + 8);
3403
+ writeU32(out, p + 8 + d.length, crc32(out, p + 4, p + 8 + d.length));
3404
+ p += 12 + d.length;
3405
+ }
3406
+ return out;
3407
+ }
3408
+ function writeU32(b, o, v) {
3409
+ b[o] = v >>> 24;
3410
+ b[o + 1] = v >>> 16 & 255;
3411
+ b[o + 2] = v >>> 8 & 255;
3412
+ b[o + 3] = v & 255;
3413
+ }
3414
+ function readU32(b, o) {
3415
+ return (b[o] << 24 | b[o + 1] << 16 | b[o + 2] << 8 | b[o + 3]) >>> 0;
3416
+ }
3417
+ function isPng(bytes) {
3418
+ return bytes.length > 8 && SIGNATURE.every((v, i) => bytes[i] === v);
3419
+ }
3420
+ function decodePng(bytes, options = {}) {
3421
+ if (!isPng(bytes)) throw new Error("Not a PNG image.");
3422
+ let p = 8;
3423
+ let width = 0;
3424
+ let height = 0;
3425
+ let depth = 0;
3426
+ let type = 0;
3427
+ let interlace = 0;
3428
+ let palette = null;
3429
+ let trns = null;
3430
+ const idat = [];
3431
+ while (p + 8 <= bytes.length) {
3432
+ const len = readU32(bytes, p);
3433
+ const t = String.fromCharCode(bytes[p + 4], bytes[p + 5], bytes[p + 6], bytes[p + 7]);
3434
+ const body = bytes.subarray(p + 8, p + 8 + len);
3435
+ if (p + 12 + len > bytes.length) throw new Error("Truncated PNG.");
3436
+ if (t === "IHDR") {
3437
+ width = readU32(body, 0);
3438
+ height = readU32(body, 4);
3439
+ depth = body[8];
3440
+ type = body[9];
3441
+ interlace = body[12];
3442
+ } else if (t === "PLTE") palette = body;
3443
+ else if (t === "tRNS") trns = body;
3444
+ else if (t === "IDAT") idat.push(body);
3445
+ else if (t === "IEND") break;
3446
+ p += 12 + len;
3447
+ }
3448
+ if (!width || !height) throw new Error("PNG has no image header.");
3449
+ if (interlace) throw new Error("Interlaced PNGs are not supported here; decode them with a canvas.");
3450
+ const maxPixels = options.maxPixels ?? 1 << 28;
3451
+ if (width * height > maxPixels) throw new Error(`PNG is too large (${width}\xD7${height}).`);
3452
+ const samples = type === 0 ? 1 : type === 2 ? 3 : type === 3 ? 1 : type === 4 ? 2 : type === 6 ? 4 : 0;
3453
+ if (!samples || ![1, 2, 4, 8, 16].includes(depth)) throw new Error(`Unsupported PNG format (type ${type}, depth ${depth}).`);
3454
+ let total = 0;
3455
+ for (const c of idat) total += c.length;
3456
+ const z = new Uint8Array(total);
3457
+ let o = 0;
3458
+ for (const c of idat) {
3459
+ z.set(c, o);
3460
+ o += c.length;
3461
+ }
3462
+ const bitsPerPixel = samples * depth;
3463
+ const stride = Math.ceil(width * bitsPerPixel / 8);
3464
+ const raw = inflate(z, { maxOutput: (stride + 1) * height, sizeHint: (stride + 1) * height });
3465
+ if (raw.length < (stride + 1) * height) throw new Error("PNG image data is truncated.");
3466
+ const bpp = Math.max(1, bitsPerPixel >> 3);
3467
+ const lines = new Uint8Array(stride * height);
3468
+ for (let y = 0; y < height; y++) {
3469
+ const f = raw[y * (stride + 1)];
3470
+ const src = y * (stride + 1) + 1;
3471
+ const dst = y * stride;
3472
+ const up = dst - stride;
3473
+ for (let x = 0; x < stride; x++) {
3474
+ const v = raw[src + x];
3475
+ const a = x >= bpp ? lines[dst + x - bpp] : 0;
3476
+ const b = y > 0 ? lines[up + x] : 0;
3477
+ const c = x >= bpp && y > 0 ? lines[up + x - bpp] : 0;
3478
+ let r;
3479
+ switch (f) {
3480
+ case 0:
3481
+ r = v;
3482
+ break;
3483
+ case 1:
3484
+ r = v + a;
3485
+ break;
3486
+ case 2:
3487
+ r = v + b;
3488
+ break;
3489
+ case 3:
3490
+ r = v + (a + b >> 1);
3491
+ break;
3492
+ case 4:
3493
+ r = v + paeth(a, b, c);
3494
+ break;
3495
+ default:
3496
+ throw new Error(`Invalid PNG filter ${f}.`);
3497
+ }
3498
+ lines[dst + x] = r & 255;
3499
+ }
3500
+ }
3501
+ const sample = (y, i) => {
3502
+ const row = y * stride;
3503
+ if (depth === 8) return lines[row + i];
3504
+ if (depth === 16) return lines[row + i * 2];
3505
+ const perByte = 8 / depth;
3506
+ const byte = lines[row + Math.floor(i / perByte)];
3507
+ const shift = 8 - depth * (i % perByte + 1);
3508
+ const v = byte >> shift & (1 << depth) - 1;
3509
+ return type === 3 ? v : Math.round(v * 255 / ((1 << depth) - 1));
3510
+ };
3511
+ const gray = options.keepGray && type === 0 && !trns;
3512
+ const channels = gray ? 1 : 4;
3513
+ const out = new Uint8ClampedArray(width * height * channels);
3514
+ const trnsGray = trns && type === 0 ? depth === 16 ? trns[0] : sample16(trns, 0, depth) : -1;
3515
+ const trnsRgb = trns && type === 2 ? [0, 1, 2].map((k) => depth === 16 ? trns[k * 2] : sample16(trns, k, depth)) : null;
3516
+ for (let y = 0; y < height; y++) {
3517
+ for (let x = 0; x < width; x++) {
3518
+ const d = (y * width + x) * channels;
3519
+ if (type === 0) {
3520
+ const v = sample(y, x);
3521
+ if (gray) out[d] = v;
3522
+ else {
3523
+ out[d] = out[d + 1] = out[d + 2] = v;
3524
+ out[d + 3] = v === trnsGray && trns ? 0 : 255;
3525
+ }
3526
+ } else if (type === 2) {
3527
+ const r = sample(y, x * 3);
3528
+ const g = sample(y, x * 3 + 1);
3529
+ const b = sample(y, x * 3 + 2);
3530
+ out[d] = r;
3531
+ out[d + 1] = g;
3532
+ out[d + 2] = b;
3533
+ out[d + 3] = trnsRgb && r === trnsRgb[0] && g === trnsRgb[1] && b === trnsRgb[2] ? 0 : 255;
3534
+ } else if (type === 3) {
3535
+ const i = sample(y, x);
3536
+ if (!palette || i * 3 + 2 >= palette.length) throw new Error("PNG palette index out of range.");
3537
+ out[d] = palette[i * 3];
3538
+ out[d + 1] = palette[i * 3 + 1];
3539
+ out[d + 2] = palette[i * 3 + 2];
3540
+ out[d + 3] = trns && i < trns.length ? trns[i] : 255;
3541
+ } else if (type === 4) {
3542
+ const v = sample(y, x * 2);
3543
+ out[d] = out[d + 1] = out[d + 2] = v;
3544
+ out[d + 3] = sample(y, x * 2 + 1);
3545
+ } else {
3546
+ out[d] = sample(y, x * 4);
3547
+ out[d + 1] = sample(y, x * 4 + 1);
3548
+ out[d + 2] = sample(y, x * 4 + 2);
3549
+ out[d + 3] = sample(y, x * 4 + 3);
3550
+ }
3551
+ }
3552
+ }
3553
+ return { width, height, channels, data: out };
3554
+ }
3555
+ function sample16(trns, k, depth) {
3556
+ const v = trns[k * 2] << 8 | trns[k * 2 + 1];
3557
+ if (depth === 8) return v & 255;
3558
+ return Math.round((v & (1 << depth) - 1) * 255 / ((1 << depth) - 1));
3559
+ }
3560
+ var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3561
+ var B64_INV = null;
3562
+ function bytesToBase64(bytes) {
3563
+ const parts = [];
3564
+ const CHUNK = 3 * 4096;
3565
+ for (let start = 0; start < bytes.length; start += CHUNK) {
3566
+ const end = Math.min(bytes.length, start + CHUNK);
3567
+ let s = "";
3568
+ let i = start;
3569
+ for (; i + 2 < end; i += 3) {
3570
+ const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
3571
+ s += B64[n >> 18] + B64[n >> 12 & 63] + B64[n >> 6 & 63] + B64[n & 63];
3572
+ }
3573
+ if (i < end) {
3574
+ const n = bytes[i] << 16 | (i + 1 < end ? bytes[i + 1] << 8 : 0);
3575
+ s += B64[n >> 18] + B64[n >> 12 & 63] + (i + 1 < end ? B64[n >> 6 & 63] : "=") + "=";
3576
+ }
3577
+ parts.push(s);
3578
+ }
3579
+ return parts.join("");
3580
+ }
3581
+ function base64ToBytes(text) {
3582
+ if (!B64_INV) {
3583
+ B64_INV = new Int16Array(128).fill(-1);
3584
+ for (let i = 0; i < 64; i++) B64_INV[B64.charCodeAt(i)] = i;
3585
+ B64_INV["-".charCodeAt(0)] = 62;
3586
+ B64_INV["_".charCodeAt(0)] = 63;
3587
+ }
3588
+ const inv = B64_INV;
3589
+ const clean = text.replace(/[\s=]/g, "");
3590
+ const out = new Uint8Array(Math.floor(clean.length * 3 / 4));
3591
+ let bits = 0;
3592
+ let acc = 0;
3593
+ let o = 0;
3594
+ for (let i = 0; i < clean.length; i++) {
3595
+ const c = clean.charCodeAt(i);
3596
+ const v = c < 128 ? inv[c] : -1;
3597
+ if (v < 0) throw new Error("Invalid base64 data.");
3598
+ acc = acc << 6 | v;
3599
+ bits += 6;
3600
+ if (bits >= 8) {
3601
+ bits -= 8;
3602
+ out[o++] = acc >> bits & 255;
3603
+ }
3604
+ }
3605
+ return out.subarray(0, o);
3606
+ }
3607
+ function parseDataUrl(url) {
3608
+ const m = /^data:([^;,]*)((?:;[^;,]*)*?),/i.exec(url);
3609
+ if (!m) return null;
3610
+ const body = url.slice(m[0].length);
3611
+ const isBase64 = /;base64/i.test(m[2]);
3612
+ if (isBase64) return { mime: m[1] || "text/plain", bytes: base64ToBytes(body) };
3613
+ const text = decodeURIComponent(body);
3614
+ const bytes = new Uint8Array(text.length);
3615
+ for (let i = 0; i < text.length; i++) bytes[i] = text.charCodeAt(i) & 255;
3616
+ return { mime: m[1] || "text/plain", bytes };
3617
+ }
3618
+ function pngDataUrl(width, height, data, channels = 4) {
3619
+ return `data:image/png;base64,${bytesToBase64(encodePng(width, height, data, channels))}`;
3620
+ }
3621
+
3622
+ export {
3623
+ DIRECT_GAUSSIAN_MAX_SIGMA,
3624
+ gaussianBlur,
3625
+ flattenLayers,
3626
+ findLayer,
3627
+ mapLayer,
3628
+ insertLayer,
3629
+ isLayerLocked,
3630
+ DEFAULT_GLOBAL_LIGHT,
3631
+ LAYER_EFFECT_KINDS,
3632
+ layerEffectLabel,
3633
+ isLayerEffectKind,
3634
+ defaultEffectGradient,
3635
+ newEffectId,
3636
+ defaultLayerEffect,
3637
+ hasActiveEffects,
3638
+ MAX_EFFECT_REACH,
3639
+ effectReach,
3640
+ effectsMargin,
3641
+ effectsScale,
3642
+ scaleLayerEffect,
3643
+ parseEffectColor,
3644
+ effectColorHex,
3645
+ LayerEffectError,
3646
+ validateEffectGradient,
3647
+ layerEffectFields,
3648
+ validateLayerEffect,
3649
+ mergeLayerEffect,
3650
+ validateLayerEffects,
3651
+ normalizeLayerEffects,
3652
+ effectsFingerprint,
3653
+ PatternTile,
3654
+ PatternPlacement,
3655
+ builtinPatternIds,
3656
+ builtinPattern,
3657
+ hashNoise,
3658
+ CURVE_LUT_SIZE,
3659
+ curveLut,
3660
+ STROKE_CHAMFER_MAX,
3661
+ contourLut,
3662
+ activeEffectItems,
3663
+ effectsAreLocal,
3664
+ buildEffectMaps,
3665
+ gradientT,
3666
+ compositeLayerEffects,
3667
+ renderLayerEffects,
3668
+ blendTile,
3669
+ RasterCompositor,
3670
+ changedTiles,
3671
+ ZlibError,
3672
+ inflateRaw,
3673
+ adler32,
3674
+ inflate,
3675
+ deflateRaw,
3676
+ deflate,
3677
+ encodePng,
3678
+ isPng,
3679
+ decodePng,
3680
+ bytesToBase64,
3681
+ base64ToBytes,
3682
+ parseDataUrl,
3683
+ pngDataUrl
3684
+ };
3685
+ //# sourceMappingURL=chunk-747CGOT2.js.map