pixi-effects 0.20.0 → 0.22.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 (83) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +6 -6
  3. package/ai/SKILL.md +3 -3
  4. package/ai/chat-template.html +3 -3
  5. package/ai/reference/cheatsheet.md +7 -7
  6. package/ai/reference/pitfalls.md +8 -1
  7. package/ai/reference/recipes.md +55 -0
  8. package/ai/template.html +5 -5
  9. package/ai/tools/check.mjs +15 -4
  10. package/dist/{Base-wYJQ2g5Q.d.cts → Base-BzlPgd-D.d.cts} +1 -1
  11. package/dist/{Base-CzfLGoDr.d.ts → Base-gu-5X0Cv.d.ts} +1 -1
  12. package/dist/Composition-BK5SH6J7.cjs +16 -0
  13. package/dist/{Composition-LL2XXU5N.cjs.map → Composition-BK5SH6J7.cjs.map} +1 -1
  14. package/dist/Composition-Q5TVG3HM.js +7 -0
  15. package/dist/{Composition-6PA4C4TV.js.map → Composition-Q5TVG3HM.js.map} +1 -1
  16. package/dist/Controller.cjs +2 -2
  17. package/dist/Controller.d.cts +2 -2
  18. package/dist/Controller.d.ts +2 -2
  19. package/dist/Controller.js +1 -1
  20. package/dist/{Movie-CBxGH5ms.d.ts → Movie--_TJvdJa.d.ts} +3 -1
  21. package/dist/{Movie-CzzJw8A-.d.cts → Movie-DMxOoTdW.d.cts} +3 -1
  22. package/dist/Presenter.cjs +2 -2
  23. package/dist/Presenter.d.cts +2 -2
  24. package/dist/Presenter.d.ts +2 -2
  25. package/dist/Presenter.js +1 -1
  26. package/dist/blendModes-5E4G2ZZQ.cjs +94 -0
  27. package/dist/blendModes-5E4G2ZZQ.cjs.map +1 -0
  28. package/dist/blendModes-HCP5XTNP.js +90 -0
  29. package/dist/blendModes-HCP5XTNP.js.map +1 -0
  30. package/dist/chunk-BPTW7L25.cjs +275 -0
  31. package/dist/chunk-BPTW7L25.cjs.map +1 -0
  32. package/dist/{chunk-DQHXVNOI.js → chunk-DC4JPY6A.js} +108 -273
  33. package/dist/chunk-DC4JPY6A.js.map +1 -0
  34. package/dist/chunk-E7RONUZ7.js +268 -0
  35. package/dist/chunk-E7RONUZ7.js.map +1 -0
  36. package/dist/{chunk-NO26HWR7.cjs → chunk-IFZOI3JQ.cjs} +131 -295
  37. package/dist/chunk-IFZOI3JQ.cjs.map +1 -0
  38. package/dist/chunk-K4JVI5HK.cjs +143 -0
  39. package/dist/chunk-K4JVI5HK.cjs.map +1 -0
  40. package/dist/{chunk-FFYA3UBU.cjs → chunk-MHOO2CVU.cjs} +290 -62
  41. package/dist/chunk-MHOO2CVU.cjs.map +1 -0
  42. package/dist/{chunk-MNM56B3X.js → chunk-NHICCDTC.js} +13 -3
  43. package/dist/chunk-NHICCDTC.js.map +1 -0
  44. package/dist/{chunk-TSPMKV2F.js → chunk-UQDEDYVM.js} +261 -33
  45. package/dist/chunk-UQDEDYVM.js.map +1 -0
  46. package/dist/{chunk-WTRKDIKH.cjs → chunk-UZTKEYFP.cjs} +13 -3
  47. package/dist/chunk-UZTKEYFP.cjs.map +1 -0
  48. package/dist/chunk-YEPDQ7I3.js +132 -0
  49. package/dist/chunk-YEPDQ7I3.js.map +1 -0
  50. package/dist/index.cjs +72 -68
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +4 -4
  53. package/dist/index.d.ts +4 -4
  54. package/dist/index.js +11 -7
  55. package/dist/index.js.map +1 -1
  56. package/dist/loader.css +1 -1
  57. package/dist/{music-CFEC7HKL.js → music-FUN7N225.js} +3 -3
  58. package/dist/{music-CFEC7HKL.js.map → music-FUN7N225.js.map} +1 -1
  59. package/dist/{music-UAN7DXK4.cjs → music-GEAH2XCT.cjs} +7 -7
  60. package/dist/{music-UAN7DXK4.cjs.map → music-GEAH2XCT.cjs.map} +1 -1
  61. package/dist/{sfx-EX4PUBMV.cjs → sfx-2DYTYNYW.cjs} +8 -8
  62. package/dist/{sfx-EX4PUBMV.cjs.map → sfx-2DYTYNYW.cjs.map} +1 -1
  63. package/dist/{sfx-U3GM2MED.js → sfx-3IDAJ3UZ.js} +3 -3
  64. package/dist/{sfx-U3GM2MED.js.map → sfx-3IDAJ3UZ.js.map} +1 -1
  65. package/dist/three.cjs +7 -6
  66. package/dist/three.cjs.map +1 -1
  67. package/dist/three.d.cts +2 -2
  68. package/dist/three.d.ts +2 -2
  69. package/dist/three.js +3 -2
  70. package/dist/three.js.map +1 -1
  71. package/dist/{types-YmUaViL1.d.cts → types-DBbZVKGS.d.cts} +7 -4
  72. package/dist/{types-YmUaViL1.d.ts → types-DBbZVKGS.d.ts} +7 -4
  73. package/docs/dsl.md +82 -3
  74. package/llms-full.txt +155 -14
  75. package/package.json +1 -1
  76. package/dist/Composition-6PA4C4TV.js +0 -5
  77. package/dist/Composition-LL2XXU5N.cjs +0 -14
  78. package/dist/chunk-DQHXVNOI.js.map +0 -1
  79. package/dist/chunk-FFYA3UBU.cjs.map +0 -1
  80. package/dist/chunk-MNM56B3X.js.map +0 -1
  81. package/dist/chunk-NO26HWR7.cjs.map +0 -1
  82. package/dist/chunk-TSPMKV2F.js.map +0 -1
  83. package/dist/chunk-WTRKDIKH.cjs.map +0 -1
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunkNO26HWR7_cjs = require('./chunk-NO26HWR7.cjs');
4
- var chunkWTRKDIKH_cjs = require('./chunk-WTRKDIKH.cjs');
3
+ var chunkIFZOI3JQ_cjs = require('./chunk-IFZOI3JQ.cjs');
4
+ var chunkK4JVI5HK_cjs = require('./chunk-K4JVI5HK.cjs');
5
+ var chunkBPTW7L25_cjs = require('./chunk-BPTW7L25.cjs');
6
+ var chunkUZTKEYFP_cjs = require('./chunk-UZTKEYFP.cjs');
5
7
  var pixi_js = require('pixi.js');
