@pixerate/editor 0.120.0 → 0.122.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/{chunk-HTAFDTNR.cjs → chunk-4PUPGALM.cjs} +251 -156
- package/dist/chunk-4PUPGALM.cjs.map +1 -0
- package/dist/{chunk-N3467VD6.js → chunk-7QN76ESJ.js} +1 -179
- package/dist/chunk-7QN76ESJ.js.map +1 -0
- package/dist/{chunk-UMVW2Q66.js → chunk-CLTTLLWA.js} +1090 -982
- package/dist/chunk-CLTTLLWA.js.map +1 -0
- package/dist/{chunk-EJVDBITD.cjs → chunk-DCLJNVZS.cjs} +2 -180
- package/dist/chunk-DCLJNVZS.cjs.map +1 -0
- package/dist/{chunk-WMRIG4XQ.cjs → chunk-DFFBJV3U.cjs} +1855 -1747
- package/dist/chunk-DFFBJV3U.cjs.map +1 -0
- package/dist/chunk-GX332W2C.js +181 -0
- package/dist/chunk-GX332W2C.js.map +1 -0
- package/dist/{chunk-BOWUU4LJ.js → chunk-IX3YKUVK.js} +105 -10
- package/dist/chunk-IX3YKUVK.js.map +1 -0
- package/dist/{chunk-TS5IEUW6.cjs → chunk-K7V27T72.cjs} +178 -3
- package/dist/chunk-K7V27T72.cjs.map +1 -0
- package/dist/chunk-MUUXNRU4.cjs +181 -0
- package/dist/chunk-MUUXNRU4.cjs.map +1 -0
- package/dist/{chunk-YHW26LS2.js → chunk-ZZPGTNXJ.js} +177 -2
- package/dist/chunk-ZZPGTNXJ.js.map +1 -0
- package/dist/{render-B05K68_h.d.ts → deep-DLwKZDFr.d.ts} +143 -134
- package/dist/{render-DnAbIKcs.d.cts → deep-QS3ppbMX.d.cts} +143 -134
- package/dist/{export-D2D8hb_u.d.ts → export-VmTofKIk.d.ts} +11 -4
- package/dist/{export-fP2hZYjm.d.cts → export-jKKj8Fg8.d.cts} +11 -4
- package/dist/image-editor/index.cjs +760 -734
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +18 -6
- package/dist/image-editor/index.d.ts +18 -6
- package/dist/image-editor/index.js +37 -11
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint/index.cjs +22 -2
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +159 -1
- package/dist/paint/index.d.ts +159 -1
- package/dist/paint/index.js +21 -1
- package/dist/paint-editor/index.cjs +95 -94
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +2 -2
- package/dist/paint-editor/index.d.ts +2 -2
- package/dist/paint-editor/index.js +12 -11
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +5 -4
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +29 -7
- package/dist/raster/index.d.ts +29 -7
- package/dist/raster/index.js +11 -10
- package/dist/vector/index.cjs +14 -12
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.js +7 -5
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/pixerate-canvas/SKILL.md +23 -1
- package/skills/pixerate-image-editor/SKILL.md +7 -1
- package/skills/pixerate-image-editor/references/photoshop-files.md +4 -3
- package/skills/pixerate-paint-editor/SKILL.md +32 -7
- package/dist/chunk-BOWUU4LJ.js.map +0 -1
- package/dist/chunk-EJVDBITD.cjs.map +0 -1
- package/dist/chunk-HTAFDTNR.cjs.map +0 -1
- package/dist/chunk-N3467VD6.js.map +0 -1
- package/dist/chunk-TS5IEUW6.cjs.map +0 -1
- package/dist/chunk-UMVW2Q66.js.map +0 -1
- package/dist/chunk-WMRIG4XQ.cjs.map +0 -1
- package/dist/chunk-YHW26LS2.js.map +0 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
invertHomography,
|
|
8
8
|
isIdentityHomography,
|
|
9
9
|
multiplyHomographies
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GX332W2C.js";
|
|
11
11
|
import {
|
|
12
12
|
TILE,
|
|
13
13
|
TiledSurface,
|
|
@@ -464,877 +464,448 @@ function stableStringify(v) {
|
|
|
464
464
|
return JSON.stringify(v);
|
|
465
465
|
}
|
|
466
466
|
|
|
467
|
-
// src/raster/
|
|
468
|
-
var
|
|
469
|
-
var
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
base;
|
|
478
|
-
profile;
|
|
479
|
-
deep;
|
|
480
|
-
depth;
|
|
481
|
-
sources;
|
|
482
|
-
linear;
|
|
483
|
-
/** The composite of `layers` over `rect` as float RGBA in the working space (`rect.width × rect.height × 4`). */
|
|
484
|
-
composite(layers, rect, sources) {
|
|
485
|
-
this.sources = sources;
|
|
486
|
-
const acc = new Float32Array(Math.max(0, rect.width * rect.height * 4));
|
|
487
|
-
if (rect.width > 0 && rect.height > 0) this.runSteps(this.buildSteps(layers), acc, true, rect);
|
|
488
|
-
return acc;
|
|
489
|
-
}
|
|
490
|
-
// --- Steps (as RasterCompositor.buildSteps) ------------------------------------
|
|
491
|
-
isPixel(l) {
|
|
492
|
-
return l.type === "pixel" || (this.profile.isPixelLike?.(l) ?? false);
|
|
467
|
+
// src/raster/curve.ts
|
|
468
|
+
var CURVE_LUT_SIZE = 4096;
|
|
469
|
+
var clamp01 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
|
|
470
|
+
function curveLut(points, size = CURVE_LUT_SIZE) {
|
|
471
|
+
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]);
|
|
472
|
+
const dedup = [];
|
|
473
|
+
for (const p of pts) {
|
|
474
|
+
if (dedup.length && Math.abs(dedup[dedup.length - 1][0] - p[0]) < 1e-6) continue;
|
|
475
|
+
dedup.push(p);
|
|
493
476
|
}
|
|
494
|
-
|
|
495
|
-
|
|
477
|
+
const out = new Float32Array(size);
|
|
478
|
+
const n = dedup.length;
|
|
479
|
+
if (n < 2) {
|
|
480
|
+
for (let k = 0; k < size; k++) out[k] = k / (size - 1);
|
|
481
|
+
return out;
|
|
496
482
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
483
|
+
const x = dedup.map((p) => p[0]);
|
|
484
|
+
const y = dedup.map((p) => p[1]);
|
|
485
|
+
const m2 = new Float64Array(n);
|
|
486
|
+
if (n > 2) {
|
|
487
|
+
const c = new Float64Array(n);
|
|
488
|
+
const d = new Float64Array(n);
|
|
489
|
+
for (let i = 1; i < n - 1; i++) {
|
|
490
|
+
const h0 = x[i] - x[i - 1];
|
|
491
|
+
const h1 = x[i + 1] - x[i];
|
|
492
|
+
const a = h0 / 6;
|
|
493
|
+
const b = (h0 + h1) / 3;
|
|
494
|
+
const cc = h1 / 6;
|
|
495
|
+
const r = (y[i + 1] - y[i]) / h1 - (y[i] - y[i - 1]) / h0;
|
|
496
|
+
const denom = b - a * c[i - 1];
|
|
497
|
+
c[i] = cc / denom;
|
|
498
|
+
d[i] = (r - a * d[i - 1]) / denom;
|
|
510
499
|
}
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
runSteps(steps, acc, empty, r) {
|
|
514
|
-
for (const s of steps) empty = this.runStep(s, acc, empty, r);
|
|
515
|
-
return empty;
|
|
500
|
+
for (let i = n - 2; i >= 1; i--) m2[i] = d[i] - c[i] * m2[i + 1];
|
|
516
501
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const layer = step.layer;
|
|
524
|
-
if (!layer.visible) return empty;
|
|
525
|
-
const clipped = step.clipped.filter((l) => l.visible);
|
|
526
|
-
const coverage = this.renderedAlone(layer) ? null : this.base.maskCoverage(layer, r, this.sources);
|
|
527
|
-
if (coverage && coverage.every((v) => v <= 0)) return empty;
|
|
528
|
-
if (step.kind === "group" && !this.renderedAlone(layer)) {
|
|
529
|
-
if (layer.blend === "pass-through" && !clipped.length) {
|
|
530
|
-
if (layer.opacity >= 1 && !coverage) return this.runSteps(step.steps, acc, empty, r);
|
|
531
|
-
const before = acc.slice();
|
|
532
|
-
const stillEmpty = this.runSteps(step.steps, acc, empty, r);
|
|
533
|
-
lerp(acc, before, layer.opacity, coverage);
|
|
534
|
-
return empty && stillEmpty;
|
|
535
|
-
}
|
|
536
|
-
const buf2 = new Float32Array(acc.length);
|
|
537
|
-
if (this.runSteps(step.steps, buf2, true, r)) return empty;
|
|
538
|
-
this.applyClipped(clipped, buf2, r);
|
|
539
|
-
const mode2 = layer.blend === "pass-through" ? "normal" : layer.blend;
|
|
540
|
-
this.blend(acc, buf2, mode2, layer.opacity, coverage);
|
|
541
|
-
return false;
|
|
502
|
+
let seg = 0;
|
|
503
|
+
for (let k = 0; k < size; k++) {
|
|
504
|
+
const xv = k / (size - 1);
|
|
505
|
+
if (xv <= x[0]) {
|
|
506
|
+
out[k] = clamp01(y[0]);
|
|
507
|
+
continue;
|
|
542
508
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const fill2 = !alone && this.isPixel(layer) ? layer.fillOpacity ?? 1 : 1;
|
|
547
|
-
const mode = this.blendOf(layer);
|
|
548
|
-
if (!clipped.length) {
|
|
549
|
-
this.blend(acc, content, mode, layer.opacity * fill2, coverage);
|
|
550
|
-
return false;
|
|
509
|
+
if (xv >= x[n - 1]) {
|
|
510
|
+
out[k] = clamp01(y[n - 1]);
|
|
511
|
+
continue;
|
|
551
512
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
513
|
+
while (seg < n - 2 && xv > x[seg + 1]) seg++;
|
|
514
|
+
const x0 = x[seg], x1 = x[seg + 1];
|
|
515
|
+
const h = x1 - x0;
|
|
516
|
+
const a = (x1 - xv) / h;
|
|
517
|
+
const b = (xv - x0) / h;
|
|
518
|
+
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;
|
|
519
|
+
out[k] = clamp01(v);
|
|
557
520
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
if (!src) continue;
|
|
578
|
-
const fill2 = !alone && this.isPixel(c) ? c.fillOpacity ?? 1 : 1;
|
|
579
|
-
const keep = new Float32Array(buf.length >> 2);
|
|
580
|
-
for (let i = 3, p = 0; i < buf.length; i += 4, p++) keep[p] = buf[i];
|
|
581
|
-
this.blend(buf, src, c.blend === "pass-through" ? "normal" : this.blendOf(c), c.opacity * fill2, coverage);
|
|
582
|
-
for (let i = 3, p = 0; i < buf.length; i += 4, p++) buf[i] = keep[p];
|
|
521
|
+
return out;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/raster/effects/patterns.ts
|
|
525
|
+
var PatternTile = class {
|
|
526
|
+
w;
|
|
527
|
+
h;
|
|
528
|
+
px;
|
|
529
|
+
constructor(p) {
|
|
530
|
+
this.w = Math.max(1, p.width | 0);
|
|
531
|
+
this.h = Math.max(1, p.height | 0);
|
|
532
|
+
const n = this.w * this.h;
|
|
533
|
+
this.px = new Float32Array(n * 4);
|
|
534
|
+
for (let i = 0; i < n; i++) {
|
|
535
|
+
const a = (p.data[i * 4 + 3] ?? 0) / 255;
|
|
536
|
+
this.px[i * 4] = (p.data[i * 4] ?? 0) / 255 * a;
|
|
537
|
+
this.px[i * 4 + 1] = (p.data[i * 4 + 1] ?? 0) / 255 * a;
|
|
538
|
+
this.px[i * 4 + 2] = (p.data[i * 4 + 2] ?? 0) / 255 * a;
|
|
539
|
+
this.px[i * 4 + 3] = a;
|
|
583
540
|
}
|
|
584
|
-
flush();
|
|
585
541
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
542
|
+
/** Bilinear wrapping sample at tile coordinates (pixel centres at `i + 0.5`), straight alpha into `out`. */
|
|
543
|
+
sample(u, v, out) {
|
|
544
|
+
u -= 0.5;
|
|
545
|
+
v -= 0.5;
|
|
546
|
+
const x0 = Math.floor(u);
|
|
547
|
+
const y0 = Math.floor(v);
|
|
548
|
+
const fx = u - x0;
|
|
549
|
+
const fy = v - y0;
|
|
550
|
+
let r = 0;
|
|
551
|
+
let g = 0;
|
|
552
|
+
let b = 0;
|
|
553
|
+
let a = 0;
|
|
554
|
+
for (let dy = 0; dy < 2; dy++) {
|
|
555
|
+
const wy2 = dy ? fy : 1 - fy;
|
|
556
|
+
if (wy2 === 0) continue;
|
|
557
|
+
const yi = ((y0 + dy) % this.h + this.h) % this.h;
|
|
558
|
+
for (let dx = 0; dx < 2; dx++) {
|
|
559
|
+
const wx2 = dx ? fx : 1 - fx;
|
|
560
|
+
if (wx2 === 0) continue;
|
|
561
|
+
const xi = ((x0 + dx) % this.w + this.w) % this.w;
|
|
562
|
+
const k = wx2 * wy2;
|
|
563
|
+
const i = (yi * this.w + xi) * 4;
|
|
564
|
+
r += this.px[i] * k;
|
|
565
|
+
g += this.px[i + 1] * k;
|
|
566
|
+
b += this.px[i + 2] * k;
|
|
567
|
+
a += this.px[i + 3] * k;
|
|
595
568
|
}
|
|
596
|
-
visible.push(node);
|
|
597
569
|
}
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
if (this.linear) mapColour(acc, srgbEncode);
|
|
601
|
-
this.deep.applyFilterRunDeep(visible, acc, r, masks, this.sources);
|
|
602
|
-
if (this.linear) mapColour(acc, srgbDecode);
|
|
570
|
+
if (a <= 0) {
|
|
571
|
+
out[0] = out[1] = out[2] = out[3] = 0;
|
|
603
572
|
return;
|
|
604
573
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
574
|
+
out[0] = r / a;
|
|
575
|
+
out[1] = g / a;
|
|
576
|
+
out[2] = b / a;
|
|
577
|
+
out[3] = Math.min(1, a);
|
|
608
578
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
579
|
+
};
|
|
580
|
+
var PatternPlacement = class {
|
|
581
|
+
ox;
|
|
582
|
+
oy;
|
|
583
|
+
c;
|
|
584
|
+
s;
|
|
585
|
+
inv;
|
|
586
|
+
/** `anchor`: the layer's top-left (used when `link`); `phase` in px; `scale` a fraction; `angle` degrees counter-clockwise. */
|
|
587
|
+
constructor(anchor, link, phase, scale, angle) {
|
|
588
|
+
const bx = link ? anchor.x : 0;
|
|
589
|
+
const by = link ? anchor.y : 0;
|
|
590
|
+
this.ox = bx + phase.x;
|
|
591
|
+
this.oy = by + phase.y;
|
|
592
|
+
this.inv = Number.isFinite(scale) && scale > 1e-3 ? 1 / scale : 1;
|
|
593
|
+
const a = (Number.isFinite(angle) ? angle : 0) * Math.PI / 180;
|
|
594
|
+
this.c = Math.cos(a);
|
|
595
|
+
this.s = Math.sin(a);
|
|
615
596
|
}
|
|
616
|
-
|
|
617
|
-
|
|
597
|
+
/** Tile coordinates of the document point `(x, y)` into `out[0..1]`. */
|
|
598
|
+
map(x, y, out) {
|
|
599
|
+
const dx = x - this.ox;
|
|
600
|
+
const dy = y - this.oy;
|
|
601
|
+
out[0] = (dx * this.c - dy * this.s) * this.inv;
|
|
602
|
+
out[1] = (dx * this.s + dy * this.c) * this.inv;
|
|
618
603
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const samples = readDeepRegion(surface, { x: r.x - ox, y: r.y - oy, width: r.width, height: r.height }, this.depth);
|
|
631
|
-
if (samples instanceof Float32Array) return samples;
|
|
632
|
-
const out = new Float32Array(samples.length);
|
|
633
|
-
for (let i = 0; i < samples.length; i++) out[i] = samples[i] / 65535;
|
|
634
|
-
return out;
|
|
635
|
-
}
|
|
636
|
-
/** 8-bit RGBA to the working space. */
|
|
637
|
-
from8(bytes) {
|
|
638
|
-
if (this.linear) return to32From8(bytes, 4);
|
|
639
|
-
const out = new Float32Array(bytes.length);
|
|
640
|
-
for (let i = 0; i < bytes.length; i++) out[i] = bytes[i] / 255;
|
|
641
|
-
return out;
|
|
642
|
-
}
|
|
643
|
-
/** The working space to 8-bit RGBA. */
|
|
644
|
-
to8(acc) {
|
|
645
|
-
const out = new Uint8ClampedArray(acc.length);
|
|
646
|
-
for (let i = 0; i < acc.length; i += 4) {
|
|
647
|
-
for (let c = 0; c < 3; c++) out[i + c] = Math.round((this.linear ? srgbEncode(acc[i + c]) : acc[i + c]) * 255);
|
|
648
|
-
out[i + 3] = Math.round(acc[i + 3] * 255);
|
|
604
|
+
};
|
|
605
|
+
var BUILTINS = {
|
|
606
|
+
checker: { name: "Checker", size: 16, paint: (x, y) => x < 8 !== y < 8 ? [204, 204, 204, 255] : [255, 255, 255, 255] },
|
|
607
|
+
stripes: { name: "Diagonal Stripes", size: 16, paint: (x, y) => (x + y) % 16 < 8 ? [40, 40, 40, 255] : [235, 235, 235, 255] },
|
|
608
|
+
dots: {
|
|
609
|
+
name: "Dots",
|
|
610
|
+
size: 12,
|
|
611
|
+
paint: (x, y) => {
|
|
612
|
+
const d = Math.hypot(x + 0.5 - 6, y + 0.5 - 6);
|
|
613
|
+
const a = Math.max(0, Math.min(1, 3.5 - d));
|
|
614
|
+
return [Math.round(255 - a * 215), Math.round(255 - a * 215), Math.round(255 - a * 215), 255];
|
|
649
615
|
}
|
|
650
|
-
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const cb = [0, 0, 0];
|
|
659
|
-
const cs = [0, 0, 0];
|
|
660
|
-
const mix = [0, 0, 0];
|
|
661
|
-
for (let i = 0, p = 0; i < dst.length; i += 4, p++) {
|
|
662
|
-
const sa = src[i + 3];
|
|
663
|
-
if (!(sa > 0)) continue;
|
|
664
|
-
const k = coverage ? alpha * coverage[p] : alpha;
|
|
665
|
-
if (!(k > 0)) continue;
|
|
666
|
-
const as = Math.min(1, sa * k);
|
|
667
|
-
const ab = dst[i + 3];
|
|
668
|
-
const ao = as + ab * (1 - as);
|
|
669
|
-
if (ao <= 0) {
|
|
670
|
-
dst[i] = dst[i + 1] = dst[i + 2] = dst[i + 3] = 0;
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
if (normal && as >= 1) {
|
|
674
|
-
dst[i] = src[i];
|
|
675
|
-
dst[i + 1] = src[i + 1];
|
|
676
|
-
dst[i + 2] = src[i + 2];
|
|
677
|
-
dst[i + 3] = 1;
|
|
678
|
-
continue;
|
|
679
|
-
}
|
|
680
|
-
for (let c = 0; c < 3; c++) {
|
|
681
|
-
cb[c] = dst[i + c];
|
|
682
|
-
cs[c] = src[i + c];
|
|
683
|
-
}
|
|
684
|
-
if (pigment) {
|
|
685
|
-
if (this.linear) for (let c = 0; c < 3; c++) cb[c] = srgbEncode(cb[c]), cs[c] = srgbEncode(cs[c]);
|
|
686
|
-
mixPigmentRgb(cb, cs, as / ao, mix);
|
|
687
|
-
for (let c = 0; c < 3; c++) dst[i + c] = this.linear ? srgbDecode(mix[c]) : mix[c];
|
|
688
|
-
dst[i + 3] = ao;
|
|
689
|
-
continue;
|
|
690
|
-
}
|
|
691
|
-
const m = normal ? cs : blendRgb(mode, cb, cs);
|
|
692
|
-
const wb = (1 - as) * ab;
|
|
693
|
-
const ws = (1 - ab) * as;
|
|
694
|
-
const wm = as * ab;
|
|
695
|
-
for (let c = 0; c < 3; c++) dst[i + c] = (wb * cb[c] + ws * cs[c] + wm * m[c]) / ao;
|
|
696
|
-
dst[i + 3] = ao;
|
|
616
|
+
},
|
|
617
|
+
grid: { name: "Grid", size: 16, paint: (x, y) => x === 0 || y === 0 ? [90, 90, 90, 255] : [250, 250, 250, 255] },
|
|
618
|
+
noise: {
|
|
619
|
+
name: "Noise",
|
|
620
|
+
size: 64,
|
|
621
|
+
paint: (x, y) => {
|
|
622
|
+
const v = Math.round(hashNoise(x, y) * 255);
|
|
623
|
+
return [v, v, v, 255];
|
|
697
624
|
}
|
|
698
625
|
}
|
|
699
626
|
};
|
|
700
|
-
function
|
|
701
|
-
|
|
702
|
-
const k = coverage ? alpha * coverage[p] : alpha;
|
|
703
|
-
if (k >= 1) continue;
|
|
704
|
-
for (let c = 0; c < 4; c++) dst[i + c] = before[i + c] + (dst[i + c] - before[i + c]) * k;
|
|
705
|
-
}
|
|
627
|
+
function builtinPatternIds() {
|
|
628
|
+
return Object.keys(BUILTINS);
|
|
706
629
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
630
|
+
var builtinCache = /* @__PURE__ */ new Map();
|
|
631
|
+
function builtinPattern(id) {
|
|
632
|
+
const def = BUILTINS[id];
|
|
633
|
+
if (!def) return null;
|
|
634
|
+
let p = builtinCache.get(id);
|
|
635
|
+
if (!p) {
|
|
636
|
+
const data = new Uint8ClampedArray(def.size * def.size * 4);
|
|
637
|
+
for (let y = 0; y < def.size; y++) {
|
|
638
|
+
for (let x = 0; x < def.size; x++) data.set(def.paint(x, y), (y * def.size + x) * 4);
|
|
639
|
+
}
|
|
640
|
+
p = { id, name: def.name, width: def.size, height: def.size, data };
|
|
641
|
+
builtinCache.set(id, p);
|
|
712
642
|
}
|
|
643
|
+
return p;
|
|
713
644
|
}
|
|
714
|
-
function
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}
|
|
721
|
-
return out;
|
|
645
|
+
function hashNoise(x, y) {
|
|
646
|
+
let h = Math.imul(x | 0, 2376512323) ^ Math.imul(y | 0, 3625334849) ^ 2654435769;
|
|
647
|
+
h ^= h >>> 15;
|
|
648
|
+
h = Math.imul(h, 739982445);
|
|
649
|
+
h ^= h >>> 12;
|
|
650
|
+
return (h & 65535) / 65536;
|
|
722
651
|
}
|
|
723
652
|
|
|
724
|
-
// src/raster/
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
653
|
+
// src/raster/effects/render.ts
|
|
654
|
+
var INSIDE_EPS = 0.5 / 255;
|
|
655
|
+
var INF = 1e20;
|
|
656
|
+
var FAR = 1e9;
|
|
657
|
+
var STROKE_CHAMFER_MAX = 20;
|
|
658
|
+
var BEVEL_H_EPS = 1e-5;
|
|
659
|
+
var fmap = (w, h, fill2 = 0) => {
|
|
660
|
+
const v = new Float32Array(w * h);
|
|
661
|
+
if (fill2) v.fill(fill2);
|
|
662
|
+
return { w, h, v };
|
|
663
|
+
};
|
|
664
|
+
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
665
|
+
var clamp012 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
|
|
666
|
+
function mapGet(m, x, y) {
|
|
667
|
+
return x < 0 || y < 0 || x >= m.w || y >= m.h ? 0 : m.v[y * m.w + x];
|
|
668
|
+
}
|
|
669
|
+
function shifted(m, dx, dy, outside) {
|
|
670
|
+
const reach = (d) => Number.isFinite(d) ? clamp(d, -1e9, 1e9) : 0;
|
|
671
|
+
dx = reach(dx);
|
|
672
|
+
dy = reach(dy);
|
|
673
|
+
const fx = Math.floor(dx);
|
|
674
|
+
const fy = Math.floor(dy);
|
|
675
|
+
const ax = dx - fx;
|
|
676
|
+
const ay = dy - fy;
|
|
677
|
+
const { w, h, v } = m;
|
|
678
|
+
const s = (x, y) => x < 0 || y < 0 || x >= w || y >= h ? outside : v[y * w + x];
|
|
679
|
+
const out = fmap(w, h);
|
|
680
|
+
for (let y = 0; y < h; y++) {
|
|
681
|
+
const sy = y - fy;
|
|
682
|
+
for (let x = 0; x < w; x++) {
|
|
683
|
+
const sx = x - fx;
|
|
684
|
+
const top = s(sx, sy) * (1 - ax) + s(sx - 1, sy) * ax;
|
|
685
|
+
const bot = s(sx, sy - 1) * (1 - ax) + s(sx - 1, sy - 1) * ax;
|
|
686
|
+
out.v[y * w + x] = top * (1 - ay) + bot * ay;
|
|
687
|
+
}
|
|
733
688
|
}
|
|
734
|
-
|
|
735
|
-
return k;
|
|
689
|
+
return out;
|
|
736
690
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
let wl = Math.floor(wIdeal);
|
|
742
|
-
if (wl % 2 === 0) wl--;
|
|
743
|
-
const wu = wl + 2;
|
|
744
|
-
const mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
|
|
745
|
-
const m = Math.round(mIdeal);
|
|
746
|
-
const sizes = [0, 1, 2].map((i) => i < m ? wl : wu);
|
|
747
|
-
return sizes.map((s) => (s - 1) / 2);
|
|
691
|
+
function mapped(m, f) {
|
|
692
|
+
const out = fmap(m.w, m.h);
|
|
693
|
+
for (let i = 0; i < m.v.length; i++) out.v[i] = f(m.v[i]);
|
|
694
|
+
return out;
|
|
748
695
|
}
|
|
749
|
-
function
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
696
|
+
function dt1(f, n, out, near, v, z) {
|
|
697
|
+
let k = 0;
|
|
698
|
+
v[0] = 0;
|
|
699
|
+
z[0] = -Infinity;
|
|
700
|
+
z[1] = Infinity;
|
|
701
|
+
for (let q = 1; q < n; q++) {
|
|
702
|
+
for (; ; ) {
|
|
703
|
+
const p = v[k];
|
|
704
|
+
const s = (f[q] + q * q - (f[p] + p * p)) / (2 * q - 2 * p);
|
|
705
|
+
if (s <= z[k] && k > 0) {
|
|
706
|
+
k--;
|
|
707
|
+
continue;
|
|
759
708
|
}
|
|
760
|
-
|
|
709
|
+
if (s <= z[k]) {
|
|
710
|
+
v[0] = q;
|
|
711
|
+
z[0] = -Infinity;
|
|
712
|
+
z[1] = Infinity;
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
k++;
|
|
716
|
+
v[k] = q;
|
|
717
|
+
z[k] = s;
|
|
718
|
+
z[k + 1] = Infinity;
|
|
719
|
+
break;
|
|
761
720
|
}
|
|
762
|
-
dst[i] = a;
|
|
763
721
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
722
|
+
k = 0;
|
|
723
|
+
for (let q = 0; q < n; q++) {
|
|
724
|
+
while (z[k + 1] < q) k++;
|
|
725
|
+
const p = v[k];
|
|
726
|
+
const d = q - p;
|
|
727
|
+
out[q] = d * d + f[p];
|
|
728
|
+
near[q] = p;
|
|
771
729
|
}
|
|
772
|
-
let from = a;
|
|
773
|
-
let to = b;
|
|
774
|
-
for (const r of radii) {
|
|
775
|
-
let sum = 0;
|
|
776
|
-
for (let i = 0; i <= Math.min(r, plen - 1); i++) sum += from[i];
|
|
777
|
-
const k = 2 * r + 1;
|
|
778
|
-
for (let i = 0; i < plen; i++) {
|
|
779
|
-
to[i] = sum / k;
|
|
780
|
-
const add = i + r + 1;
|
|
781
|
-
const drop = i - r;
|
|
782
|
-
if (add < plen) sum += from[add];
|
|
783
|
-
if (drop >= 0) sum -= from[drop];
|
|
784
|
-
}
|
|
785
|
-
const t = from;
|
|
786
|
-
from = to;
|
|
787
|
-
to = t;
|
|
788
|
-
}
|
|
789
|
-
for (let i = 0; i < len; i++) dst[i] = from[i + pad];
|
|
790
730
|
}
|
|
791
|
-
function
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
const
|
|
795
|
-
const
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const b = new Float32Array(plen);
|
|
805
|
-
passes.push((buf, off, stride, len) => boxesLine(buf, off, stride, len, radii, edge, a, b, line));
|
|
806
|
-
}
|
|
807
|
-
for (const pass of passes) {
|
|
731
|
+
function edtNearest(inside, w, h) {
|
|
732
|
+
const total = w * h;
|
|
733
|
+
const n = Math.max(w, h);
|
|
734
|
+
const f = new Float64Array(n);
|
|
735
|
+
const o = new Float64Array(n);
|
|
736
|
+
const nr = new Int32Array(n);
|
|
737
|
+
const v = new Int32Array(n);
|
|
738
|
+
const z = new Float64Array(n + 1);
|
|
739
|
+
const gc = new Float64Array(total);
|
|
740
|
+
const nc = new Int32Array(total);
|
|
741
|
+
for (let x = 0; x < w; x++) {
|
|
742
|
+
for (let y = 0; y < h; y++) f[y] = inside[y * w + x] ? 0 : INF;
|
|
743
|
+
dt1(f, h, o, nr, v, z);
|
|
808
744
|
for (let y = 0; y < h; y++) {
|
|
809
|
-
|
|
810
|
-
|
|
745
|
+
gc[x * h + y] = o[y];
|
|
746
|
+
nc[x * h + y] = nr[y];
|
|
811
747
|
}
|
|
812
748
|
}
|
|
813
|
-
|
|
749
|
+
const d = new Float32Array(total);
|
|
750
|
+
const near = new Int32Array(total);
|
|
751
|
+
for (let y = 0; y < h; y++) {
|
|
752
|
+
for (let x = 0; x < w; x++) f[x] = gc[x * h + y];
|
|
753
|
+
dt1(f, w, o, nr, v, z);
|
|
814
754
|
for (let x = 0; x < w; x++) {
|
|
815
|
-
|
|
816
|
-
|
|
755
|
+
d[y * w + x] = Math.sqrt(o[x]);
|
|
756
|
+
const px = nr[x];
|
|
757
|
+
near[y * w + x] = nc[px * h + y] * w + px;
|
|
817
758
|
}
|
|
818
759
|
}
|
|
819
|
-
return
|
|
760
|
+
return { d, near };
|
|
820
761
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
var
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
762
|
+
var S2 = Math.SQRT2;
|
|
763
|
+
var S5 = 2.236068;
|
|
764
|
+
var FWD = [
|
|
765
|
+
[-1, 0, 1],
|
|
766
|
+
[-1, -1, S2],
|
|
767
|
+
[0, -1, 1],
|
|
768
|
+
[1, -1, S2],
|
|
769
|
+
[-1, -2, S5],
|
|
770
|
+
[1, -2, S5],
|
|
771
|
+
[-2, -1, S5],
|
|
772
|
+
[2, -1, S5]
|
|
773
|
+
];
|
|
774
|
+
function chamferFrom(d, w, h) {
|
|
775
|
+
const ox = FWD.map((f) => f[0]);
|
|
776
|
+
const oy = FWD.map((f) => f[1]);
|
|
777
|
+
const wt = FWD.map((f) => f[2]);
|
|
778
|
+
const k = FWD.length;
|
|
779
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
780
|
+
const sign = pass === 0 ? 1 : -1;
|
|
781
|
+
for (let yy = 0; yy < h; yy++) {
|
|
782
|
+
const y = pass === 0 ? yy : h - 1 - yy;
|
|
783
|
+
for (let xx = 0; xx < w; xx++) {
|
|
784
|
+
const x = pass === 0 ? xx : w - 1 - xx;
|
|
785
|
+
const i = y * w + x;
|
|
786
|
+
let best = d[i];
|
|
787
|
+
for (let j = 0; j < k; j++) {
|
|
788
|
+
const nx = x + ox[j] * sign;
|
|
789
|
+
const ny = y + oy[j] * sign;
|
|
790
|
+
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
|
791
|
+
const c = d[ny * w + nx] + wt[j];
|
|
792
|
+
if (c < best) best = c;
|
|
793
|
+
}
|
|
794
|
+
d[i] = best;
|
|
795
|
+
}
|
|
831
796
|
}
|
|
832
|
-
}
|
|
833
|
-
|
|
797
|
+
}
|
|
798
|
+
return d;
|
|
799
|
+
}
|
|
800
|
+
function distOutside(s, chamfer) {
|
|
801
|
+
const { w, h, v } = s;
|
|
802
|
+
const n = v.length;
|
|
803
|
+
const out = new Float32Array(n);
|
|
804
|
+
if (chamfer) {
|
|
805
|
+
const start = new Float32Array(n);
|
|
806
|
+
for (let i = 0; i < n; i++) start[i] = v[i] > INSIDE_EPS ? 1 - Math.min(v[i], 1) : INF;
|
|
807
|
+
const d2 = chamferFrom(start, w, h);
|
|
808
|
+
for (let i = 0; i < n; i++) out[i] = v[i] > INSIDE_EPS ? -0.5 : d2[i] - 0.5;
|
|
809
|
+
return out;
|
|
810
|
+
}
|
|
811
|
+
const inside = new Uint8Array(n);
|
|
812
|
+
for (let i = 0; i < n; i++) inside[i] = v[i] > INSIDE_EPS ? 1 : 0;
|
|
813
|
+
const { d, near } = edtNearest(inside, w, h);
|
|
814
|
+
for (let i = 0; i < n; i++) out[i] = inside[i] ? -0.5 : d[i] - 0.5 + (1 - Math.min(v[near[i]], 1));
|
|
834
815
|
return out;
|
|
835
816
|
}
|
|
836
|
-
function
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
if (hit) return hit;
|
|
846
|
-
}
|
|
817
|
+
function localCoverage(s) {
|
|
818
|
+
const out = { w: s.w, h: s.h, v: new Float32Array(s.v) };
|
|
819
|
+
for (let y = 0; y < s.h; y++) {
|
|
820
|
+
for (let x = 0; x < s.w; x++) {
|
|
821
|
+
const a = s.v[y * s.w + x];
|
|
822
|
+
if (a <= INSIDE_EPS) continue;
|
|
823
|
+
let mx = a;
|
|
824
|
+
for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) mx = Math.max(mx, mapGet(s, x + dx, y + dy));
|
|
825
|
+
out.v[y * s.w + x] = Math.min(a / mx, 1);
|
|
847
826
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
return walk(layers, null, []);
|
|
827
|
+
}
|
|
828
|
+
return out;
|
|
851
829
|
}
|
|
852
|
-
function
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
830
|
+
function distInside(s, chamfer) {
|
|
831
|
+
const { w, h, v } = s;
|
|
832
|
+
const n = v.length;
|
|
833
|
+
const out = new Float32Array(n);
|
|
834
|
+
if (chamfer) {
|
|
835
|
+
const cov = localCoverage(s);
|
|
836
|
+
const start = new Float32Array(n);
|
|
837
|
+
for (let i = 0; i < n; i++) start[i] = v[i] <= INSIDE_EPS ? 0 : cov.v[i] < 1 - INSIDE_EPS ? cov.v[i] : INF;
|
|
838
|
+
const d2 = chamferFrom(start, w, h);
|
|
839
|
+
for (let i = 0; i < n; i++) out[i] = v[i] <= INSIDE_EPS ? -0.5 : d2[i] - 0.5;
|
|
840
|
+
return out;
|
|
841
|
+
}
|
|
842
|
+
const outside = new Uint8Array(n);
|
|
843
|
+
for (let i = 0; i < n; i++) outside[i] = v[i] <= INSIDE_EPS ? 1 : 0;
|
|
844
|
+
const { d } = edtNearest(outside, w, h);
|
|
845
|
+
for (let i = 0; i < n; i++) out[i] = outside[i] ? -0.5 : d[i] - 0.5;
|
|
846
|
+
return out;
|
|
847
|
+
}
|
|
848
|
+
function edgeDistances(s, reach) {
|
|
849
|
+
const { w, h, v } = s;
|
|
850
|
+
const n = v.length;
|
|
851
|
+
const edge = new Uint8Array(n);
|
|
852
|
+
const inside = new Uint8Array(n);
|
|
853
|
+
for (let y = 0; y < h; y++) {
|
|
854
|
+
for (let x = 0; x < w; x++) {
|
|
855
|
+
const i = y * w + x;
|
|
856
|
+
const a = v[i];
|
|
857
|
+
if (a <= INSIDE_EPS) continue;
|
|
858
|
+
inside[i] = 1;
|
|
859
|
+
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;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
const { d, near } = edtNearest(edge, w, h);
|
|
863
|
+
const din = new Float32Array(n);
|
|
864
|
+
const dout = new Float32Array(n);
|
|
865
|
+
for (let y = 0; y < h; y++) {
|
|
866
|
+
for (let x = 0; x < w; x++) {
|
|
867
|
+
const i = y * w + x;
|
|
868
|
+
const q = near[i];
|
|
869
|
+
let sd;
|
|
870
|
+
if (d[i] > reach || d[i] >= INF / 2) {
|
|
871
|
+
sd = inside[i] ? -(d[i] + 0.5) : d[i] - 0.5;
|
|
862
872
|
} else {
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
873
|
+
const qx = q % w;
|
|
874
|
+
const qy = (q - qx) / w;
|
|
875
|
+
const g = (dx, dy) => mapGet(s, qx + dx, qy + dy);
|
|
876
|
+
let nx = -(g(1, -1) + 2 * g(1, 0) + g(1, 1) - g(-1, -1) - 2 * g(-1, 0) - g(-1, 1));
|
|
877
|
+
let ny = -(g(-1, 1) + 2 * g(0, 1) + g(1, 1) - g(-1, -1) - 2 * g(0, -1) - g(1, -1));
|
|
878
|
+
const len = Math.hypot(nx, ny);
|
|
879
|
+
if (len > 1e-6) {
|
|
880
|
+
nx /= len;
|
|
881
|
+
ny /= len;
|
|
882
|
+
} else if (d[i] > 0) {
|
|
883
|
+
nx = (x - qx) / d[i];
|
|
884
|
+
ny = (y - qy) / d[i];
|
|
885
|
+
} else {
|
|
886
|
+
nx = 1;
|
|
887
|
+
ny = 0;
|
|
867
888
|
}
|
|
889
|
+
sd = (x - qx) * nx + (y - qy) * ny - (Math.min(v[q], 1) - 0.5);
|
|
868
890
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if (next) out.push(next);
|
|
872
|
-
} else if (out) out.push(l);
|
|
891
|
+
din[i] = inside[i] ? Math.max(-sd, inside[i] && !edge[i] ? 0.5 : -0.5) : -0.5;
|
|
892
|
+
dout[i] = inside[i] && !edge[i] ? -0.5 : Math.max(sd, -0.5);
|
|
873
893
|
}
|
|
874
|
-
|
|
875
|
-
};
|
|
876
|
-
const result = walk(layers);
|
|
877
|
-
return changed ? result : layers;
|
|
894
|
+
}
|
|
895
|
+
return { din, dout };
|
|
878
896
|
}
|
|
879
|
-
function
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
return
|
|
888
|
-
const c = childrenOf(g);
|
|
889
|
-
return c ? { ...g, children: put(c) } : g;
|
|
890
|
-
});
|
|
897
|
+
function dilate(s, r) {
|
|
898
|
+
if (!(r > 0)) return s;
|
|
899
|
+
const d = distOutside(s, true);
|
|
900
|
+
const out = { w: s.w, h: s.h, v: new Float32Array(s.v) };
|
|
901
|
+
for (let i = 0; i < d.length; i++) {
|
|
902
|
+
const o = out.v[i];
|
|
903
|
+
out.v[i] = d[i] < 0 ? Math.min(o + r, 1) : Math.max(o, clamp012(r + 0.5 - d[i]));
|
|
904
|
+
}
|
|
905
|
+
return out;
|
|
891
906
|
}
|
|
892
|
-
function
|
|
893
|
-
return
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// src/raster/effects/patterns.ts
|
|
897
|
-
var PatternTile = class {
|
|
898
|
-
w;
|
|
899
|
-
h;
|
|
900
|
-
px;
|
|
901
|
-
constructor(p) {
|
|
902
|
-
this.w = Math.max(1, p.width | 0);
|
|
903
|
-
this.h = Math.max(1, p.height | 0);
|
|
904
|
-
const n = this.w * this.h;
|
|
905
|
-
this.px = new Float32Array(n * 4);
|
|
906
|
-
for (let i = 0; i < n; i++) {
|
|
907
|
-
const a = (p.data[i * 4 + 3] ?? 0) / 255;
|
|
908
|
-
this.px[i * 4] = (p.data[i * 4] ?? 0) / 255 * a;
|
|
909
|
-
this.px[i * 4 + 1] = (p.data[i * 4 + 1] ?? 0) / 255 * a;
|
|
910
|
-
this.px[i * 4 + 2] = (p.data[i * 4 + 2] ?? 0) / 255 * a;
|
|
911
|
-
this.px[i * 4 + 3] = a;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
/** Bilinear wrapping sample at tile coordinates (pixel centres at `i + 0.5`), straight alpha into `out`. */
|
|
915
|
-
sample(u, v, out) {
|
|
916
|
-
u -= 0.5;
|
|
917
|
-
v -= 0.5;
|
|
918
|
-
const x0 = Math.floor(u);
|
|
919
|
-
const y0 = Math.floor(v);
|
|
920
|
-
const fx = u - x0;
|
|
921
|
-
const fy = v - y0;
|
|
922
|
-
let r = 0;
|
|
923
|
-
let g = 0;
|
|
924
|
-
let b = 0;
|
|
925
|
-
let a = 0;
|
|
926
|
-
for (let dy = 0; dy < 2; dy++) {
|
|
927
|
-
const wy2 = dy ? fy : 1 - fy;
|
|
928
|
-
if (wy2 === 0) continue;
|
|
929
|
-
const yi = ((y0 + dy) % this.h + this.h) % this.h;
|
|
930
|
-
for (let dx = 0; dx < 2; dx++) {
|
|
931
|
-
const wx2 = dx ? fx : 1 - fx;
|
|
932
|
-
if (wx2 === 0) continue;
|
|
933
|
-
const xi = ((x0 + dx) % this.w + this.w) % this.w;
|
|
934
|
-
const k = wx2 * wy2;
|
|
935
|
-
const i = (yi * this.w + xi) * 4;
|
|
936
|
-
r += this.px[i] * k;
|
|
937
|
-
g += this.px[i + 1] * k;
|
|
938
|
-
b += this.px[i + 2] * k;
|
|
939
|
-
a += this.px[i + 3] * k;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
if (a <= 0) {
|
|
943
|
-
out[0] = out[1] = out[2] = out[3] = 0;
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
out[0] = r / a;
|
|
947
|
-
out[1] = g / a;
|
|
948
|
-
out[2] = b / a;
|
|
949
|
-
out[3] = Math.min(1, a);
|
|
950
|
-
}
|
|
951
|
-
};
|
|
952
|
-
var PatternPlacement = class {
|
|
953
|
-
ox;
|
|
954
|
-
oy;
|
|
955
|
-
c;
|
|
956
|
-
s;
|
|
957
|
-
inv;
|
|
958
|
-
/** `anchor`: the layer's top-left (used when `link`); `phase` in px; `scale` a fraction; `angle` degrees counter-clockwise. */
|
|
959
|
-
constructor(anchor, link, phase, scale, angle) {
|
|
960
|
-
const bx = link ? anchor.x : 0;
|
|
961
|
-
const by = link ? anchor.y : 0;
|
|
962
|
-
this.ox = bx + phase.x;
|
|
963
|
-
this.oy = by + phase.y;
|
|
964
|
-
this.inv = Number.isFinite(scale) && scale > 1e-3 ? 1 / scale : 1;
|
|
965
|
-
const a = (Number.isFinite(angle) ? angle : 0) * Math.PI / 180;
|
|
966
|
-
this.c = Math.cos(a);
|
|
967
|
-
this.s = Math.sin(a);
|
|
968
|
-
}
|
|
969
|
-
/** Tile coordinates of the document point `(x, y)` into `out[0..1]`. */
|
|
970
|
-
map(x, y, out) {
|
|
971
|
-
const dx = x - this.ox;
|
|
972
|
-
const dy = y - this.oy;
|
|
973
|
-
out[0] = (dx * this.c - dy * this.s) * this.inv;
|
|
974
|
-
out[1] = (dx * this.s + dy * this.c) * this.inv;
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
var BUILTINS = {
|
|
978
|
-
checker: { name: "Checker", size: 16, paint: (x, y) => x < 8 !== y < 8 ? [204, 204, 204, 255] : [255, 255, 255, 255] },
|
|
979
|
-
stripes: { name: "Diagonal Stripes", size: 16, paint: (x, y) => (x + y) % 16 < 8 ? [40, 40, 40, 255] : [235, 235, 235, 255] },
|
|
980
|
-
dots: {
|
|
981
|
-
name: "Dots",
|
|
982
|
-
size: 12,
|
|
983
|
-
paint: (x, y) => {
|
|
984
|
-
const d = Math.hypot(x + 0.5 - 6, y + 0.5 - 6);
|
|
985
|
-
const a = Math.max(0, Math.min(1, 3.5 - d));
|
|
986
|
-
return [Math.round(255 - a * 215), Math.round(255 - a * 215), Math.round(255 - a * 215), 255];
|
|
987
|
-
}
|
|
988
|
-
},
|
|
989
|
-
grid: { name: "Grid", size: 16, paint: (x, y) => x === 0 || y === 0 ? [90, 90, 90, 255] : [250, 250, 250, 255] },
|
|
990
|
-
noise: {
|
|
991
|
-
name: "Noise",
|
|
992
|
-
size: 64,
|
|
993
|
-
paint: (x, y) => {
|
|
994
|
-
const v = Math.round(hashNoise(x, y) * 255);
|
|
995
|
-
return [v, v, v, 255];
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
};
|
|
999
|
-
function builtinPatternIds() {
|
|
1000
|
-
return Object.keys(BUILTINS);
|
|
1001
|
-
}
|
|
1002
|
-
var builtinCache = /* @__PURE__ */ new Map();
|
|
1003
|
-
function builtinPattern(id) {
|
|
1004
|
-
const def = BUILTINS[id];
|
|
1005
|
-
if (!def) return null;
|
|
1006
|
-
let p = builtinCache.get(id);
|
|
1007
|
-
if (!p) {
|
|
1008
|
-
const data = new Uint8ClampedArray(def.size * def.size * 4);
|
|
1009
|
-
for (let y = 0; y < def.size; y++) {
|
|
1010
|
-
for (let x = 0; x < def.size; x++) data.set(def.paint(x, y), (y * def.size + x) * 4);
|
|
1011
|
-
}
|
|
1012
|
-
p = { id, name: def.name, width: def.size, height: def.size, data };
|
|
1013
|
-
builtinCache.set(id, p);
|
|
1014
|
-
}
|
|
1015
|
-
return p;
|
|
1016
|
-
}
|
|
1017
|
-
function hashNoise(x, y) {
|
|
1018
|
-
let h = Math.imul(x | 0, 2376512323) ^ Math.imul(y | 0, 3625334849) ^ 2654435769;
|
|
1019
|
-
h ^= h >>> 15;
|
|
1020
|
-
h = Math.imul(h, 739982445);
|
|
1021
|
-
h ^= h >>> 12;
|
|
1022
|
-
return (h & 65535) / 65536;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
// src/raster/curve.ts
|
|
1026
|
-
var CURVE_LUT_SIZE = 4096;
|
|
1027
|
-
var clamp01 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
|
|
1028
|
-
function curveLut(points, size = CURVE_LUT_SIZE) {
|
|
1029
|
-
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]);
|
|
1030
|
-
const dedup = [];
|
|
1031
|
-
for (const p of pts) {
|
|
1032
|
-
if (dedup.length && Math.abs(dedup[dedup.length - 1][0] - p[0]) < 1e-6) continue;
|
|
1033
|
-
dedup.push(p);
|
|
1034
|
-
}
|
|
1035
|
-
const out = new Float32Array(size);
|
|
1036
|
-
const n = dedup.length;
|
|
1037
|
-
if (n < 2) {
|
|
1038
|
-
for (let k = 0; k < size; k++) out[k] = k / (size - 1);
|
|
1039
|
-
return out;
|
|
1040
|
-
}
|
|
1041
|
-
const x = dedup.map((p) => p[0]);
|
|
1042
|
-
const y = dedup.map((p) => p[1]);
|
|
1043
|
-
const m2 = new Float64Array(n);
|
|
1044
|
-
if (n > 2) {
|
|
1045
|
-
const c = new Float64Array(n);
|
|
1046
|
-
const d = new Float64Array(n);
|
|
1047
|
-
for (let i = 1; i < n - 1; i++) {
|
|
1048
|
-
const h0 = x[i] - x[i - 1];
|
|
1049
|
-
const h1 = x[i + 1] - x[i];
|
|
1050
|
-
const a = h0 / 6;
|
|
1051
|
-
const b = (h0 + h1) / 3;
|
|
1052
|
-
const cc = h1 / 6;
|
|
1053
|
-
const r = (y[i + 1] - y[i]) / h1 - (y[i] - y[i - 1]) / h0;
|
|
1054
|
-
const denom = b - a * c[i - 1];
|
|
1055
|
-
c[i] = cc / denom;
|
|
1056
|
-
d[i] = (r - a * d[i - 1]) / denom;
|
|
1057
|
-
}
|
|
1058
|
-
for (let i = n - 2; i >= 1; i--) m2[i] = d[i] - c[i] * m2[i + 1];
|
|
1059
|
-
}
|
|
1060
|
-
let seg = 0;
|
|
1061
|
-
for (let k = 0; k < size; k++) {
|
|
1062
|
-
const xv = k / (size - 1);
|
|
1063
|
-
if (xv <= x[0]) {
|
|
1064
|
-
out[k] = clamp01(y[0]);
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
if (xv >= x[n - 1]) {
|
|
1068
|
-
out[k] = clamp01(y[n - 1]);
|
|
1069
|
-
continue;
|
|
1070
|
-
}
|
|
1071
|
-
while (seg < n - 2 && xv > x[seg + 1]) seg++;
|
|
1072
|
-
const x0 = x[seg], x1 = x[seg + 1];
|
|
1073
|
-
const h = x1 - x0;
|
|
1074
|
-
const a = (x1 - xv) / h;
|
|
1075
|
-
const b = (xv - x0) / h;
|
|
1076
|
-
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;
|
|
1077
|
-
out[k] = clamp01(v);
|
|
1078
|
-
}
|
|
1079
|
-
return out;
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
// src/raster/effects/render.ts
|
|
1083
|
-
var INSIDE_EPS = 0.5 / 255;
|
|
1084
|
-
var INF = 1e20;
|
|
1085
|
-
var FAR = 1e9;
|
|
1086
|
-
var STROKE_CHAMFER_MAX = 20;
|
|
1087
|
-
var BEVEL_H_EPS = 1e-5;
|
|
1088
|
-
var fmap = (w, h, fill2 = 0) => {
|
|
1089
|
-
const v = new Float32Array(w * h);
|
|
1090
|
-
if (fill2) v.fill(fill2);
|
|
1091
|
-
return { w, h, v };
|
|
1092
|
-
};
|
|
1093
|
-
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
1094
|
-
var clamp012 = (v) => v <= 0 ? 0 : v >= 1 ? 1 : v;
|
|
1095
|
-
function mapGet(m, x, y) {
|
|
1096
|
-
return x < 0 || y < 0 || x >= m.w || y >= m.h ? 0 : m.v[y * m.w + x];
|
|
1097
|
-
}
|
|
1098
|
-
function shifted(m, dx, dy, outside) {
|
|
1099
|
-
const reach = (d) => Number.isFinite(d) ? clamp(d, -1e9, 1e9) : 0;
|
|
1100
|
-
dx = reach(dx);
|
|
1101
|
-
dy = reach(dy);
|
|
1102
|
-
const fx = Math.floor(dx);
|
|
1103
|
-
const fy = Math.floor(dy);
|
|
1104
|
-
const ax = dx - fx;
|
|
1105
|
-
const ay = dy - fy;
|
|
1106
|
-
const { w, h, v } = m;
|
|
1107
|
-
const s = (x, y) => x < 0 || y < 0 || x >= w || y >= h ? outside : v[y * w + x];
|
|
1108
|
-
const out = fmap(w, h);
|
|
1109
|
-
for (let y = 0; y < h; y++) {
|
|
1110
|
-
const sy = y - fy;
|
|
1111
|
-
for (let x = 0; x < w; x++) {
|
|
1112
|
-
const sx = x - fx;
|
|
1113
|
-
const top = s(sx, sy) * (1 - ax) + s(sx - 1, sy) * ax;
|
|
1114
|
-
const bot = s(sx, sy - 1) * (1 - ax) + s(sx - 1, sy - 1) * ax;
|
|
1115
|
-
out.v[y * w + x] = top * (1 - ay) + bot * ay;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
return out;
|
|
1119
|
-
}
|
|
1120
|
-
function mapped(m, f) {
|
|
1121
|
-
const out = fmap(m.w, m.h);
|
|
1122
|
-
for (let i = 0; i < m.v.length; i++) out.v[i] = f(m.v[i]);
|
|
1123
|
-
return out;
|
|
1124
|
-
}
|
|
1125
|
-
function dt1(f, n, out, near, v, z) {
|
|
1126
|
-
let k = 0;
|
|
1127
|
-
v[0] = 0;
|
|
1128
|
-
z[0] = -Infinity;
|
|
1129
|
-
z[1] = Infinity;
|
|
1130
|
-
for (let q = 1; q < n; q++) {
|
|
1131
|
-
for (; ; ) {
|
|
1132
|
-
const p = v[k];
|
|
1133
|
-
const s = (f[q] + q * q - (f[p] + p * p)) / (2 * q - 2 * p);
|
|
1134
|
-
if (s <= z[k] && k > 0) {
|
|
1135
|
-
k--;
|
|
1136
|
-
continue;
|
|
1137
|
-
}
|
|
1138
|
-
if (s <= z[k]) {
|
|
1139
|
-
v[0] = q;
|
|
1140
|
-
z[0] = -Infinity;
|
|
1141
|
-
z[1] = Infinity;
|
|
1142
|
-
break;
|
|
1143
|
-
}
|
|
1144
|
-
k++;
|
|
1145
|
-
v[k] = q;
|
|
1146
|
-
z[k] = s;
|
|
1147
|
-
z[k + 1] = Infinity;
|
|
1148
|
-
break;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
k = 0;
|
|
1152
|
-
for (let q = 0; q < n; q++) {
|
|
1153
|
-
while (z[k + 1] < q) k++;
|
|
1154
|
-
const p = v[k];
|
|
1155
|
-
const d = q - p;
|
|
1156
|
-
out[q] = d * d + f[p];
|
|
1157
|
-
near[q] = p;
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
function edtNearest(inside, w, h) {
|
|
1161
|
-
const total = w * h;
|
|
1162
|
-
const n = Math.max(w, h);
|
|
1163
|
-
const f = new Float64Array(n);
|
|
1164
|
-
const o = new Float64Array(n);
|
|
1165
|
-
const nr = new Int32Array(n);
|
|
1166
|
-
const v = new Int32Array(n);
|
|
1167
|
-
const z = new Float64Array(n + 1);
|
|
1168
|
-
const gc = new Float64Array(total);
|
|
1169
|
-
const nc = new Int32Array(total);
|
|
1170
|
-
for (let x = 0; x < w; x++) {
|
|
1171
|
-
for (let y = 0; y < h; y++) f[y] = inside[y * w + x] ? 0 : INF;
|
|
1172
|
-
dt1(f, h, o, nr, v, z);
|
|
1173
|
-
for (let y = 0; y < h; y++) {
|
|
1174
|
-
gc[x * h + y] = o[y];
|
|
1175
|
-
nc[x * h + y] = nr[y];
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
const d = new Float32Array(total);
|
|
1179
|
-
const near = new Int32Array(total);
|
|
1180
|
-
for (let y = 0; y < h; y++) {
|
|
1181
|
-
for (let x = 0; x < w; x++) f[x] = gc[x * h + y];
|
|
1182
|
-
dt1(f, w, o, nr, v, z);
|
|
1183
|
-
for (let x = 0; x < w; x++) {
|
|
1184
|
-
d[y * w + x] = Math.sqrt(o[x]);
|
|
1185
|
-
const px = nr[x];
|
|
1186
|
-
near[y * w + x] = nc[px * h + y] * w + px;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
return { d, near };
|
|
1190
|
-
}
|
|
1191
|
-
var S2 = Math.SQRT2;
|
|
1192
|
-
var S5 = 2.236068;
|
|
1193
|
-
var FWD = [
|
|
1194
|
-
[-1, 0, 1],
|
|
1195
|
-
[-1, -1, S2],
|
|
1196
|
-
[0, -1, 1],
|
|
1197
|
-
[1, -1, S2],
|
|
1198
|
-
[-1, -2, S5],
|
|
1199
|
-
[1, -2, S5],
|
|
1200
|
-
[-2, -1, S5],
|
|
1201
|
-
[2, -1, S5]
|
|
1202
|
-
];
|
|
1203
|
-
function chamferFrom(d, w, h) {
|
|
1204
|
-
const ox = FWD.map((f) => f[0]);
|
|
1205
|
-
const oy = FWD.map((f) => f[1]);
|
|
1206
|
-
const wt = FWD.map((f) => f[2]);
|
|
1207
|
-
const k = FWD.length;
|
|
1208
|
-
for (let pass = 0; pass < 2; pass++) {
|
|
1209
|
-
const sign = pass === 0 ? 1 : -1;
|
|
1210
|
-
for (let yy = 0; yy < h; yy++) {
|
|
1211
|
-
const y = pass === 0 ? yy : h - 1 - yy;
|
|
1212
|
-
for (let xx = 0; xx < w; xx++) {
|
|
1213
|
-
const x = pass === 0 ? xx : w - 1 - xx;
|
|
1214
|
-
const i = y * w + x;
|
|
1215
|
-
let best = d[i];
|
|
1216
|
-
for (let j = 0; j < k; j++) {
|
|
1217
|
-
const nx = x + ox[j] * sign;
|
|
1218
|
-
const ny = y + oy[j] * sign;
|
|
1219
|
-
if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
|
|
1220
|
-
const c = d[ny * w + nx] + wt[j];
|
|
1221
|
-
if (c < best) best = c;
|
|
1222
|
-
}
|
|
1223
|
-
d[i] = best;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
return d;
|
|
1228
|
-
}
|
|
1229
|
-
function distOutside(s, chamfer) {
|
|
1230
|
-
const { w, h, v } = s;
|
|
1231
|
-
const n = v.length;
|
|
1232
|
-
const out = new Float32Array(n);
|
|
1233
|
-
if (chamfer) {
|
|
1234
|
-
const start = new Float32Array(n);
|
|
1235
|
-
for (let i = 0; i < n; i++) start[i] = v[i] > INSIDE_EPS ? 1 - Math.min(v[i], 1) : INF;
|
|
1236
|
-
const d2 = chamferFrom(start, w, h);
|
|
1237
|
-
for (let i = 0; i < n; i++) out[i] = v[i] > INSIDE_EPS ? -0.5 : d2[i] - 0.5;
|
|
1238
|
-
return out;
|
|
1239
|
-
}
|
|
1240
|
-
const inside = new Uint8Array(n);
|
|
1241
|
-
for (let i = 0; i < n; i++) inside[i] = v[i] > INSIDE_EPS ? 1 : 0;
|
|
1242
|
-
const { d, near } = edtNearest(inside, w, h);
|
|
1243
|
-
for (let i = 0; i < n; i++) out[i] = inside[i] ? -0.5 : d[i] - 0.5 + (1 - Math.min(v[near[i]], 1));
|
|
1244
|
-
return out;
|
|
1245
|
-
}
|
|
1246
|
-
function localCoverage(s) {
|
|
1247
|
-
const out = { w: s.w, h: s.h, v: new Float32Array(s.v) };
|
|
1248
|
-
for (let y = 0; y < s.h; y++) {
|
|
1249
|
-
for (let x = 0; x < s.w; x++) {
|
|
1250
|
-
const a = s.v[y * s.w + x];
|
|
1251
|
-
if (a <= INSIDE_EPS) continue;
|
|
1252
|
-
let mx = a;
|
|
1253
|
-
for (let dy = -1; dy <= 1; dy++) for (let dx = -1; dx <= 1; dx++) mx = Math.max(mx, mapGet(s, x + dx, y + dy));
|
|
1254
|
-
out.v[y * s.w + x] = Math.min(a / mx, 1);
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
return out;
|
|
1258
|
-
}
|
|
1259
|
-
function distInside(s, chamfer) {
|
|
1260
|
-
const { w, h, v } = s;
|
|
1261
|
-
const n = v.length;
|
|
1262
|
-
const out = new Float32Array(n);
|
|
1263
|
-
if (chamfer) {
|
|
1264
|
-
const cov = localCoverage(s);
|
|
1265
|
-
const start = new Float32Array(n);
|
|
1266
|
-
for (let i = 0; i < n; i++) start[i] = v[i] <= INSIDE_EPS ? 0 : cov.v[i] < 1 - INSIDE_EPS ? cov.v[i] : INF;
|
|
1267
|
-
const d2 = chamferFrom(start, w, h);
|
|
1268
|
-
for (let i = 0; i < n; i++) out[i] = v[i] <= INSIDE_EPS ? -0.5 : d2[i] - 0.5;
|
|
1269
|
-
return out;
|
|
1270
|
-
}
|
|
1271
|
-
const outside = new Uint8Array(n);
|
|
1272
|
-
for (let i = 0; i < n; i++) outside[i] = v[i] <= INSIDE_EPS ? 1 : 0;
|
|
1273
|
-
const { d } = edtNearest(outside, w, h);
|
|
1274
|
-
for (let i = 0; i < n; i++) out[i] = outside[i] ? -0.5 : d[i] - 0.5;
|
|
1275
|
-
return out;
|
|
1276
|
-
}
|
|
1277
|
-
function edgeDistances(s, reach) {
|
|
1278
|
-
const { w, h, v } = s;
|
|
1279
|
-
const n = v.length;
|
|
1280
|
-
const edge = new Uint8Array(n);
|
|
1281
|
-
const inside = new Uint8Array(n);
|
|
1282
|
-
for (let y = 0; y < h; y++) {
|
|
1283
|
-
for (let x = 0; x < w; x++) {
|
|
1284
|
-
const i = y * w + x;
|
|
1285
|
-
const a = v[i];
|
|
1286
|
-
if (a <= INSIDE_EPS) continue;
|
|
1287
|
-
inside[i] = 1;
|
|
1288
|
-
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;
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
const { d, near } = edtNearest(edge, w, h);
|
|
1292
|
-
const din = new Float32Array(n);
|
|
1293
|
-
const dout = new Float32Array(n);
|
|
1294
|
-
for (let y = 0; y < h; y++) {
|
|
1295
|
-
for (let x = 0; x < w; x++) {
|
|
1296
|
-
const i = y * w + x;
|
|
1297
|
-
const q = near[i];
|
|
1298
|
-
let sd;
|
|
1299
|
-
if (d[i] > reach || d[i] >= INF / 2) {
|
|
1300
|
-
sd = inside[i] ? -(d[i] + 0.5) : d[i] - 0.5;
|
|
1301
|
-
} else {
|
|
1302
|
-
const qx = q % w;
|
|
1303
|
-
const qy = (q - qx) / w;
|
|
1304
|
-
const g = (dx, dy) => mapGet(s, qx + dx, qy + dy);
|
|
1305
|
-
let nx = -(g(1, -1) + 2 * g(1, 0) + g(1, 1) - g(-1, -1) - 2 * g(-1, 0) - g(-1, 1));
|
|
1306
|
-
let ny = -(g(-1, 1) + 2 * g(0, 1) + g(1, 1) - g(-1, -1) - 2 * g(0, -1) - g(1, -1));
|
|
1307
|
-
const len = Math.hypot(nx, ny);
|
|
1308
|
-
if (len > 1e-6) {
|
|
1309
|
-
nx /= len;
|
|
1310
|
-
ny /= len;
|
|
1311
|
-
} else if (d[i] > 0) {
|
|
1312
|
-
nx = (x - qx) / d[i];
|
|
1313
|
-
ny = (y - qy) / d[i];
|
|
1314
|
-
} else {
|
|
1315
|
-
nx = 1;
|
|
1316
|
-
ny = 0;
|
|
1317
|
-
}
|
|
1318
|
-
sd = (x - qx) * nx + (y - qy) * ny - (Math.min(v[q], 1) - 0.5);
|
|
1319
|
-
}
|
|
1320
|
-
din[i] = inside[i] ? Math.max(-sd, inside[i] && !edge[i] ? 0.5 : -0.5) : -0.5;
|
|
1321
|
-
dout[i] = inside[i] && !edge[i] ? -0.5 : Math.max(sd, -0.5);
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
return { din, dout };
|
|
1325
|
-
}
|
|
1326
|
-
function dilate(s, r) {
|
|
1327
|
-
if (!(r > 0)) return s;
|
|
1328
|
-
const d = distOutside(s, true);
|
|
1329
|
-
const out = { w: s.w, h: s.h, v: new Float32Array(s.v) };
|
|
1330
|
-
for (let i = 0; i < d.length; i++) {
|
|
1331
|
-
const o = out.v[i];
|
|
1332
|
-
out.v[i] = d[i] < 0 ? Math.min(o + r, 1) : Math.max(o, clamp012(r + 0.5 - d[i]));
|
|
1333
|
-
}
|
|
1334
|
-
return out;
|
|
1335
|
-
}
|
|
1336
|
-
function boxWidth(w) {
|
|
1337
|
-
return Number.isNaN(w) ? 1 : clamp(w, 1, MAX_EFFECT_REACH);
|
|
907
|
+
function boxWidth(w) {
|
|
908
|
+
return Number.isNaN(w) ? 1 : clamp(w, 1, MAX_EFFECT_REACH);
|
|
1338
909
|
}
|
|
1339
910
|
function boxGeom(bw) {
|
|
1340
911
|
bw = boxWidth(bw);
|
|
@@ -2139,10 +1710,13 @@ function compositeLayerEffects(input) {
|
|
|
2139
1710
|
const { backdrop, content, rect, maps, ctx } = input;
|
|
2140
1711
|
const items = maps.items;
|
|
2141
1712
|
const n = rect.width * rect.height;
|
|
1713
|
+
const cs = content instanceof Float32Array ? 1 : 1 / 255;
|
|
1714
|
+
const bs = backdrop instanceof Float32Array ? 1 : 1 / 255;
|
|
1715
|
+
const floatOut = backdrop instanceof Float32Array;
|
|
2142
1716
|
const shape = new Float32Array(n);
|
|
2143
|
-
for (let i = 0; i < n; i++) shape[i] = content[i * 4 + 3]
|
|
1717
|
+
for (let i = 0; i < n; i++) shape[i] = content[i * 4 + 3] * cs;
|
|
2144
1718
|
const before = new Float32Array(n * 4);
|
|
2145
|
-
for (let i = 0; i < n * 4; i++) before[i] = backdrop[i]
|
|
1719
|
+
for (let i = 0; i < n * 4; i++) before[i] = backdrop[i] * bs;
|
|
2146
1720
|
const work = new Float32Array(before);
|
|
2147
1721
|
const fx = (i, k) => crop(maps.per[i][k], maps.rect, rect, 0);
|
|
2148
1722
|
const bounds = maps.bounds ?? rect;
|
|
@@ -2170,178 +1744,692 @@ function compositeLayerEffects(input) {
|
|
|
2170
1744
|
knockOut(m, fill2);
|
|
2171
1745
|
paintGlow(work, m, e, env);
|
|
2172
1746
|
}
|
|
2173
|
-
const lay = new Float32Array(n * 4);
|
|
2174
|
-
for (let i = 0; i < n; i++) {
|
|
2175
|
-
lay[i * 4] = content[i * 4]
|
|
2176
|
-
lay[i * 4 + 1] = content[i * 4 + 1]
|
|
2177
|
-
lay[i * 4 + 2] = content[i * 4 + 2]
|
|
2178
|
-
lay[i * 4 + 3] = inside(shape[i]) ? fill2 : 0;
|
|
1747
|
+
const lay = new Float32Array(n * 4);
|
|
1748
|
+
for (let i = 0; i < n; i++) {
|
|
1749
|
+
lay[i * 4] = content[i * 4] * cs;
|
|
1750
|
+
lay[i * 4 + 1] = content[i * 4 + 1] * cs;
|
|
1751
|
+
lay[i * 4 + 2] = content[i * 4 + 2] * cs;
|
|
1752
|
+
lay[i * 4 + 3] = inside(shape[i]) ? fill2 : 0;
|
|
1753
|
+
}
|
|
1754
|
+
const premerge = !normalLike;
|
|
1755
|
+
if (premerge) {
|
|
1756
|
+
const sep2 = isSeparableBlendMode(input.blend);
|
|
1757
|
+
for (let i = 0; i < n; i++) {
|
|
1758
|
+
const p = i * 4;
|
|
1759
|
+
const sr = lay[p];
|
|
1760
|
+
const sg = lay[p + 1];
|
|
1761
|
+
const sb = lay[p + 2];
|
|
1762
|
+
const sa = lay[p + 3];
|
|
1763
|
+
lay[p] = work[p];
|
|
1764
|
+
lay[p + 1] = work[p + 1];
|
|
1765
|
+
lay[p + 2] = work[p + 2];
|
|
1766
|
+
lay[p + 3] = work[p + 3];
|
|
1767
|
+
compositeAt(lay, p, input.blend, sep2, sr, sg, sb, sa);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
const rel = (m) => {
|
|
1771
|
+
for (let i = 0; i < n; i++) m[i] = inside(shape[i]) ? Math.min(m[i] / shape[i], 1) : 0;
|
|
1772
|
+
return m;
|
|
1773
|
+
};
|
|
1774
|
+
const full = new Float32Array(n);
|
|
1775
|
+
for (let i = 0; i < n; i++) full[i] = inside(shape[i]) ? 1 : 0;
|
|
1776
|
+
for (const i of rev) {
|
|
1777
|
+
const e = items[i];
|
|
1778
|
+
if (e.kind === "pattern-overlay") paintPattern(lay, full, e, env, e.blend, e.opacity);
|
|
1779
|
+
}
|
|
1780
|
+
for (const i of rev) {
|
|
1781
|
+
const e = items[i];
|
|
1782
|
+
if (e.kind === "gradient-overlay") paintGradient(lay, full, e.gradient, e.gradient.align ? bounds : input.canvas, env, e.blend, e.opacity);
|
|
1783
|
+
}
|
|
1784
|
+
for (const i of rev) {
|
|
1785
|
+
const e = items[i];
|
|
1786
|
+
if (e.kind === "color-overlay") paintColor(lay, full, e.color, e.blend, e.opacity);
|
|
1787
|
+
}
|
|
1788
|
+
for (const i of rev) {
|
|
1789
|
+
const e = items[i];
|
|
1790
|
+
if (e.kind === "satin") paintColor(lay, rel(fx(i, 0)), e.color, e.blend, e.opacity);
|
|
1791
|
+
}
|
|
1792
|
+
for (const i of rev) {
|
|
1793
|
+
const e = items[i];
|
|
1794
|
+
if (e.kind === "inner-glow") paintGlow(lay, rel(fx(i, 0)), e, env);
|
|
1795
|
+
}
|
|
1796
|
+
for (const i of rev) {
|
|
1797
|
+
const e = items[i];
|
|
1798
|
+
if (e.kind === "inner-shadow") paintColor(lay, rel(fx(i, 0)), e.color, e.blend, e.opacity);
|
|
1799
|
+
}
|
|
1800
|
+
const strokeIdx = items.map((e, i) => e.kind === "stroke" ? i : -1).filter((i) => i >= 0);
|
|
1801
|
+
const din = strokeIdx.length && maps.din ? crop(maps.din, maps.rect, rect, 0) : null;
|
|
1802
|
+
const frameOf = (i) => maps.frames.get(i) ?? bounds;
|
|
1803
|
+
for (let k = strokeIdx.length - 1; k >= 0; k--) {
|
|
1804
|
+
const st = items[strokeIdx[k]];
|
|
1805
|
+
const [inW] = strokeWidths(st);
|
|
1806
|
+
if (!(inW > 0) || !din) continue;
|
|
1807
|
+
const m = new Float32Array(n);
|
|
1808
|
+
for (let i = 0; i < n; i++) m[i] = inside(shape[i]) ? clamp012(inW + 0.5 - din[i]) : 0;
|
|
1809
|
+
paintFill(lay, m, st.fill, frameOf(strokeIdx[k]), env, st.blend, st.opacity);
|
|
1810
|
+
}
|
|
1811
|
+
for (const i of rev) {
|
|
1812
|
+
const e = items[i];
|
|
1813
|
+
if (e.kind !== "bevel-emboss" || maps.bevelPaint[i] !== "inner") continue;
|
|
1814
|
+
paintColor(lay, rel(fx(i, 0)), e.highlightColor, e.highlightBlend, e.highlightOpacity);
|
|
1815
|
+
paintColor(lay, rel(fx(i, 1)), e.shadowColor, e.shadowBlend, e.shadowOpacity);
|
|
1816
|
+
}
|
|
1817
|
+
for (let i = 0; i < n; i++) lay[i * 4 + 3] *= Math.min(shape[i], 1);
|
|
1818
|
+
const outside = strokeIdx.filter((i) => strokeWidths(items[i])[1] > 0);
|
|
1819
|
+
if (outside.length) {
|
|
1820
|
+
const dout = maps.dout ? crop(maps.dout, maps.rect, rect, FAR) : null;
|
|
1821
|
+
const doutExact = maps.doutExact ? crop(maps.doutExact, maps.rect, rect, FAR) : null;
|
|
1822
|
+
const base = new Float32Array(work);
|
|
1823
|
+
const acc = new Float32Array(n * 4);
|
|
1824
|
+
const cover = new Float32Array(n);
|
|
1825
|
+
for (const si of outside) {
|
|
1826
|
+
const st = items[si];
|
|
1827
|
+
const outW = strokeWidths(st)[1];
|
|
1828
|
+
const d = (outW >= STROKE_CHAMFER_MAX ? doutExact : dout) ?? new Float32Array(n).fill(FAR);
|
|
1829
|
+
const share = new Float32Array(n);
|
|
1830
|
+
const ones = new Float32Array(n);
|
|
1831
|
+
for (let i = 0; i < n; i++) {
|
|
1832
|
+
const band = clamp012(outW + 0.5 - d[i]);
|
|
1833
|
+
let k;
|
|
1834
|
+
if (inside(shape[i])) {
|
|
1835
|
+
const c = clamp012(lay[i * 4 + 3]);
|
|
1836
|
+
k = c >= 1 ? 1 : clamp012((1 - clamp012(shape[i])) / (1 - c));
|
|
1837
|
+
} else k = band;
|
|
1838
|
+
share[i] = k * st.opacity * (1 - cover[i]);
|
|
1839
|
+
cover[i] += share[i];
|
|
1840
|
+
ones[i] = share[i] > 0 ? 1 : 0;
|
|
1841
|
+
}
|
|
1842
|
+
const blended = new Float32Array(base);
|
|
1843
|
+
paintFill(blended, ones, st.fill, frameOf(si), env, st.blend, 1);
|
|
1844
|
+
for (let i = 0; i < n; i++) {
|
|
1845
|
+
const s = share[i];
|
|
1846
|
+
if (!(s > 0)) continue;
|
|
1847
|
+
const p = i * 4;
|
|
1848
|
+
const ba = blended[p + 3];
|
|
1849
|
+
acc[p] += s * blended[p] * ba;
|
|
1850
|
+
acc[p + 1] += s * blended[p + 1] * ba;
|
|
1851
|
+
acc[p + 2] += s * blended[p + 2] * ba;
|
|
1852
|
+
acc[p + 3] += s * ba;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
for (let i = 0; i < n; i++) {
|
|
1856
|
+
const c = cover[i];
|
|
1857
|
+
if (c <= 0) continue;
|
|
1858
|
+
const p = i * 4;
|
|
1859
|
+
const k = 1 - Math.min(c, 1);
|
|
1860
|
+
const a = work[p + 3] * k + acc[p + 3];
|
|
1861
|
+
if (a > 0) {
|
|
1862
|
+
work[p] = (work[p] * work[p + 3] * k + acc[p]) / a;
|
|
1863
|
+
work[p + 1] = (work[p + 1] * work[p + 3] * k + acc[p + 1]) / a;
|
|
1864
|
+
work[p + 2] = (work[p + 2] * work[p + 3] * k + acc[p + 2]) / a;
|
|
1865
|
+
work[p + 3] = Math.min(a, 1);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
for (const i of rev) {
|
|
1870
|
+
const e = items[i];
|
|
1871
|
+
if (e.kind !== "bevel-emboss" || maps.bevelPaint[i] !== "outer") continue;
|
|
1872
|
+
paintColor(work, fx(i, 0), e.highlightColor, e.highlightBlend, e.highlightOpacity);
|
|
1873
|
+
paintColor(work, fx(i, 1), e.shadowColor, e.shadowBlend, e.shadowOpacity);
|
|
1874
|
+
}
|
|
1875
|
+
const mode = premerge ? "normal" : input.blend;
|
|
1876
|
+
const sep = isSeparableBlendMode(mode);
|
|
1877
|
+
for (let i = 0; i < n; i++) {
|
|
1878
|
+
const p = i * 4;
|
|
1879
|
+
if (lay[p + 3] > 0) compositeAt(work, p, mode, sep, lay[p], lay[p + 1], lay[p + 2], lay[p + 3]);
|
|
1880
|
+
}
|
|
1881
|
+
for (const i of rev) {
|
|
1882
|
+
const e = items[i];
|
|
1883
|
+
if (e.kind !== "bevel-emboss" || maps.bevelPaint[i] !== "both") continue;
|
|
1884
|
+
paintColor(work, fx(i, 0), e.highlightColor, e.highlightBlend, e.highlightOpacity);
|
|
1885
|
+
paintColor(work, fx(i, 1), e.shadowColor, e.shadowBlend, e.shadowOpacity);
|
|
1886
|
+
}
|
|
1887
|
+
const op = clamp012(input.opacity);
|
|
1888
|
+
for (let i = 0; i < n; i++) {
|
|
1889
|
+
const p = i * 4;
|
|
1890
|
+
const aa = before[p + 3];
|
|
1891
|
+
const ba = work[p + 3];
|
|
1892
|
+
const alpha = aa + (ba - aa) * op;
|
|
1893
|
+
if (alpha <= 0) {
|
|
1894
|
+
backdrop[p] = backdrop[p + 1] = backdrop[p + 2] = backdrop[p + 3] = 0;
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
if (floatOut) {
|
|
1898
|
+
for (let c = 0; c < 3; c++) backdrop[p + c] = (before[p + c] * aa + (work[p + c] * ba - before[p + c] * aa) * op) / alpha;
|
|
1899
|
+
backdrop[p + 3] = alpha;
|
|
1900
|
+
continue;
|
|
1901
|
+
}
|
|
1902
|
+
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);
|
|
1903
|
+
backdrop[p + 3] = Math.round(alpha * 255);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
function renderLayerEffects(effects, layer, backdrop, canvas, ctx) {
|
|
1907
|
+
const items = activeEffectItems(effects);
|
|
1908
|
+
const n = layer.rect.width * layer.rect.height;
|
|
1909
|
+
const shape = new Float32Array(n);
|
|
1910
|
+
for (let i = 0; i < n; i++) shape[i] = layer.content[i * 4 + 3] / 255;
|
|
1911
|
+
const maps = buildEffectMaps(items, shape, layer.rect, ctx);
|
|
1912
|
+
compositeLayerEffects({
|
|
1913
|
+
backdrop,
|
|
1914
|
+
content: layer.content,
|
|
1915
|
+
rect: layer.rect,
|
|
1916
|
+
maps,
|
|
1917
|
+
blend: layer.blend,
|
|
1918
|
+
opacity: layer.opacity,
|
|
1919
|
+
fillOpacity: layer.fillOpacity ?? 1,
|
|
1920
|
+
canvas,
|
|
1921
|
+
ctx
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
// src/raster/deep-compositor.ts
|
|
1926
|
+
var isGroup = (l) => l.type === "group";
|
|
1927
|
+
var DeepCompositor = class {
|
|
1928
|
+
constructor(base, profile, deep, depth) {
|
|
1929
|
+
this.base = base;
|
|
1930
|
+
this.profile = profile;
|
|
1931
|
+
this.deep = deep;
|
|
1932
|
+
this.depth = depth;
|
|
1933
|
+
this.linear = depth === 32;
|
|
1934
|
+
}
|
|
1935
|
+
base;
|
|
1936
|
+
profile;
|
|
1937
|
+
deep;
|
|
1938
|
+
depth;
|
|
1939
|
+
sources;
|
|
1940
|
+
linear;
|
|
1941
|
+
/** The composite of `layers` over `rect` as float RGBA in the working space (`rect.width × rect.height × 4`). */
|
|
1942
|
+
composite(layers, rect, sources) {
|
|
1943
|
+
this.sources = sources;
|
|
1944
|
+
const acc = new Float32Array(Math.max(0, rect.width * rect.height * 4));
|
|
1945
|
+
if (rect.width > 0 && rect.height > 0) this.runSteps(this.buildSteps(layers), acc, true, rect);
|
|
1946
|
+
return acc;
|
|
1947
|
+
}
|
|
1948
|
+
// --- Steps (as RasterCompositor.buildSteps) ------------------------------------
|
|
1949
|
+
isPixel(l) {
|
|
1950
|
+
return l.type === "pixel" || (this.profile.isPixelLike?.(l) ?? false);
|
|
1951
|
+
}
|
|
1952
|
+
isFilter(l) {
|
|
1953
|
+
return this.profile.isFilterLayer?.(l) ?? false;
|
|
1954
|
+
}
|
|
1955
|
+
buildSteps(layers) {
|
|
1956
|
+
const steps = [];
|
|
1957
|
+
for (const layer of layers) {
|
|
1958
|
+
const last = steps[steps.length - 1];
|
|
1959
|
+
if (layer.clipped && last && (last.kind === "content" || last.kind === "group")) {
|
|
1960
|
+
last.clipped.push(layer);
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
if (this.isFilter(layer)) {
|
|
1964
|
+
if (last && last.kind === "adjust") last.layers.push(layer);
|
|
1965
|
+
else steps.push({ kind: "adjust", layers: [layer] });
|
|
1966
|
+
} else if (isGroup(layer)) steps.push({ kind: "group", layer, clipped: [], steps: this.buildSteps(layer.children ?? []) });
|
|
1967
|
+
else steps.push({ kind: "content", layer, clipped: [] });
|
|
1968
|
+
}
|
|
1969
|
+
return steps;
|
|
1970
|
+
}
|
|
1971
|
+
runSteps(steps, acc, empty, r) {
|
|
1972
|
+
for (const s of steps) empty = this.runStep(s, acc, empty, r);
|
|
1973
|
+
return empty;
|
|
1974
|
+
}
|
|
1975
|
+
runStep(step, acc, empty, r) {
|
|
1976
|
+
if (step.kind === "adjust") {
|
|
1977
|
+
if (empty) return true;
|
|
1978
|
+
this.applyFilterRun(step.layers, acc, r);
|
|
1979
|
+
return false;
|
|
1980
|
+
}
|
|
1981
|
+
const layer = step.layer;
|
|
1982
|
+
if (!layer.visible) return empty;
|
|
1983
|
+
const clipped = step.clipped.filter((l) => l.visible);
|
|
1984
|
+
if (step.kind === "content" && this.deepEffects(layer)) return this.runEffects(layer, clipped, acc, r) ? false : empty;
|
|
1985
|
+
const coverage = this.renderedAlone(layer) ? null : this.base.maskCoverage(layer, r, this.sources);
|
|
1986
|
+
if (coverage && coverage.every((v) => v <= 0)) return empty;
|
|
1987
|
+
if (step.kind === "group" && !this.renderedAlone(layer)) {
|
|
1988
|
+
if (layer.blend === "pass-through" && !clipped.length) {
|
|
1989
|
+
if (layer.opacity >= 1 && !coverage) return this.runSteps(step.steps, acc, empty, r);
|
|
1990
|
+
const before = acc.slice();
|
|
1991
|
+
const stillEmpty = this.runSteps(step.steps, acc, empty, r);
|
|
1992
|
+
lerp(acc, before, layer.opacity, coverage);
|
|
1993
|
+
return empty && stillEmpty;
|
|
1994
|
+
}
|
|
1995
|
+
const buf2 = new Float32Array(acc.length);
|
|
1996
|
+
if (this.runSteps(step.steps, buf2, true, r)) return empty;
|
|
1997
|
+
this.applyClipped(clipped, buf2, r);
|
|
1998
|
+
const mode2 = layer.blend === "pass-through" ? "normal" : layer.blend;
|
|
1999
|
+
this.blend(acc, buf2, mode2, layer.opacity, coverage);
|
|
2000
|
+
return false;
|
|
2001
|
+
}
|
|
2002
|
+
const content = this.content(layer, r);
|
|
2003
|
+
if (!content) return empty;
|
|
2004
|
+
const alone = this.renderedAlone(layer);
|
|
2005
|
+
const fill2 = !alone && this.isPixel(layer) ? layer.fillOpacity ?? 1 : 1;
|
|
2006
|
+
const mode = this.blendOf(layer);
|
|
2007
|
+
if (!clipped.length) {
|
|
2008
|
+
this.blend(acc, content, mode, layer.opacity * fill2, coverage);
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
const buf = new Float32Array(acc.length);
|
|
2012
|
+
this.blend(buf, content, "normal", fill2, coverage);
|
|
2013
|
+
this.applyClipped(clipped, buf, r);
|
|
2014
|
+
this.blend(acc, buf, mode, layer.opacity, null);
|
|
2015
|
+
return false;
|
|
2016
|
+
}
|
|
2017
|
+
/**
|
|
2018
|
+
* A pixel layer with effects onto `acc` over `r`: its deep content (filters, mask, clipped
|
|
2019
|
+
* layers) and the effects composited in float. Effect maps come from the 8-bit shape. At 32
|
|
2020
|
+
* bits the effects composite in sRGB-encoded values (their colours and blends are defined
|
|
2021
|
+
* there) and the result is decoded back. Returns whether anything was drawn.
|
|
2022
|
+
*/
|
|
2023
|
+
runEffects(layer, clipped, acc, r) {
|
|
2024
|
+
const fx = this.base.effectsInput(layer, r, this.sources);
|
|
2025
|
+
if (!fx) return false;
|
|
2026
|
+
const a = fx.rect;
|
|
2027
|
+
const content = this.content(layer, a) ?? new Float32Array(a.width * a.height * 4);
|
|
2028
|
+
const coverage = this.base.maskCoverage(layer, a, this.sources);
|
|
2029
|
+
if (coverage) for (let i = 0; i < coverage.length; i++) content[i * 4 + 3] *= coverage[i];
|
|
2030
|
+
if (clipped.length) this.applyClipped(clipped, content, a);
|
|
2031
|
+
const sub = new Float32Array(a.width * a.height * 4);
|
|
2032
|
+
for (let y = 0; y < a.height; y++) {
|
|
2033
|
+
const s = ((a.y - r.y + y) * r.width + (a.x - r.x)) * 4;
|
|
2034
|
+
sub.set(acc.subarray(s, s + a.width * 4), y * a.width * 4);
|
|
2035
|
+
}
|
|
2036
|
+
if (this.linear) {
|
|
2037
|
+
encodeInPlace(sub);
|
|
2038
|
+
encodeInPlace(content);
|
|
2039
|
+
}
|
|
2040
|
+
compositeLayerEffects({ ...fx, backdrop: sub, content });
|
|
2041
|
+
if (this.linear) decodeInPlace(sub);
|
|
2042
|
+
for (let y = 0; y < a.height; y++) acc.set(sub.subarray(y * a.width * 4, (y + 1) * a.width * 4), ((a.y - r.y + y) * r.width + (a.x - r.x)) * 4);
|
|
2043
|
+
return true;
|
|
2179
2044
|
}
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
const
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2045
|
+
applyClipped(clipped, buf, r) {
|
|
2046
|
+
let run = [];
|
|
2047
|
+
const flush = () => {
|
|
2048
|
+
if (run.length) this.applyFilterRun(run, buf, r);
|
|
2049
|
+
run = [];
|
|
2050
|
+
};
|
|
2051
|
+
for (const c of clipped) {
|
|
2052
|
+
if (this.isFilter(c)) {
|
|
2053
|
+
run.push(c);
|
|
2054
|
+
continue;
|
|
2055
|
+
}
|
|
2056
|
+
flush();
|
|
2057
|
+
const alone = this.renderedAlone(c);
|
|
2058
|
+
const coverage = alone ? null : this.base.maskCoverage(c, r, this.sources);
|
|
2059
|
+
let src;
|
|
2060
|
+
if (isGroup(c) && !alone) {
|
|
2061
|
+
src = new Float32Array(buf.length);
|
|
2062
|
+
if (this.runSteps(this.buildSteps(c.children ?? []), src, true, r)) continue;
|
|
2063
|
+
} else src = this.content(c, r);
|
|
2064
|
+
if (!src) continue;
|
|
2065
|
+
const fill2 = !alone && this.isPixel(c) ? c.fillOpacity ?? 1 : 1;
|
|
2066
|
+
const keep = new Float32Array(buf.length >> 2);
|
|
2067
|
+
for (let i = 3, p = 0; i < buf.length; i += 4, p++) keep[p] = buf[i];
|
|
2068
|
+
this.blend(buf, src, c.blend === "pass-through" ? "normal" : this.blendOf(c), c.opacity * fill2, coverage);
|
|
2069
|
+
for (let i = 3, p = 0; i < buf.length; i += 4, p++) buf[i] = keep[p];
|
|
2194
2070
|
}
|
|
2071
|
+
flush();
|
|
2195
2072
|
}
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2073
|
+
applyFilterRun(layers, acc, r) {
|
|
2074
|
+
const visible = [];
|
|
2075
|
+
let masks;
|
|
2076
|
+
for (const node of layers) {
|
|
2077
|
+
if (!node.visible) continue;
|
|
2078
|
+
const cov = this.base.maskCoverage(node, r, this.sources);
|
|
2079
|
+
if (cov) {
|
|
2080
|
+
if (cov.every((v) => v <= 0)) continue;
|
|
2081
|
+
(masks ??= /* @__PURE__ */ new Map()).set(node.id, cov);
|
|
2082
|
+
}
|
|
2083
|
+
visible.push(node);
|
|
2084
|
+
}
|
|
2085
|
+
if (!visible.length) return;
|
|
2086
|
+
if (this.deep.applyFilterRunDeep) {
|
|
2087
|
+
if (this.linear) mapColour(acc, srgbEncode);
|
|
2088
|
+
this.deep.applyFilterRunDeep(visible, acc, r, masks, this.sources);
|
|
2089
|
+
if (this.linear) mapColour(acc, srgbDecode);
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
const bytes = this.to8(acc);
|
|
2093
|
+
this.profile.applyFilterRun?.(visible, bytes, r, masks, this.sources);
|
|
2094
|
+
acc.set(this.from8(bytes));
|
|
2205
2095
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2096
|
+
// --- Layer content ---------------------------------------------------------------
|
|
2097
|
+
/** Layers drawn by the 8-bit compositor alone (effects, pixel filters, content the editor draws). */
|
|
2098
|
+
/** A pixel(-like) layer with effects: composited in float through `compositeLayerEffects`. */
|
|
2099
|
+
deepEffects(layer) {
|
|
2100
|
+
return hasActiveEffects(layer.effects) && !this.isFilter(layer) && this.isPixel(layer) && !isGroup(layer);
|
|
2209
2101
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
if (
|
|
2102
|
+
renderedAlone(layer) {
|
|
2103
|
+
if (hasActiveEffects(layer.effects) && !this.isFilter(layer) && !this.deepEffects(layer)) return true;
|
|
2104
|
+
if (this.isPixel(layer)) return Boolean(this.profile.pixelFilters?.(layer)) && !this.deep.pixelFiltersDeep?.(layer);
|
|
2105
|
+
return !isGroup(layer);
|
|
2213
2106
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
if (e.kind === "satin") paintColor(lay, rel(fx(i, 0)), e.color, e.blend, e.opacity);
|
|
2107
|
+
blendOf(layer) {
|
|
2108
|
+
return this.profile.contentBlend?.(layer) ?? layer.blend;
|
|
2217
2109
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
if (
|
|
2110
|
+
/** A layer's float RGBA over `r` (straight alpha, working space), or `null` when it has none there. */
|
|
2111
|
+
content(layer, r) {
|
|
2112
|
+
if (this.renderedAlone(layer)) {
|
|
2113
|
+
const solo = { ...layer, blend: isGroup(layer) && layer.blend === "pass-through" ? "pass-through" : "normal", opacity: 1, clipped: false };
|
|
2114
|
+
const bytes = this.base.composite([solo], r, this.sources);
|
|
2115
|
+
return this.from8(bytes);
|
|
2116
|
+
}
|
|
2117
|
+
const surface = this.sources.surfaceOf ? this.sources.surfaceOf(layer) : layer.surface;
|
|
2118
|
+
if (!surface) return null;
|
|
2119
|
+
const ox = layer.offset?.x ?? 0;
|
|
2120
|
+
const oy = layer.offset?.y ?? 0;
|
|
2121
|
+
const filters = this.isPixel(layer) ? this.deep.pixelFiltersDeep?.(layer) ?? null : null;
|
|
2122
|
+
if (!filters) return this.samples(surface, { x: r.x - ox, y: r.y - oy, width: r.width, height: r.height });
|
|
2123
|
+
const m = filters.reach;
|
|
2124
|
+
const W = this.sources.width;
|
|
2125
|
+
const H = this.sources.height;
|
|
2126
|
+
const x0 = m ? Math.max(0, r.x - m) : r.x;
|
|
2127
|
+
const y0 = m ? Math.max(0, r.y - m) : r.y;
|
|
2128
|
+
const x1 = m ? Math.min(W, r.x + r.width + m) : r.x + r.width;
|
|
2129
|
+
const y1 = m ? Math.min(H, r.y + r.height + m) : r.y + r.height;
|
|
2130
|
+
if (x1 <= x0 || y1 <= y0) return new Float32Array(r.width * r.height * 4);
|
|
2131
|
+
const big = { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
2132
|
+
const data = this.samples(surface, { ...big, x: big.x - ox, y: big.y - oy });
|
|
2133
|
+
filters.apply(data, big.width, big.height, this.linear);
|
|
2134
|
+
if (big.x === r.x && big.y === r.y && big.width === r.width && big.height === r.height) return data;
|
|
2135
|
+
const out = new Float32Array(r.width * r.height * 4);
|
|
2136
|
+
for (let y = 0; y < r.height; y++) {
|
|
2137
|
+
const sy = r.y + y - big.y;
|
|
2138
|
+
if (sy < 0 || sy >= big.height) continue;
|
|
2139
|
+
for (let x = 0; x < r.width; x++) {
|
|
2140
|
+
const sx = r.x + x - big.x;
|
|
2141
|
+
if (sx < 0 || sx >= big.width) continue;
|
|
2142
|
+
const s = (sy * big.width + sx) * 4;
|
|
2143
|
+
const d = (y * r.width + x) * 4;
|
|
2144
|
+
out[d] = data[s];
|
|
2145
|
+
out[d + 1] = data[s + 1];
|
|
2146
|
+
out[d + 2] = data[s + 2];
|
|
2147
|
+
out[d + 3] = data[s + 3];
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
return out;
|
|
2221
2151
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2152
|
+
/** A surface's samples over `rect` (layer space) in the working space. */
|
|
2153
|
+
samples(surface, rect) {
|
|
2154
|
+
const samples = readDeepRegion(surface, rect, this.depth);
|
|
2155
|
+
if (samples instanceof Float32Array) return samples;
|
|
2156
|
+
const out = new Float32Array(samples.length);
|
|
2157
|
+
for (let i = 0; i < samples.length; i++) out[i] = samples[i] / 65535;
|
|
2158
|
+
return out;
|
|
2225
2159
|
}
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
if (!(inW > 0) || !din) continue;
|
|
2233
|
-
const m = new Float32Array(n);
|
|
2234
|
-
for (let i = 0; i < n; i++) m[i] = inside(shape[i]) ? clamp012(inW + 0.5 - din[i]) : 0;
|
|
2235
|
-
paintFill(lay, m, st.fill, frameOf(strokeIdx[k]), env, st.blend, st.opacity);
|
|
2160
|
+
/** 8-bit RGBA to the working space. */
|
|
2161
|
+
from8(bytes) {
|
|
2162
|
+
if (this.linear) return to32From8(bytes, 4);
|
|
2163
|
+
const out = new Float32Array(bytes.length);
|
|
2164
|
+
for (let i = 0; i < bytes.length; i++) out[i] = bytes[i] / 255;
|
|
2165
|
+
return out;
|
|
2236
2166
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2167
|
+
/** The working space to 8-bit RGBA. */
|
|
2168
|
+
to8(acc) {
|
|
2169
|
+
const out = new Uint8ClampedArray(acc.length);
|
|
2170
|
+
for (let i = 0; i < acc.length; i += 4) {
|
|
2171
|
+
for (let c = 0; c < 3; c++) out[i + c] = Math.round((this.linear ? srgbEncode(acc[i + c]) : acc[i + c]) * 255);
|
|
2172
|
+
out[i + 3] = Math.round(acc[i + 3] * 255);
|
|
2173
|
+
}
|
|
2174
|
+
return out;
|
|
2242
2175
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
const
|
|
2248
|
-
const
|
|
2249
|
-
const
|
|
2250
|
-
const
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
} else k = band;
|
|
2264
|
-
share[i] = k * st.opacity * (1 - cover[i]);
|
|
2265
|
-
cover[i] += share[i];
|
|
2266
|
-
ones[i] = share[i] > 0 ? 1 : 0;
|
|
2176
|
+
// --- Blending --------------------------------------------------------------------
|
|
2177
|
+
/** `src` over `dst` with `mode` at `alpha` × coverage (straight alpha, float), as `blendTile`. */
|
|
2178
|
+
blend(dst, src, mode, alpha, coverage) {
|
|
2179
|
+
if (!(alpha > 0)) return;
|
|
2180
|
+
const normal = mode === "normal";
|
|
2181
|
+
const pigment = mode === "pigment";
|
|
2182
|
+
const cb = [0, 0, 0];
|
|
2183
|
+
const cs = [0, 0, 0];
|
|
2184
|
+
const mix = [0, 0, 0];
|
|
2185
|
+
for (let i = 0, p = 0; i < dst.length; i += 4, p++) {
|
|
2186
|
+
const sa = src[i + 3];
|
|
2187
|
+
if (!(sa > 0)) continue;
|
|
2188
|
+
const k = coverage ? alpha * coverage[p] : alpha;
|
|
2189
|
+
if (!(k > 0)) continue;
|
|
2190
|
+
const as = Math.min(1, sa * k);
|
|
2191
|
+
const ab = dst[i + 3];
|
|
2192
|
+
const ao = as + ab * (1 - as);
|
|
2193
|
+
if (ao <= 0) {
|
|
2194
|
+
dst[i] = dst[i + 1] = dst[i + 2] = dst[i + 3] = 0;
|
|
2195
|
+
continue;
|
|
2267
2196
|
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2197
|
+
if (normal && as >= 1) {
|
|
2198
|
+
dst[i] = src[i];
|
|
2199
|
+
dst[i + 1] = src[i + 1];
|
|
2200
|
+
dst[i + 2] = src[i + 2];
|
|
2201
|
+
dst[i + 3] = 1;
|
|
2202
|
+
continue;
|
|
2203
|
+
}
|
|
2204
|
+
for (let c = 0; c < 3; c++) {
|
|
2205
|
+
cb[c] = dst[i + c];
|
|
2206
|
+
cs[c] = src[i + c];
|
|
2207
|
+
}
|
|
2208
|
+
if (pigment) {
|
|
2209
|
+
if (this.linear) for (let c = 0; c < 3; c++) cb[c] = srgbEncode(cb[c]), cs[c] = srgbEncode(cs[c]);
|
|
2210
|
+
mixPigmentRgb(cb, cs, as / ao, mix);
|
|
2211
|
+
for (let c = 0; c < 3; c++) dst[i + c] = this.linear ? srgbDecode(mix[c]) : mix[c];
|
|
2212
|
+
dst[i + 3] = ao;
|
|
2213
|
+
continue;
|
|
2279
2214
|
}
|
|
2215
|
+
const m = normal ? cs : blendRgb(mode, cb, cs);
|
|
2216
|
+
const wb = (1 - as) * ab;
|
|
2217
|
+
const ws = (1 - ab) * as;
|
|
2218
|
+
const wm = as * ab;
|
|
2219
|
+
for (let c = 0; c < 3; c++) dst[i + c] = (wb * cb[c] + ws * cs[c] + wm * m[c]) / ao;
|
|
2220
|
+
dst[i + 3] = ao;
|
|
2280
2221
|
}
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
function lerp(dst, before, alpha, coverage) {
|
|
2225
|
+
for (let i = 0, p = 0; i < dst.length; i += 4, p++) {
|
|
2226
|
+
const k = coverage ? alpha * coverage[p] : alpha;
|
|
2227
|
+
if (k >= 1) continue;
|
|
2228
|
+
for (let c = 0; c < 4; c++) dst[i + c] = before[i + c] + (dst[i + c] - before[i + c]) * k;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
function mapColour(data, f) {
|
|
2232
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
2233
|
+
data[i] = f(data[i]);
|
|
2234
|
+
data[i + 1] = f(data[i + 1]);
|
|
2235
|
+
data[i + 2] = f(data[i + 2]);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
function workingToSamples(data, depth) {
|
|
2239
|
+
if (depth === 32) return data;
|
|
2240
|
+
const out = new Uint16Array(data.length);
|
|
2241
|
+
for (let i = 0; i < data.length; i++) {
|
|
2242
|
+
const v = data[i];
|
|
2243
|
+
out[i] = v > 0 ? v < 1 ? Math.round(v * 65535) : 65535 : 0;
|
|
2244
|
+
}
|
|
2245
|
+
return out;
|
|
2246
|
+
}
|
|
2247
|
+
function encodeInPlace(d) {
|
|
2248
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
2249
|
+
d[i] = srgbEncode(d[i]);
|
|
2250
|
+
d[i + 1] = srgbEncode(d[i + 1]);
|
|
2251
|
+
d[i + 2] = srgbEncode(d[i + 2]);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
function decodeInPlace(d) {
|
|
2255
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
2256
|
+
d[i] = srgbDecode(d[i]);
|
|
2257
|
+
d[i + 1] = srgbDecode(d[i + 1]);
|
|
2258
|
+
d[i + 2] = srgbDecode(d[i + 2]);
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// src/raster/blur.ts
|
|
2263
|
+
function gaussianKernel(sigma) {
|
|
2264
|
+
const r = Math.ceil(sigma * 3);
|
|
2265
|
+
const k = new Float32Array(2 * r + 1);
|
|
2266
|
+
let s = 0;
|
|
2267
|
+
for (let i = -r; i <= r; i++) {
|
|
2268
|
+
const v = Math.exp(-(i * i) / (2 * sigma * sigma));
|
|
2269
|
+
k[i + r] = v;
|
|
2270
|
+
s += v;
|
|
2271
|
+
}
|
|
2272
|
+
for (let i = 0; i < k.length; i++) k[i] /= s;
|
|
2273
|
+
return k;
|
|
2274
|
+
}
|
|
2275
|
+
var DIRECT_GAUSSIAN_MAX_SIGMA = 8;
|
|
2276
|
+
function boxRadii(sigma) {
|
|
2277
|
+
const n = 3;
|
|
2278
|
+
const wIdeal = Math.sqrt(12 * sigma * sigma / n + 1);
|
|
2279
|
+
let wl = Math.floor(wIdeal);
|
|
2280
|
+
if (wl % 2 === 0) wl--;
|
|
2281
|
+
const wu = wl + 2;
|
|
2282
|
+
const mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
|
|
2283
|
+
const m = Math.round(mIdeal);
|
|
2284
|
+
const sizes = [0, 1, 2].map((i) => i < m ? wl : wu);
|
|
2285
|
+
return sizes.map((s) => (s - 1) / 2);
|
|
2286
|
+
}
|
|
2287
|
+
function convolveLine(src, off, stride, len, k, edge, dst) {
|
|
2288
|
+
const r = (k.length - 1) / 2;
|
|
2289
|
+
for (let i = 0; i < len; i++) {
|
|
2290
|
+
let a = 0;
|
|
2291
|
+
const lo = i - r;
|
|
2292
|
+
for (let j = 0; j < k.length; j++) {
|
|
2293
|
+
let x = lo + j;
|
|
2294
|
+
if (x < 0 || x >= len) {
|
|
2295
|
+
if (edge === "zero") continue;
|
|
2296
|
+
x = x < 0 ? 0 : len - 1;
|
|
2292
2297
|
}
|
|
2298
|
+
a += src[off + x * stride] * k[j];
|
|
2293
2299
|
}
|
|
2300
|
+
dst[i] = a;
|
|
2294
2301
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2302
|
+
}
|
|
2303
|
+
function boxesLine(src, off, stride, len, radii, edge, a, b, dst) {
|
|
2304
|
+
const pad = radii.reduce((s, r) => s + r, 0);
|
|
2305
|
+
const plen = len + 2 * pad;
|
|
2306
|
+
for (let i = 0; i < plen; i++) {
|
|
2307
|
+
const j = i - pad;
|
|
2308
|
+
a[i] = j < 0 ? edge === "zero" ? 0 : src[off] : j >= len ? edge === "zero" ? 0 : src[off + (len - 1) * stride] : src[off + j * stride];
|
|
2300
2309
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
for (
|
|
2304
|
-
|
|
2305
|
-
|
|
2310
|
+
let from = a;
|
|
2311
|
+
let to = b;
|
|
2312
|
+
for (const r of radii) {
|
|
2313
|
+
let sum = 0;
|
|
2314
|
+
for (let i = 0; i <= Math.min(r, plen - 1); i++) sum += from[i];
|
|
2315
|
+
const k = 2 * r + 1;
|
|
2316
|
+
for (let i = 0; i < plen; i++) {
|
|
2317
|
+
to[i] = sum / k;
|
|
2318
|
+
const add = i + r + 1;
|
|
2319
|
+
const drop = i - r;
|
|
2320
|
+
if (add < plen) sum += from[add];
|
|
2321
|
+
if (drop >= 0) sum -= from[drop];
|
|
2322
|
+
}
|
|
2323
|
+
const t = from;
|
|
2324
|
+
from = to;
|
|
2325
|
+
to = t;
|
|
2306
2326
|
}
|
|
2307
|
-
for (
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2327
|
+
for (let i = 0; i < len; i++) dst[i] = from[i + pad];
|
|
2328
|
+
}
|
|
2329
|
+
function gaussianBlur(src, w, h, sigma, edge = "zero") {
|
|
2330
|
+
const out = Float32Array.from(src);
|
|
2331
|
+
if (!(sigma >= 0.1) || w <= 0 || h <= 0) return out;
|
|
2332
|
+
const s = Math.min(sigma, 4 * Math.max(w, h));
|
|
2333
|
+
const line = new Float32Array(Math.max(w, h));
|
|
2334
|
+
const passes = [];
|
|
2335
|
+
if (s <= DIRECT_GAUSSIAN_MAX_SIGMA) {
|
|
2336
|
+
const k = gaussianKernel(s);
|
|
2337
|
+
passes.push((buf, off, stride, len) => convolveLine(buf, off, stride, len, k, edge, line));
|
|
2338
|
+
} else {
|
|
2339
|
+
const radii = boxRadii(s);
|
|
2340
|
+
const plen = Math.max(w, h) + 2 * radii.reduce((a2, r) => a2 + r, 0);
|
|
2341
|
+
const a = new Float32Array(plen);
|
|
2342
|
+
const b = new Float32Array(plen);
|
|
2343
|
+
passes.push((buf, off, stride, len) => boxesLine(buf, off, stride, len, radii, edge, a, b, line));
|
|
2312
2344
|
}
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
const ba = work[p + 3];
|
|
2318
|
-
const alpha = aa + (ba - aa) * op;
|
|
2319
|
-
if (alpha <= 0) {
|
|
2320
|
-
backdrop[p] = backdrop[p + 1] = backdrop[p + 2] = backdrop[p + 3] = 0;
|
|
2321
|
-
continue;
|
|
2345
|
+
for (const pass of passes) {
|
|
2346
|
+
for (let y = 0; y < h; y++) {
|
|
2347
|
+
pass(out, y * w, 1, w);
|
|
2348
|
+
out.set(line.subarray(0, w), y * w);
|
|
2322
2349
|
}
|
|
2323
|
-
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);
|
|
2324
|
-
backdrop[p + 3] = Math.round(alpha * 255);
|
|
2325
2350
|
}
|
|
2351
|
+
for (const pass of passes) {
|
|
2352
|
+
for (let x = 0; x < w; x++) {
|
|
2353
|
+
pass(out, x, w, h);
|
|
2354
|
+
for (let y = 0; y < h; y++) out[y * w + x] = line[y];
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
return out;
|
|
2326
2358
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2359
|
+
|
|
2360
|
+
// src/raster/layers.ts
|
|
2361
|
+
var childrenOf = (l) => l.type === "group" ? l.children ?? null : null;
|
|
2362
|
+
function flattenLayers(layers) {
|
|
2363
|
+
const out = [];
|
|
2364
|
+
const walk = (list) => {
|
|
2365
|
+
for (const l of list) {
|
|
2366
|
+
out.push(l);
|
|
2367
|
+
const c = childrenOf(l);
|
|
2368
|
+
if (c) walk(c);
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
walk(layers);
|
|
2372
|
+
return out;
|
|
2373
|
+
}
|
|
2374
|
+
function findLayer(layers, id) {
|
|
2375
|
+
const walk = (list, parent, path) => {
|
|
2376
|
+
for (let i = 0; i < list.length; i++) {
|
|
2377
|
+
const l = list[i];
|
|
2378
|
+
if (l.id === id) return { layer: l, parent, index: i, path };
|
|
2379
|
+
const c = childrenOf(l);
|
|
2380
|
+
if (c) {
|
|
2381
|
+
const g = l;
|
|
2382
|
+
const hit = walk(c, g, [...path, g]);
|
|
2383
|
+
if (hit) return hit;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
return null;
|
|
2387
|
+
};
|
|
2388
|
+
return walk(layers, null, []);
|
|
2389
|
+
}
|
|
2390
|
+
function mapLayer(layers, id, fn) {
|
|
2391
|
+
let changed = false;
|
|
2392
|
+
const walk = (list) => {
|
|
2393
|
+
let out = null;
|
|
2394
|
+
for (let i = 0; i < list.length; i++) {
|
|
2395
|
+
const l = list[i];
|
|
2396
|
+
let next = l;
|
|
2397
|
+
if (l.id === id) {
|
|
2398
|
+
next = fn(l);
|
|
2399
|
+
changed = true;
|
|
2400
|
+
} else {
|
|
2401
|
+
const c = childrenOf(l);
|
|
2402
|
+
if (c) {
|
|
2403
|
+
const children = walk(c);
|
|
2404
|
+
if (children !== c) next = { ...l, children };
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
if (next !== l) {
|
|
2408
|
+
if (!out) out = list.slice(0, i);
|
|
2409
|
+
if (next) out.push(next);
|
|
2410
|
+
} else if (out) out.push(l);
|
|
2411
|
+
}
|
|
2412
|
+
return out ?? list;
|
|
2413
|
+
};
|
|
2414
|
+
const result = walk(layers);
|
|
2415
|
+
return changed ? result : layers;
|
|
2416
|
+
}
|
|
2417
|
+
function insertLayer(layers, layer, parentId, index) {
|
|
2418
|
+
const put = (list) => {
|
|
2419
|
+
const at = index === void 0 ? list.length : Math.max(0, Math.min(list.length, index));
|
|
2420
|
+
const next = list.slice();
|
|
2421
|
+
next.splice(at, 0, layer);
|
|
2422
|
+
return next;
|
|
2423
|
+
};
|
|
2424
|
+
if (!parentId) return put(layers);
|
|
2425
|
+
return mapLayer(layers, parentId, (g) => {
|
|
2426
|
+
const c = childrenOf(g);
|
|
2427
|
+
return c ? { ...g, children: put(c) } : g;
|
|
2343
2428
|
});
|
|
2344
2429
|
}
|
|
2430
|
+
function isLayerLocked(layer, what) {
|
|
2431
|
+
return Boolean(layer.locks?.all || layer.locks?.[what]);
|
|
2432
|
+
}
|
|
2345
2433
|
|
|
2346
2434
|
// src/raster/compositor.ts
|
|
2347
2435
|
var TILE_PIXELS = TILE * TILE;
|
|
@@ -2591,6 +2679,26 @@ var RasterCompositor = class {
|
|
|
2591
2679
|
this.setSources(sources);
|
|
2592
2680
|
return this.maskTile(layer, r);
|
|
2593
2681
|
}
|
|
2682
|
+
/**
|
|
2683
|
+
* For the float compositor: everything `compositeLayerEffects` needs for `layer` over `r` but
|
|
2684
|
+
* the pixels (the area its effects reach inside `r`, the effect maps built from its 8-bit
|
|
2685
|
+
* shape, blend, opacities, canvas and context), or `null` when nothing reaches `r`.
|
|
2686
|
+
*/
|
|
2687
|
+
effectsInput(layer, r, sources) {
|
|
2688
|
+
this.setSources(sources);
|
|
2689
|
+
const maps = this.effectMapsOf(layer);
|
|
2690
|
+
const rect = this.effectArea(layer, maps, r);
|
|
2691
|
+
if (!rect) return null;
|
|
2692
|
+
return {
|
|
2693
|
+
rect,
|
|
2694
|
+
maps,
|
|
2695
|
+
blend: isGroup2(layer) ? layer.blend === "pass-through" ? "normal" : layer.blend : this.contentBlend(layer),
|
|
2696
|
+
opacity: layer.opacity,
|
|
2697
|
+
fillOpacity: this.isPixel(layer) ? layer.fillOpacity ?? 1 : 1,
|
|
2698
|
+
canvas: { x: 0, y: 0, width: this.sources.width, height: this.sources.height },
|
|
2699
|
+
ctx: this.effectContext()
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2594
2702
|
// --- Steps -------------------------------------------------------------------
|
|
2595
2703
|
/** Pixel layers, and the layer kinds the profile composites like them. */
|
|
2596
2704
|
isPixel(l) {
|
|
@@ -4162,22 +4270,13 @@ export {
|
|
|
4162
4270
|
validateLayerEffects,
|
|
4163
4271
|
normalizeLayerEffects,
|
|
4164
4272
|
effectsFingerprint,
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
DIRECT_GAUSSIAN_MAX_SIGMA,
|
|
4168
|
-
gaussianBlur,
|
|
4169
|
-
flattenLayers,
|
|
4170
|
-
findLayer,
|
|
4171
|
-
mapLayer,
|
|
4172
|
-
insertLayer,
|
|
4173
|
-
isLayerLocked,
|
|
4273
|
+
CURVE_LUT_SIZE,
|
|
4274
|
+
curveLut,
|
|
4174
4275
|
PatternTile,
|
|
4175
4276
|
PatternPlacement,
|
|
4176
4277
|
builtinPatternIds,
|
|
4177
4278
|
builtinPattern,
|
|
4178
4279
|
hashNoise,
|
|
4179
|
-
CURVE_LUT_SIZE,
|
|
4180
|
-
curveLut,
|
|
4181
4280
|
STROKE_CHAMFER_MAX,
|
|
4182
4281
|
contourLut,
|
|
4183
4282
|
activeEffectItems,
|
|
@@ -4188,6 +4287,15 @@ export {
|
|
|
4188
4287
|
effectFillColors,
|
|
4189
4288
|
compositeLayerEffects,
|
|
4190
4289
|
renderLayerEffects,
|
|
4290
|
+
DeepCompositor,
|
|
4291
|
+
workingToSamples,
|
|
4292
|
+
DIRECT_GAUSSIAN_MAX_SIGMA,
|
|
4293
|
+
gaussianBlur,
|
|
4294
|
+
flattenLayers,
|
|
4295
|
+
findLayer,
|
|
4296
|
+
mapLayer,
|
|
4297
|
+
insertLayer,
|
|
4298
|
+
isLayerLocked,
|
|
4191
4299
|
blendTile,
|
|
4192
4300
|
RasterCompositor,
|
|
4193
4301
|
changedTiles,
|
|
@@ -4202,4 +4310,4 @@ export {
|
|
|
4202
4310
|
warpSurface,
|
|
4203
4311
|
compositeSurfaceOver
|
|
4204
4312
|
};
|
|
4205
|
-
//# sourceMappingURL=chunk-
|
|
4313
|
+
//# sourceMappingURL=chunk-CLTTLLWA.js.map
|