@pixerate/editor 0.248.0 → 0.250.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 (122) hide show
  1. package/README.md +1 -0
  2. package/dist/animation-editor/index.cjs +118 -114
  3. package/dist/animation-editor/index.js +11 -7
  4. package/dist/{brushstrokeRender-QLCXROJK.js → brushstrokeRender-2FHFLSEG.js} +8 -4
  5. package/dist/{brushstrokeRender-DTDPFBLC.cjs → brushstrokeRender-QGRXOKDA.cjs} +15 -11
  6. package/dist/canvas/index.cjs +71 -15
  7. package/dist/canvas/index.d.cts +1 -0
  8. package/dist/canvas/index.d.ts +1 -0
  9. package/dist/canvas/index.js +58 -2
  10. package/dist/canvas/stickers.d.cts +35 -0
  11. package/dist/canvas/stickers.d.ts +35 -0
  12. package/dist/{chunk-RXZL4NG5.js → chunk-226QDRO3.js} +1 -1
  13. package/dist/{chunk-SC2RZ5EF.js → chunk-2VJAVXLI.js} +2 -2
  14. package/dist/{chunk-OLNIPTB3.cjs → chunk-2VX55NFM.cjs} +1 -320
  15. package/dist/chunk-3K6HWEXZ.cjs +92 -0
  16. package/dist/{chunk-UOOK7HTX.js → chunk-3PPJRMIG.js} +1 -1
  17. package/dist/{chunk-ZFSLEOSQ.js → chunk-3TWOCSCY.js} +11 -9
  18. package/dist/chunk-4D6KP5BV.js +92 -0
  19. package/dist/chunk-4JNWA3VT.cjs +302 -0
  20. package/dist/{chunk-L3JFOTBQ.js → chunk-5E2Q7ZHI.js} +1 -1
  21. package/dist/{chunk-2ONK3ERD.cjs → chunk-75K3W4D6.cjs} +20 -20
  22. package/dist/{chunk-AU6MWQKZ.js → chunk-ABL7IWFZ.js} +1 -1
  23. package/dist/chunk-ATK6ABN3.cjs +303 -0
  24. package/dist/{chunk-7T7JWTVE.cjs → chunk-BOHDJJ2O.cjs} +8 -8
  25. package/dist/{chunk-2PBZBRP5.js → chunk-D5KGAO4Z.js} +10 -92
  26. package/dist/{chunk-HB4ICNNA.cjs → chunk-DPYAO5U6.cjs} +42 -42
  27. package/dist/{chunk-6G6ZDDDB.cjs → chunk-GT77U7QG.cjs} +303 -149
  28. package/dist/{chunk-PQ3H2AAN.cjs → chunk-I7NJPHWV.cjs} +122 -204
  29. package/dist/{chunk-42TWUPNJ.js → chunk-IRSTHLQQ.js} +1 -320
  30. package/dist/{chunk-GHCSC2WP.cjs → chunk-ISIO63Q5.cjs} +21 -15
  31. package/dist/{chunk-OFH6Q67U.js → chunk-JA57JIT5.js} +9 -3
  32. package/dist/{chunk-VMROIWLL.cjs → chunk-KBYB7OWB.cjs} +3 -3
  33. package/dist/{chunk-Q2HA52V4.js → chunk-KDLBJ6QR.js} +2 -2
  34. package/dist/{chunk-WVS5KQDH.cjs → chunk-M2VKFE5L.cjs} +246 -244
  35. package/dist/{chunk-5GLYUSNT.js → chunk-NOHJP5TK.js} +1 -1
  36. package/dist/{chunk-XNRBMYNB.js → chunk-NTRAVM2I.js} +1 -1
  37. package/dist/{chunk-VNBUDVD4.js → chunk-OTEPAQJX.js} +228 -74
  38. package/dist/chunk-OTTT5N3K.js +322 -0
  39. package/dist/{chunk-QOBCBGGI.cjs → chunk-QKXO6JPP.cjs} +14 -14
  40. package/dist/{chunk-NZ3O3A5C.js → chunk-SMSGP2HQ.js} +1 -1
  41. package/dist/chunk-SZTI6EER.js +13 -0
  42. package/dist/chunk-U4NUOKBT.js +303 -0
  43. package/dist/chunk-U67EN7GG.cjs +13 -0
  44. package/dist/chunk-W5M3J7PV.cjs +322 -0
  45. package/dist/{chunk-4P6H35HU.js → chunk-WA44NKPF.js} +2 -2
  46. package/dist/chunk-WKH5ZAUA.js +302 -0
  47. package/dist/{chunk-L6REDLNB.cjs → chunk-WPUXGDH4.cjs} +7 -7
  48. package/dist/{chunk-P3NIPIKS.js → chunk-WQCNC4DK.js} +1 -1
  49. package/dist/{chunk-IBDU6EGZ.cjs → chunk-X7WRL5ZZ.cjs} +35 -35
  50. package/dist/{chunk-XWTRATIM.cjs → chunk-YM6ZBTXG.cjs} +23 -23
  51. package/dist/{chunk-TPWB4Y6L.cjs → chunk-YW43M56C.cjs} +1 -1
  52. package/dist/{chunk-K5DS2JTA.cjs → chunk-Z3BRH4JK.cjs} +6 -6
  53. package/dist/{chunk-XNZFVYC2.cjs → chunk-ZBCT5MEA.cjs} +7 -7
  54. package/dist/effects/helpers.d.cts +6 -0
  55. package/dist/effects/helpers.d.ts +6 -0
  56. package/dist/effects/index.cjs +62 -60
  57. package/dist/effects/index.js +5 -3
  58. package/dist/effects/tools.d.cts +6 -0
  59. package/dist/effects/tools.d.ts +6 -0
  60. package/dist/image-editor/effects.d.cts +1 -1
  61. package/dist/image-editor/effects.d.ts +1 -1
  62. package/dist/image-editor/index.cjs +1097 -1069
  63. package/dist/image-editor/index.d.cts +1 -0
  64. package/dist/image-editor/index.d.ts +1 -0
  65. package/dist/image-editor/index.js +58 -30
  66. package/dist/image-editor/stickers.d.cts +9 -0
  67. package/dist/image-editor/stickers.d.ts +9 -0
  68. package/dist/paint/index.cjs +8 -5
  69. package/dist/paint/index.js +13 -10
  70. package/dist/paint-editor/controller.d.cts +37 -1
  71. package/dist/paint-editor/controller.d.ts +37 -1
  72. package/dist/paint-editor/index.cjs +753 -609
  73. package/dist/paint-editor/index.d.cts +3 -0
  74. package/dist/paint-editor/index.d.ts +3 -0
  75. package/dist/paint-editor/index.js +187 -43
  76. package/dist/paint-editor/sticker-art.d.cts +55 -0
  77. package/dist/paint-editor/sticker-art.d.ts +55 -0
  78. package/dist/paint-editor/stickers.d.cts +30 -17
  79. package/dist/paint-editor/stickers.d.ts +30 -17
  80. package/dist/paint-editor/stroke.d.cts +4 -0
  81. package/dist/paint-editor/stroke.d.ts +4 -0
  82. package/dist/paint-editor/ui.d.cts +10 -2
  83. package/dist/paint-editor/ui.d.ts +10 -2
  84. package/dist/raster/index.cjs +6 -4
  85. package/dist/raster/index.js +12 -10
  86. package/dist/stickers/browser.d.cts +48 -0
  87. package/dist/stickers/browser.d.ts +48 -0
  88. package/dist/stickers/combine.d.cts +10 -0
  89. package/dist/stickers/combine.d.ts +10 -0
  90. package/dist/stickers/conformance.d.cts +18 -0
  91. package/dist/stickers/conformance.d.ts +18 -0
  92. package/dist/stickers/editor.d.cts +14 -0
  93. package/dist/stickers/editor.d.ts +14 -0
  94. package/dist/stickers/errors.d.cts +6 -0
  95. package/dist/stickers/errors.d.ts +6 -0
  96. package/dist/stickers/fake.d.cts +16 -0
  97. package/dist/stickers/fake.d.ts +16 -0
  98. package/dist/stickers/helpers.d.cts +51 -0
  99. package/dist/stickers/helpers.d.ts +51 -0
  100. package/dist/stickers/index.cjs +337 -0
  101. package/dist/stickers/index.d.cts +16 -0
  102. package/dist/stickers/index.d.ts +16 -0
  103. package/dist/stickers/index.js +337 -0
  104. package/dist/stickers/stamps.d.cts +13 -0
  105. package/dist/stickers/stamps.d.ts +13 -0
  106. package/dist/stickers/tools.d.cts +81 -0
  107. package/dist/stickers/tools.d.ts +81 -0
  108. package/dist/stickers/types.d.cts +123 -0
  109. package/dist/stickers/types.d.ts +123 -0
  110. package/dist/vector/index.cjs +1018 -981
  111. package/dist/vector/index.d.cts +1 -0
  112. package/dist/vector/index.d.ts +1 -0
  113. package/dist/vector/index.js +44 -7
  114. package/dist/vector/stickers.d.cts +11 -0
  115. package/dist/vector/stickers.d.ts +11 -0
  116. package/dist/video-editor/index.cjs +344 -344
  117. package/dist/video-editor/index.js +4 -4
  118. package/package.json +11 -1
  119. package/skills/README.md +1 -0
  120. package/skills/pixerate-image-editor/SKILL.md +1 -1
  121. package/skills/pixerate-paint-editor/SKILL.md +8 -5
  122. package/skills/pixerate-stickers/SKILL.md +57 -0
