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
@@ -4,9 +4,24 @@ import { analyzeAudio, fft, resampleThrough, playSpan } from './chunk-UHNSL2KJ.j
4
4
  import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-VNP7QCJE.js';
5
5
  import { describeLayer } from './chunk-32MMNDDN.js';
6
6
  import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
7
- import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
7
+ import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert, Text, CanvasTextGenerator, CanvasPool } from 'pixi.js';
8
8
  import { Output, BufferTarget, CanvasSource, AudioBufferSource, Input, ALL_FORMATS, BlobSource, WavOutputFormat, Conversion, VideoSampleSink, OggOutputFormat, QUALITY_VERY_HIGH, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_VERY_LOW, MkvOutputFormat, WebMOutputFormat, MovOutputFormat, Mp4OutputFormat, canEncodeVideo, getFirstEncodableAudioCodec } from 'mediabunny';
9
9
 
10
+ // src/core/gpuShare.ts
11
+ function sharedGpuOf(renderer) {
12
+ const r = renderer;
13
+ if (!r || r.name !== "webgpu" || !r.gpu?.device) return void 0;
14
+ const uid = r.uid;
15
+ return {
16
+ device: r.gpu.device,
17
+ adopt(source, texture) {
18
+ const data = source._gpuData;
19
+ if (!data || typeof data !== "object") throw new Error("this PixiJS keeps no _gpuData on its texture sources");
20
+ data[uid] = { textureView: null, textureViews: /* @__PURE__ */ Object.create(null), gpuTexture: texture, destroy() {
21
+ } };
22
+ }
23
+ };
24
+ }
10
25
  var registered = false;
11
26
  function ensureLoadersRegistered(audioContext) {
12
27
  if (registered) return;
@@ -173,7 +188,7 @@ function inspectFonts(root) {
173
188
  });
174
189
  const missing = [];
175
190
  const used = /* @__PURE__ */ new Set();
