@pixerate/editor 0.253.0 → 0.254.1
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/dist/animation-editor/index.cjs +53 -53
- package/dist/animation-editor/index.js +2 -2
- package/dist/canvas/changeFeed.d.cts +6 -0
- package/dist/canvas/changeFeed.d.ts +6 -0
- package/dist/canvas/index.cjs +16 -9
- package/dist/canvas/index.js +10 -3
- package/dist/{chunk-QKXO6JPP.cjs → chunk-2OQGPEHH.cjs} +14 -14
- package/dist/{chunk-Z3BRH4JK.cjs → chunk-557BDWKO.cjs} +6 -6
- package/dist/{chunk-ZBCT5MEA.cjs → chunk-CBVNCWJM.cjs} +7 -7
- package/dist/{chunk-226QDRO3.js → chunk-DHQPRKXF.js} +1 -1
- package/dist/{chunk-ABL7IWFZ.js → chunk-GBEF2D4P.js} +1 -1
- package/dist/{chunk-ISIO63Q5.cjs → chunk-L2JTLW6B.cjs} +150 -25
- package/dist/{chunk-JA57JIT5.js → chunk-MV7CYFMJ.js} +133 -8
- package/dist/{chunk-5E2Q7ZHI.js → chunk-MWUZCEFT.js} +1 -1
- package/dist/effects/conformance.d.cts +1 -0
- package/dist/effects/conformance.d.ts +1 -0
- package/dist/effects/fake.d.cts +7 -1
- package/dist/effects/fake.d.ts +7 -1
- package/dist/effects/helpers.d.cts +36 -4
- package/dist/effects/helpers.d.ts +36 -4
- package/dist/effects/index.cjs +331 -102
- package/dist/effects/index.js +238 -9
- package/dist/effects/types.d.cts +84 -5
- package/dist/effects/types.d.ts +84 -5
- package/dist/image-editor/index.cjs +15 -15
- package/dist/image-editor/index.js +1 -1
- package/dist/paint-editor/index.cjs +7 -7
- package/dist/paint-editor/index.js +2 -2
- package/dist/vector/index.cjs +6 -6
- package/dist/vector/index.js +1 -1
- package/dist/video-editor/index.cjs +344 -344
- package/dist/video-editor/index.js +4 -4
- package/package.json +1 -1
|
@@ -3,11 +3,15 @@ import {
|
|
|
3
3
|
} from "./chunk-V2ZCJRDM.js";
|
|
4
4
|
|
|
5
5
|
// src/effects/types.ts
|
|
6
|
-
var EFFECT_KINDS = ["shader", "vector", "motion", "camera"];
|
|
7
|
-
var EFFECT_USES = ["filter", "transition", "generator", "overlay", "title", "motion", "interactive", "camera"];
|
|
6
|
+
var EFFECT_KINDS = ["shader", "vector", "motion", "camera", "timing"];
|
|
7
|
+
var EFFECT_USES = ["filter", "transition", "generator", "overlay", "title", "motion", "interactive", "camera", "timing"];
|
|
8
8
|
var CAMERA_MOVE_ROLES = ["transition", "hold"];
|
|
9
9
|
var EFFECT_ACCEPTS = ["path"];
|
|
10
10
|
var EFFECT_PATH_FORMAT = "svg-path";
|
|
11
|
+
var TIMING_DEFAULT_FPS = 30;
|
|
12
|
+
var TIMING_MAX_FRAMES = 128;
|
|
13
|
+
var TIMING_MAX_FPS = 240;
|
|
14
|
+
var TIMING_BLENDS = ["average", "lighten"];
|
|
11
15
|
var EFFECT_SUBJECT_KINDS = ["image", "video", "svg", "text", "slot"];
|
|
12
16
|
var EFFECT_LAYER_ROLES = ["background", "subject", "overlay"];
|
|
13
17
|
var EFFECT_TRANSITION_MS = 800;
|
|
@@ -87,10 +91,11 @@ function resolveEffectValues(description, values) {
|
|
|
87
91
|
if (errors.length) throw new EffectControlError(description.id, errors);
|
|
88
92
|
return { ...description.defaults, ...values ?? {} };
|
|
89
93
|
}
|
|
94
|
+
var PIXELLESS_KINDS = ["motion", "camera", "timing"];
|
|
90
95
|
function effectOffered(e, uses) {
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
if (
|
|
96
|
+
const pixelless = PIXELLESS_KINDS.includes(e.kind);
|
|
97
|
+
if (!uses) return !pixelless;
|
|
98
|
+
if (pixelless && !uses.includes(e.kind)) return false;
|
|
94
99
|
return e.uses.some((u) => uses.includes(u));
|
|
95
100
|
}
|
|
96
101
|
function normaliseEffectTag(tag) {
|
|
@@ -299,6 +304,117 @@ function cameraBoxOfView(view, stage) {
|
|
|
299
304
|
const height = stage.height / view.zoom;
|
|
300
305
|
return { x: -view.scrollX, y: -view.scrollY, width, height };
|
|
301
306
|
}
|
|
307
|
+
function checkTimingFootage(footageMs, id) {
|
|
308
|
+
if (typeof footageMs !== "number" || !Number.isFinite(footageMs) || footageMs <= 0) {
|
|
309
|
+
throw new EffectUnsupportedError(`The footage's length is a number of ms above 0; it was ${String(footageMs)}.`, id);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function checkTimingRequest(request, id) {
|
|
313
|
+
if (!request || typeof request !== "object") throw new EffectUnsupportedError("A timing request is { time, footage, fps? }: ms into the retimed clip, the footage's length in ms, and its frame rate.", id);
|
|
314
|
+
checkTimingFootage(request.footage, id);
|
|
315
|
+
const fps = request.fps ?? TIMING_DEFAULT_FPS;
|
|
316
|
+
if (typeof fps !== "number" || !Number.isFinite(fps) || fps <= 0 || fps > TIMING_MAX_FPS) {
|
|
317
|
+
throw new EffectUnsupportedError(`The footage's frame rate is above 0 and at most ${TIMING_MAX_FPS} frames per second; it was ${String(fps)}.`, id);
|
|
318
|
+
}
|
|
319
|
+
if (typeof request.time !== "number" || !Number.isFinite(request.time)) throw new EffectUnsupportedError(`The time is a number of ms into the retimed clip; it was ${String(request.time)}.`, id);
|
|
320
|
+
return fps;
|
|
321
|
+
}
|
|
322
|
+
var lastFrameOf = (footageMs, fps) => Math.max(0, Math.ceil(footageMs * fps / 1e3 - 1e-9) - 1);
|
|
323
|
+
function normaliseTimingSample(raw, request, blend = "average") {
|
|
324
|
+
const fps = request.fps ?? TIMING_DEFAULT_FPS;
|
|
325
|
+
const frameMs = 1e3 / fps;
|
|
326
|
+
const last = lastFrameOf(request.footage, fps);
|
|
327
|
+
const frameOf = (t) => Math.min(last, Math.max(0, Math.floor(t / frameMs + 1e-6)));
|
|
328
|
+
const weights = /* @__PURE__ */ new Map();
|
|
329
|
+
for (const f of raw) {
|
|
330
|
+
if (!Number.isFinite(f.time) || !Number.isFinite(f.weight) || f.weight <= 0) continue;
|
|
331
|
+
const i = frameOf(f.time);
|
|
332
|
+
weights.set(i, (weights.get(i) ?? 0) + f.weight);
|
|
333
|
+
}
|
|
334
|
+
if (!weights.size) weights.set(Number.isFinite(request.time) ? frameOf(request.time) : 0, 1);
|
|
335
|
+
let frames = [...weights.entries()].sort((a, b) => a[0] - b[0]);
|
|
336
|
+
if (frames.length > TIMING_MAX_FRAMES) {
|
|
337
|
+
const stride = frames.length / TIMING_MAX_FRAMES;
|
|
338
|
+
frames = Array.from({ length: TIMING_MAX_FRAMES }, (_, k) => frames[Math.floor(k * stride)]);
|
|
339
|
+
}
|
|
340
|
+
const total = frames.reduce((sum, [, w]) => sum + w, 0);
|
|
341
|
+
return { frames: frames.map(([i, w]) => ({ time: i * 1e3 / fps, weight: w / total })), blend };
|
|
342
|
+
}
|
|
343
|
+
function timingSampleProblems(sample, request) {
|
|
344
|
+
if (!sample || typeof sample !== "object") return ["the sample is not an object { frames, blend }"];
|
|
345
|
+
const { frames, blend } = sample;
|
|
346
|
+
const problems = [];
|
|
347
|
+
if (!TIMING_BLENDS.includes(blend)) problems.push(`blend "${String(blend)}" is not one of ${TIMING_BLENDS.join(", ")}`);
|
|
348
|
+
if (!Array.isArray(frames) || !frames.length) return [...problems, "frames must be a list of at least one { time, weight }"];
|
|
349
|
+
if (frames.length > TIMING_MAX_FRAMES) problems.push(`it has ${frames.length} frames; at most ${TIMING_MAX_FRAMES} are allowed`);
|
|
350
|
+
const fps = request.fps ?? TIMING_DEFAULT_FPS;
|
|
351
|
+
const last = lastFrameOf(request.footage, fps);
|
|
352
|
+
let total = 0;
|
|
353
|
+
let previous = -Infinity;
|
|
354
|
+
for (const f of frames) {
|
|
355
|
+
if (!f || !Number.isFinite(f.time) || !Number.isFinite(f.weight)) {
|
|
356
|
+
problems.push(`frame ${JSON.stringify(f)} needs a finite time and weight`);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const index = f.time * fps / 1e3;
|
|
360
|
+
if (Math.abs(index - Math.round(index)) > 1e-6) problems.push(`frame time ${f.time} is not the start of a frame at ${fps} fps (index \xD7 ${+(1e3 / fps).toFixed(6)} ms)`);
|
|
361
|
+
else if (Math.round(index) < 0 || Math.round(index) > last) problems.push(`frame time ${f.time} is outside the footage (frames 0 to ${last}, the last at ${+(last * 1e3 / fps).toFixed(6)} ms)`);
|
|
362
|
+
if (f.time <= previous) problems.push(`frame times must be distinct and earliest first; ${f.time} follows ${previous}`);
|
|
363
|
+
previous = f.time;
|
|
364
|
+
if (!(f.weight > 0)) problems.push(`frame at ${f.time} has weight ${f.weight}; weights are > 0`);
|
|
365
|
+
total += f.weight;
|
|
366
|
+
}
|
|
367
|
+
if (Math.abs(total - 1) > 1e-6) problems.push(`weights add up to ${total}, not 1`);
|
|
368
|
+
return problems;
|
|
369
|
+
}
|
|
370
|
+
function blendTimingFrames(frames, blend, out) {
|
|
371
|
+
if (!frames.length) throw new EffectUnsupportedError("There are no frames to blend; a timing sample has at least one.");
|
|
372
|
+
const { width, height } = frames[0].pixels;
|
|
373
|
+
for (const f of frames) {
|
|
374
|
+
if (f.pixels.width !== width || f.pixels.height !== height) throw new EffectUnsupportedError(`A ${f.pixels.width}\xD7${f.pixels.height} frame cannot be blended with ${width}\xD7${height} frames.`);
|
|
375
|
+
}
|
|
376
|
+
const target = out ?? createEffectPixels(width, height);
|
|
377
|
+
if (target.width !== width || target.height !== height) throw new EffectUnsupportedError(`The output is ${target.width}\xD7${target.height}; the frames are ${width}\xD7${height}.`);
|
|
378
|
+
const o = target.data;
|
|
379
|
+
if (frames.length === 1) {
|
|
380
|
+
o.set(frames[0].pixels.data);
|
|
381
|
+
return target;
|
|
382
|
+
}
|
|
383
|
+
const s = new Float64Array(o.length);
|
|
384
|
+
if (blend === "lighten") {
|
|
385
|
+
const heaviest = Math.max(...frames.map((f) => f.weight));
|
|
386
|
+
for (const f of frames) {
|
|
387
|
+
const k = heaviest > 0 ? f.weight / heaviest : 1;
|
|
388
|
+
const p = f.pixels.data;
|
|
389
|
+
for (let i = 0; i < s.length; i += 4) {
|
|
390
|
+
const a = p[i + 3] / 255 * k;
|
|
391
|
+
for (let c = 0; c < 3; c++) if (p[i + c] * a > s[i + c]) s[i + c] = p[i + c] * a;
|
|
392
|
+
if (a > s[i + 3]) s[i + 3] = a;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (let i = 0; i < s.length; i += 4) {
|
|
396
|
+
for (let c = 0; c < 3; c++) o[i + c] = Math.round(s[i + c]);
|
|
397
|
+
o[i + 3] = Math.round(s[i + 3] * 255);
|
|
398
|
+
}
|
|
399
|
+
return target;
|
|
400
|
+
}
|
|
401
|
+
let total = 0;
|
|
402
|
+
for (const f of frames) {
|
|
403
|
+
total += f.weight;
|
|
404
|
+
const p = f.pixels.data;
|
|
405
|
+
for (let i = 0; i < s.length; i += 4) {
|
|
406
|
+
const a = p[i + 3] * f.weight;
|
|
407
|
+
for (let c = 0; c < 3; c++) s[i + c] += p[i + c] * a;
|
|
408
|
+
s[i + 3] += a;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
for (let i = 0; i < s.length; i += 4) {
|
|
412
|
+
const a = s[i + 3];
|
|
413
|
+
for (let c = 0; c < 3; c++) o[i + c] = a > 0 ? Math.round(s[i + c] / a) : 0;
|
|
414
|
+
o[i + 3] = total > 0 ? Math.round(a / total) : 0;
|
|
415
|
+
}
|
|
416
|
+
return target;
|
|
417
|
+
}
|
|
302
418
|
|
|
303
419
|
// src/effects/tools.ts
|
|
304
420
|
var ANNOTATIONS = {
|
|
@@ -392,12 +508,12 @@ function createEffectToolProvider(host) {
|
|
|
392
508
|
{
|
|
393
509
|
name: "search_effects",
|
|
394
510
|
title: "Search effects",
|
|
395
|
-
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion, camera), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one), and accepts ["path"] for effects that draw along a path you give. For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms), camera: { role, durationMs, cut } for camera moves, accepts }], total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
511
|
+
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion, camera, timing), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one), and accepts ["path"] for effects that draw along a path you give. For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms), camera: { role, durationMs, cut } for camera moves, accepts }] (timing effects, kind timing, retime video footage and have no duration: their length depends on the footage), total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
396
512
|
inputSchema: object({
|
|
397
513
|
query: { type: "string", description: "Words to match (default: every effect)." },
|
|
398
|
-
kind: { type: "string", enum: [...EFFECT_KINDS], description: "shader (pixels), vector (animated SVG), motion (animates vector art)
|
|
514
|
+
kind: { type: "string", enum: [...EFFECT_KINDS], description: "shader (pixels), vector (animated SVG), motion (animates vector art), camera (moves a scene's camera; no pixels) or timing (retimes video footage: slow motion, reverse, freeze, long exposure; no pixels of its own)." },
|
|
399
515
|
category: { type: "string", minLength: 1, description: `The provider's category, e.g. "gradient", "light", "transition".` },
|
|
400
|
-
use: { type: "string", enum: [...EFFECT_USES], description: "What the effect is for: filter changes a picture, transition blends two, generator draws a background, overlay draws over content, title draws a title, motion animates vector art, interactive reacts to input, camera moves a scene's camera." },
|
|
516
|
+
use: { type: "string", enum: [...EFFECT_USES], description: "What the effect is for: filter changes a picture, transition blends two, generator draws a background, overlay draws over content, title draws a title, motion animates vector art, interactive reacts to input, camera moves a scene's camera, timing retimes video footage." },
|
|
401
517
|
tags: { type: "array", items: { type: "string", minLength: 1 }, description: "Only effects carrying every one of these tags, as search_effects lists them (any case; spaces count as hyphens)." },
|
|
402
518
|
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." },
|
|
403
519
|
accepts: { type: "array", items: { type: "string", enum: [...EFFECT_ACCEPTS], description: "path: the effect has a control that draws along a path given at run time." }, description: "Only effects that take every one of these (path: effects that draw along a path you give; describe_effect names the controls in pathControls)." }
|
|
@@ -557,6 +673,10 @@ export {
|
|
|
557
673
|
CAMERA_MOVE_ROLES,
|
|
558
674
|
EFFECT_ACCEPTS,
|
|
559
675
|
EFFECT_PATH_FORMAT,
|
|
676
|
+
TIMING_DEFAULT_FPS,
|
|
677
|
+
TIMING_MAX_FRAMES,
|
|
678
|
+
TIMING_MAX_FPS,
|
|
679
|
+
TIMING_BLENDS,
|
|
560
680
|
EFFECT_SUBJECT_KINDS,
|
|
561
681
|
EFFECT_LAYER_ROLES,
|
|
562
682
|
EFFECT_TRANSITION_MS,
|
|
@@ -597,5 +717,10 @@ export {
|
|
|
597
717
|
resolveCameraMoveSpec,
|
|
598
718
|
cameraViewOfBox,
|
|
599
719
|
cameraBoxOfView,
|
|
720
|
+
checkTimingFootage,
|
|
721
|
+
checkTimingRequest,
|
|
722
|
+
normaliseTimingSample,
|
|
723
|
+
timingSampleProblems,
|
|
724
|
+
blendTimingFrames,
|
|
600
725
|
createEffectToolProvider
|
|
601
726
|
};
|
|
@@ -22,4 +22,5 @@ export interface EffectConformanceOptions {
|
|
|
22
22
|
/** Also check sequence templates when supportsTemplates is true (default true). */
|
|
23
23
|
templates?: boolean;
|
|
24
24
|
}
|
|
25
|
+
/** Runs the contract checks against `provider` and returns every issue found (empty when it conforms). */
|
|
25
26
|
export declare function checkEffectProvider(provider: EffectProvider, options?: EffectConformanceOptions): Promise<EffectConformanceIssue[]>;
|
|
@@ -22,4 +22,5 @@ export interface EffectConformanceOptions {
|
|
|
22
22
|
/** Also check sequence templates when supportsTemplates is true (default true). */
|
|
23
23
|
templates?: boolean;
|
|
24
24
|
}
|
|
25
|
+
/** Runs the contract checks against `provider` and returns every issue found (empty when it conforms). */
|
|
25
26
|
export declare function checkEffectProvider(provider: EffectProvider, options?: EffectConformanceOptions): Promise<EffectConformanceIssue[]>;
|
package/dist/effects/fake.d.cts
CHANGED
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
* With `camera: true` it also has camera moves (no pixels): `glide` and `whip` (transitions; the
|
|
18
18
|
* whip cuts halfway) and `drift` (a hold that zooms in slowly).
|
|
19
19
|
*
|
|
20
|
+
* With `timing: true` it also has timing effects (no pixels; they retime video footage): `speed`
|
|
21
|
+
* (slow or fast, blending frames), `freeze` (holds one frame, then plays on) and `echo` (fading
|
|
22
|
+
* copies of the frames before, averaged or lightened).
|
|
23
|
+
*
|
|
20
24
|
* With `templates: true` it also has two sequence templates (see `fake-templates.ts`):
|
|
21
25
|
* `fake.title-sequence` and `fake.picture-reveal`.
|
|
22
26
|
*/
|
|
@@ -30,8 +34,10 @@ export interface FakeEffectProviderOptions {
|
|
|
30
34
|
authoring?: boolean;
|
|
31
35
|
/** Adds the camera moves and `cameraView` / `sampleCameraMove` (default false). */
|
|
32
36
|
camera?: boolean;
|
|
37
|
+
/** Adds the timing effects and `timingLength` / `timingSample` (default false). */
|
|
38
|
+
timing?: boolean;
|
|
33
39
|
/** Adds two sequence templates and `listTemplates` / `describeTemplate` / `instantiateTemplate` (default false). */
|
|
34
40
|
templates?: boolean;
|
|
35
41
|
}
|
|
36
|
-
/** A deterministic CPU effects provider with
|
|
42
|
+
/** A deterministic CPU effects provider with nine effects, and camera moves, timing effects and templates on request (see the module comment). */
|
|
37
43
|
export declare function createFakeEffectProvider(options?: FakeEffectProviderOptions): EffectProvider;
|
package/dist/effects/fake.d.ts
CHANGED
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
* With `camera: true` it also has camera moves (no pixels): `glide` and `whip` (transitions; the
|
|
18
18
|
* whip cuts halfway) and `drift` (a hold that zooms in slowly).
|
|
19
19
|
*
|
|
20
|
+
* With `timing: true` it also has timing effects (no pixels; they retime video footage): `speed`
|
|
21
|
+
* (slow or fast, blending frames), `freeze` (holds one frame, then plays on) and `echo` (fading
|
|
22
|
+
* copies of the frames before, averaged or lightened).
|
|
23
|
+
*
|
|
20
24
|
* With `templates: true` it also has two sequence templates (see `fake-templates.ts`):
|
|
21
25
|
* `fake.title-sequence` and `fake.picture-reveal`.
|
|
22
26
|
*/
|
|
@@ -30,8 +34,10 @@ export interface FakeEffectProviderOptions {
|
|
|
30
34
|
authoring?: boolean;
|
|
31
35
|
/** Adds the camera moves and `cameraView` / `sampleCameraMove` (default false). */
|
|
32
36
|
camera?: boolean;
|
|
37
|
+
/** Adds the timing effects and `timingLength` / `timingSample` (default false). */
|
|
38
|
+
timing?: boolean;
|
|
33
39
|
/** Adds two sequence templates and `listTemplates` / `describeTemplate` / `instantiateTemplate` (default false). */
|
|
34
40
|
templates?: boolean;
|
|
35
41
|
}
|
|
36
|
-
/** A deterministic CPU effects provider with
|
|
42
|
+
/** A deterministic CPU effects provider with nine effects, and camera moves, timing effects and templates on request (see the module comment). */
|
|
37
43
|
export declare function createFakeEffectProvider(options?: FakeEffectProviderOptions): EffectProvider;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* and a provider slot editors hold.
|
|
4
4
|
*/
|
|
5
5
|
import { type SchemaValidationError } from '../tools/schema.cjs';
|
|
6
|
-
import type { CameraMoveRole, CameraMoveSpec, CameraMoveSample, CameraMoveSampleRequest, CameraStage, CameraView, EffectDescription, EffectImageSource, EffectPixels, EffectProvider, EffectSearchOptions, EffectSummary, EffectUse, EffectValues } from './types.cjs';
|
|
6
|
+
import type { CameraMoveRole, CameraMoveSpec, CameraMoveSample, CameraMoveSampleRequest, CameraStage, CameraView, EffectDescription, EffectImageSource, EffectPixels, EffectProvider, EffectSearchOptions, EffectSummary, EffectUse, EffectValues, TimingBlend, TimingFrame, TimingSample, TimingSampleRequest } from './types.cjs';
|
|
7
7
|
/** Problems with `values` against an effect's control schema (unknown controls, types, ranges). Partial values are fine. */
|
|
8
8
|
export declare function effectValueErrors(description: Pick<EffectDescription, 'controls'>, values: unknown): SchemaValidationError[];
|
|
9
9
|
/**
|
|
@@ -13,9 +13,9 @@ export declare function effectValueErrors(description: Pick<EffectDescription, '
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function resolveEffectValues(description: Pick<EffectDescription, 'id' | 'controls' | 'defaults'>, values?: EffectValues): Record<string, EffectValues[string]>;
|
|
15
15
|
/**
|
|
16
|
-
* Whether an editor offering `uses` should offer `e` (in a picker or an `effect` enum). Motion
|
|
17
|
-
* and
|
|
18
|
-
* them (`motion`, `camera`); with no `uses`, every other effect is.
|
|
16
|
+
* Whether an editor offering `uses` should offer `e` (in a picker or an `effect` enum). Motion,
|
|
17
|
+
* camera and timing effects draw no picture of their own, so they are offered only where `uses`
|
|
18
|
+
* names them (`motion`, `camera`, `timing`); with no `uses`, every other effect is.
|
|
19
19
|
*/
|
|
20
20
|
export declare function effectOffered(e: Pick<EffectSummary, 'kind' | 'uses'>, uses?: readonly EffectUse[]): boolean;
|
|
21
21
|
/** A tag as tags are compared: trimmed, lower case, spaces and underscores as single hyphens (`"Game Boy"` → `game-boy`). */
|
|
@@ -128,3 +128,35 @@ export interface CameraBox {
|
|
|
128
128
|
export declare function cameraViewOfBox(box: CameraBox, stage: CameraStage): CameraView;
|
|
129
129
|
/** The box of the scene a view shows on `stage` (its tilt, turn, roll and blur aside). */
|
|
130
130
|
export declare function cameraBoxOfView(view: CameraView, stage: CameraStage): CameraBox;
|
|
131
|
+
/** Throws `EffectUnsupportedError` unless `footageMs` is a footage length a timing effect takes (a number of ms > 0). */
|
|
132
|
+
export declare function checkTimingFootage(footageMs: number, id?: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Checks a timing request (footage, time and fps), throwing `EffectUnsupportedError` with a
|
|
135
|
+
* sentence naming the valid range, and gives its fps (default `TIMING_DEFAULT_FPS`).
|
|
136
|
+
*/
|
|
137
|
+
export declare function checkTimingRequest(request: TimingSampleRequest, id?: string): number;
|
|
138
|
+
/**
|
|
139
|
+
* Raw footage frames (any times, any positive weights) as a `TimingSample`: each time held within
|
|
140
|
+
* the footage and moved to the start of its frame, frames on the same footage frame merged, at
|
|
141
|
+
* most `TIMING_MAX_FRAMES` (evenly spaced), weights scaled to add up to 1, earliest first. Frames
|
|
142
|
+
* with a time or weight that is not a finite number, or a weight ≤ 0, are dropped; with none left,
|
|
143
|
+
* the sample is the frame at `request.time`. Providers can build `timingSample` on it.
|
|
144
|
+
*/
|
|
145
|
+
export declare function normaliseTimingSample(raw: readonly TimingFrame[], request: TimingSampleRequest, blend?: TimingBlend): TimingSample;
|
|
146
|
+
/**
|
|
147
|
+
* What is wrong with `sample` as the answer to `request` (see `TimingSample`), as sentences; empty
|
|
148
|
+
* when it keeps every rule. `checkEffectProvider` uses it; hosts can check a provider's samples
|
|
149
|
+
* with it.
|
|
150
|
+
*/
|
|
151
|
+
export declare function timingSampleProblems(sample: unknown, request: TimingSampleRequest): string[];
|
|
152
|
+
/**
|
|
153
|
+
* Blends footage frames by weight as a timing sample says (see `TimingBlend`): the reference for
|
|
154
|
+
* hosts that blend in their own pipeline, and a ready blend for `EffectPixels`. Reads and writes
|
|
155
|
+
* 8-bit `data` (straight alpha, rounded); `float` is not used. Writes into `out` when given (the
|
|
156
|
+
* frames' size), else a new picture. Throws `EffectUnsupportedError` for no frames or frames of
|
|
157
|
+
* different sizes.
|
|
158
|
+
*/
|
|
159
|
+
export declare function blendTimingFrames(frames: readonly {
|
|
160
|
+
pixels: EffectPixels;
|
|
161
|
+
weight: number;
|
|
162
|
+
}[], blend: TimingBlend, out?: EffectPixels): EffectPixels;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* and a provider slot editors hold.
|
|
4
4
|
*/
|
|
5
5
|
import { type SchemaValidationError } from '../tools/schema.js';
|
|
6
|
-
import type { CameraMoveRole, CameraMoveSpec, CameraMoveSample, CameraMoveSampleRequest, CameraStage, CameraView, EffectDescription, EffectImageSource, EffectPixels, EffectProvider, EffectSearchOptions, EffectSummary, EffectUse, EffectValues } from './types.js';
|
|
6
|
+
import type { CameraMoveRole, CameraMoveSpec, CameraMoveSample, CameraMoveSampleRequest, CameraStage, CameraView, EffectDescription, EffectImageSource, EffectPixels, EffectProvider, EffectSearchOptions, EffectSummary, EffectUse, EffectValues, TimingBlend, TimingFrame, TimingSample, TimingSampleRequest } from './types.js';
|
|
7
7
|
/** Problems with `values` against an effect's control schema (unknown controls, types, ranges). Partial values are fine. */
|
|
8
8
|
export declare function effectValueErrors(description: Pick<EffectDescription, 'controls'>, values: unknown): SchemaValidationError[];
|
|
9
9
|
/**
|
|
@@ -13,9 +13,9 @@ export declare function effectValueErrors(description: Pick<EffectDescription, '
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function resolveEffectValues(description: Pick<EffectDescription, 'id' | 'controls' | 'defaults'>, values?: EffectValues): Record<string, EffectValues[string]>;
|
|
15
15
|
/**
|
|
16
|
-
* Whether an editor offering `uses` should offer `e` (in a picker or an `effect` enum). Motion
|
|
17
|
-
* and
|
|
18
|
-
* them (`motion`, `camera`); with no `uses`, every other effect is.
|
|
16
|
+
* Whether an editor offering `uses` should offer `e` (in a picker or an `effect` enum). Motion,
|
|
17
|
+
* camera and timing effects draw no picture of their own, so they are offered only where `uses`
|
|
18
|
+
* names them (`motion`, `camera`, `timing`); with no `uses`, every other effect is.
|
|
19
19
|
*/
|
|
20
20
|
export declare function effectOffered(e: Pick<EffectSummary, 'kind' | 'uses'>, uses?: readonly EffectUse[]): boolean;
|
|
21
21
|
/** A tag as tags are compared: trimmed, lower case, spaces and underscores as single hyphens (`"Game Boy"` → `game-boy`). */
|
|
@@ -128,3 +128,35 @@ export interface CameraBox {
|
|
|
128
128
|
export declare function cameraViewOfBox(box: CameraBox, stage: CameraStage): CameraView;
|
|
129
129
|
/** The box of the scene a view shows on `stage` (its tilt, turn, roll and blur aside). */
|
|
130
130
|
export declare function cameraBoxOfView(view: CameraView, stage: CameraStage): CameraBox;
|
|
131
|
+
/** Throws `EffectUnsupportedError` unless `footageMs` is a footage length a timing effect takes (a number of ms > 0). */
|
|
132
|
+
export declare function checkTimingFootage(footageMs: number, id?: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Checks a timing request (footage, time and fps), throwing `EffectUnsupportedError` with a
|
|
135
|
+
* sentence naming the valid range, and gives its fps (default `TIMING_DEFAULT_FPS`).
|
|
136
|
+
*/
|
|
137
|
+
export declare function checkTimingRequest(request: TimingSampleRequest, id?: string): number;
|
|
138
|
+
/**
|
|
139
|
+
* Raw footage frames (any times, any positive weights) as a `TimingSample`: each time held within
|
|
140
|
+
* the footage and moved to the start of its frame, frames on the same footage frame merged, at
|
|
141
|
+
* most `TIMING_MAX_FRAMES` (evenly spaced), weights scaled to add up to 1, earliest first. Frames
|
|
142
|
+
* with a time or weight that is not a finite number, or a weight ≤ 0, are dropped; with none left,
|
|
143
|
+
* the sample is the frame at `request.time`. Providers can build `timingSample` on it.
|
|
144
|
+
*/
|
|
145
|
+
export declare function normaliseTimingSample(raw: readonly TimingFrame[], request: TimingSampleRequest, blend?: TimingBlend): TimingSample;
|
|
146
|
+
/**
|
|
147
|
+
* What is wrong with `sample` as the answer to `request` (see `TimingSample`), as sentences; empty
|
|
148
|
+
* when it keeps every rule. `checkEffectProvider` uses it; hosts can check a provider's samples
|
|
149
|
+
* with it.
|
|
150
|
+
*/
|
|
151
|
+
export declare function timingSampleProblems(sample: unknown, request: TimingSampleRequest): string[];
|
|
152
|
+
/**
|
|
153
|
+
* Blends footage frames by weight as a timing sample says (see `TimingBlend`): the reference for
|
|
154
|
+
* hosts that blend in their own pipeline, and a ready blend for `EffectPixels`. Reads and writes
|
|
155
|
+
* 8-bit `data` (straight alpha, rounded); `float` is not used. Writes into `out` when given (the
|
|
156
|
+
* frames' size), else a new picture. Throws `EffectUnsupportedError` for no frames or frames of
|
|
157
|
+
* different sizes.
|
|
158
|
+
*/
|
|
159
|
+
export declare function blendTimingFrames(frames: readonly {
|
|
160
|
+
pixels: EffectPixels;
|
|
161
|
+
weight: number;
|
|
162
|
+
}[], blend: TimingBlend, out?: EffectPixels): EffectPixels;
|