pixi-effects 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +26 -7
- package/ai/tools/lookup.mjs +14 -2
- package/dist/{Base-CXsMdmJB.d.ts → Base-CdF7AJop.d.ts} +1 -1
- package/dist/{Base-pNq6sVGl.d.cts → Base-CmiFiwQM.d.cts} +1 -1
- package/dist/Composition-BMRCE5FH.js +7 -0
- package/dist/{Composition-NDXKQHB4.js.map → Composition-BMRCE5FH.js.map} +1 -1
- package/dist/Composition-XRLYR6GL.cjs +16 -0
- package/dist/{Composition-OLH7RC5Z.cjs.map → Composition-XRLYR6GL.cjs.map} +1 -1
- package/dist/Controller.cjs +130 -1
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +24 -2
- package/dist/Controller.d.ts +24 -2
- package/dist/Controller.js +130 -1
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-f0tQG9y5.d.ts → Movie-Bh1HPdKw.d.cts} +30 -2
- package/dist/{Movie-CL6SsAWe.d.cts → Movie-D5WFfZdZ.d.ts} +30 -2
- package/dist/{Spans-QRMN2EZO.cjs → Spans-4IRBSOLI.cjs} +11 -11
- package/dist/{Spans-QRMN2EZO.cjs.map → Spans-4IRBSOLI.cjs.map} +1 -1
- package/dist/{Spans-QHHWC2PB.js → Spans-MWP2FX3T.js} +6 -6
- package/dist/{Spans-QHHWC2PB.js.map → Spans-MWP2FX3T.js.map} +1 -1
- package/dist/{blendModes-WIUCYIMC.cjs → blendModes-KU7XLV7U.cjs} +5 -5
- package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-KU7XLV7U.cjs.map} +1 -1
- package/dist/{blendModes-PP5NPBXY.js → blendModes-OOI3T6BE.js} +4 -4
- package/dist/{blendModes-PP5NPBXY.js.map → blendModes-OOI3T6BE.js.map} +1 -1
- package/dist/{chunk-ITDYFBB6.js → chunk-2JFFKHXO.js} +5 -5
- package/dist/chunk-2JFFKHXO.js.map +1 -0
- package/dist/{chunk-GL2AQFLQ.cjs → chunk-42ML5AKN.cjs} +5 -5
- package/dist/chunk-42ML5AKN.cjs.map +1 -0
- package/dist/{chunk-XV4L72FW.js → chunk-AZ6AINSW.js} +5 -5
- package/dist/{chunk-XV4L72FW.js.map → chunk-AZ6AINSW.js.map} +1 -1
- package/dist/{chunk-FVZEBYLW.cjs → chunk-CC7SUR7Q.cjs} +237 -54
- package/dist/chunk-CC7SUR7Q.cjs.map +1 -0
- package/dist/{chunk-Y253JQOH.js → chunk-G3DX65HO.js} +110 -10
- package/dist/chunk-G3DX65HO.js.map +1 -0
- package/dist/{chunk-LEECUCTG.cjs → chunk-KJSEQVBV.cjs} +129 -29
- package/dist/chunk-KJSEQVBV.cjs.map +1 -0
- package/dist/{chunk-IMQJVRMT.cjs → chunk-NEUPEENI.cjs} +5 -5
- package/dist/{chunk-IMQJVRMT.cjs.map → chunk-NEUPEENI.cjs.map} +1 -1
- package/dist/{chunk-67LG7MN5.js → chunk-VF56PRAA.js} +205 -22
- package/dist/chunk-VF56PRAA.js.map +1 -0
- package/dist/{chunk-RTJD3ZSW.js → chunk-WIIU42FH.js} +4 -4
- package/dist/{chunk-RTJD3ZSW.js.map → chunk-WIIU42FH.js.map} +1 -1
- package/dist/{chunk-5IJENZOG.cjs → chunk-Y5UWFH6Y.cjs} +91 -91
- package/dist/{chunk-5IJENZOG.cjs.map → chunk-Y5UWFH6Y.cjs.map} +1 -1
- package/dist/index.cjs +33 -33
- package/dist/index.d.cts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +5 -5
- package/dist/loader.css +1 -1
- package/dist/present.cjs +5 -5
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +5 -5
- package/dist/three.cjs +6 -6
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +2 -2
- package/dist/{types-CavnUvd5.d.ts → types-B6zDTU2V.d.cts} +14 -1
- package/dist/{types-CavnUvd5.d.cts → types-B6zDTU2V.d.ts} +14 -1
- package/docs/api.md +15 -4
- package/docs/dsl.md +7 -5
- package/llms-full.txt +95 -18
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +3 -3
- package/skills/pixi-effects/reference/cheatsheet.md +7 -6
- package/skills/pixi-effects/reference/pitfalls.md +3 -0
- package/skills/pixi-effects/reference/recipes.md +60 -0
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-NDXKQHB4.js +0 -7
- package/dist/Composition-OLH7RC5Z.cjs +0 -16
- package/dist/chunk-67LG7MN5.js.map +0 -1
- package/dist/chunk-FVZEBYLW.cjs.map +0 -1
- package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
- package/dist/chunk-ITDYFBB6.js.map +0 -1
- package/dist/chunk-LEECUCTG.cjs.map +0 -1
- package/dist/chunk-Y253JQOH.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk42ML5AKN_cjs = require('./chunk-42ML5AKN.cjs');
|
|
4
4
|
var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
|
|
5
5
|
|
|
6
6
|
// src/core/blend.ts
|
|
@@ -40,7 +40,7 @@ function usesAdvancedBlend(spec) {
|
|
|
40
40
|
};
|
|
41
41
|
return visit(spec);
|
|
42
42
|
}
|
|
43
|
-
var sayNow = (message, spec) => console.warn(`pixi-effects: ${
|
|
43
|
+
var sayNow = (message, spec) => console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(spec)}: ${message}`);
|
|
44
44
|
function blendUnavailableMessage(mode) {
|
|
45
45
|
return `blendMode "${mode}" could not be set up (the blend filters are not registered); the layer is drawn normal`;
|
|
46
46
|
}
|
|
@@ -85,7 +85,7 @@ function blendFilterFor(mode) {
|
|
|
85
85
|
}
|
|
86
86
|
async function enableAdvancedBlend(renderer) {
|
|
87
87
|
try {
|
|
88
|
-
const { registerBlendModes } = await import('./blendModes-
|
|
88
|
+
const { registerBlendModes } = await import('./blendModes-KU7XLV7U.cjs');
|
|
89
89
|
registerBlendModes();
|
|
90
90
|
} catch (e) {
|
|
91
91
|
const why = e instanceof Error ? e.message : String(e);
|
|
@@ -137,5 +137,5 @@ exports.isAdvancedBlend = isAdvancedBlend;
|
|
|
137
137
|
exports.maxConcurrentAdvanced = maxConcurrentAdvanced;
|
|
138
138
|
exports.setBlendFilterFactory = setBlendFilterFactory;
|
|
139
139
|
exports.usesAdvancedBlend = usesAdvancedBlend;
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
140
|
+
//# sourceMappingURL=chunk-NEUPEENI.cjs.map
|
|
141
|
+
//# sourceMappingURL=chunk-NEUPEENI.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/blend.ts"],"names":["suggestName","describeLayer"],"mappings":"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","file":"chunk-IMQJVRMT.cjs","sourcesContent":["import { describeLayer } from './lint';\nimport { suggestName } from './options';\n\n/** The modes Pixi draws itself (`add`, `screen`, `multiply`) and `normal`. */\nexport const BASIC_BLEND_MODES = ['normal', 'add', 'screen', 'multiply'] as const;\n/** The modes that need the backdrop: our own `BlendModeFilter`s (`src/filters/blendModes.ts`), loaded only when a movie uses one. */\nexport const ADVANCED_BLEND_MODES = [\n 'overlay', 'soft-light', 'hard-light', 'color-dodge', 'color-burn', 'darken', 'lighten', 'difference', 'exclusion',\n 'hue', 'saturation', 'color', 'luminosity', 'linear-burn',\n] as const;\n/** Every `blendMode` there is (the one table the warnings, the types and the docs check against). */\nexport const BLEND_MODES: readonly string[] = [...BASIC_BLEND_MODES, ...ADVANCED_BLEND_MODES];\n\nexport function isAdvancedBlend(mode: unknown): boolean {\n return typeof mode === 'string' && (ADVANCED_BLEND_MODES as readonly string[]).includes(mode);\n}\n\n/** Why `mode` is not a blend mode, with the likely name and the choices; null when it is one. */\nexport function blendProblem(mode: unknown): string | null {\n if (typeof mode === 'string' && BLEND_MODES.includes(mode)) return null;\n const guess = typeof mode === 'string' ? suggestName(mode, BLEND_MODES) : null;\n const shown = typeof mode === 'string' ? `\"${mode}\"` : String(mode);\n return `blendMode ${shown} is not supported${guess ? `; did you mean \"${guess}\"?` : ''} (use ${BLEND_MODES.join(', ')})`;\n}\n\n/** Does any layer of this composition (or one nested in it, or a mask layer) use an advanced blend? */\nexport function usesAdvancedBlend(spec: unknown): boolean {\n const visit = (n: unknown): boolean => {\n if (!n || typeof n !== 'object') return false;\n if (Array.isArray(n)) return n.some(visit);\n const o = n as Record<string, unknown>;\n return isAdvancedBlend(o.blendMode) || visit(o.sequences) || visit(o.mask) || visit(o.layer); // (`layer`: a mask written as { layer: <spec>, invert })\n };\n return visit(spec);\n}\n\n/** What to say about a layer's blend mode: `Composition` collects these per build, so the same mistake in 300 particle layers is said once. */\nexport type BlendReport = (message: string, spec: { name?: string; type: string }) => void;\nconst sayNow: BlendReport = (message, spec) => console.warn(`pixi-effects: ${describeLayer(spec)}: ${message}`);\n\n/** The blend filters of an advanced mode were not registered (a movie registers them in `Movie.init`, or loading them failed): the layer is drawn normal. */\nexport function blendUnavailableMessage(mode: string): string {\n return `blendMode \"${mode}\" could not be set up (the blend filters are not registered); the layer is drawn normal`;\n}\n\n/**\n * Apply a layer's `blendMode` to its display object; an unknown mode, or an advanced mode while the blend filters are not registered, says so\n * and the layer is drawn normal. Returns the blend filter it added to the layer's filter chain (the caller destroys it with the layer), if any.\n */\nexport function applyBlendMode(\n spec: { blendMode?: string; name?: string; type: string },\n display: { blendMode?: unknown; filters?: unknown },\n report: BlendReport = sayNow,\n): BlendFilterLike | null {\n const mode = spec.blendMode;\n if (mode === undefined) return null;\n const problem = blendProblem(mode);\n if (problem) { report(problem, spec); return null; }\n if (isAdvancedBlend(mode) && !blendRegistered()) { report(blendUnavailableMessage(mode), spec); return null; }\n // A container's blend mode applies INSIDE a filter's offscreen pass (against transparent black: multiply gave\n // solid black) and not to the filtered result. With filters, the pass that draws onto the backdrop is the last\n // filter's, so the mode goes there; the container stays normal.\n const filters = display.filters;\n if (Array.isArray(filters) && filters.length > 0) {\n if (isAdvancedBlend(mode)) {\n // an advanced mode is a filter of its own (it reads the backdrop): it joins the chain as the last pass\n const blend = blendFilterFor(mode);\n if (!blend) { report(blendUnavailableMessage(mode), spec); return null; }\n display.filters = [...filters, blend];\n return blend;\n }\n (filters[filters.length - 1] as { blendMode?: unknown }).blendMode = mode;\n return null;\n }\n display.blendMode = mode;\n return null;\n}\n\n/** What blend.ts needs of a blend filter (the real ones are Pixi filters; tests use plain objects). */\nexport type BlendFilterLike = { blendMode?: unknown; destroy(): void };\nlet factory: ((mode: string) => BlendFilterLike | null) | null = null;\n\n/** `filters/blendModes.ts` hands its filter maker here when it registers, so this file never imports Pixi. */\nexport function setBlendFilterFactory(f: ((mode: string) => BlendFilterLike | null) | null): void {\n factory = f;\n}\n\n/** Are the blend filters registered (a movie that uses an advanced mode registers them in `Movie.init`)? */\nexport function blendRegistered(): boolean {\n return factory !== null;\n}\n\n/** A fresh blend filter for an advanced mode, or null when the blends are not registered (or the mode is not an advanced one). */\nexport function blendFilterFor(mode: string): BlendFilterLike | null {\n return isAdvancedBlend(mode) && factory ? factory(mode) : null;\n}\n\n/**\n * Called by `Movie.init` when a layer uses an advanced mode, and only then: loads our blend filters (a separate chunk), registers them on\n * the pixi the movie runs on, and on WebGL turns the back buffer on (an advanced blend reads what is already drawn; WebGPU copies the\n * backdrop by itself, and has no such switch). A movie that uses none never reaches this, so nothing about it changes.\n */\nexport async function enableAdvancedBlend(renderer: unknown): Promise<void> {\n try {\n const { registerBlendModes } = await import('../filters/blendModes');\n registerBlendModes();\n } catch (e) {\n // a page that serves dist/index.js without its chunk, a CDN hiccup: the movie still plays, its advanced blends drawn normal\n const why = e instanceof Error ? e.message : String(e);\n console.warn(`pixi-effects: the blend filters could not be loaded (${why}); layers with an advanced blendMode are drawn normal. Serve the whole dist/ folder (the filters are the blendModes-*.js file next to index.js)`);\n return;\n }\n const bb = (renderer as { backBuffer?: { useBackBuffer: boolean } }).backBuffer;\n if (bb) bb.useBackBuffer = true;\n}\n\n/** This many layers with an advanced blend on screen at once is slow: each is a full-frame pass (measured on WebGL: about 1.5–2 ms each at 1080p). */\nexport const MANY_ADVANCED = 10;\n\ntype Counted = { at?: number; duration?: number; blendMode?: unknown; type?: string; sequences?: unknown };\n\n/** The layers a composition draws (its children each blend one by one when it has a mode): null, camera and audio layers draw nothing; nested compositions are walked. */\nfunction drawnLeaves(sequences: unknown): number {\n if (!Array.isArray(sequences)) return 0;\n let n = 0;\n for (const s of sequences as Counted[]) {\n if (!s || s.type === 'null' || s.type === 'camera' || s.type === 'audio') continue;\n n += s.type === 'composition' ? drawnLeaves(s.sequences) : 1;\n }\n return n;\n}\n\n/**\n * The full-frame passes one layer costs while it is on screen: 1 for a layer with an advanced mode; for a composition with one, one for each\n * drawn layer inside it (its children inherit the mode and blend one by one); for a composition without, the most that its own layers add\n * at one instant (a card with three blends in it, on screen with four other cards, is twelve).\n */\nfunction passesOf(l: Counted, span: number): number {\n const isComposition = l.type === 'composition';\n if (isAdvancedBlend(l.blendMode)) return isComposition ? Math.max(1, drawnLeaves(l.sequences)) : 1;\n return isComposition && Array.isArray(l.sequences) ? maxConcurrentAdvanced(l.sequences as Counted[], l.duration ?? span) : 0;\n}\n\n/** The most full-frame advanced-blend passes on screen at one instant, from the layers' `at` and `duration` (a layer with no duration lasts to the end). */\nexport function maxConcurrentAdvanced(layers: ReadonlyArray<Counted>, span: number): number {\n const events: Array<[number, number]> = [];\n for (const l of layers) {\n const w = passesOf(l, span);\n if (w === 0) continue;\n const at = l.at ?? 0;\n const end = l.duration === undefined ? span : at + l.duration;\n const start = Math.max(0, at); // a negative `at` starts before the composition: only what is after 0 is on screen\n if (end > start) events.push([start, w], [end, -w]);\n }\n events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // at one instant, the layers that end go before the ones that start\n let now = 0, most = 0;\n for (const [, d] of events) { now += d; most = Math.max(most, now); }\n return most;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/blend.ts"],"names":["suggestName","describeLayer"],"mappings":"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","file":"chunk-NEUPEENI.cjs","sourcesContent":["import { describeLayer } from './lint';\nimport { suggestName } from './options';\n\n/** The modes Pixi draws itself (`add`, `screen`, `multiply`) and `normal`. */\nexport const BASIC_BLEND_MODES = ['normal', 'add', 'screen', 'multiply'] as const;\n/** The modes that need the backdrop: our own `BlendModeFilter`s (`src/filters/blendModes.ts`), loaded only when a movie uses one. */\nexport const ADVANCED_BLEND_MODES = [\n 'overlay', 'soft-light', 'hard-light', 'color-dodge', 'color-burn', 'darken', 'lighten', 'difference', 'exclusion',\n 'hue', 'saturation', 'color', 'luminosity', 'linear-burn',\n] as const;\n/** Every `blendMode` there is (the one table the warnings, the types and the docs check against). */\nexport const BLEND_MODES: readonly string[] = [...BASIC_BLEND_MODES, ...ADVANCED_BLEND_MODES];\n\nexport function isAdvancedBlend(mode: unknown): boolean {\n return typeof mode === 'string' && (ADVANCED_BLEND_MODES as readonly string[]).includes(mode);\n}\n\n/** Why `mode` is not a blend mode, with the likely name and the choices; null when it is one. */\nexport function blendProblem(mode: unknown): string | null {\n if (typeof mode === 'string' && BLEND_MODES.includes(mode)) return null;\n const guess = typeof mode === 'string' ? suggestName(mode, BLEND_MODES) : null;\n const shown = typeof mode === 'string' ? `\"${mode}\"` : String(mode);\n return `blendMode ${shown} is not supported${guess ? `; did you mean \"${guess}\"?` : ''} (use ${BLEND_MODES.join(', ')})`;\n}\n\n/** Does any layer of this composition (or one nested in it, or a mask layer) use an advanced blend? */\nexport function usesAdvancedBlend(spec: unknown): boolean {\n const visit = (n: unknown): boolean => {\n if (!n || typeof n !== 'object') return false;\n if (Array.isArray(n)) return n.some(visit);\n const o = n as Record<string, unknown>;\n return isAdvancedBlend(o.blendMode) || visit(o.sequences) || visit(o.mask) || visit(o.layer); // (`layer`: a mask written as { layer: <spec>, invert })\n };\n return visit(spec);\n}\n\n/** What to say about a layer's blend mode: `Composition` collects these per build, so the same mistake in 300 particle layers is said once. */\nexport type BlendReport = (message: string, spec: { name?: string; type: string }) => void;\nconst sayNow: BlendReport = (message, spec) => console.warn(`pixi-effects: ${describeLayer(spec)}: ${message}`);\n\n/** The blend filters of an advanced mode were not registered (a movie registers them in `Movie.init`, or loading them failed): the layer is drawn normal. */\nexport function blendUnavailableMessage(mode: string): string {\n return `blendMode \"${mode}\" could not be set up (the blend filters are not registered); the layer is drawn normal`;\n}\n\n/**\n * Apply a layer's `blendMode` to its display object; an unknown mode, or an advanced mode while the blend filters are not registered, says so\n * and the layer is drawn normal. Returns the blend filter it added to the layer's filter chain (the caller destroys it with the layer), if any.\n */\nexport function applyBlendMode(\n spec: { blendMode?: string; name?: string; type: string },\n display: { blendMode?: unknown; filters?: unknown },\n report: BlendReport = sayNow,\n): BlendFilterLike | null {\n const mode = spec.blendMode;\n if (mode === undefined) return null;\n const problem = blendProblem(mode);\n if (problem) { report(problem, spec); return null; }\n if (isAdvancedBlend(mode) && !blendRegistered()) { report(blendUnavailableMessage(mode), spec); return null; }\n // A container's blend mode applies INSIDE a filter's offscreen pass (against transparent black: multiply gave\n // solid black) and not to the filtered result. With filters, the pass that draws onto the backdrop is the last\n // filter's, so the mode goes there; the container stays normal.\n const filters = display.filters;\n if (Array.isArray(filters) && filters.length > 0) {\n if (isAdvancedBlend(mode)) {\n // an advanced mode is a filter of its own (it reads the backdrop): it joins the chain as the last pass\n const blend = blendFilterFor(mode);\n if (!blend) { report(blendUnavailableMessage(mode), spec); return null; }\n display.filters = [...filters, blend];\n return blend;\n }\n (filters[filters.length - 1] as { blendMode?: unknown }).blendMode = mode;\n return null;\n }\n display.blendMode = mode;\n return null;\n}\n\n/** What blend.ts needs of a blend filter (the real ones are Pixi filters; tests use plain objects). */\nexport type BlendFilterLike = { blendMode?: unknown; destroy(): void };\nlet factory: ((mode: string) => BlendFilterLike | null) | null = null;\n\n/** `filters/blendModes.ts` hands its filter maker here when it registers, so this file never imports Pixi. */\nexport function setBlendFilterFactory(f: ((mode: string) => BlendFilterLike | null) | null): void {\n factory = f;\n}\n\n/** Are the blend filters registered (a movie that uses an advanced mode registers them in `Movie.init`)? */\nexport function blendRegistered(): boolean {\n return factory !== null;\n}\n\n/** A fresh blend filter for an advanced mode, or null when the blends are not registered (or the mode is not an advanced one). */\nexport function blendFilterFor(mode: string): BlendFilterLike | null {\n return isAdvancedBlend(mode) && factory ? factory(mode) : null;\n}\n\n/**\n * Called by `Movie.init` when a layer uses an advanced mode, and only then: loads our blend filters (a separate chunk), registers them on\n * the pixi the movie runs on, and on WebGL turns the back buffer on (an advanced blend reads what is already drawn; WebGPU copies the\n * backdrop by itself, and has no such switch). A movie that uses none never reaches this, so nothing about it changes.\n */\nexport async function enableAdvancedBlend(renderer: unknown): Promise<void> {\n try {\n const { registerBlendModes } = await import('../filters/blendModes');\n registerBlendModes();\n } catch (e) {\n // a page that serves dist/index.js without its chunk, a CDN hiccup: the movie still plays, its advanced blends drawn normal\n const why = e instanceof Error ? e.message : String(e);\n console.warn(`pixi-effects: the blend filters could not be loaded (${why}); layers with an advanced blendMode are drawn normal. Serve the whole dist/ folder (the filters are the blendModes-*.js file next to index.js)`);\n return;\n }\n const bb = (renderer as { backBuffer?: { useBackBuffer: boolean } }).backBuffer;\n if (bb) bb.useBackBuffer = true;\n}\n\n/** This many layers with an advanced blend on screen at once is slow: each is a full-frame pass (measured on WebGL: about 1.5–2 ms each at 1080p). */\nexport const MANY_ADVANCED = 10;\n\ntype Counted = { at?: number; duration?: number; blendMode?: unknown; type?: string; sequences?: unknown };\n\n/** The layers a composition draws (its children each blend one by one when it has a mode): null, camera and audio layers draw nothing; nested compositions are walked. */\nfunction drawnLeaves(sequences: unknown): number {\n if (!Array.isArray(sequences)) return 0;\n let n = 0;\n for (const s of sequences as Counted[]) {\n if (!s || s.type === 'null' || s.type === 'camera' || s.type === 'audio') continue;\n n += s.type === 'composition' ? drawnLeaves(s.sequences) : 1;\n }\n return n;\n}\n\n/**\n * The full-frame passes one layer costs while it is on screen: 1 for a layer with an advanced mode; for a composition with one, one for each\n * drawn layer inside it (its children inherit the mode and blend one by one); for a composition without, the most that its own layers add\n * at one instant (a card with three blends in it, on screen with four other cards, is twelve).\n */\nfunction passesOf(l: Counted, span: number): number {\n const isComposition = l.type === 'composition';\n if (isAdvancedBlend(l.blendMode)) return isComposition ? Math.max(1, drawnLeaves(l.sequences)) : 1;\n return isComposition && Array.isArray(l.sequences) ? maxConcurrentAdvanced(l.sequences as Counted[], l.duration ?? span) : 0;\n}\n\n/** The most full-frame advanced-blend passes on screen at one instant, from the layers' `at` and `duration` (a layer with no duration lasts to the end). */\nexport function maxConcurrentAdvanced(layers: ReadonlyArray<Counted>, span: number): number {\n const events: Array<[number, number]> = [];\n for (const l of layers) {\n const w = passesOf(l, span);\n if (w === 0) continue;\n const at = l.at ?? 0;\n const end = l.duration === undefined ? span : at + l.duration;\n const start = Math.max(0, at); // a negative `at` starts before the composition: only what is after 0 is on screen\n if (end > start) events.push([start, w], [end, -w]);\n }\n events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); // at one instant, the layers that end go before the ones that start\n let now = 0, most = 0;\n for (const [, d] of events) { now += d; most = Math.max(most, now); }\n return most;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CompositionSequence, maskSourceOf } from './chunk-
|
|
2
|
-
import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-
|
|
3
|
-
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-
|
|
1
|
+
import { CompositionSequence, maskSourceOf } from './chunk-AZ6AINSW.js';
|
|
2
|
+
import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-G3DX65HO.js';
|
|
3
|
+
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-WIIU42FH.js';
|
|
4
4
|
import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
|
|
5
5
|
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
|
|
6
6
|
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';
|
|
@@ -390,6 +390,7 @@ function findSilences(channels, sampleRate, opts = {}) {
|
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
// src/core/inspectAudio.ts
|
|
393
|
+
var BURIED_DB = 12;
|
|
393
394
|
var INAUDIBLE_DB = -50;
|
|
394
395
|
var db = (x) => Math.round(10 * Math.max(-120, 20 * Math.log10(x))) / 10;
|
|
395
396
|
var mono = (chans) => {
|
|
@@ -475,6 +476,7 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
475
476
|
return measure(Array.from({ length: b.numberOfChannels }, (_, c) => b.getChannelData(c)), b.sampleRate);
|
|
476
477
|
};
|
|
477
478
|
const scoreNotes = [];
|
|
479
|
+
const buried = [];
|
|
478
480
|
const list = [...sources].sort((a, b) => a.start - b.start).map((s) => {
|
|
479
481
|
const layer = s.layer ?? "an audio layer";
|
|
480
482
|
const source = s.source ?? "audio";
|
|
@@ -502,8 +504,22 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
502
504
|
const over = s.music.seconds - plays;
|
|
503
505
|
if (over > 2 && over / s.music.seconds > 0.15) scoreNotes.push(`${layer} (${source}): the score is ${Math.round(s.music.seconds * 10) / 10}s long but only ${Math.round(plays * 100) / 100}s of it play, so it is cut off mid-phrase. Write fewer bars (or a faster tempo) so it ends with the movie`);
|
|
504
506
|
}
|
|
505
|
-
|
|
507
|
+
let belowMixDb;
|
|
508
|
+
if (!clock && !s.sourceMap && s.start < movieDuration && ownDb > INAUDIBLE_DB) {
|
|
509
|
+
const at = s.start + sound.loudestAt;
|
|
510
|
+
const around = db(peakIn(Math.max(0, at - 0.05), Math.min(movieDuration, at + 0.05)));
|
|
511
|
+
belowMixDb = Math.max(0, Math.round((around - ownDb) * 10) / 10);
|
|
512
|
+
if (!s.music && source.startsWith("sfx") && sound.length <= 4 && belowMixDb >= BURIED_DB) {
|
|
513
|
+
buried.push({ layer, source, ownDb, topVolume, around, belowMixDb, at: Math.round(at * 100) / 100 });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return { layer, source, start: s.start, end: s.end, peakDb, sound, ...belowMixDb !== void 0 ? { belowMixDb } : {}, ...s.music ? { music: s.music } : {} };
|
|
506
517
|
});
|
|
518
|
+
if (buried.length > 0) {
|
|
519
|
+
const worst = buried.reduce((a, b) => b.belowMixDb > a.belowMixDb ? b : a);
|
|
520
|
+
const names = new Set(buried.map((x) => x.layer + x.source));
|
|
521
|
+
scoreNotes.push(`${names.size === 1 ? "1 sound effect is" : `${names.size} sound effects are`} buried (${BURIED_DB} dB or more under the loudest sample of the mix around its loudest moment${buried.length > names.size ? `, ${buried.length} times in all` : ""}): the worst is ${worst.layer} (${worst.source}), its own peak ${worst.ownDb} dBFS at volume ${worst.topVolume} against ${worst.around} dBFS at ${worst.at} s, ${worst.belowMixDb} dB louder. If it is meant to be heard, raise its volume (an accent over music needs 2 to 3) or lower what plays there; every source's belowMixDb has its own number`);
|
|
522
|
+
}
|
|
507
523
|
const whole = measureLoudness(chans, sr);
|
|
508
524
|
const silences = findSilences(chans, sr, { minLength: 1 });
|
|
509
525
|
if (whole.clippedSamples > 0) issues.push(`${whole.clippedSamples} sample(s) of the mix are at full scale (clipped) \u2014 lower the volume of the layers that play loudest`);
|
|
@@ -1882,6 +1898,20 @@ function rejectReservedNameCollision(filters, seqName) {
|
|
|
1882
1898
|
}
|
|
1883
1899
|
|
|
1884
1900
|
// src/core/inspect.ts
|
|
1901
|
+
function chainOf(node) {
|
|
1902
|
+
const chain = [];
|
|
1903
|
+
for (let n = node; n && n.parent; n = n.parent) {
|
|
1904
|
+
const i = n.parent.children?.indexOf(n);
|
|
1905
|
+
if (i === void 0 || i < 0) return null;
|
|
1906
|
+
chain.unshift(i);
|
|
1907
|
+
}
|
|
1908
|
+
return chain.length ? chain : null;
|
|
1909
|
+
}
|
|
1910
|
+
function drawnAfter(a, b) {
|
|
1911
|
+
if (!a || !b) return null;
|
|
1912
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) if (a[i] !== b[i]) return a[i] > b[i];
|
|
1913
|
+
return null;
|
|
1914
|
+
}
|
|
1885
1915
|
var FAINT = 0.3;
|
|
1886
1916
|
var intersection = (a, b) => {
|
|
1887
1917
|
const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
|
|
@@ -1925,7 +1955,11 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1925
1955
|
const scene = /* @__PURE__ */ new Map();
|
|
1926
1956
|
const scaledToNothing = /* @__PURE__ */ new Set();
|
|
1927
1957
|
const hasMotion = /* @__PURE__ */ new Set();
|
|
1928
|
-
const
|
|
1958
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1959
|
+
const specs = /* @__PURE__ */ new Map();
|
|
1960
|
+
const chains = /* @__PURE__ */ new Map();
|
|
1961
|
+
const chainAlpha = /* @__PURE__ */ new Map();
|
|
1962
|
+
const walk2 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
|
|
1929
1963
|
const blending = /* @__PURE__ */ new Set();
|
|
1930
1964
|
const now = comp.childTime(time);
|
|
1931
1965
|
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);
|
|
@@ -1969,6 +2003,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1969
2003
|
if (threeD) info.depthBlur = Number(depthBlur.toFixed(2));
|
|
1970
2004
|
if (threeD && light !== void 0) info.light = Number(light.toFixed(2));
|
|
1971
2005
|
layers.push(info);
|
|
2006
|
+
chainAlpha.set(info, parentAlpha * alpha);
|
|
2007
|
+
chains.set(info, chainOf(display));
|
|
2008
|
+
specs.set(info, seq.spec);
|
|
2009
|
+
kinds.set(info, seq.spec.type === "shape" ? String(seq.spec.shape) : seq.spec.type);
|
|
1972
2010
|
if (motion.ever) hasMotion.add(info);
|
|
1973
2011
|
let shown = bounds ? clip(bounds, view) : null;
|
|
1974
2012
|
const maskTarget = seq.maskSequence?.target;
|
|
@@ -1981,7 +2019,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1981
2019
|
const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
|
|
1982
2020
|
if (group) scene.set(info, group);
|
|
1983
2021
|
if (zero || seq.showsNothingYet) scaledToNothing.add(info);
|
|
1984
|
-
if (seq instanceof CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero);
|
|
2022
|
+
if (seq instanceof CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
|
|
1985
2023
|
});
|
|
1986
2024
|
};
|
|
1987
2025
|
walk2(root, "", false, true);
|
|
@@ -2017,6 +2055,42 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
2017
2055
|
if (share > 0.25) issues.push(`text layers "${texts[i].path}" and "${texts[j].path}" overlap by ${Math.round(share * 100)}% of the smaller one`);
|
|
2018
2056
|
}
|
|
2019
2057
|
}
|
|
2058
|
+
const FILLED = /* @__PURE__ */ new Set(["rect", "circle", "ellipse", "image", "video"]);
|
|
2059
|
+
for (const t of texts) {
|
|
2060
|
+
const tb = seen.get(t);
|
|
2061
|
+
if (!tb) continue;
|
|
2062
|
+
const area = tb.width * tb.height;
|
|
2063
|
+
if (!(area > 0)) continue;
|
|
2064
|
+
for (const o of layers) {
|
|
2065
|
+
if (o === t || o.type === "text" || !o.visible || !o.bounds) continue;
|
|
2066
|
+
if (o.path.startsWith(t.path + "/") || t.path.startsWith(o.path + "/")) continue;
|
|
2067
|
+
const kind = kinds.get(o) ?? o.type, spec = specs.get(o) ?? {};
|
|
2068
|
+
const initial = spec.initial ?? {};
|
|
2069
|
+
const hairline = (kind === "line" || kind === "rect") && Math.min(o.bounds.width, o.bounds.height) < 12 && Math.max(o.bounds.width, o.bounds.height) >= 40;
|
|
2070
|
+
const soft = spec.blendMode !== void 0 && spec.blendMode !== "normal" || Array.isArray(spec.filters) && spec.filters.length > 0;
|
|
2071
|
+
const filled = kind === "image" || kind === "video" || initial.fillColor !== void 0 || spec.fillGradient !== void 0 || initial.fillGradient !== void 0;
|
|
2072
|
+
if (o.threeD || t.threeD) continue;
|
|
2073
|
+
const over = drawnAfter(chains.get(o) ?? null, chains.get(t) ?? null) ?? layers.indexOf(o) > layers.indexOf(t);
|
|
2074
|
+
const opacity = chainAlpha.get(o) ?? o.alpha;
|
|
2075
|
+
const hides = FILLED.has(kind) && !hairline && filled && !soft && over && opacity >= 0.8;
|
|
2076
|
+
const cuts = hairline && !soft && over && opacity >= FAINT;
|
|
2077
|
+
if (!hides && !cuts) continue;
|
|
2078
|
+
const st = scene.get(t), so = scene.get(o);
|
|
2079
|
+
if (st && so && st !== so) continue;
|
|
2080
|
+
const ob = seen.get(o);
|
|
2081
|
+
if (!ob) continue;
|
|
2082
|
+
const covered = intersection(tb, ob) / area;
|
|
2083
|
+
if (cuts) {
|
|
2084
|
+
const horizontal = ob.width >= ob.height;
|
|
2085
|
+
const mid = horizontal ? ob.y + ob.height / 2 : ob.x + ob.width / 2;
|
|
2086
|
+
const lo = horizontal ? tb.y + tb.height * 0.2 : tb.x + tb.width * 0.2, hi = horizontal ? tb.y + tb.height * 0.8 : tb.x + tb.width * 0.8;
|
|
2087
|
+
const along = horizontal ? Math.min(ob.x + ob.width, tb.x + tb.width) - Math.max(ob.x, tb.x) : Math.min(ob.y + ob.height, tb.y + tb.height) - Math.max(ob.y, tb.y);
|
|
2088
|
+
if (mid < lo || mid > hi || along < 0.3 * (horizontal ? tb.width : tb.height)) continue;
|
|
2089
|
+
} else if (covered < 0.1) continue;
|
|
2090
|
+
const word = kind === "image" || kind === "video" ? kind : "shape";
|
|
2091
|
+
issues.push(`text layer "${t.path}" and ${word} layer "${o.path}" overlap: it covers ${Math.round(covered * 100)}% of the text (drawn ${over ? "over" : "under"} it)`);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2020
2094
|
const mode = opts.layers ?? "all";
|
|
2021
2095
|
const listed = mode === "none" ? [] : mode === "visible" ? layers.filter((l) => l.visible) : layers;
|
|
2022
2096
|
return { frame, time, canvas, summary: { layers: layers.length, visible: layers.filter((l) => l.visible).length }, issues, layers: listed };
|
|
@@ -2643,6 +2717,10 @@ var Movie = class {
|
|
|
2643
2717
|
_silent = 0;
|
|
2644
2718
|
/** render() / peek() in flight: a rebuild does not swap the composition under them. */
|
|
2645
2719
|
_busy = 0;
|
|
2720
|
+
/** > 0 while the movie is moving its scene to another moment (a seek, a motion-blurred frame, the poster): a thumbnail does not start then. */
|
|
2721
|
+
_moving = 0;
|
|
2722
|
+
/** The thumbnail being drawn, if any: a seek waits for it (it has the scene on another moment for a few microtasks). */
|
|
2723
|
+
_thumbRun = null;
|
|
2646
2724
|
_idleWaiters = [];
|
|
2647
2725
|
/** What went into the mix, and its peak before limiting — kept for inspectAudio(). */
|
|
2648
2726
|
_audioSources = [];
|
|
@@ -2690,6 +2768,7 @@ var Movie = class {
|
|
|
2690
2768
|
if (this._posterFrame === null || !this.timeline) return;
|
|
2691
2769
|
const back = this.currentFrame;
|
|
2692
2770
|
this.currentFrame = this._posterFrame;
|
|
2771
|
+
this._moving++;
|
|
2693
2772
|
try {
|
|
2694
2773
|
this.timeline.time(this._timeOf(this._posterFrame));
|
|
2695
2774
|
await this._awaitVideoFrames();
|
|
@@ -2697,6 +2776,7 @@ var Movie = class {
|
|
|
2697
2776
|
this._renderNow();
|
|
2698
2777
|
} finally {
|
|
2699
2778
|
this.currentFrame = back;
|
|
2779
|
+
this._moving--;
|
|
2700
2780
|
}
|
|
2701
2781
|
this._atPoster = true;
|
|
2702
2782
|
}
|
|
@@ -3083,12 +3163,18 @@ var Movie = class {
|
|
|
3083
3163
|
this.emit("error", { where, message: error instanceof Error ? error.message : String(error), error });
|
|
3084
3164
|
}
|
|
3085
3165
|
async _goto(frame) {
|
|
3086
|
-
this.
|
|
3087
|
-
this.
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3166
|
+
if (this._thumbRun) await this._thumbRun;
|
|
3167
|
+
this._moving++;
|
|
3168
|
+
try {
|
|
3169
|
+
this._atPoster = false;
|
|
3170
|
+
this.currentFrame = Math.max(0, Math.min(frame, this.totalFrames));
|
|
3171
|
+
this.timeline.time(this._timeOf(this.currentFrame));
|
|
3172
|
+
await this._awaitVideoFrames();
|
|
3173
|
+
this._updateSpace();
|
|
3174
|
+
this._renderNow();
|
|
3175
|
+
} finally {
|
|
3176
|
+
this._moving--;
|
|
3177
|
+
}
|
|
3092
3178
|
this.emit("frame", { frame: this.currentFrame, totalFrames: this.totalFrames });
|
|
3093
3179
|
}
|
|
3094
3180
|
/**
|
|
@@ -3136,6 +3222,69 @@ var Movie = class {
|
|
|
3136
3222
|
if (o.as === "imageData") return out.getContext("2d").getImageData(0, 0, out.width, out.height);
|
|
3137
3223
|
return encodeCanvas(out, o.type, o.as, o.quality);
|
|
3138
3224
|
}
|
|
3225
|
+
/**
|
|
3226
|
+
* Why `thumbnail()` is off for this movie: `'variables'` (it declares `vars`: the picture depends on them and changes when they do) or `'video'` (a video
|
|
3227
|
+
* layer: its frame must be waited for, which would seek the playing video). `null`: it works.
|
|
3228
|
+
*/
|
|
3229
|
+
get thumbnailsOff() {
|
|
3230
|
+
if (this._declared.size > 0) return "variables";
|
|
3231
|
+
if (!this._rootSequence) return null;
|
|
3232
|
+
const found = [];
|
|
3233
|
+
collectVideoSequences(this._rootSequence, found);
|
|
3234
|
+
return found.some((v) => v.spec?.type === "video") ? "video" : null;
|
|
3235
|
+
}
|
|
3236
|
+
/**
|
|
3237
|
+
* A small picture of any frame, drawn WITHOUT moving the playhead or changing what is on the canvas, so it can be made while the movie plays or is
|
|
3238
|
+
* paused (a seek-bar preview). The scene is put on that moment, drawn, copied, and put back, all within the microtasks of one task: the browser never
|
|
3239
|
+
* shows the in-between. Gives `null` when it cannot be made right now: the movie has thumbnails off (see `thumbnailsOff`), is moving somewhere else
|
|
3240
|
+
* (a seek, a render, a peek), or is another thumbnail's turn. It is the picture `snapshot()` gives at that size (without motion blur).
|
|
3241
|
+
*/
|
|
3242
|
+
async thumbnail(frame, opts = {}) {
|
|
3243
|
+
warnUnknownOptions("movie.thumbnail()", opts, ["width", "scale"]);
|
|
3244
|
+
this._requireReady("thumbnail");
|
|
3245
|
+
if (opts.width !== void 0 && !(opts.width > 0 && opts.width <= this.width * 4)) throw new Error(`pixi-effects: movie.thumbnail(): width must be above 0 and at most ${this.width * 4} px, got ${opts.width}`);
|
|
3246
|
+
if (opts.scale !== void 0 && !(opts.scale > 0 && opts.scale <= 4)) throw new Error(`pixi-effects: movie.thumbnail(): scale must be above 0 and at most 4, got ${opts.scale}`);
|
|
3247
|
+
if (this.thumbnailsOff || this._moving > 0 || this._busy > 0 || this._thumbRun) return null;
|
|
3248
|
+
const w = Math.max(1, Math.round(opts.width ?? (opts.scale !== void 0 ? this.width * opts.scale : 160)));
|
|
3249
|
+
const h = Math.max(1, Math.round(w * this.height / this.width));
|
|
3250
|
+
let done;
|
|
3251
|
+
this._thumbRun = new Promise((r) => {
|
|
3252
|
+
done = r;
|
|
3253
|
+
});
|
|
3254
|
+
try {
|
|
3255
|
+
return await this._drawThumbnail(clampFrame(frame, this.totalFrames), w, h);
|
|
3256
|
+
} finally {
|
|
3257
|
+
this._thumbRun = null;
|
|
3258
|
+
done();
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
async _drawThumbnail(frame, w, h) {
|
|
3262
|
+
const stage = this.app.canvas;
|
|
3263
|
+
const out = document.createElement("canvas");
|
|
3264
|
+
out.width = w;
|
|
3265
|
+
out.height = h;
|
|
3266
|
+
const keep = this.timeline.time();
|
|
3267
|
+
const t = this._timeOf(frame);
|
|
3268
|
+
this._nominalTime = t;
|
|
3269
|
+
try {
|
|
3270
|
+
this.timeline.time(t);
|
|
3271
|
+
await this._awaitVideoFrames(t);
|
|
3272
|
+
this._updateSpace(t);
|
|
3273
|
+
this._renderNow();
|
|
3274
|
+
out.getContext("2d").drawImage(stage, 0, 0, w, h);
|
|
3275
|
+
} finally {
|
|
3276
|
+
this._nominalTime = keep;
|
|
3277
|
+
try {
|
|
3278
|
+
this.timeline.time(keep);
|
|
3279
|
+
await this._awaitVideoFrames(keep);
|
|
3280
|
+
this._updateSpace(keep);
|
|
3281
|
+
this._renderNow();
|
|
3282
|
+
} finally {
|
|
3283
|
+
this._nominalTime = null;
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
return out;
|
|
3287
|
+
}
|
|
3139
3288
|
async onionSkin(opts = {}) {
|
|
3140
3289
|
warnUnknownOptions("movie.onionSkin()", opts, ["from", "to", "count", "scale", "as", "motionBlur"]);
|
|
3141
3290
|
this._requireReady("onionSkin");
|
|
@@ -3314,9 +3463,11 @@ var Movie = class {
|
|
|
3314
3463
|
async _exposeFrame(frame, mb, target, settle) {
|
|
3315
3464
|
const g = target.getContext("2d");
|
|
3316
3465
|
const stage = this.app.canvas;
|
|
3466
|
+
if (this._thumbRun) await this._thumbRun;
|
|
3317
3467
|
this._atPoster = false;
|
|
3318
3468
|
const times = blurTimes(frame, this.frameRate, mb, Math.max(0, this.duration - END_MARGIN));
|
|
3319
3469
|
this._nominalTime = this._timeOf(Math.max(0, Math.min(frame, this.totalFrames)));
|
|
3470
|
+
this._moving++;
|
|
3320
3471
|
try {
|
|
3321
3472
|
for (let k = 0; k < times.length; k++) {
|
|
3322
3473
|
const t = times[k];
|
|
@@ -3330,6 +3481,7 @@ var Movie = class {
|
|
|
3330
3481
|
}
|
|
3331
3482
|
} finally {
|
|
3332
3483
|
this._nominalTime = null;
|
|
3484
|
+
this._moving--;
|
|
3333
3485
|
}
|
|
3334
3486
|
g.globalAlpha = 1;
|
|
3335
3487
|
if (settle) await this._goto(frame);
|
|
@@ -3638,7 +3790,7 @@ function measureText(text, style = {}) {
|
|
|
3638
3790
|
return { width: m.width, height: m.height, lines: text.split("\n").length };
|
|
3639
3791
|
}
|
|
3640
3792
|
function splitText(text, style = {}, opts = {}) {
|
|
3641
|
-
warnUnknownOptions("splitText()", opts, ["by", "x", "y", "align"]);
|
|
3793
|
+
warnUnknownOptions("splitText()", opts, ["by", "x", "y", "align", "vertical"]);
|
|
3642
3794
|
let by = opts.by ?? "chars";
|
|
3643
3795
|
if (!BY.includes(by)) {
|
|
3644
3796
|
const g = suggestName(String(by), BY);
|
|
@@ -3657,6 +3809,7 @@ function splitText(text, style = {}, opts = {}) {
|
|
|
3657
3809
|
}
|
|
3658
3810
|
if (text === "") return [];
|
|
3659
3811
|
const ts = new TextStyle(style);
|
|
3812
|
+
if (opts.vertical) return splitVertical(text, style, ts, x0, y0, align, by);
|
|
3660
3813
|
const lineHeight = CanvasTextMetrics.measureText("M", ts).lineHeight ?? CanvasTextMetrics.measureText("M", ts).height;
|
|
3661
3814
|
const sentinel = widthOf("|", ts);
|
|
3662
3815
|
const out = [];
|
|
@@ -3680,6 +3833,36 @@ function splitText(text, style = {}, opts = {}) {
|
|
|
3680
3833
|
});
|
|
3681
3834
|
return out;
|
|
3682
3835
|
}
|
|
3836
|
+
var PUNCT = new Set("\u3001\u3002\uFF0C\uFF0E\uFF64\uFF61");
|
|
3837
|
+
var SMALL = new Set("\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308E\u30A1\u30A3\u30A5\u30A7\u30A9\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6");
|
|
3838
|
+
var TURNED = new Set("\u30FC\u2015\u2014\uFF0D\uFF5E\u301C\u2026\u2025\uFF08\uFF09()\uFF3B\uFF3D[]\uFF5B\uFF5D{}\u300C\u300D\u300E\u300F\u3008\u3009\u300A\u300B\u3010\u3011\u3014\u3015<>\u226A\u226B");
|
|
3839
|
+
function splitVertical(text, style, ts, right, top, align, by) {
|
|
3840
|
+
if (by !== "chars") console.warn(`pixi-effects: splitText(): vertical text has only a "chars" form (by "${by}" has no vertical layout); using "chars"`);
|
|
3841
|
+
const em = typeof style.fontSize === "number" ? style.fontSize : Number(ts.fontSize) || 26;
|
|
3842
|
+
const spacing = typeof style.letterSpacing === "number" ? style.letterSpacing : 0;
|
|
3843
|
+
const cell = em + spacing;
|
|
3844
|
+
const lineHeight = CanvasTextMetrics.measureText("M", ts).lineHeight ?? CanvasTextMetrics.measureText("M", ts).height;
|
|
3845
|
+
const out = [];
|
|
3846
|
+
text.split("\n").forEach((line, lineNo) => {
|
|
3847
|
+
const chars = Array.from(line);
|
|
3848
|
+
const start = align === "center" ? top - chars.length * cell / 2 : align === "right" ? top - chars.length * cell : top;
|
|
3849
|
+
const column = right - lineHeight / 2 - lineNo * lineHeight;
|
|
3850
|
+
chars.forEach((ch, i) => {
|
|
3851
|
+
if (/\s/.test(ch)) return;
|
|
3852
|
+
let cx = column, cy = start + i * cell + cell / 2;
|
|
3853
|
+
if (PUNCT.has(ch)) {
|
|
3854
|
+
cx += 0.5 * em;
|
|
3855
|
+
cy -= 0.5 * em;
|
|
3856
|
+
} else if (SMALL.has(ch)) {
|
|
3857
|
+
cx += 0.1 * em;
|
|
3858
|
+
cy -= 0.1 * em;
|
|
3859
|
+
}
|
|
3860
|
+
const width = widthOf(ch, ts);
|
|
3861
|
+
out.push({ text: ch, x: cx - width / 2, y: cy - lineHeight / 2, width, index: out.length, line: lineNo, cx, cy, rotation: TURNED.has(ch) ? 90 : 0 });
|
|
3862
|
+
});
|
|
3863
|
+
});
|
|
3864
|
+
return out;
|
|
3865
|
+
}
|
|
3683
3866
|
|
|
3684
3867
|
// src/presets/_ease.ts
|
|
3685
3868
|
function parseEase2(name) {
|
|
@@ -4195,7 +4378,7 @@ function wiggle(opts) {
|
|
|
4195
4378
|
// src/presets/animateText.ts
|
|
4196
4379
|
var TWEEN_KEYS = ["preset", "from", "to", "duration", "ease"];
|
|
4197
4380
|
var TIME_KEYS = ["at", "delay", "start", "begin", "time"];
|
|
4198
|
-
var KEYS5 = ["by", "x", "y", "align", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
|
|
4381
|
+
var KEYS5 = ["by", "x", "y", "align", "vertical", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
|
|
4199
4382
|
var PRESETS = {
|
|
4200
4383
|
rise: { from: { y: 40, alpha: 0 }, duration: 0.5, ease: "back.out(2)" },
|
|
4201
4384
|
drop: { from: { y: -50, alpha: 0 }, duration: 0.7, ease: "bounce.out" },
|
|
@@ -4208,14 +4391,14 @@ var PRESETS = {
|
|
|
4208
4391
|
var PRESET_NAMES = Object.keys(PRESETS);
|
|
4209
4392
|
function animateText(text, style, options) {
|
|
4210
4393
|
warnUnknownOptions("animateText()", options, KEYS5);
|
|
4211
|
-
const { by = "chars", x = 0, y = 0, align = "left", name = "text", at = 0, duration, idle, styleFor } = options;
|
|
4394
|
+
const { by = "chars", x = 0, y = 0, align = "left", vertical = false, name = "text", at = 0, duration, idle, styleFor } = options;
|
|
4212
4395
|
for (const [n, v] of [["x", x], ["y", y]]) {
|
|
4213
4396
|
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
4214
4397
|
throw new Error(`animateText(): ${n} must be a number of pixels (e.g. ${n}: 640), got ${JSON.stringify(v)}. The letters are measured once, when the layers are made, so an expression such as 'GW/2' cannot be used here: compute the number (W / 2) yourself. \`y\` is the TOP of the line.`);
|
|
4215
4398
|
}
|
|
4216
4399
|
}
|
|
4217
4400
|
if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`animateText(): duration must be a positive number of seconds (how long until the last piece is gone), got ${duration}`);
|
|
4218
|
-
const pieces = splitText(text, style, { by, x, y, align });
|
|
4401
|
+
const pieces = splitText(text, style, { by, x, y, align, ...vertical ? { vertical } : {} });
|
|
4219
4402
|
if (pieces.length === 0) return [];
|
|
4220
4403
|
const lineHeight = measureText("M", style).height;
|
|
4221
4404
|
const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
|
|
@@ -4234,12 +4417,12 @@ function animateText(text, style, options) {
|
|
|
4234
4417
|
if (inTween && outTween.at - at < inTween.duration - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the entrance has finished (it takes ${inTween.duration} s from at ${at}): use out.at \u2265 ${+(at + inTween.duration).toFixed(3)}`);
|
|
4235
4418
|
}
|
|
4236
4419
|
return pieces.map((p, i) => {
|
|
4237
|
-
const cx = p.x + p.width / 2, cy = p.y + lineHeight / 2;
|
|
4238
|
-
const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: 0 };
|
|
4420
|
+
const cx = p.cx ?? p.x + p.width / 2, cy = p.cy ?? p.y + lineHeight / 2;
|
|
4421
|
+
const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: p.rotation ?? 0 };
|
|
4239
4422
|
const start = at + delays[i];
|
|
4240
4423
|
const len = end - start;
|
|
4241
4424
|
const keyframes = [];
|
|
4242
|
-
const place = (key, v) => key === "x" || key === "y" ? rest[key] + v : v;
|
|
4425
|
+
const place = (key, v) => key === "x" || key === "y" || key === "rotation" ? rest[key] + v : v;
|
|
4243
4426
|
if (inTween) {
|
|
4244
4427
|
keyframes.push({
|
|
4245
4428
|
at: 0,
|
|
@@ -4278,7 +4461,7 @@ function animateText(text, style, options) {
|
|
|
4278
4461
|
style: styleFor ? { ...style, ...styleFor(p) } : style,
|
|
4279
4462
|
at: start,
|
|
4280
4463
|
duration: len,
|
|
4281
|
-
initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5 },
|
|
4464
|
+
initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5, ...rest.rotation !== 0 ? { rotation: rest.rotation } : {} },
|
|
4282
4465
|
keyframes
|
|
4283
4466
|
};
|
|
4284
4467
|
});
|
|
@@ -4620,5 +4803,5 @@ function withFade(spec, opts) {
|
|
|
4620
4803
|
}
|
|
4621
4804
|
|
|
4622
4805
|
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, splitText, stagger, wiggle, withFade };
|
|
4623
|
-
//# sourceMappingURL=chunk-
|
|
4624
|
-
//# sourceMappingURL=chunk-
|
|
4806
|
+
//# sourceMappingURL=chunk-VF56PRAA.js.map
|
|
4807
|
+
//# sourceMappingURL=chunk-VF56PRAA.js.map
|