6
8
  var gsap = require('gsap');
7
9
 
@@ -47,6 +49,91 @@ function assignDepthOrder(items) {
47
49
  }
48
50
  return out;
49
51
  }
52
+ var DISC_TAPS = 48;
53
+ var GL_VERTEX = `#version 300 es
54
+ in vec2 aPosition;
55
+ out vec2 vTextureCoord;
56
+ uniform vec4 uInputSize;
57
+ uniform vec4 uOutputFrame;
58
+ uniform vec4 uOutputTexture;
59
+ void main(void) {
60
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
61
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
62
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
63
+ gl_Position = vec4(position, 0.0, 1.0);
64
+ vTextureCoord = aPosition * (uOutputFrame.zw * uInputSize.zw);
65
+ }`;
66
+ var GL_FRAGMENT = `#version 300 es
67
+ precision highp float;
68
+ in vec2 vTextureCoord;
69
+ uniform sampler2D uTexture;
70
+ uniform vec4 uInputSize;
71
+ uniform float uRadius;
72
+ out vec4 finalColor;
73
+ void main(void) {
74
+ vec4 acc = vec4(0.0);
75
+ for (int i = 0; i < ${DISC_TAPS}; i++) {
76
+ float r = sqrt((float(i) + 0.5) / ${DISC_TAPS}.0) * uRadius;
77
+ float a = float(i) * 2.39996323;
78
+ acc += texture(uTexture, vTextureCoord + vec2(cos(a), sin(a)) * r * uInputSize.zw);
79
+ }
80
+ finalColor = acc / ${DISC_TAPS}.0;
81
+ }`;
82
+ var WGSL = `
83
+ struct GlobalFilterUniforms {
84
+ uInputSize: vec4<f32>, uInputPixel: vec4<f32>, uInputClamp: vec4<f32>,
85
+ uOutputFrame: vec4<f32>, uGlobalFrame: vec4<f32>, uOutputTexture: vec4<f32>,
86
+ };
87
+ struct DiscUniforms { uRadius: f32 };
88
+ @group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
89
+ @group(0) @binding(1) var uTexture: texture_2d<f32>;
90
+ @group(0) @binding(2) var uSampler : sampler;
91
+ @group(1) @binding(0) var<uniform> disc : DiscUniforms;
92
+ struct VSOutput { @builtin(position) position: vec4<f32>, @location(0) uv : vec2<f32> };
93
+ fn filterVertexPosition(aPosition : vec2<f32>) -> vec4<f32> {
94
+ var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
95
+ position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
96
+ position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
97
+ return vec4<f32>(position, 0.0, 1.0);
98
+ }
99
+ fn filterTextureCoord(aPosition : vec2<f32>) -> vec2<f32> {
100
+ return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
101
+ }
102
+ @vertex
103
+ fn mainVertex(@location(0) aPosition : vec2<f32>) -> VSOutput {
104
+ return VSOutput(filterVertexPosition(aPosition), filterTextureCoord(aPosition));
105
+ }
106
+ @fragment
107
+ fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
108
+ var acc = vec4<f32>(0.0);
109
+ for (var i = 0; i < ${DISC_TAPS}; i++) {
110
+ let r = sqrt((f32(i) + 0.5) / ${DISC_TAPS}.0) * disc.uRadius;
111
+ let a = f32(i) * 2.39996323;
112
+ acc += textureSampleLevel(uTexture, uSampler, uv + vec2<f32>(cos(a), sin(a)) * r * gfu.uInputSize.zw, 0.0);
113
+ }
114
+ return acc / ${DISC_TAPS}.0;
115
+ }`;
116
+ var DiscBlurFilter = class extends pixi_js.Filter {
117
+ constructor(radius = 8) {
118
+ super({
119
+ glProgram: pixi_js.GlProgram.from({ vertex: GL_VERTEX, fragment: GL_FRAGMENT, name: "disc-blur" }),
120
+ gpuProgram: pixi_js.GpuProgram.from({ vertex: { source: WGSL, entryPoint: "mainVertex" }, fragment: { source: WGSL, entryPoint: "mainFragment" } }),
121
+ resources: { disc: new pixi_js.UniformGroup({ uRadius: { value: radius, type: "f32" } }) }
122
+ });
123
+ this.radius = radius;
124
+ }
125
+ /** Blur radius in pixels. The padding follows it, so the blurred edge is not cut off. */
126
+ get radius() {
127
+ return this.resources.disc.uniforms.uRadius;
128
+ }
129
+ set radius(v) {
130
+ const r = Math.max(0, v);
131
+ this.resources.disc.uniforms.uRadius = r;
132
+ this.padding = Math.ceil(r) + 2;
133
+ }
134
+ };
135
+
136
+ // src/space/Layer3D.ts
50
137
  var MAX_TEXTURE_SIZE = 4096;