@@ -5,7 +5,7 @@ import {
5
5
  moveAnchorHandle,
6
6
  subpathSegment,
7
7
  subpathSegmentCount
8
- } from "./chunk-Q2HA52V4.js";
8
+ } from "./chunk-KDLBJ6QR.js";
9
9
  import {
10
10
  segmentPoint
11
11
  } from "./chunk-WEIK3UYB.js";
@@ -18,7 +18,7 @@ import {
18
18
  unionBounds,
19
19
  vectorGradientPoints,
20
20
  widthProfileAt
21
- } from "./chunk-Q2HA52V4.js";
21
+ } from "./chunk-KDLBJ6QR.js";
22
22
  import {
23
23
  segmentLength,
24
24
  segmentParamAtLength,
@@ -1,3 +1,14 @@
1
+ import {
2
+ STAMP_KINDS,
3
+ describeStickerRef,
4
+ editorStampOf,
5
+ normalizeStickerRef,
6
+ stampCoverage,
7
+ stickerIdFor
8
+ } from "./chunk-WKH5ZAUA.js";
9
+ import {
10
+ StickerError
11
+ } from "./chunk-SZTI6EER.js";
1
12
  import {
2
13
  CoverageMap,
3
14
  DEFAULT_BLENDER,
@@ -11,16 +22,22 @@ import {
11
22
  TileMedium,
12
23
  dabRect,
13
24
  grainCatch,
14
- hash2,
15
25
  normalizeBlender,
16
26
  normalizeBrush,
17
27
  normalizeMixer,
18
28
  normalizeSmudge,
19
- rand01,
20
- randSigned,
21
29
  rasterizeDab,
22
30
  readPaintSource
23
- } from "./chunk-2PBZBRP5.js";
31
+ } from "./chunk-D5KGAO4Z.js";
32
+ import {
33
+ hash2,
34
+ rand01,
35
+ randSigned
36
+ } from "./chunk-4D6KP5BV.js";
37
+ import {
38
+ decodePng,
39
+ parseDataUrl
40
+ } from "./chunk-OTTT5N3K.js";
24
41
  import {
25
42
  TILE,
26
43
  TiledSurface,
@@ -586,14 +603,129 @@ function toStrokePoint(p) {
586
603
  return { x: p.x, y: p.y, pressure: pressureOf(p), ...typeof p.t === "number" ? { t: p.t } : {} };
587
604
  }
588
605
 
606
+ // src/paint-editor/sticker-art.ts
607
+ var STICKER_ART_SIZE = 256;
608
+ var defaultStickerRasterizer = async (asset, size) => {
609
+ if (asset.kind === "image") {
610
+ const parsed = parseDataUrl(asset.url);
611
+ if (parsed && parsed.mime === "image/png") {
612
+ const png = decodePng(parsed.bytes);
613
+ return { width: png.width, height: png.height, data: new Uint8ClampedArray(png.data) };
614
+ }
615
+ }
616
+ const g = globalThis;
617
+ if (!g.createImageBitmap || !g.OffscreenCanvas) {
618
+ throw new StickerError("unsupported_format", `This environment can't draw ${asset.kind === "svg" ? "SVG" : "non-PNG"} sticker art; pass rasterizeSticker in the paint editor's options (or use a provider that offers png).`);
619
+ }
620
+ const blob = asset.kind === "svg" ? new Blob([asset.svg], { type: "image/svg+xml" }) : await (await g.fetch(asset.url)).blob();
621
+ const bitmap = await g.createImageBitmap(blob);
622
+ const scale = size / Math.max(bitmap.width, bitmap.height, 1);
623
+ const w = Math.max(1, Math.round(bitmap.width * scale));
624
+ const h = Math.max(1, Math.round(bitmap.height * scale));
625
+ const canvas = new g.OffscreenCanvas(w, h);
626
+ const ctx = canvas.getContext("2d");
627
+ ctx.drawImage(bitmap, 0, 0, w, h);
628
+ const img = ctx.getImageData(0, 0, w, h);
629
+ return { width: w, height: h, data: img.data };
630
+ };
631
+ var StickerArtCache = class {
632
+ entries = /* @__PURE__ */ new Map();
633
+ /** The art for `ref` when it has loaded (editor stamps never need any). */
634
+ get = (ref) => {
635
+ const e = this.entries.get(ref);
636
+ return e?.status === "ready" ? e.art : void 0;
637
+ };
638
+ /** `ready`, `loading`, `failed` (with the reason) or `none` (never asked for). Editor stamps are always `ready`. */
639
+ status(ref) {
640
+ if (editorStampOf(ref)) return { status: "ready" };
641
+ const e = this.entries.get(ref);
642
+ if (!e) return { status: "none" };
643
+ return e.status === "failed" ? { status: "failed", error: e.error } : { status: e.status };
644
+ }
645
+ /**
646
+ * Loads and rasterises `ref` from `provider` once (later calls share the load). Unknown refs
647
+ * reject with the closest known ones; failures are kept so they are reported, and retried on
648
+ * the next call.
649
+ */
650
+ ensure(provider, ref, rasterize) {
651
+ if (editorStampOf(ref)) return Promise.resolve(null);
652
+ const e = this.entries.get(ref);
653
+ if (e?.status === "ready") return Promise.resolve(e.art);
654
+ if (e?.status === "loading") return e.promise;
655
+ const promise = (async () => {
656
+ const d = await describeStickerRef(provider, ref);
657
+ const id = stickerIdFor(provider, ref);
658
+ const max = provider.capabilities.maxSize ?? STICKER_ART_SIZE;
659
+ const size = Math.min(STICKER_ART_SIZE, max);
660
+ const asset = await provider.load(id, d.formats.includes("png") ? { format: "png", size } : { format: d.formats[0] ?? "svg", size });
661
+ const px = await rasterize(asset, size);
662
+ if (!(px.width > 0 && px.height > 0) || px.data.length !== px.width * px.height * 4) throw new StickerError("load_failed", `The art for "${ref}" came back empty or the wrong size.`);
663
+ return { ...px, tintable: d.tintable };
664
+ })();
665
+ this.entries.set(ref, { status: "loading", promise });
666
+ return promise.then(
667
+ (art) => {
668
+ this.entries.set(ref, { status: "ready", art });
669
+ return art;
670
+ },
671
+ (err) => {
672
+ const message = err instanceof Error ? err.message : String(err);
673
+ this.entries.set(ref, { status: "failed", error: message });
674
+ throw err;
675
+ }
676
+ );
677
+ }
678
+ /** Forgets every load (a new provider). */
679
+ clear() {
680
+ this.entries.clear();
681
+ }
682
+ };
683
+ function sampleArt(art, x, y, out) {
684
+ const { width: w, height: h, data } = art;
685
+ const fx = x - 0.5;
686
+ const fy = y - 0.5;
687
+ const x0 = Math.floor(fx);
688
+ const y0 = Math.floor(fy);
689
+ const tx = fx - x0;
690
+ const ty = fy - y0;
691
+ let r = 0;
692
+ let g = 0;
693
+ let b = 0;
694
+ let a = 0;
695
+ for (let k = 0; k < 4; k++) {
696
+ const xx = x0 + (k & 1);
697
+ const yy = y0 + (k >> 1);
698
+ if (xx < 0 || yy < 0 || xx >= w || yy >= h) continue;
699
+ const wgt = (k & 1 ? tx : 1 - tx) * (k >> 1 ? ty : 1 - ty);
700
+ const i = (yy * w + xx) * 4;
701
+ const al = data[i + 3] * wgt;
702
+ r += data[i] * al;
703
+ g += data[i + 1] * al;
704
+ b += data[i + 2] * al;
705
+ a += al;
706
+ }
707
+ out[0] = a > 0 ? r / a / 255 : 0;
708
+ out[1] = a > 0 ? g / a / 255 : 0;
709
+ out[2] = a > 0 ? b / a / 255 : 0;
710
+ out[3] = a / 255;
711
+ return out;
712
+ }
713
+
589
714
  // src/paint-editor/stickers.ts
590
- var STAMP_KINDS = ["dot", "star", "heart", "leaf", "flower", "confetti", "splat", "sparkle", "ring"];
591
- var DEFAULT_STICKER = { stamps: ["star"], density: 0.4, sizeJitter: 0.4, rotationJitter: 1, scatter: 0.5, colorJitter: 0.2 };
715
+ var MAX_STICKER_STAMPS = 16;
716
+ var DEFAULT_STICKER = { stamps: ["editor:star"], density: 0.4, sizeJitter: 0.4, rotationJitter: 1, scatter: 0.5, colorJitter: 0.2 };
592
717
  var unit2 = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
593
718
  function normalizeStickerSettings(raw = {}, base = DEFAULT_STICKER) {
594
- const stamps = Array.isArray(raw.stamps) ? raw.stamps.filter((s) => STAMP_KINDS.includes(s)) : base.stamps;
719
+ const refs = (list) => list.flatMap((s) => {
720
+ try {
721
+ return [normalizeStickerRef(s)];
722
+ } catch {
723
+ return [];
724
+ }
725
+ });
726
+ const stamps = Array.isArray(raw.stamps) ? refs(raw.stamps) : refs(base.stamps);
595
727
  return {
596
- stamps: stamps.length ? [...new Set(stamps)] : [...base.stamps],
728
+ stamps: stamps.length ? [...new Set(stamps)].slice(0, MAX_STICKER_STAMPS) : refs(base.stamps.length ? base.stamps : DEFAULT_STICKER.stamps),
597
729
  density: unit2(raw.density, base.density),
598
730
  sizeJitter: unit2(raw.sizeJitter, base.sizeJitter),
599
731
  rotationJitter: unit2(raw.rotationJitter, base.rotationJitter),
@@ -601,59 +733,6 @@ function normalizeStickerSettings(raw = {}, base = DEFAULT_STICKER) {
601
733
  colorJitter: unit2(raw.colorJitter, base.colorJitter)
602
734
  };
603
735
  }
604
- function stampCoverage(kind, u, v, t) {
605
- const r = Math.hypot(u, v);
606
- const a = Math.atan2(v, u);
607
- const edge = (d) => Math.min(1, Math.max(0, d * 24 + 0.5));
608
- switch (kind) {
609
- case "dot":
610
- return edge(0.95 - r);
611
- case "ring":
612
- return edge(Math.min(0.95 - r, r - 0.6));
613
- case "star": {
614
- const f = (a + Math.PI / 2) * 5 / (2 * Math.PI) % 1;
615
- const notch = Math.min(f < 0 ? f + 1 : f, 1 - (f < 0 ? f + 1 : f)) * 2;
616
- return edge(0.95 - 0.55 * notch - r);
617
- }
618
- case "sparkle": {
619
- const q = Math.pow(Math.abs(u) / 0.95, 0.6) + Math.pow(Math.abs(v) / 0.95, 0.6);
620
- return edge((1 - q) * 0.4);
621
- }
622
- case "heart": {
623
- const x = u * 1.1;
624
- const y = -v * 1.1 + 0.15;
625
- const q = x * x + y * y - 0.55;
626
- return edge(-(q * q * q - x * x * y * y * y) * 3);
627
- }
628
- case "leaf": {
629
- const w = 0.42 * Math.sqrt(Math.max(0, 1 - u * u));
630
- const inside = edge(w - Math.abs(v + 0.15 * u * u));
631
- const rib = Math.abs(v + 0.15 * u * u) < 0.03 && Math.abs(u) < 0.85 ? 0.65 : 1;
632
- return inside * rib;
633
- }
634
- case "flower": {
635
- const petals = 0.6 + 0.35 * Math.abs(Math.cos(2.5 * a));
636
- const body = edge(petals - r);
637
- return r < 0.22 ? Math.max(body, 1) * 0.85 : body;
638
- }
639
- case "confetti": {
640
- return edge(Math.min(0.9 - Math.abs(u), 0.35 - Math.abs(v)));
641
- }
642
- case "splat": {
643
- let rr = 0.55;
644
- for (let k = 1; k <= 3; k++) rr += 0.08 * Math.sin(a * (k + 2) + hash2(k, 1, t) * 6.28) / k;
645
- let c = edge(rr - r);
646
- for (let k = 0; k < 4; k++) {
647
- const ang = hash2(k, 7, t) * Math.PI * 2;
648
- const dist = 0.72 + 0.2 * hash2(k, 9, t);
649
- const dx = u - Math.cos(ang) * dist;
650
- const dy = v - Math.sin(ang) * dist;
651
- c = Math.max(c, edge(0.08 + 0.06 * hash2(k, 11, t) - Math.hypot(dx, dy)));
652
- }
653
- return c;
654
- }
655
- }
656
- }
657
736
  function hexRgb(hex) {
658
737
  const n = parseInt(hex.slice(1, 7), 16);
659
738
  return [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
@@ -664,7 +743,22 @@ function stickerRect(s) {
664
743
  const y0 = Math.floor(s.y - r);
665
744
  return { x: x0, y: y0, width: Math.ceil(s.x + r) - x0, height: Math.ceil(s.y + r) - y0 };
666
745
  }
667
- function drawSticker(medium, s) {
746
+ function stickerStamp(s) {
747
+ if (s.ref) {
748
+ try {
749
+ return editorStampOf(s.ref);
750
+ } catch {
751
+ return null;
752
+ }
753
+ }
754
+ return s.stamp && STAMP_KINDS.includes(s.stamp) ? s.stamp : null;
755
+ }
756
+ function drawSticker(medium, s, art) {
757
+ const kind = stickerStamp(s);
758
+ if (!kind) {
759
+ const a2 = art?.(s.ref);
760
+ return a2 ? drawArtSticker(medium, s, a2) : stickerRect(s);
761
+ }
668
762
  const rect = stickerRect(s);
669
763
  const n = rect.width * rect.height;
670
764
  const cov = new Float32Array(n);
@@ -682,7 +776,7 @@ function drawSticker(medium, s) {
682
776
  const v = (-dx * sa + dy * ca) / rad;
683
777
  if (u < -1.05 || u > 1.05 || v < -1.05 || v > 1.05) continue;
684
778
  let c = 0;
685
- for (let k = 0; k < 4; k++) c += stampCoverage(s.stamp, u + ((k & 1) - 0.5) / rad / 2, v + ((k >> 1) - 0.5) / rad / 2, s.seed);
779
+ for (let k = 0; k < 4; k++) c += stampCoverage(kind, u + ((k & 1) - 0.5) / rad / 2, v + ((k >> 1) - 0.5) / rad / 2, s.seed);
686
780
  c /= 4;
687
781
  if (c <= 0) continue;
688
782
  cov[p] = c;
@@ -695,6 +789,35 @@ function drawSticker(medium, s) {
695
789
  medium.deposit(rect, cov, s.opacity, [r, g, b], null, field);
696
790
  return rect;
697
791
  }
792
+ function drawArtSticker(medium, s, art) {
793
+ const rect = stickerRect(s);
794
+ const n = rect.width * rect.height;
795
+ const cov = new Float32Array(n);
796
+ const field = new Float32Array(n * 3);
797
+ const a = s.rotation * Math.PI / 180;
798
+ const ca = Math.cos(a);
799
+ const sa = Math.sin(a);
800
+ const scale = Math.max(art.width, art.height) / s.size;
801
+ const [r, g, b] = hexRgb(s.color);
802
+ const px = [0, 0, 0, 0];
803
+ for (let yy = 0, p = 0; yy < rect.height; yy++) {
804
+ for (let xx = 0; xx < rect.width; xx++, p++) {
805
+ const dx = rect.x + xx + 0.5 - s.x;
806
+ const dy = rect.y + yy + 0.5 - s.y;
807
+ const ax = (dx * ca + dy * sa) * scale + art.width / 2;
808
+ const ay = (-dx * sa + dy * ca) * scale + art.height / 2;
809
+ if (ax < -1 || ay < -1 || ax > art.width + 1 || ay > art.height + 1) continue;
810
+ sampleArt(art, ax, ay, px);
811
+ if (!(px[3] > 0)) continue;
812
+ cov[p] = px[3];
813
+ field[p * 3] = art.tintable ? r : px[0];
814
+ field[p * 3 + 1] = art.tintable ? g : px[1];
815
+ field[p * 3 + 2] = art.tintable ? b : px[2];
816
+ }
817
+ }
818
+ medium.deposit(rect, cov, s.opacity, [r, g, b], null, field);
819
+ return rect;
820
+ }
698
821
  function jitterColor(hex, amount, seed, i) {
699
822
  const [r, g, b] = hexRgb(hex);
700
823
  const lch = oklabToOklch(rgbToOklab({ r, g, b, a: 1 }));
@@ -730,6 +853,8 @@ var StickerSpray = class {
730
853
  init;
731
854
  dabCount = 0;
732
855
  stickers = [];
856
+ /** Refs the spray picked but could not draw because their art had not loaded. */
857
+ skipped = /* @__PURE__ */ new Set();
733
858
  smoother;
734
859
  walkers;
735
860
  copies;
@@ -776,9 +901,17 @@ var StickerSpray = class {
776
901
  this.next++;
777
902
  return;
778
903
  }
904
+ const ref = st.stamps[Math.floor(rand01(seed, i, 57) * st.stamps.length)] ?? "editor:dot";
905
+ const stamp = stickerStamp({ ref });
906
+ if (!stamp && !this.init.art?.(ref)) {
907
+ this.skipped.add(ref);
908
+ this.next++;
909
+ return;
910
+ }
779
911
  const sticker = {
780
912
  id: `st${this.next++}`,
781
- stamp: st.stamps[Math.floor(rand01(seed, i, 57) * st.stamps.length)] ?? "dot",
913
+ ref,
914
+ ...stamp ? { stamp } : {},
782
915
  x: Math.round((d.x + Math.cos(a) * dist) * 100) / 100,
783
916
  y: Math.round((d.y + Math.sin(a) * dist) * 100) / 100,
784
917
  size: Math.max(2, Math.round(size * 10) / 10),
@@ -788,7 +921,7 @@ var StickerSpray = class {
788
921
  seed: Math.floor(rand01(seed, i, 65) * 1e9)
789
922
  };
790
923
  this.stickers.push(sticker);
791
- rects.push(drawSticker(this.medium, sticker));
924
+ rects.push(drawSticker(this.medium, sticker, this.init.art));
792
925
  this.dabCount++;
793
926
  }
794
927
  sampler;
@@ -807,16 +940,24 @@ var StickerSpray = class {
807
940
  return { color: "#" + s.rgb.map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, "0")).join(""), alpha: s.alpha };
808
941
  }
809
942
  };
810
- function renderStickers(stickers, target) {
943
+ function renderStickers(stickers, target, art) {
811
944
  const medium = new TileMedium(target, {});
812
- for (const s of stickers) drawSticker(medium, s);
945
+ for (const s of stickers) drawSticker(medium, s, art);
813
946
  }
814
947
  function normalizeStickerUpdate(u) {
815
948
  const out = {};
949
+ if (u.ref !== void 0 && u.stamp !== void 0) throw new Error("Give ref or stamp, not both (stamp is the older name for an editor stamp).");
816
950
  if (u.stamp !== void 0) {
817
- if (!STAMP_KINDS.includes(u.stamp)) throw new Error(`stamp ${JSON.stringify(u.stamp)} is not a stamp; use ${STAMP_KINDS.join(", ")}.`);
951
+ if (!STAMP_KINDS.includes(u.stamp)) throw new Error(`stamp ${JSON.stringify(u.stamp)} is not a stamp; use ${STAMP_KINDS.join(", ")}, or ref for any sticker (find_stickers).`);
952
+ out.ref = `editor:${u.stamp}`;
818
953
  out.stamp = u.stamp;
819
954
  }
955
+ if (u.ref !== void 0) {
956
+ out.ref = normalizeStickerRef(u.ref);
957
+ const stamp = stickerStamp({ ref: out.ref });
958
+ if (stamp) out.stamp = stamp;
959
+ else out.stamp = void 0;
960
+ }
820
961
  for (const k of ["x", "y"]) if (u[k] !== void 0) {
821
962
  if (!Number.isFinite(u[k])) throw new Error(`${k} must be a number of pixels.`);
822
963
  out[k] = u[k];
@@ -839,7 +980,7 @@ function normalizeStickerUpdate(u) {
839
980
  }
840
981
  return out;
841
982
  }
842
- function stickerSurface(stickers, base, dirty) {
983
+ function stickerSurface(stickers, base, dirty, art) {
843
984
  const only = base && dirty ? new Set(dirty.flatMap((r) => tilesInRect(r).map(({ tx, ty }) => tileKey(tx, ty)))) : null;
844
985
  const live = /* @__PURE__ */ new Map();
845
986
  let scratch = null;
@@ -857,7 +998,7 @@ function stickerSurface(stickers, base, dirty) {
857
998
  }
858
999
  };
859
1000
  const reaches = (s) => !only || tilesInRect(stickerRect(s)).some(({ tx, ty }) => only.has(tileKey(tx, ty)));
860
- renderStickers(stickers.filter(reaches), target);
1001
+ renderStickers(stickers.filter(reaches), target, art);
861
1002
  const tiles = /* @__PURE__ */ new Map();
862
1003
  if (only && base) {
863
1004
  for (const [k, t] of base.tiles) if (!only.has(k)) tiles.set(k, t);
@@ -868,7 +1009,7 @@ function stickerSurface(stickers, base, dirty) {
868
1009
  }
869
1010
  return new TiledSurface(4, tiles, 0);
870
1011
  }
871
- function stickerAt(stickers, x, y) {
1012
+ function stickerAt(stickers, x, y, art) {
872
1013
  for (let i = stickers.length - 1; i >= 0; i--) {
873
1014
  const s = stickers[i];
874
1015
  const rad = s.size / 2;
@@ -878,7 +1019,16 @@ function stickerAt(stickers, x, y) {
878
1019
  const u = (dx * Math.cos(a) + dy * Math.sin(a)) / rad;
879
1020
  const v = (-dx * Math.sin(a) + dy * Math.cos(a)) / rad;
880
1021
  if (Math.abs(u) > 1.05 || Math.abs(v) > 1.05) continue;
881
- if (s.size < 16 || stampCoverage(s.stamp, u, v, s.seed) > 0.25) return s;
1022
+ if (s.size < 16) return s;
1023
+ const kind = stickerStamp(s);
1024
+ if (kind) {
1025
+ if (stampCoverage(kind, u, v, s.seed) > 0.25) return s;
1026
+ continue;
1027
+ }
1028
+ const pic = art?.(s.ref);
1029
+ if (!pic) return s;
1030
+ const scale = Math.max(pic.width, pic.height) / 2;
1031
+ if (sampleArt(pic, u * scale + pic.width / 2, v * scale + pic.height / 2, [0, 0, 0, 0])[3] > 0.25) return s;
882
1032
  }
883
1033
  return null;
884
1034
  }
@@ -2425,11 +2575,15 @@ export {
2425
2575
  DryStroke,
2426
2576
  mixerTip,
2427
2577
  toStrokePoint,
2428
- STAMP_KINDS,
2578
+ STICKER_ART_SIZE,
2579
+ defaultStickerRasterizer,
2580
+ StickerArtCache,
2581
+ sampleArt,
2582
+ MAX_STICKER_STAMPS,
2429
2583
  DEFAULT_STICKER,
2430
2584
  normalizeStickerSettings,
2431
- stampCoverage,
2432
2585
  stickerRect,
2586
+ stickerStamp,
2433
2587
  drawSticker,
2434
2588
  StickerSpray,
2435
2589
  renderStickers,