pixi-effects 0.42.0 → 0.44.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 (40) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +131 -2
  5. package/dist/Controller.d.cts +2 -2
  6. package/dist/Controller.d.ts +2 -2
  7. package/dist/{Movie-BHGhg-fJ.d.cts → Movie-B3VOAgwc.d.cts} +5 -3
  8. package/dist/{Movie-CBVFZ1kg.d.ts → Movie-CQCVUqRs.d.ts} +5 -3
  9. package/dist/{chunk-NFJKBWYO.js → chunk-4MBTYREC.js} +100 -31
  10. package/dist/chunk-4MBTYREC.js.map +1 -0
  11. package/dist/{chunk-WVZLAIKK.cjs → chunk-MAAM45ZX.cjs} +99 -30
  12. package/dist/chunk-MAAM45ZX.cjs.map +1 -0
  13. package/dist/index.cjs +25 -25
  14. package/dist/index.d.cts +3 -4
  15. package/dist/index.d.ts +3 -4
  16. package/dist/index.js +1 -1
  17. package/dist/loader.css +1 -1
  18. package/dist/present.cjs +1 -1
  19. package/dist/present.d.cts +2 -2
  20. package/dist/present.d.ts +2 -2
  21. package/dist/present.js +1 -1
  22. package/dist/three.cjs +166 -38
  23. package/dist/three.cjs.map +1 -1
  24. package/dist/three.d.cts +37 -2
  25. package/dist/three.d.ts +37 -2
  26. package/dist/three.js +168 -40
  27. package/dist/three.js.map +1 -1
  28. package/dist/{types-DIh93lFb.d.cts → types-C5xo0fzb.d.cts} +92 -1
  29. package/dist/{types-DIh93lFb.d.ts → types-C5xo0fzb.d.ts} +92 -1
  30. package/docs/api.md +4 -2
  31. package/docs/dsl.md +4 -2
  32. package/llms-full.txt +12 -8
  33. package/package.json +2 -1
  34. package/skills/pixi-effects/SKILL.md +2 -2
  35. package/skills/pixi-effects/reference/recipes.md +2 -2
  36. package/skills/pixi-effects/template.html +6 -6
  37. package/dist/Base-DQWIJ6bJ.d.cts +0 -60
  38. package/dist/Base-Jgkud1Aw.d.ts +0 -60
  39. package/dist/chunk-NFJKBWYO.js.map +0 -1
  40. package/dist/chunk-WVZLAIKK.cjs.map +0 -1
@@ -9,6 +9,21 @@ var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
9
9
  var pixi_js = require('pixi.js');
10
10
  var mediabunny = require('mediabunny');
11
11
 
12
+ // src/core/gpuShare.ts
13
+ function sharedGpuOf(renderer) {
14
+ const r = renderer;
15
+ if (!r || r.name !== "webgpu" || !r.gpu?.device) return void 0;
16
+ const uid = r.uid;
17
+ return {
18
+ device: r.gpu.device,
19
+ adopt(source, texture) {
20
+ const data = source._gpuData;
21
+ if (!data || typeof data !== "object") throw new Error("this PixiJS keeps no _gpuData on its texture sources");
22
+ data[uid] = { textureView: null, textureViews: /* @__PURE__ */ Object.create(null), gpuTexture: texture, destroy() {
23
+ } };
24
+ }
25
+ };
26
+ }
12
27
  var registered = false;
13
28
  function ensureLoadersRegistered(audioContext) {
14
29
  if (registered) return;
@@ -175,7 +190,7 @@ function inspectFonts(root) {
175
190
  });
176
191
  const missing = [];
177
192
  const used = /* @__PURE__ */ new Set();