51
138
  var SUPERSAMPLE = 2;
52
139
  var MAX_TEXTURE_PIXELS = 2048 * 2048;
@@ -65,8 +152,8 @@ function readLayerTransform(target) {
65
152
  x: c.x,
66
153
  y: c.y,
67
154
  z: c.z ?? 0,
68
- rotationX: (c.rotationX ?? 0) * chunkNO26HWR7_cjs.DEG,
69
- rotationY: (c.rotationY ?? 0) * chunkNO26HWR7_cjs.DEG,
155
+ rotationX: (c.rotationX ?? 0) * chunkIFZOI3JQ_cjs.DEG,
156
+ rotationY: (c.rotationY ?? 0) * chunkIFZOI3JQ_cjs.DEG,
70
157
  rotationZ: c.rotation,
71
158
  scaleX: c.scale.x,
72
159
  scaleY: c.scale.y,
@@ -100,6 +187,11 @@ var Layer3D = class {
100
187
  retired = [];
101
188
  size = null;
102
189
  warnedBehind = false;
190
+ /** Depth-of-field blur radius (px) from the last `setBlur`; 0 = no filter. */
191
+ blur = 0;
192
+ blurFilter = null;
193
+ blendWas = "normal";
194
+ blendFilter = null;
103
195
  update(host, basis) {
104
196
  this.flushRetired();
105
197
  const target = this.seq.target;
@@ -111,19 +203,19 @@ var Layer3D = class {
111
203
  if (!(needW >= 1 && needH >= 1) || !Number.isFinite(bounds.x) || !Number.isFinite(bounds.y)) return this.hide();
112
204
  const size = this.sizeFor(needW, needH);
113
205
  const frame = { x: bounds.x - pad, y: bounds.y - pad, width: size.w, height: size.h };
114
- const projected = chunkNO26HWR7_cjs.projectLayer(readLayerTransform(target), frame, basis);
206
+ const projected = chunkIFZOI3JQ_cjs.projectLayer(readLayerTransform(target), frame, basis);
115
207
  this.depth = projected.depth;
116
208
  if (!projected.visible) {
117
209
  if (!this.warnedBehind) {
118
210
  this.warnedBehind = true;
119
211
  if (this.seq.spec.hideBehindCamera) return this.hide();
120
- if (projected.depth > chunkNO26HWR7_cjs.NEAR) {
212
+ if (projected.depth > chunkIFZOI3JQ_cjs.NEAR) {
121
213
  console.warn(
122
- `pixi-effects: ${chunkNO26HWR7_cjs.describeLayer(this.seq.spec)}: part of it (a corner) reaches the camera plane (z = ${(target.z ?? 0).toFixed(0)}, rotationX ${(target.rotationX ?? 0).toFixed(0)}\xB0, rotationY ${(target.rotationY ?? 0).toFixed(0)}\xB0), and a layer is hidden whole when any corner is at or behind it. Tilt it less (rotationX / rotationY), make it smaller, or keep the camera further away from it (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
214
+ `pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(this.seq.spec)}: part of it (a corner) reaches the camera plane (z = ${(target.z ?? 0).toFixed(0)}, rotationX ${(target.rotationX ?? 0).toFixed(0)}\xB0, rotationY ${(target.rotationY ?? 0).toFixed(0)}\xB0), and a layer is hidden whole when any corner is at or behind it. Tilt it less (rotationX / rotationY), make it smaller, or keep the camera further away from it (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
123
215
  );
124
216
  } else {
125
217
  console.warn(
126
- `pixi-effects: ${chunkNO26HWR7_cjs.describeLayer(this.seq.spec)} is at or behind the camera plane (z = ${(target.z ?? 0).toFixed(0)}) and is hidden. If that is unintended, lower its z or move the camera back (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
218
+ `pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(this.seq.spec)} is at or behind the camera plane (z = ${(target.z ?? 0).toFixed(0)}) and is hidden. If that is unintended, lower its z or move the camera back (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
127
219
  );
128
220
  }
129
221
  }
@@ -140,7 +232,46 @@ var Layer3D = class {
140
232
  this.display.geometry.setCorners(...projected.corners);
141
233
  this.display.visible = true;
142
234
  }
235
+ /**
236
+ * Depth-of-field blur on the projected mesh (screen space), so a tilted or scaled layer blurs by the size it is drawn at.
237
+ * A radius under MIN_BLUR removes the filter: a sharp layer costs nothing. A blend mode moves to the filter while it is on
238
+ * (a filtered object blends as a whole), and comes back when it goes.
239
+ */
240
+ setBlur(radius) {
241
+ const d = this.display;
242
+ const r = radius > chunkIFZOI3JQ_cjs.MIN_BLUR ? Math.min(radius, chunkIFZOI3JQ_cjs.MAX_BLUR) : 0;
243
+ this.blur = r;
244
+ if (r === 0) {
245
+ if (this.blurFilter) {
246
+ d.filters = null;
247
+ d.blendMode = this.blendWas;
248
+ this.blurFilter.destroy();
249
+ this.blurFilter = null;
250
+ this.blendFilter?.destroy();
251
+ this.blendFilter = null;
252
+ }
253
+ return;
254
+ }
255
+ if (!this.blurFilter) {
256
+ this.blurFilter = new DiscBlurFilter(r);
257
+ this.blendWas = d.blendMode ?? "normal";
258
+ const chain = [this.blurFilter];
259
+ if (chunkK4JVI5HK_cjs.isAdvancedBlend(this.blendWas)) {
260
+ this.blendFilter = chunkK4JVI5HK_cjs.blendFilterFor(this.blendWas);
261
+ if (this.blendFilter) {
262
+ chain.push(this.blendFilter);
263
+ d.blendMode = "normal";
264
+ }
265
+ } else if (this.blendWas !== "normal" && this.blendWas !== "inherit") {
266
+ this.blurFilter.blendMode = this.blendWas;
267
+ d.blendMode = "normal";
268
+ }
269
+ d.filters = chain;
270
+ }
271
+ this.blurFilter.radius = r;
272
+ }
143
273
  destroy() {
274
+ this.setBlur(0);
144
275
  this.display.destroy();
145
276
  this.rt?.destroy(true);
146
277
  this.rt = null;
@@ -192,6 +323,7 @@ var Layer3D = class {
192
323
  // src/space/lint.ts
193
324
  var defaultWarn = (m) => console.warn(m);
194
325
  var FOV_HINT = `perspective is the camera's "fov" \u2014 add a { type: "camera" } layer and set fov in its initial / keyframes`;
326
+ var DOF_HINT = `depth of field is the camera's "focus" (the plane that is sharp: a layer name or a z) and "aperture" (how shallow: 0 = off, 30 = default) \u2014 put them in the camera's initial / keyframes`;
195
327
  var LAYER_ALIASES = {
196
328
  rotateX: 'did you mean "rotationX"?',
197
329
  rotateY: 'did you mean "rotationY"?',
@@ -205,14 +337,21 @@ var LAYER_ALIASES = {
205
337
  };
206
338
  var CAMERA_ALIASES = {
207
339
  pointOfInterest: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
208
- lookAt: 'did you mean "lookAtX / lookAtY / lookAtZ"?'
340
+ lookAt: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
341
+ depthOfField: DOF_HINT,
342
+ dof: DOF_HINT,
343
+ focalDistance: DOF_HINT,
344
+ focusDistance: DOF_HINT,
345
+ fStop: DOF_HINT,
346
+ blurAmount: DOF_HINT
209
347
  };
210
- var CAMERA_PROPS = ["x", "y", "z", "fov", "lookAtX", "lookAtY", "lookAtZ", "offsetX", "offsetY", "offsetZ", "lookOffsetX", "lookOffsetY", "lookOffsetZ"];
348
+ var CAMERA_PROPS = ["x", "y", "z", "fov", "focus", "aperture", "lookAtX", "lookAtY", "lookAtZ", "offsetX", "offsetY", "offsetZ", "lookOffsetX", "lookOffsetY", "lookOffsetZ"];
211
349
  var NEEDS_THREE_D = ["z", "rotationX", "rotationY"];
350
+ var CAMERA_ONLY = ["focus", "aperture"];
212
351
  var SKEW_KEYS = ["skew", "skewX", "skewY"];
213
352
  function lintSequence(spec, warn = defaultWarn) {
214
- const who = chunkNO26HWR7_cjs.describeLayer(spec);
215
- const keys = chunkNO26HWR7_cjs.collectPropKeys(spec);
353
+ const who = chunkBPTW7L25_cjs.describeLayer(spec);
354
+ const keys = chunkBPTW7L25_cjs.collectPropKeys(spec);
216
355
  const explained = /* @__PURE__ */ new Set();
217
356
  for (const k of keys) {
218
357
  const hint = LAYER_ALIASES[k] ?? (spec.type === "camera" ? CAMERA_ALIASES[k] : void 0);
@@ -237,6 +376,9 @@ function lintSequence(spec, warn = defaultWarn) {
237
376
  }
238
377
  return;
239
378
  }
379
+ for (const k of CAMERA_ONLY) {
380
+ if (keys.has(k)) warn(`pixi-effects: ${who}: "${k}" is a camera property and does nothing on this layer: write it in the initial / keyframes of a { type: 'camera' } layer`);
381
+ }
240
382
  if (spec.type === "audio") {
241
383
  if (spec.threeD) warn(`pixi-effects: ${who}: threeD has no effect on audio`);
242
384
  return;
@@ -251,31 +393,32 @@ function lintSequence(spec, warn = defaultWarn) {
251
393
  }
252
394
  }
253
395
  }
254
-
255
- // src/core/blend.ts
256
- var MODES = ["normal", "add", "screen", "multiply"];
257
- function applyBlendMode(spec, display) {
258
- const mode = spec.blendMode;
259
- if (mode === void 0) return;
260
- if (!MODES.includes(mode)) {
261
- console.warn(`pixi-effects: ${chunkNO26HWR7_cjs.describeLayer(spec)}: blendMode "${mode}" is not supported (use ${MODES.join(", ")})`);
262
- return;
263
- }
264
- const filters = display.filters;
265
- if (Array.isArray(filters) && filters.length > 0) {
266
- filters[filters.length - 1].blendMode = mode;
267
- return;
396
+ function lintFocus(camera, siblings, warn = defaultWarn) {
397
+ if (camera.type !== "camera") return;
398
+ const who = chunkBPTW7L25_cjs.describeLayer(camera);
399
+ for (const p of chunkIFZOI3JQ_cjs.focusProblems(camera, siblings)) {
400
+ if (p.kind === "missing") {
401
+ warn(`pixi-effects: ${who}: focus: no layer named "${p.name}"${p.hint ? `; did you mean "${p.hint}"?` : ""} (the z = 0 plane is used). Write a threeD layer's name or a z number`);
402
+ } else if (p.kind === "not-threeD") {
403
+ warn(`pixi-effects: ${who}: focus "${p.name}" is not a threeD layer, so it has no depth (the z = 0 plane is used). Write a threeD layer's name or a z number`);
404
+ } else if (p.kind === "duplicate") {
405
+ warn(`pixi-effects: ${who}: focus "${p.name}": ${p.count} layers are named "${p.name}"; the first threeD one is used. Give the layers different names`);
406
+ } else {
407
+ warn(`pixi-effects: ${who}: focus "${p.name}" reads only the first z of that layer; its z moves later and the focus does not follow. Animate focus itself with numbers to follow it`);
408
+ }
268
409
  }
269
- display.blendMode = mode;
270
410
  }
271
411
 
272
412
  // src/sequences/Composition.ts
273
- var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
413
+ var CompositionSequence = class extends chunkIFZOI3JQ_cjs.Sequence {
274
414
  _innerContainer = null;
275
415
  _compositionShape = null;
276
416
  _children = [];
277
417
  _cameras = [];
278
418
  _layers3d = [];
419
+ _dofSaid = { behind: false, many: false };
420
+ /** What this composition made for blend modes (filters added to layers' chains, groups and their carrier filters): destroyed with it. */
421
+ _blendOwned = [];
279
422
  /** Drawn children in stack order (cameras and target-less sequences excluded). */
280
423
  _visual = [];
281
424
  /** Layers drawn inside a null layer (`parent`): they are not in the composition's own stack. */
@@ -328,27 +471,46 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
328
471
  this.intrinsicWidth = width;
329
472
  this.intrinsicHeight = height;
330
473
  this._innerContainer = inner;
331
- const base = chunkNO26HWR7_cjs.timeRemapOf(this.spec);
332
- const span = base ? chunkNO26HWR7_cjs.contentLength(base, this.duration) : this.duration;
333
- this._remap = base ? chunkNO26HWR7_cjs.remapOf(this.spec, "time", span) : null;
474
+ const base = chunkIFZOI3JQ_cjs.timeRemapOf(this.spec);
475
+ const span = base ? chunkIFZOI3JQ_cjs.contentLength(base, this.duration) : this.duration;
476
+ this._remap = base ? chunkIFZOI3JQ_cjs.remapOf(this.spec, "time", span) : null;
334
477
  this._contentSpan = span;
335
478
  this._compositionShape = { width, height, duration: span };
336
479
  const lateKeyframes = [];
337
480
  for (const s of this.spec.sequences ?? []) {
338
481
  lintSequence(s);
339
- chunkNO26HWR7_cjs.lintTiming(s, span, (message, kind) => kind === "late-keyframe" ? lateKeyframes.push(message) : console.warn(message));
340
- chunkNO26HWR7_cjs.lintText(s);
341
- chunkNO26HWR7_cjs.lintKeys(s);
482
+ lintFocus(s, this.spec.sequences ?? []);
483
+ chunkBPTW7L25_cjs.lintTiming(s, span, (message, kind) => kind === "late-keyframe" ? lateKeyframes.push(message) : console.warn(message));
484
+ chunkBPTW7L25_cjs.lintText(s);
485
+ chunkBPTW7L25_cjs.lintKeys(s);
342
486
  }
343
- for (const message of chunkNO26HWR7_cjs.summarizeWarnings(lateKeyframes)) console.warn(message);
344
- this._children = await chunkNO26HWR7_cjs.buildSequenceTree(
487
+ for (const message of chunkBPTW7L25_cjs.summarizeWarnings(lateKeyframes)) console.warn(message);
488
+ const crowd = chunkK4JVI5HK_cjs.maxConcurrentAdvanced(this.spec.sequences ?? [], span);
489
+ if (crowd >= chunkK4JVI5HK_cjs.MANY_ADVANCED) {
490
+ console.warn(`pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(this.spec)}: ${crowd} layers with an advanced blendMode are on screen at once (a composition with one counts each layer inside it, since its children blend one by one); each is a full-frame pass (about 2 ms on WebGL). Put fewer layers in the blend (or draw the glow as one layer), or use add / screen / multiply`);
491
+ }
492
+ this._children = await chunkIFZOI3JQ_cjs.buildSequenceTree(
345
493
  this.spec.sequences ?? [],
346
494
  this._compositionShape,
347
495
  this.root
348
496
  );
349
497
  this._resolveParents();
498
+ const layerNames = new Set(this._children.map((c) => c.spec.name).filter((n) => !!n));
499
+ for (const cam of this._children) {
500
+ if (!(cam instanceof chunkIFZOI3JQ_cjs.CameraSequence)) continue;
501
+ cam.resolveFocusNames((name) => {
502
+ const hit = this._children.find((c) => c.spec.name === name && c.spec.threeD);
503
+ return hit ? chunkIFZOI3JQ_cjs.layerInitialZ(hit.spec, hit.scope()) : void 0;
504
+ }, layerNames);
505
+ }
506
+ const blendSaid = /* @__PURE__ */ new Map();
507
+ const report = (message, spec) => {
508
+ const seen = blendSaid.get(message);
509
+ if (seen) seen.n++;
510
+ else blendSaid.set(message, { spec, n: 1 });
511
+ };
350
512
  for (const child of this._children) {
351
- if (child instanceof chunkNO26HWR7_cjs.CameraSequence) {
513
+ if (child instanceof chunkIFZOI3JQ_cjs.CameraSequence) {
352
514
  this._cameras.push(child);
353
515
  continue;
354
516
  }
@@ -361,25 +523,57 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
361
523
  inner.addChild(layer.display);
362
524
  this._layers3d.push(layer);
363
525
  this._visual.push({ seq: child, layer });
364
- applyBlendMode(child.spec, layer.display);
526
+ const added3d = chunkK4JVI5HK_cjs.applyBlendMode(child.spec, layer.display, report);
527
+ if (added3d) this._blendOwned.push(added3d);
365
528
  if (maskSpec) {
366
- console.warn(`pixi-effects: ${chunkNO26HWR7_cjs.describeLayer(child.spec)}: mask is not supported on threeD layers yet; ignored`);
529
+ console.warn(`pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(child.spec)}: mask is not supported on threeD layers yet; ignored`);
367
530
  }
368
531
  continue;
369
532
  }
370
- into.addChild(child.target);
533
+ const mode = child.spec.blendMode;
534
+ const eraseMask = !!maskSpec && child.spec.maskInverted === true && mode !== void 0 && mode !== "normal" && chunkK4JVI5HK_cjs.blendProblem(mode) === null;
535
+ const alphaMaskLosesBlend = !!maskSpec && (maskSpec.type === "text" || maskSpec.type === "image" || maskSpec.type === "video") && !eraseMask && mode !== void 0 && mode !== "normal" && chunkK4JVI5HK_cjs.blendProblem(mode) === null;
536
+ if (alphaMaskLosesBlend) {
537
+ report(`blendMode "${mode}" cannot be used together with ${maskSpec.type === "image" ? "an" : "a"} ${maskSpec.type} layer as the mask (the blend is lost); the layer is drawn normal. Put the ${maskSpec.type} on a layer of its own with the blendMode instead of masking with it, or use maskInverted`, child.spec);
538
+ }
539
+ let wrap;
540
+ if (eraseMask) {
541
+ wrap = new pixi_js.Container();
542
+ wrap.addChild(child.target);
543
+ let carrier = null;
544
+ if (chunkK4JVI5HK_cjs.isAdvancedBlend(mode)) carrier = chunkK4JVI5HK_cjs.blendFilterFor(mode);
545
+ else {
546
+ const f = new pixi_js.AlphaFilter({ alpha: 1 });
547
+ f.blendMode = mode;
548
+ carrier = f;
549
+ }
550
+ if (carrier) {
551
+ wrap.filters = [carrier];
552
+ this._blendOwned.push(carrier);
553
+ } else report(chunkK4JVI5HK_cjs.blendUnavailableMessage(mode), child.spec);
554
+ this._blendOwned.push(wrap);
555
+ into.addChild(wrap);
556
+ } else into.addChild(child.target);
371
557
  if (holder) this._nested.push(child);
372
- else this._visual.push({ seq: child, layer: null });
373
- applyBlendMode(child.spec, child.target);
558
+ else this._visual.push({ seq: child, layer: null, wrap });
559
+ if (!wrap && !alphaMaskLosesBlend) {
560
+ const added = chunkK4JVI5HK_cjs.applyBlendMode(child.spec, child.target, report);
561
+ if (added) this._blendOwned.push(added);
562
+ }
374
563
  if (maskSpec) {
375
564
  const lifetime = maskSpec.at === void 0 ? { at: child.at, duration: maskSpec.duration ?? child.duration } : {};
376
- const built = await chunkNO26HWR7_cjs.buildSequenceTree([{ ...maskSpec, ...lifetime }], this._compositionShape, this.root);
565
+ const built = await chunkIFZOI3JQ_cjs.buildSequenceTree([{ ...maskSpec, ...lifetime }], this._compositionShape, this.root);
377
566
  const maskSeq = built[0];
378
567
  if (maskSeq?.target) {
379
- into.addChild(maskSeq.target);
568
+ (wrap ?? into).addChild(maskSeq.target);
380
569
  const inverse = child.spec.maskInverted ?? false;
381
570
  const t = child.target;
382
- if (inverse || maskSpec.type === "text") {
571
+ if (wrap) {
572
+ const mt = maskSeq.target;
573
+ const mf = Array.isArray(mt.filters) && mt.filters.length > 0 ? mt.filters[mt.filters.length - 1] : null;
574
+ if (mf) mf.blendMode = "erase";
575
+ else mt.blendMode = "erase";
576
+ } else if (inverse || maskSpec.type === "text") {
383
577
  const effect = new pixi_js.AlphaMask({ mask: maskSeq.target });
384
578
  effect.inverse = inverse;
385
579
  t.setMask?.({ mask: void 0, inverse });
@@ -390,14 +584,17 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
390
584
  t.mask = maskSeq.target;
391
585
  }
392
586
  child.maskSequence = maskSeq;
393
- maskSeq.keepHidden = maskSpec.type === "image" || maskSpec.type === "video";
587
+ maskSeq.keepHidden = !wrap && (maskSpec.type === "image" || maskSpec.type === "video");
394
588
  }
395
589
  }
396
590
  }
591
+ for (const [message, seen] of blendSaid) {
592
+ console.warn(`pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(seen.spec)}: ${message}${seen.n > 1 ? ` (and ${seen.n - 1} more layers have the same mistake)` : ""}`);
593
+ }
397
594
  if (this._layers3d.length > 0) {
398
595
  inner.sortableChildren = true;
399
596
  this._visual.forEach((v, i) => {
400
- (v.layer?.display ?? v.seq.target).zIndex = i;
597
+ (v.layer?.display ?? v.wrap ?? v.seq.target).zIndex = i;
401
598
  });
402
599
  } else if (this._cameras.length > 0) {
403
600
  console.warn("pixi-effects: camera has no effect: this composition has no threeD layers");
@@ -445,11 +642,11 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
445
642
  this._clock = clock;
446
643
  const scope = this.scope();
447
644
  if (remap.start !== void 0 && remap.timeKfs.length === 0) clock.time = remap.start;
448
- chunkNO26HWR7_cjs.bindClock(timeline, clock, "time", remap, this.duration, offset + this.at, scope);
645
+ chunkIFZOI3JQ_cjs.bindClock(timeline, clock, "time", remap, this.duration, offset + this.at, scope);
449
646
  inner.time(inner.duration());
450
647
  inner.time(0);
451
648
  last = NaN;
452
- this._warnUnreachable(chunkNO26HWR7_cjs.clockTable(remap, "time", this.duration, offset + this.at, scope, offset + this.at + this.duration), offset + this.at, offset + this.at + this.duration);
649
+ this._warnUnreachable(chunkIFZOI3JQ_cjs.clockTable(remap, "time", this.duration, offset + this.at, scope, offset + this.at + this.duration), offset + this.at, offset + this.at + this.duration);
453
650
  }
454
651
  /**
455
652
  * A child the clock never dwells in is never visible: say so. Its `at` is in THIS composition's local time, so the usual mistake is
@@ -473,7 +670,7 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
473
670
  }
474
671
  if (dwell >= 2) continue;
475
672
  console.warn(
476
- `pixi-effects: ${chunkNO26HWR7_cjs.describeLayer(child.spec)} starts at ${at}s of ${chunkNO26HWR7_cjs.describeLayer(this.spec)}'s own time, but its clock only reaches ${Number(table.min.toFixed(3))}\u2013${Number(table.max.toFixed(3))}s (${how}), so it is never visible. Layers inside a remapped composition are placed in that composition's local time.`
673
+ `pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(child.spec)} starts at ${at}s of ${chunkBPTW7L25_cjs.describeLayer(this.spec)}'s own time, but its clock only reaches ${Number(table.min.toFixed(3))}\u2013${Number(table.max.toFixed(3))}s (${how}), so it is never visible. Layers inside a remapped composition are placed in that composition's local time.`
477
674
  );
478
675
  }
479
676
  }
@@ -487,26 +684,27 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
487
684
  for (let p = this._parentOf.get(seq); p; p = this._parentOf.get(p)) out.push(p);
488
685
  return out;
489
686
  };
490
- return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof chunkNO26HWR7_cjs.NullSequence)).map((seq) => {
687
+ return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof chunkIFZOI3JQ_cjs.NullSequence)).map((seq) => {
491
688
  const layer = this._visual.find((v) => v.seq === seq)?.layer ?? null;
492
689
  return {
493
690
  seq,
494
691
  display: layer?.display ?? seq.target,
495
692
  threeD: layer !== null,
496
- carriers: carriersOf(seq)
693
+ carriers: carriersOf(seq),
694
+ depthBlur: layer?.blur ?? 0
497
695
  };
498
696
  });
499
697
  }
500
698
  /** Match every `parent: 'name'` to a null layer; anything that cannot work is said out loud and drawn unparented. */
501
699
  _resolveParents() {
502
- const nulls = this._children.filter((c) => c instanceof chunkNO26HWR7_cjs.NullSequence);
700
+ const nulls = this._children.filter((c) => c instanceof chunkIFZOI3JQ_cjs.NullSequence);
503
701
  const nullNames = nulls.map((n) => n.spec.name).filter((n) => !!n);
504
702
  const warned = /* @__PURE__ */ new Set();
505
703
  for (const child of this._children) {
506
704
  const want = child.spec.parent;
507
705
  if (want === void 0) continue;
508
- const who = chunkNO26HWR7_cjs.describeLayer(child.spec);
509
- if (child instanceof chunkNO26HWR7_cjs.CameraSequence) {
706
+ const who = chunkBPTW7L25_cjs.describeLayer(child.spec);
707
+ if (child instanceof chunkIFZOI3JQ_cjs.CameraSequence) {
510
708
  console.warn(`pixi-effects: ${who}: a camera cannot have a parent; ignored`);
511
709
  continue;
512
710
  }
@@ -516,11 +714,11 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
516
714
  }
517
715
  const same = this._children.filter((c) => c.spec.name === want);
518
716
  if (same.length === 0) {
519
- const hint = chunkWTRKDIKH_cjs.suggestName(want, nullNames);
717
+ const hint = chunkUZTKEYFP_cjs.suggestName(want, nullNames);
520
718
  console.warn(`pixi-effects: ${who}: parent "${want}": no layer with that name${hint ? `; did you mean "${hint}"?` : ""} (the layer is drawn without a parent)`);
521
719
  continue;
522
720
  }
523
- const holder = same.find((c) => c instanceof chunkNO26HWR7_cjs.NullSequence);
721
+ const holder = same.find((c) => c instanceof chunkIFZOI3JQ_cjs.NullSequence);
524
722
  if (!holder) {
525
723
  console.warn(`pixi-effects: ${who}: parent "${want}" is a ${same[0].spec.type} layer; only { type: 'null' } layers can be parents (the layer is drawn without a parent)`);
526
724
  continue;
@@ -560,12 +758,14 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
560
758
  const { width, height } = this._compositionShape;
561
759
  const compEnd = this._childBase + (this.duration ?? 0);
562
760
  const active = pickActiveCamera(
563
- this._cameras.map((cam) => ({ cam, ...cam.window() })),
761
+ this._cameras.map((cam2) => ({ cam: cam2, ...cam2.window() })),
564
762
  childT,
565
763
  compEnd
566
764
  );
567
- const basis = chunkNO26HWR7_cjs.cameraBasis(active ? active.cam.state() : chunkNO26HWR7_cjs.homeCamera(width, height), width, height);
765
+ const cam = active ? active.cam.state() : chunkIFZOI3JQ_cjs.homeCamera(width, height);
766
+ const basis = chunkIFZOI3JQ_cjs.cameraBasis(cam, width, height);
568
767
  for (const layer of this._layers3d) layer.update(host, basis);
768
+ this._applyDepthOfField(cam, basis);
569
769
  const order = assignDepthOrder(
570
770
  this._visual.map((v, i) => ({ stackIndex: i, threeD: v.layer !== null, depth: v.layer?.depth ?? 0 }))
571
771
  );
@@ -573,6 +773,32 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
573
773
  if (v.layer) v.layer.display.zIndex = order[i];
574
774
  });
575
775
  }
776
+ /** Depth of field: each threeD layer is blurred by how far its depth is from the focal plane (a pure function of the camera and the layers). */
777
+ _applyDepthOfField(cam, basis) {
778
+ const aperture = cam.aperture ?? 0;
779
+ if (!(aperture > 0)) {
780
+ for (const l of this._layers3d) l.setBlur(0);
781
+ return;
782
+ }
783
+ const focusDepth = chunkIFZOI3JQ_cjs.projectPoint(basis, { x: cam.lookAtX, y: cam.lookAtY, z: cam.focus ?? 0 }).depth;
784
+ if (!(focusDepth > chunkIFZOI3JQ_cjs.NEAR)) {
785
+ if (!this._dofSaid.behind) {
786
+ this._dofSaid.behind = true;
787
+ console.warn(`pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(this.spec)}: the camera's focus (z = ${(cam.focus ?? 0).toFixed(0)}) is at or behind the camera, so nothing is blurred. Put focus in front of the camera (a z below the camera's z)`);
788
+ }
789
+ for (const l of this._layers3d) l.setBlur(0);
790
+ return;
791
+ }
792
+ let blurred = 0;
793
+ for (const l of this._layers3d) {
794
+ l.setBlur(l.display.visible ? chunkIFZOI3JQ_cjs.blurRadius(aperture, basis.focal, l.depth, focusDepth) : 0);
795
+ if (l.blur > 0) blurred++;
796
+ }
797
+ if (blurred >= chunkIFZOI3JQ_cjs.MANY_BLURRED && !this._dofSaid.many) {
798
+ this._dofSaid.many = true;
799
+ console.warn(`pixi-effects: ${chunkBPTW7L25_cjs.describeLayer(this.spec)}: ${blurred} layers are blurred by depth of field at once; each costs a filter pass per frame (WebGPU more). Merge the far layers or lower aperture`);
800
+ }
801
+ }
576
802
  collectAudio(out, baseTime) {
577
803
  super.collectAudio(out, baseTime);
578
804
  const remap = this._remap;
@@ -584,7 +810,7 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
584
810
  const inner = [];
585
811
  for (const child of this._children) child.collectAudio(inner, 0);
586
812
  const lo = baseTime + this.at, hi = lo + this.duration;
587
- const table = chunkNO26HWR7_cjs.clockTable(remap, "time", this.duration, lo, this.scope(), hi);
813
+ const table = chunkIFZOI3JQ_cjs.clockTable(remap, "time", this.duration, lo, this.scope(), hi);
588
814
  for (const d of inner) {
589
815
  const prev = d.warp;
590
816
  out.push({ ...d, warp: (t) => t < lo || t > hi ? NaN : prev ? prev(table.at(t)) : table.at(t) });
@@ -597,6 +823,8 @@ var CompositionSequence = class extends chunkNO26HWR7_cjs.Sequence {
597
823
  for (const l of this._layers3d) l.destroy();
598
824
  this._layers3d = [];
599
825
  for (const c of this._children) c.destroy();
826
+ for (const o of this._blendOwned) o.destroy();
827
+ this._blendOwned = [];
600
828
  super.destroy();
601
829
  }
602
830
  };
@@ -608,5 +836,5 @@ function frameOf(child) {
608
836
  }
609
837
 
610
838
  exports.CompositionSequence = CompositionSequence;
611
- //# sourceMappingURL=chunk-FFYA3UBU.cjs.map
612
- //# sourceMappingURL=chunk-FFYA3UBU.cjs.map
839
+ //# sourceMappingURL=chunk-MHOO2CVU.cjs.map
840
+ //# sourceMappingURL=chunk-MHOO2CVU.cjs.map