176
- const walk2 = (seq, index) => {
191
+ const walk3 = (seq, index) => {
177
192
  const spec = seq.spec;
178
193
  if (spec.type === "text") {
179
194
  const raw = spec.style?.fontFamily;
@@ -182,10 +197,10 @@ function inspectFonts(root) {
182
197
  for (const f of list) used.add(f.replace(/^["']|["']$/g, "").toLowerCase());
183
198
  if (list.length && !list.some(fontAvailable) && !list.some((f) => failed.has(f.replace(/^["']|["']$/g, "")))) missing.push({ layer: spec.name ?? `text#${index}`, family: family2 });
184
199
  }
185
- if (seq.maskSequence) walk2(seq.maskSequence, index);
186
- (seq._children ?? []).forEach((child, i) => walk2(child, i));
200
+ if (seq.maskSequence) walk3(seq.maskSequence, index);
201
+ (seq._children ?? []).forEach((child, i) => walk3(child, i));
187
202
  };
188
- walk2(root, 0);
203
+ walk3(root, 0);
189
204
  const all = [...failed];
190
205
  return { missing, failed: all.filter((f) => used.has(f.toLowerCase())), failedUnused: all.filter((f) => !used.has(f.toLowerCase())) };
191
206
  }
@@ -1511,16 +1526,16 @@ function canvasReport(root) {
1511
1526
  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") : "";
1512
1527
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1513
1528
  }
1514
- if (seq instanceof CompositionSequence) walk2(seq);
1529
+ if (seq instanceof CompositionSequence) walk3(seq);
1515
1530
  };
1516
- const walk2 = (comp) => {
1531
+ const walk3 = (comp) => {
1517
1532
  for (const { seq } of comp.layers()) {
1518
1533
  visit(seq);
1519
1534
  const mask = seq.maskSequence;
1520
1535
  if (mask) visit(mask);
1521
1536
  }
1522
1537
  };
1523
- walk2(root);
1538
+ walk3(root);
1524
1539
  return out;
1525
1540
  }
1526
1541
  var canvasProblems = (root) => canvasReport(root).filter((r) => !r.ok).map((r) => r.problem);
@@ -1587,7 +1602,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1587
1602
  const specs = /* @__PURE__ */ new Map();
1588
1603
  const chains = /* @__PURE__ */ new Map();
1589
1604
  const chainAlpha = /* @__PURE__ */ new Map();
1590
- const walk2 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
1605
+ const walk3 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
1591
1606
  const blending = /* @__PURE__ */ new Set();
1592
1607
  const now = comp.childTime(time);
1593
1608
  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);
@@ -1634,6 +1649,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1634
1649
  if (typeof drawn.renderer === "string") {
1635
1650
  info.renderer = drawn.renderer;
1636
1651
  if (typeof drawn.backend === "string") info.backend = drawn.backend;
1652
+ if (info.renderer === "webgpu") info.sharedGpu = drawn.sharedGpu === true;
1637
1653
  }
1638
1654
  layers.push(info);
1639
1655
  chainAlpha.set(info, parentAlpha * alpha);
@@ -1652,10 +1668,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1652
1668
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1653
1669
  if (group) scene.set(info, group);
1654
1670
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1655
- if (seq instanceof CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1671
+ if (seq instanceof CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1656
1672
  });
1657
1673
  };
1658
- walk2(root, "", false, true);
1674
+ walk3(root, "", false, true);
1659
1675
  const issues = [];
1660
1676
  const texts = layers.filter((l) => l.type === "text" && l.visible && l.alpha >= FAINT && l.bounds);
1661
1677
  for (const l of texts) {
@@ -1782,7 +1798,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1782
1798
  return true;
1783
1799
  };
1784
1800
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1785
- const walk2 = (comp, prefix, parentVisible) => {
1801
+ const walk3 = (comp, prefix, parentVisible) => {
1786
1802
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1787
1803
  const d = display;
1788
1804
  if (!d) return;
@@ -1795,7 +1811,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1795
1811
  if (!visible) return;
1796
1812
  const isComposition = seq instanceof CompositionSequence;
1797
1813
  if (isComposition && !threeD) {
1798
- walk2(seq, path + "/", visible);
1814
+ walk3(seq, path + "/", visible);
1799
1815
  return;
1800
1816
  }
1801
1817
  if (!wanted(name, path)) return;
@@ -1823,7 +1839,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1823
1839
  hit = { name, path, type: seq.spec.type, kind: "three", point, three };
1824
1840
  } else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) {
1825
1841
  let opaque = true;
1826
- const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture : void 0;
1842
+ const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture ?? deps.textureOf?.(d) : void 0;
1827
1843
  if (tex?.source?.resource) {
1828
1844
  const res = tex.source.resolution ?? 1;
1829
1845
  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);
@@ -1845,9 +1861,56 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1845
1861
  found.push({ hit, order: orderOf(d) });
1846
1862
  });
1847
1863
  };
1848
- walk2(root, "", true);
1864
+ walk3(root, "", true);
1849
1865
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1850
1866
  }
1867
+ var cache = /* @__PURE__ */ new WeakMap();
1868
+ function textPictureOf(display) {
1869
+ if (!(display instanceof Text)) return null;
1870
+ const style = display.style;
1871
+ const key = `${display.text}|${display.resolution}|${style.styleKey ?? ""}`;
1872
+ const kept = cache.get(display);
1873
+ if (kept?.key === key) return kept.picture;
1874
+ try {
1875
+ const { canvasAndContext, frame } = CanvasTextGenerator.getCanvasAndContext({ text: display.text, style: display.style, resolution: display.resolution });
1876
+ const copy = document.createElement("canvas");
1877
+ copy.width = canvasAndContext.canvas.width;
1878
+ copy.height = canvasAndContext.canvas.height;
1879
+ copy.getContext("2d").drawImage(canvasAndContext.canvas, 0, 0);
1880
+ CanvasPool.returnCanvasAndContext(canvasAndContext);
1881
+ const picture = { frame: { x: frame.x, y: frame.y, width: frame.width, height: frame.height }, source: { resolution: display.resolution, resource: copy } };
1882
+ cache.set(display, { key, picture });
1883
+ return picture;
1884
+ } catch {
1885
+ return null;
1886
+ }
1887
+ }
1888
+
1889
+ // src/core/reuse.ts
1890
+ var lendable = (s) => typeof s.reuseKey === "function";
1891
+ var borrower = (s) => typeof s.commitReuse === "function";
1892
+ function walk(root, visit) {
1893
+ for (const { seq } of root.layers()) {
1894
+ visit(seq);
1895
+ const mask = seq.maskSequence;
1896
+ if (mask) visit(mask);
1897
+ if (seq instanceof CompositionSequence) walk(seq, visit);
1898
+ }
1899
+ }
1900
+ function reusePoolOf(old) {
1901
+ const free = /* @__PURE__ */ new Map();
1902
+ if (old) walk(old, (seq) => {
1903
+ if (!lendable(seq)) return;
1904
+ const key = seq.reuseKey();
1905
+ if (key !== null) free.set(key, [...free.get(key) ?? [], seq]);
1906
+ });
1907
+ return { claim: (key) => free.get(key)?.shift() ?? null };
1908
+ }
1909
+ function commitReuse(root) {
1910
+ walk(root, (seq) => {
1911
+ if (borrower(seq)) seq.commitReuse();
1912
+ });
1913
+ }
1851
1914
 
1852
1915
  // src/core/hover.ts
1853
1916
  var keyOf = (h) => h ? `${h.path}${h.three ? `#${h.three.object}` : ""}` : null;
@@ -1941,7 +2004,7 @@ function detailOf(seq) {
1941
2004
  if (s.music && typeof s.music === "object") return "music";
1942
2005
  return void 0;
1943
2006
  }
1944
- function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
2007
+ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
1945
2008
  const entries = [];
1946
2009
  comp._children.forEach((seq, i) => {
1947
2010
  const name = seq.spec.name ?? `${seq.spec.type}#${i}`;
@@ -1954,7 +2017,7 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
1954
2017
  const isComp = seq instanceof CompositionSequence;
1955
2018
  if (isComp) {
1956
2019
  const label = seq.remapLabel();
1957
- walk(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
2020
+ walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
1958
2021
  }
1959
2022
  entries.push({ row, inner, groupable: !isComp });
1960
2023
  });
@@ -2002,7 +2065,7 @@ function collectTimeline(root, duration) {
2002
2065
  const rows = [];
2003
2066
  const transitions = [];
2004
2067
  const label = root.remapLabel();
2005
- walk(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
2068
+ walk2(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
2006
2069
  return { duration, rows, transitions };
2007
2070
  }
2008
2071
  var COLORS = {
@@ -2150,28 +2213,28 @@ function substitute(s, vars) {
2150
2213
  }
2151
2214
  function applyVars(spec, vars) {
2152
2215
  if (Object.keys(vars).length === 0) return spec;
2153
- const walk2 = (v) => {
2216
+ const walk3 = (v) => {
2154
2217
  if (typeof v === "string") return substitute(v, vars);
2155
- if (Array.isArray(v)) return v.map(walk2);
2218
+ if (Array.isArray(v)) return v.map(walk3);
2156
2219
  if (isPlain(v)) {
2157
2220
  const o = {};
2158
- for (const [k, x] of Object.entries(v)) o[k] = walk2(x);
2221
+ for (const [k, x] of Object.entries(v)) o[k] = walk3(x);
2159
2222
  return o;
2160
2223
  }
2161
2224
  return v;
2162
2225
  };
2163
- return walk2(spec);
2226
+ return walk3(spec);
2164
2227
  }
2165
2228
  var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2166
2229
  function lintVars(spec, vars) {
2167
2230
  const declared = Object.keys(vars);
2168
2231
  const strings = [];
2169
- const walk2 = (v) => {
2232
+ const walk3 = (v) => {
2170
2233
  if (typeof v === "string") strings.push(v);
2171
- else if (Array.isArray(v)) v.forEach(walk2);
2172
- else if (isPlain(v)) Object.values(v).forEach(walk2);
2234
+ else if (Array.isArray(v)) v.forEach(walk3);
2235
+ else if (isPlain(v)) Object.values(v).forEach(walk3);
2173
2236
  };
2174
- walk2(spec);
2237
+ walk3(spec);
2175
2238
  const out = [];
2176
2239
  for (const name of declared) {
2177
2240
  const word = new RegExp(`(?<![A-Za-z0-9_.])${escapeRe(name)}(?![A-Za-z0-9_])`);
@@ -2725,14 +2788,18 @@ var Movie = class {
2725
2788
  * Build the composition tree for these variables and this size, off stage: `{name}` replaced, the root shape (with the numeric variables for the
2726
2789
  * expressions), transitions expanded, every layer built. Used by `init` and by `rebuild`.
2727
2790
  */
2728
- async _construct(composition, vars, width, height, lp, sizeWins = this._resized) {
2791
+ async _construct(composition, vars, width, height, lp, sizeWins = this._resized, reuse) {
2729
2792
  const spec = applyVars(composition, vars);
2793
+ const gpu = sharedGpuOf(this.app?.renderer);
2794
+ const gpuField = gpu ? { gpu } : {};
2730
2795
  const rootShape = {
2731
2796
  width,
2732
2797
  height,
2733
2798
  duration: this.duration,
2734
2799
  frameRate: this.frameRate,
2735
2800
  vars: numericVars(vars),
2801
+ ...gpuField,
2802
+ ...reuse ? { reuse } : {},
2736
2803
  ...lp ? { onLayerBuilt: async () => {
2737
2804
  lp.tick();
2738
2805
  await lp.yieldIfDue();
@@ -2889,7 +2956,7 @@ var Movie = class {
2889
2956
  if (change.composition !== void 0) for (const w of lintVars(change.composition, vars)) console.warn(w);
2890
2957
  changed = changed || sizeChanged || change.composition !== void 0 || reason === "rebuild";
2891
2958
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
2892
- sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged);
2959
+ sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
2893
2960
  alive();
2894
2961
  const timeline = createTimeline({ paused: true, defaults: { ease: "none" } });
2895
2962
  lengthen(timeline, this.duration);
@@ -2929,6 +2996,7 @@ var Movie = class {
2929
2996
  if (old?.target) root.removeChild(old.target);
2930
2997
  if (sequence.target) root.addChild(sequence.target);
2931
2998
  this._rootSequence = sequence;
2999
+ commitReuse(sequence);
2932
3000
  this.timeline = timeline;
2933
3001
  this._timeFilters = collectTimeFilters(sequence);
2934
3002
  const unmute = muteBoundTextureWarning();
@@ -3203,7 +3271,8 @@ var Movie = class {
3203
3271
  warnUnknownOptions("movie.pick()", opts, ["only", "ignore", "alpha"]);
3204
3272
  this._requireReady("pick");
3205
3273
  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))`);
3206
- return pickScene(this._rootSequence, x, y, opts);
3274
+ const textureOf = textPictureOf;
3275
+ return pickScene(this._rootSequence, x, y, opts, { textureOf });
3207
3276
  }
3208
3277
  /**
3209
3278
  * 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
@@ -4724,5 +4793,5 @@ function withFade(spec, opts) {
4724
4793
  }
4725
4794
 
4726
4795
  export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, samplePath, splitText, stagger, wiggle, withFade };
4727
- //# sourceMappingURL=chunk-NFJKBWYO.js.map
4728
- //# sourceMappingURL=chunk-NFJKBWYO.js.map
4796
+ //# sourceMappingURL=chunk-4MBTYREC.js.map
4797
+ //# sourceMappingURL=chunk-4MBTYREC.js.map