178
- const walk2 = (seq, index) => {
193
+ const walk3 = (seq, index) => {
179
194
  const spec = seq.spec;
180
195
  if (spec.type === "text") {
181
196
  const raw = spec.style?.fontFamily;
@@ -184,10 +199,10 @@ function inspectFonts(root) {
184
199
  for (const f of list) used.add(f.replace(/^["']|["']$/g, "").toLowerCase());
185
200
  if (list.length && !list.some(fontAvailable) && !list.some((f) => failed.has(f.replace(/^["']|["']$/g, "")))) missing.push({ layer: spec.name ?? `text#${index}`, family: family2 });
186
201
  }
187
- if (seq.maskSequence) walk2(seq.maskSequence, index);
188
- (seq._children ?? []).forEach((child, i) => walk2(child, i));
202
+ if (seq.maskSequence) walk3(seq.maskSequence, index);
203
+ (seq._children ?? []).forEach((child, i) => walk3(child, i));
189
204
  };
190
- walk2(root, 0);
205
+ walk3(root, 0);
191
206
  const all = [...failed];
192
207
  return { missing, failed: all.filter((f) => used.has(f.toLowerCase())), failedUnused: all.filter((f) => !used.has(f.toLowerCase())) };
193
208
  }
@@ -1513,16 +1528,16 @@ function canvasReport(root) {
1513
1528
  const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its ${kind === "three" ? "update or render threw" : "draw threw"}, or the layer failed to build): ${kind === "three" ? "the warning above says why" : "it shows a magenta checkerboard there; the warning above says why"}, so the layer cannot be called deterministic` : differs.length ? `${name} does not draw the same picture for the same time (checked at ${differs.map(describeDiff).join(", ")}; drawn in two orders after one draw that was thrown away): ` + (kind === "three" ? "usually its update uses Math.random, Date.now, a clock, or state that builds up from call to call: a mixer advanced with mixer.update(dt) is one (pose a clip with mixer.setTime(t), or threeGltf(url).pose(t)), and so is a value moved by the time since the last call. A skinned model of your own (not loaded by threeGltf) that moves and casts a shadow does it too: three culls a skinned mesh by a bounding sphere that does not follow it, so set mesh.frustumCulled = false. Set every object from t, so playback, a seek and an export agree" : "usually its draw uses Math.random, Date.now or state that outlives the call (a value it keeps from the last call), so playback, a seek and an export would not agree: use info.random() (seeded) and derive everything from t. If it uses none of these, look at the canvases it draws from: make sprites and masks with info.canvas(w, h) (not document.createElement) and draw them into the picture before the strokes that depend on them") : "";
1514
1529
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1515
1530
  }
1516
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk2(seq);
1531
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq);
1517
1532
  };
1518
- const walk2 = (comp) => {
1533
+ const walk3 = (comp) => {
1519
1534
  for (const { seq } of comp.layers()) {
1520
1535
  visit(seq);
1521
1536
  const mask = seq.maskSequence;
1522
1537
  if (mask) visit(mask);
1523
1538
  }
1524
1539
  };
1525
- walk2(root);
1540
+ walk3(root);
1526
1541
  return out;
1527
1542
  }
1528
1543
  var canvasProblems = (root) => canvasReport(root).filter((r) => !r.ok).map((r) => r.problem);
@@ -1589,7 +1604,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1589
1604
  const specs = /* @__PURE__ */ new Map();
1590
1605
  const chains = /* @__PURE__ */ new Map();
1591
1606
  const chainAlpha = /* @__PURE__ */ new Map();
1592
- const walk2 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
1607
+ const walk3 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
1593
1608
  const blending = /* @__PURE__ */ new Set();
1594
1609
  const now = comp.childTime(time);
1595
1610
  const cameraMotion = (comp.spec.sequences ?? []).filter((q) => q.type === "camera").reduce((m, q) => either(m, motionOf(q.keyframes, now - comp.childBase - (q.at ?? 0), q.duration ?? comp.duration ?? Infinity, () => true)), NO_MOTION);
@@ -1636,6 +1651,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1636
1651
  if (typeof drawn.renderer === "string") {
1637
1652
  info.renderer = drawn.renderer;
1638
1653
  if (typeof drawn.backend === "string") info.backend = drawn.backend;
1654
+ if (info.renderer === "webgpu") info.sharedGpu = drawn.sharedGpu === true;
1639
1655
  }
1640
1656
  layers.push(info);
1641
1657
  chainAlpha.set(info, parentAlpha * alpha);
@@ -1654,10 +1670,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1654
1670
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1655
1671
  if (group) scene.set(info, group);
1656
1672
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1657
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1673
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1658
1674
  });
1659
1675
  };
1660
- walk2(root, "", false, true);
1676
+ walk3(root, "", false, true);
1661
1677
  const issues = [];
1662
1678
  const texts = layers.filter((l) => l.type === "text" && l.visible && l.alpha >= FAINT && l.bounds);
1663
1679
  for (const l of texts) {
@@ -1784,7 +1800,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1784
1800
  return true;
1785
1801
  };
1786
1802
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1787
- const walk2 = (comp, prefix, parentVisible) => {
1803
+ const walk3 = (comp, prefix, parentVisible) => {
1788
1804
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1789
1805
  const d = display;
1790
1806
  if (!d) return;
@@ -1797,7 +1813,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1797
1813
  if (!visible) return;
1798
1814
  const isComposition = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1799
1815
  if (isComposition && !threeD) {
1800
- walk2(seq, path + "/", visible);
1816
+ walk3(seq, path + "/", visible);
1801
1817
  return;
1802
1818
  }
1803
1819
  if (!wanted(name, path)) return;
@@ -1825,7 +1841,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1825
1841
  hit = { name, path, type: seq.spec.type, kind: "three", point, three };
1826
1842
  } else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) {
1827
1843
  let opaque = true;
1828
- const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture : void 0;
1844
+ const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture ?? deps.textureOf?.(d) : void 0;
1829
1845
  if (tex?.source?.resource) {
1830
1846
  const res = tex.source.resolution ?? 1;
1831
1847
  const a = readAlpha(tex.source.resource, (tex.frame.x + (local.x - lb.x) / lb.width * tex.frame.width) * res, (tex.frame.y + (local.y - lb.y) / lb.height * tex.frame.height) * res);
@@ -1847,9 +1863,56 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1847
1863
  found.push({ hit, order: orderOf(d) });
1848
1864
  });
1849
1865
  };
1850
- walk2(root, "", true);
1866
+ walk3(root, "", true);
1851
1867
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1852
1868
  }
1869
+ var cache = /* @__PURE__ */ new WeakMap();
1870
+ function textPictureOf(display) {
1871
+ if (!(display instanceof pixi_js.Text)) return null;
1872
+ const style = display.style;
1873
+ const key = `${display.text}|${display.resolution}|${style.styleKey ?? ""}`;
1874
+ const kept = cache.get(display);
1875
+ if (kept?.key === key) return kept.picture;
1876
+ try {
1877
+ const { canvasAndContext, frame } = pixi_js.CanvasTextGenerator.getCanvasAndContext({ text: display.text, style: display.style, resolution: display.resolution });
1878
+ const copy = document.createElement("canvas");
1879
+ copy.width = canvasAndContext.canvas.width;
1880
+ copy.height = canvasAndContext.canvas.height;
1881
+ copy.getContext("2d").drawImage(canvasAndContext.canvas, 0, 0);
1882
+ pixi_js.CanvasPool.returnCanvasAndContext(canvasAndContext);
1883
+ const picture = { frame: { x: frame.x, y: frame.y, width: frame.width, height: frame.height }, source: { resolution: display.resolution, resource: copy } };
1884
+ cache.set(display, { key, picture });
1885
+ return picture;
1886
+ } catch {
1887
+ return null;
1888
+ }
1889
+ }
1890
+
1891
+ // src/core/reuse.ts
1892
+ var lendable = (s) => typeof s.reuseKey === "function";
1893
+ var borrower = (s) => typeof s.commitReuse === "function";
1894
+ function walk(root, visit) {
1895
+ for (const { seq } of root.layers()) {
1896
+ visit(seq);
1897
+ const mask = seq.maskSequence;
1898
+ if (mask) visit(mask);
1899
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk(seq, visit);
1900
+ }
1901
+ }
1902
+ function reusePoolOf(old) {
1903
+ const free = /* @__PURE__ */ new Map();
1904
+ if (old) walk(old, (seq) => {
1905
+ if (!lendable(seq)) return;
1906
+ const key = seq.reuseKey();
1907
+ if (key !== null) free.set(key, [...free.get(key) ?? [], seq]);
1908
+ });
1909
+ return { claim: (key) => free.get(key)?.shift() ?? null };
1910
+ }
1911
+ function commitReuse(root) {
1912
+ walk(root, (seq) => {
1913
+ if (borrower(seq)) seq.commitReuse();
1914
+ });
1915
+ }
1853
1916
 
1854
1917
  // src/core/hover.ts
1855
1918
  var keyOf = (h) => h ? `${h.path}${h.three ? `#${h.three.object}` : ""}` : null;
@@ -1943,7 +2006,7 @@ function detailOf(seq) {
1943
2006
  if (s.music && typeof s.music === "object") return "music";
1944
2007
  return void 0;
1945
2008
  }
1946
- function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
2009
+ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
1947
2010
  const entries = [];
1948
2011
  comp._children.forEach((seq, i) => {
1949
2012
  const name = seq.spec.name ?? `${seq.spec.type}#${i}`;
@@ -1956,7 +2019,7 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
1956
2019
  const isComp = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1957
2020
  if (isComp) {
1958
2021
  const label = seq.remapLabel();
1959
- walk(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
2022
+ walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
1960
2023
  }
1961
2024
  entries.push({ row, inner, groupable: !isComp });
1962
2025
  });
@@ -2004,7 +2067,7 @@ function collectTimeline(root, duration) {
2004
2067
  const rows = [];
2005
2068
  const transitions = [];
2006
2069
  const label = root.remapLabel();
2007
- walk(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
2070
+ walk2(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
2008
2071
  return { duration, rows, transitions };
2009
2072
  }
2010
2073
  var COLORS = {
@@ -2152,28 +2215,28 @@ function substitute(s, vars) {
2152
2215
  }
2153
2216
  function applyVars(spec, vars) {
2154
2217
  if (Object.keys(vars).length === 0) return spec;
2155
- const walk2 = (v) => {
2218
+ const walk3 = (v) => {
2156
2219
  if (typeof v === "string") return substitute(v, vars);
2157
- if (Array.isArray(v)) return v.map(walk2);
2220
+ if (Array.isArray(v)) return v.map(walk3);
2158
2221
  if (isPlain(v)) {
2159
2222
  const o = {};
2160
- for (const [k, x] of Object.entries(v)) o[k] = walk2(x);
2223
+ for (const [k, x] of Object.entries(v)) o[k] = walk3(x);
2161
2224
  return o;
2162
2225
  }
2163
2226
  return v;
2164
2227
  };
2165
- return walk2(spec);
2228
+ return walk3(spec);
2166
2229
  }
2167
2230
  var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2168
2231
  function lintVars(spec, vars) {
2169
2232
  const declared = Object.keys(vars);
2170
2233
  const strings = [];
2171
- const walk2 = (v) => {
2234
+ const walk3 = (v) => {
2172
2235
  if (typeof v === "string") strings.push(v);
2173
- else if (Array.isArray(v)) v.forEach(walk2);
2174
- else if (isPlain(v)) Object.values(v).forEach(walk2);
2236
+ else if (Array.isArray(v)) v.forEach(walk3);
2237
+ else if (isPlain(v)) Object.values(v).forEach(walk3);
2175
2238
  };
2176
- walk2(spec);
2239
+ walk3(spec);
2177
2240
  const out = [];
2178
2241
  for (const name of declared) {
2179
2242
  const word = new RegExp(`(?<![A-Za-z0-9_.])${escapeRe(name)}(?![A-Za-z0-9_])`);
@@ -2727,14 +2790,18 @@ var Movie = class {
2727
2790
  * Build the composition tree for these variables and this size, off stage: `{name}` replaced, the root shape (with the numeric variables for the
2728
2791
  * expressions), transitions expanded, every layer built. Used by `init` and by `rebuild`.
2729
2792
  */
2730
- async _construct(composition, vars, width, height, lp, sizeWins = this._resized) {
2793
+ async _construct(composition, vars, width, height, lp, sizeWins = this._resized, reuse) {
2731
2794
  const spec = applyVars(composition, vars);
2795
+ const gpu = sharedGpuOf(this.app?.renderer);
2796
+ const gpuField = gpu ? { gpu } : {};
2732
2797
  const rootShape = {
2733
2798
  width,
2734
2799
  height,
2735
2800
  duration: this.duration,
2736
2801
  frameRate: this.frameRate,
2737
2802
  vars: numericVars(vars),
2803
+ ...gpuField,
2804
+ ...reuse ? { reuse } : {},
2738
2805
  ...lp ? { onLayerBuilt: async () => {
2739
2806
  lp.tick();
2740
2807
  await lp.yieldIfDue();
@@ -2891,7 +2958,7 @@ var Movie = class {
2891
2958
  if (change.composition !== void 0) for (const w of lintVars(change.composition, vars)) console.warn(w);
2892
2959
  changed = changed || sizeChanged || change.composition !== void 0 || reason === "rebuild";
2893
2960
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
2894
- sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged);
2961
+ sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
2895
2962
  alive();
2896
2963
  const timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2897
2964
  chunkX62H3RBG_cjs.lengthen(timeline, this.duration);
@@ -2931,6 +2998,7 @@ var Movie = class {
2931
2998
  if (old?.target) root.removeChild(old.target);
2932
2999
  if (sequence.target) root.addChild(sequence.target);
2933
3000
  this._rootSequence = sequence;
3001
+ commitReuse(sequence);
2934
3002
  this.timeline = timeline;
2935
3003
  this._timeFilters = collectTimeFilters(sequence);
2936
3004
  const unmute = muteBoundTextureWarning();
@@ -3205,7 +3273,8 @@ var Movie = class {
3205
3273
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.pick()", opts, ["only", "ignore", "alpha"]);
3206
3274
  this._requireReady("pick");
3207
3275
  if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`pixi-effects: movie.pick(x, y) needs two numbers (canvas pixels), got ${x}, ${y}. For a pointer event: movie.pick(...movie.canvasPoint(event))`);
3208
- return pickScene(this._rootSequence, x, y, opts);
3276
+ const textureOf = textPictureOf;
3277
+ return pickScene(this._rootSequence, x, y, opts, { textureOf });
3209
3278
  }
3210
3279
  /**
3211
3280
  * Which layer the pointer is over, as callbacks: `onEnter(hit)` when the layer (or, in a `three` layer, the object) under the pointer changes to another, `onLeave(hit)` for the one it
@@ -4749,5 +4818,5 @@ exports.splitText = splitText;
4749
4818
  exports.stagger = stagger;
4750
4819
  exports.wiggle = wiggle;
4751
4820
  exports.withFade = withFade;
4752
- //# sourceMappingURL=chunk-WVZLAIKK.cjs.map
4753
- //# sourceMappingURL=chunk-WVZLAIKK.cjs.map
4821
+ //# sourceMappingURL=chunk-MAAM45ZX.cjs.map
4822
+ //# sourceMappingURL=chunk-MAAM45ZX.cjs.map