@pixerate/editor 0.240.0 → 0.242.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/dist/animation-editor/controller.d.cts +10 -0
- package/dist/animation-editor/controller.d.ts +10 -0
- package/dist/animation-editor/effects.d.cts +1 -1
- package/dist/animation-editor/effects.d.ts +1 -1
- package/dist/animation-editor/index.cjs +195 -146
- package/dist/animation-editor/index.js +63 -14
- package/dist/{brushstrokeRender-WB67GMUP.js → brushstrokeRender-MOYGEXOC.js} +2 -2
- package/dist/{brushstrokeRender-MPOM7BFW.cjs → brushstrokeRender-UKADOV5D.cjs} +5 -5
- package/dist/canvas/index.cjs +15 -15
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-NKTNLJUJ.cjs → chunk-5BR7LC24.cjs} +99 -11
- package/dist/{chunk-ZXNO3ZOX.cjs → chunk-7F4G74UQ.cjs} +35 -35
- package/dist/{chunk-A7PQNIBX.cjs → chunk-7V7QMFTI.cjs} +14 -14
- package/dist/{chunk-AD7GA3TL.cjs → chunk-AFLMXITL.cjs} +23 -23
- package/dist/{chunk-JAOSL334.cjs → chunk-GHCSC2WP.cjs} +38 -16
- package/dist/{chunk-PP6BXVRL.cjs → chunk-HGGJ36HR.cjs} +1 -1
- package/dist/{chunk-BBUE3UJL.cjs → chunk-HW72ABGR.cjs} +86 -86
- package/dist/{chunk-7YGBSFC6.js → chunk-ILM6GYAX.js} +1 -1
- package/dist/{chunk-JWCKJNDR.cjs → chunk-K5DS2JTA.cjs} +6 -6
- package/dist/{chunk-THXFU3JB.cjs → chunk-KCMV2534.cjs} +2 -2
- package/dist/{chunk-WDDUPWVG.js → chunk-L3JFOTBQ.js} +1 -1
- package/dist/{chunk-3E36VDMJ.js → chunk-NE6OOKWJ.js} +1 -1
- package/dist/{chunk-SIK3LT6M.js → chunk-OFH6Q67U.js} +28 -6
- package/dist/{chunk-RV5DKZHU.js → chunk-QP2B3MID.js} +1 -1
- package/dist/{chunk-G3RUKJ5C.js → chunk-RXZL4NG5.js} +1 -1
- package/dist/{chunk-UUBO6RB5.js → chunk-UDBDYZET.js} +1 -1
- package/dist/{chunk-TEFQISBY.js → chunk-X3ZINLIP.js} +1 -1
- package/dist/{chunk-PJYG6FCF.js → chunk-XBZK47LV.js} +98 -10
- package/dist/{chunk-ZQIWBJGH.cjs → chunk-XNZFVYC2.cjs} +7 -7
- package/dist/{chunk-XK4SST43.js → chunk-ZDPLZDTT.js} +1 -1
- package/dist/effects/fake.d.cts +4 -0
- package/dist/effects/fake.d.ts +4 -0
- package/dist/effects/helpers.d.cts +6 -0
- package/dist/effects/helpers.d.ts +6 -0
- package/dist/effects/index.cjs +222 -94
- package/dist/effects/index.js +131 -3
- package/dist/effects/types.d.cts +31 -0
- package/dist/effects/types.d.ts +31 -0
- package/dist/image-editor/index.cjs +788 -788
- package/dist/image-editor/index.js +5 -5
- package/dist/paint-editor/controller.d.cts +67 -0
- package/dist/paint-editor/controller.d.ts +67 -0
- package/dist/paint-editor/effect-paint.d.cts +60 -3
- package/dist/paint-editor/effect-paint.d.ts +60 -3
- package/dist/paint-editor/index.cjs +682 -436
- package/dist/paint-editor/index.js +284 -38
- package/dist/paint-editor/stickers.d.cts +6 -0
- package/dist/paint-editor/stickers.d.ts +6 -0
- package/dist/paint-editor/thick.d.cts +4 -0
- package/dist/paint-editor/thick.d.ts +4 -0
- package/dist/paint-editor/wet.d.cts +8 -0
- package/dist/paint-editor/wet.d.ts +8 -0
- package/dist/vector/index.cjs +968 -968
- package/dist/vector/index.js +5 -5
- package/dist/video-editor/controller.d.cts +49 -1
- package/dist/video-editor/controller.d.ts +49 -1
- package/dist/video-editor/effect-path.d.cts +66 -0
- package/dist/video-editor/effect-path.d.ts +66 -0
- package/dist/video-editor/index.cjs +616 -427
- package/dist/video-editor/index.d.cts +1 -0
- package/dist/video-editor/index.d.ts +1 -0
- package/dist/video-editor/index.js +193 -4
- package/package.json +1 -1
- package/skills/pixerate-animation-editor/SKILL.md +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +17 -21
- package/skills/pixerate-paint-editor/references/effect-paint.md +25 -0
- package/skills/pixerate-video-editor/SKILL.md +9 -2
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunkGHCSC2WPcjs = require('./chunk-GHCSC2WP.cjs');
|
|
8
8
|
|
|
9
9
|
// src/effects/templates.ts
|
|
10
10
|
var SLOT_NAME = /^[A-Za-z][\w-]*$/;
|
|
@@ -133,7 +133,7 @@ function searchTemplateSummaries(catalog, options = {}) {
|
|
|
133
133
|
const words = (_nullishCoalesce(options.query, () => ( ""))).toLowerCase().split(/\s+/).filter(Boolean);
|
|
134
134
|
const scored = [];
|
|
135
135
|
for (const t of catalog) {
|
|
136
|
-
if (!
|
|
136
|
+
if (!_chunkGHCSC2WPcjs.hasEffectTags.call(void 0, t, options.tags)) continue;
|
|
137
137
|
let score = 0;
|
|
138
138
|
const name = t.name.toLowerCase();
|
|
139
139
|
const rest = [t.id, ..._nullishCoalesce(t.tags, () => ( [])), t.description].join(" ").toLowerCase();
|
|
@@ -200,7 +200,7 @@ function checkEffectSequence(sequence, catalog) {
|
|
|
200
200
|
const p = effectSubjectProblem(layer.subject);
|
|
201
201
|
if (p) problems.push(`${where}: ${p}.`);
|
|
202
202
|
}
|
|
203
|
-
if (layer.role !== void 0 && !
|
|
203
|
+
if (layer.role !== void 0 && !_chunkGHCSC2WPcjs.EFFECT_LAYER_ROLES.includes(layer.role)) problems.push(`${where}: role is one of ${_chunkGHCSC2WPcjs.EFFECT_LAYER_ROLES.join(", ")}.`);
|
|
204
204
|
if (layer.rect !== void 0 && !isRect(layer.rect)) problems.push(`${where}: rect must be { x, y, width, height } with width and height > 0.`);
|
|
205
205
|
for (const k of ["from", "until"]) if (layer[k] !== void 0 && !isLayerTime(layer[k])) problems.push(`${where}: ${k} is ms \u2265 0 or { shots: n } with n \u2265 1.`);
|
|
206
206
|
if (layer.opacity !== void 0 && !(typeof layer.opacity === "number" && layer.opacity >= 0 && layer.opacity <= 1)) problems.push(`${where}: opacity is 0\u20131.`);
|
|
@@ -235,8 +235,8 @@ function effectSequenceSlots(sequence) {
|
|
|
235
235
|
return [...out];
|
|
236
236
|
}
|
|
237
237
|
async function fillEffectTemplate(provider, id, values = {}, options = {}) {
|
|
238
|
-
if (!provider) throw new (0,
|
|
239
|
-
if (!provider.capabilities.supportsTemplates || !provider.describeTemplate || !provider.instantiateTemplate) throw new (0,
|
|
238
|
+
if (!provider) throw new (0, _chunkGHCSC2WPcjs.NoEffectsProviderError)();
|
|
239
|
+
if (!provider.capabilities.supportsTemplates || !provider.describeTemplate || !provider.instantiateTemplate) throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)(`${provider.name} has no sequence templates (capabilities.supportsTemplates is not set).`);
|
|
240
240
|
const template = await provider.describeTemplate(id);
|
|
241
241
|
const errors = checkTemplateValues(template.slots, values);
|
|
242
242
|
if (errors.length) throw new Error(`${errors.join(" ")} describe_template lists what each slot takes.`);
|
|
@@ -256,7 +256,7 @@ function effectSequenceTiming(sequence) {
|
|
|
256
256
|
const leadIns = [];
|
|
257
257
|
let t = 0;
|
|
258
258
|
sequence.shots.forEach((shot, i) => {
|
|
259
|
-
const transition = i > 0 && shot.transition ? _nullishCoalesce(shot.transition.ms, () => (
|
|
259
|
+
const transition = i > 0 && shot.transition ? _nullishCoalesce(shot.transition.ms, () => ( _chunkGHCSC2WPcjs.EFFECT_TRANSITION_MS)) : 0;
|
|
260
260
|
const move = typeof _optionalChain([shot, 'access', _4 => _4.camera, 'optionalAccess', _5 => _5.in]) === "object" && i > 0 ? _nullishCoalesce(shot.camera.in.ms, () => ( 0)) : 0;
|
|
261
261
|
const lead = Math.max(transition, move);
|
|
262
262
|
starts.push(t);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunk5BR7LC24cjs = require('./chunk-5BR7LC24.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var _chunkCVFAAZXEcjs = require('./chunk-CVFAAZXE.cjs');
|
|
@@ -2527,7 +2527,7 @@ function vectorExtrudeFaces(path, params) {
|
|
|
2527
2527
|
// src/vector/brushstroke.ts
|
|
2528
2528
|
var VECTOR_BRUSHSTROKE_SCALE = 2;
|
|
2529
2529
|
function vectorBrushstrokeBrushes() {
|
|
2530
|
-
return
|
|
2530
|
+
return _chunk5BR7LC24cjs.BUILTIN_BRUSHES.filter((b) => _chunkPQ3H2AANcjs.isBrushstrokeBrush.call(void 0, b));
|
|
2531
2531
|
}
|
|
2532
2532
|
function vectorBrushstrokeBrush(id) {
|
|
2533
2533
|
return _nullishCoalesce(vectorBrushstrokeBrushes().find((b) => b.id === id), () => ( null));
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
var EFFECT_KINDS = ["shader", "vector", "motion", "camera"];
|
|
7
7
|
var EFFECT_USES = ["filter", "transition", "generator", "overlay", "title", "motion", "interactive", "camera"];
|
|
8
8
|
var CAMERA_MOVE_ROLES = ["transition", "hold"];
|
|
9
|
+
var EFFECT_ACCEPTS = ["path"];
|
|
10
|
+
var EFFECT_PATH_FORMAT = "svg-path";
|
|
9
11
|
var EFFECT_SUBJECT_KINDS = ["image", "video", "svg", "text", "slot"];
|
|
10
12
|
var EFFECT_LAYER_ROLES = ["background", "subject", "overlay"];
|
|
11
13
|
var EFFECT_TRANSITION_MS = 800;
|
|
@@ -104,7 +106,11 @@ function countEffectTags(effects) {
|
|
|
104
106
|
for (const e of effects) for (const tag of new Set((e.tags ?? []).map(normaliseEffectTag))) if (tag) counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
105
107
|
return [...counts].map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
|
|
106
108
|
}
|
|
107
|
-
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags);
|
|
109
|
+
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags) && (!o.accepts?.length || o.accepts.every((a) => e.accepts?.includes(a)));
|
|
110
|
+
function pathControlsOf(d) {
|
|
111
|
+
const isPath = (s) => !!s && (s.format === EFFECT_PATH_FORMAT || [...s.anyOf ?? [], ...s.oneOf ?? []].some((x) => isPath(x)));
|
|
112
|
+
return Object.entries(d.controls?.properties ?? {}).filter(([, s]) => isPath(s)).map(([name]) => name);
|
|
113
|
+
}
|
|
108
114
|
function searchEffectSummaries(catalog, query = "", options = {}) {
|
|
109
115
|
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
110
116
|
const scored = [];
|
|
@@ -322,7 +328,8 @@ var summaryOut = (e) => ({
|
|
|
322
328
|
transparent: e.transparent,
|
|
323
329
|
loop: e.loop,
|
|
324
330
|
...e.duration !== void 0 ? { duration: e.duration } : {},
|
|
325
|
-
...e.camera ? { camera: e.camera } : {}
|
|
331
|
+
...e.camera ? { camera: e.camera } : {},
|
|
332
|
+
...e.accepts?.length ? { accepts: e.accepts } : {}
|
|
326
333
|
});
|
|
327
334
|
function createEffectToolProvider(host) {
|
|
328
335
|
let catalog = null;
|
|
@@ -380,21 +387,32 @@ function createEffectToolProvider(host) {
|
|
|
380
387
|
{
|
|
381
388
|
name: "search_effects",
|
|
382
389
|
title: "Search effects",
|
|
383
|
-
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). 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 }], 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.`,
|
|
390
|
+
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.`,
|
|
384
391
|
inputSchema: object({
|
|
385
392
|
query: { type: "string", description: "Words to match (default: every effect)." },
|
|
386
393
|
kind: { type: "string", enum: [...EFFECT_KINDS], description: "shader (pixels), vector (animated SVG), motion (animates vector art) or camera (moves a scene's camera; no pixels)." },
|
|
387
394
|
category: { type: "string", minLength: 1, description: `The provider's category, e.g. "gradient", "light", "transition".` },
|
|
388
395
|
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." },
|
|
389
396
|
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)." },
|
|
390
|
-
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." }
|
|
397
|
+
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." },
|
|
398
|
+
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)." }
|
|
391
399
|
}),
|
|
392
400
|
annotations: { title: "Search effects", ...ANNOTATIONS.read },
|
|
393
401
|
handler: async (args) => {
|
|
394
402
|
const p = host.provider();
|
|
395
403
|
if (!p) return { available: false, reason: NO_PROVIDER, effects: [], total: 0 };
|
|
396
404
|
const tags = args.tags;
|
|
397
|
-
const
|
|
405
|
+
const accepts = args.accepts;
|
|
406
|
+
let found = (await explain(async () => p.search(args.query ?? "", { kind: args.kind, category: args.category, use: args.use, tags, ...accepts?.length ? { accepts } : {} }))).filter((e) => hasEffectTags(e, tags));
|
|
407
|
+
if (accepts?.includes("path")) {
|
|
408
|
+
const kept = [];
|
|
409
|
+
for (const e of found) {
|
|
410
|
+
if (e.accepts) {
|
|
411
|
+
if (accepts.every((a) => e.accepts.includes(a))) kept.push(e);
|
|
412
|
+
} else if (kept.length < 200 && pathControlsOf(await explain(async () => p.describe(e.id))).length) kept.push(e);
|
|
413
|
+
}
|
|
414
|
+
found = kept;
|
|
415
|
+
}
|
|
398
416
|
const limit = args.limit ?? 20;
|
|
399
417
|
const out = { available: true, provider: { id: p.id, name: p.name, capabilities: p.capabilities }, effects: found.slice(0, limit).map(summaryOut), total: found.length, tags: countEffectTags(found).slice(0, TAG_LIMIT) };
|
|
400
418
|
if (tags?.length && !found.length) {
|
|
@@ -416,7 +434,8 @@ function createEffectToolProvider(host) {
|
|
|
416
434
|
const p = host.provider();
|
|
417
435
|
if (!p) return { available: false, reason: NO_PROVIDER };
|
|
418
436
|
const d = await explain(async () => p.describe(args.effect));
|
|
419
|
-
|
|
437
|
+
const pathControls = pathControlsOf(d);
|
|
438
|
+
return { available: true, ...summaryOut(d), controls: d.controls, defaults: d.defaults, presets: d.presets, inputs: d.inputs, appliesHere: effectOffered(d, host.applyUses), ...pathControls.length ? { pathControls } : {} };
|
|
420
439
|
}
|
|
421
440
|
},
|
|
422
441
|
{
|
|
@@ -531,6 +550,8 @@ export {
|
|
|
531
550
|
EFFECT_KINDS,
|
|
532
551
|
EFFECT_USES,
|
|
533
552
|
CAMERA_MOVE_ROLES,
|
|
553
|
+
EFFECT_ACCEPTS,
|
|
554
|
+
EFFECT_PATH_FORMAT,
|
|
534
555
|
EFFECT_SUBJECT_KINDS,
|
|
535
556
|
EFFECT_LAYER_ROLES,
|
|
536
557
|
EFFECT_TRANSITION_MS,
|
|
@@ -550,6 +571,7 @@ export {
|
|
|
550
571
|
normaliseEffectTag,
|
|
551
572
|
hasEffectTags,
|
|
552
573
|
countEffectTags,
|
|
574
|
+
pathControlsOf,
|
|
553
575
|
searchEffectSummaries,
|
|
554
576
|
createEffectPixels,
|
|
555
577
|
isEffectPixels,
|
|
@@ -74,7 +74,7 @@ function profileAlongPolyline(points, values, count = 32) {
|
|
|
74
74
|
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
75
75
|
var loading = null;
|
|
76
76
|
function loadCanvasBrushstrokeRenderer() {
|
|
77
|
-
loading ??= import("./brushstrokeRender-
|
|
77
|
+
loading ??= import("./brushstrokeRender-MOYGEXOC.js").catch((error) => {
|
|
78
78
|
loading = null;
|
|
79
79
|
throw error;
|
|
80
80
|
});
|
|
@@ -36,12 +36,50 @@ import {
|
|
|
36
36
|
} from "./chunk-F67XMVDI.js";
|
|
37
37
|
|
|
38
38
|
// src/paint-editor/effect-paint.ts
|
|
39
|
+
var EFFECT_STRIP_HEIGHT = 256;
|
|
40
|
+
var EFFECT_MAP_REPEATS = ["repeat", "mirror", "clamp"];
|
|
41
|
+
function effectPaintSize(p, canvas) {
|
|
42
|
+
return p.map ? { width: p.map.length, height: EFFECT_STRIP_HEIGHT } : { width: canvas.width, height: canvas.height };
|
|
43
|
+
}
|
|
44
|
+
function readEffectStrokeMap(raw) {
|
|
45
|
+
if (raw === void 0 || raw === null || raw.by === "canvas") return void 0;
|
|
46
|
+
const r = raw;
|
|
47
|
+
if (r.by !== "stroke") throw new Error(`map.by ${JSON.stringify(r.by)} is not a mapping; use "canvas" (fixed to the canvas) or "stroke" (along each stroke).`);
|
|
48
|
+
const length = r.length === void 0 ? 300 : Number(r.length);
|
|
49
|
+
if (!(Number.isFinite(length) && length >= 16 && length <= 4096)) throw new Error(`map.length ${JSON.stringify(r.length)} is out of range; use 16..4096 px of stroke per repeat.`);
|
|
50
|
+
const repeat = r.repeat === void 0 ? "repeat" : r.repeat;
|
|
51
|
+
if (!EFFECT_MAP_REPEATS.includes(repeat)) throw new Error(`map.repeat ${JSON.stringify(r.repeat)} is not a repeat mode; use ${EFFECT_MAP_REPEATS.join(", ")}.`);
|
|
52
|
+
return { by: "stroke", length: Math.round(length), repeat };
|
|
53
|
+
}
|
|
54
|
+
function stripIndex(img, map, dab, x, y) {
|
|
55
|
+
const ca = Math.cos(dab.direction);
|
|
56
|
+
const sa = Math.sin(dab.direction);
|
|
57
|
+
const dx = x - dab.x;
|
|
58
|
+
const dy = y - dab.y;
|
|
59
|
+
let u = (dab.travel + dx * ca - dy * sa) / map.length;
|
|
60
|
+
if (map.repeat === "clamp") u = Math.min(1, Math.max(0, u));
|
|
61
|
+
else if (map.repeat === "mirror") {
|
|
62
|
+
const m = (u % 2 + 2) % 2;
|
|
63
|
+
u = m > 1 ? 2 - m : m;
|
|
64
|
+
} else u = (u % 1 + 1) % 1;
|
|
65
|
+
const v = Math.min(1, Math.max(0, ((dx * sa + dy * ca) / Math.max(0.5, dab.radius) + 1) / 2));
|
|
66
|
+
const px = Math.min(img.width - 1, Math.floor(u * img.width));
|
|
67
|
+
const py = Math.min(img.height - 1, Math.floor(v * img.height));
|
|
68
|
+
return (py * img.width + px) * 4;
|
|
69
|
+
}
|
|
39
70
|
function effectPaintKey(p) {
|
|
40
|
-
return JSON.stringify({ type: "effect", effect: p.effect, provider: p.provider, values: p.values, time: p.time });
|
|
71
|
+
return JSON.stringify({ type: "effect", effect: p.effect, provider: p.provider, values: p.values, time: p.time, ...p.map ? { map: p.map } : {} });
|
|
41
72
|
}
|
|
42
73
|
function isPaintEffectPaint(raw) {
|
|
43
74
|
return Boolean(raw) && typeof raw === "object" && raw.type === "effect";
|
|
44
75
|
}
|
|
76
|
+
function readMapLeniently(raw) {
|
|
77
|
+
try {
|
|
78
|
+
return readEffectStrokeMap(raw);
|
|
79
|
+
} catch {
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
45
83
|
function readPaintEffectPaint(raw) {
|
|
46
84
|
if (!isPaintEffectPaint(raw)) return null;
|
|
47
85
|
const r = raw;
|
|
@@ -51,11 +89,27 @@ function readPaintEffectPaint(raw) {
|
|
|
51
89
|
effect: r.effect,
|
|
52
90
|
provider: typeof r.provider === "string" ? r.provider : "",
|
|
53
91
|
values: JSON.parse(JSON.stringify(r.values)),
|
|
54
|
-
time: typeof r.time === "number" && Number.isFinite(r.time) && r.time > 0 ? r.time : 0
|
|
92
|
+
time: typeof r.time === "number" && Number.isFinite(r.time) && r.time > 0 ? r.time : 0,
|
|
93
|
+
...readMapLeniently(r.map) ? { map: readMapLeniently(r.map) } : {},
|
|
94
|
+
...r.live === true ? { live: true } : {}
|
|
55
95
|
};
|
|
56
96
|
}
|
|
57
|
-
function sampleEffectField(img, rect, vals, field) {
|
|
97
|
+
function sampleEffectField(img, rect, vals, field, dab) {
|
|
58
98
|
const { width, height, data } = img;
|
|
99
|
+
if (img.map && dab) {
|
|
100
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
101
|
+
for (let xx = 0; xx < rect.width; xx++, k++) {
|
|
102
|
+
const i = stripIndex(img, img.map, dab, rect.x + xx + 0.5, rect.y + yy + 0.5);
|
|
103
|
+
const f = k * 3;
|
|
104
|
+
const a = data[i + 3];
|
|
105
|
+
if (a < 255) vals[k] *= a / 255;
|
|
106
|
+
field[f] = data[i] / 255;
|
|
107
|
+
field[f + 1] = data[i + 1] / 255;
|
|
108
|
+
field[f + 2] = data[i + 2] / 255;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
59
113
|
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
60
114
|
const y = rect.y + yy;
|
|
61
115
|
const inY = y >= 0 && y < height;
|
|
@@ -76,7 +130,14 @@ function sampleEffectField(img, rect, vals, field) {
|
|
|
76
130
|
}
|
|
77
131
|
}
|
|
78
132
|
}
|
|
79
|
-
function sampleEffectAt(img, x, y, out) {
|
|
133
|
+
function sampleEffectAt(img, x, y, out, dab) {
|
|
134
|
+
if (img.map && dab) {
|
|
135
|
+
const i2 = stripIndex(img, img.map, dab, x, y);
|
|
136
|
+
out[0] = img.data[i2] / 255;
|
|
137
|
+
out[1] = img.data[i2 + 1] / 255;
|
|
138
|
+
out[2] = img.data[i2 + 2] / 255;
|
|
139
|
+
return img.data[i2 + 3] / 255;
|
|
140
|
+
}
|
|
80
141
|
const px = Math.floor(x);
|
|
81
142
|
const py = Math.floor(y);
|
|
82
143
|
if (px < 0 || py < 0 || px >= img.width || py >= img.height) return 0;
|
|
@@ -86,6 +147,20 @@ function sampleEffectAt(img, x, y, out) {
|
|
|
86
147
|
out[2] = img.data[i + 2] / 255;
|
|
87
148
|
return img.data[i + 3] / 255;
|
|
88
149
|
}
|
|
150
|
+
function effectDabField(img, rect, cov, centre, mixed, field, steps = 0, dab = null) {
|
|
151
|
+
sampleEffectField(img, rect, cov, field, dab);
|
|
152
|
+
const d0 = mixed[0] - centre[0];
|
|
153
|
+
const d1 = mixed[1] - centre[1];
|
|
154
|
+
const d2 = mixed[2] - centre[2];
|
|
155
|
+
const n = rect.width * rect.height;
|
|
156
|
+
for (let k = 0; k < n; k++) {
|
|
157
|
+
if (steps) cov[k] = Math.round(cov[k] * steps) / steps;
|
|
158
|
+
const f = k * 3;
|
|
159
|
+
field[f] = Math.min(1, Math.max(0, field[f] + d0));
|
|
160
|
+
field[f + 1] = Math.min(1, Math.max(0, field[f + 1] + d1));
|
|
161
|
+
field[f + 2] = Math.min(1, Math.max(0, field[f + 2] + d2));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
89
164
|
|
|
90
165
|
// src/paint-editor/paper.ts
|
|
91
166
|
var PAPER_FAMILIES = ["watercolor", "drawing", "pastel", "canvas", "panel", "smooth"];
|
|
@@ -490,7 +565,7 @@ var DryStroke = class {
|
|
|
490
565
|
if (this.options.effect) {
|
|
491
566
|
if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
|
|
492
567
|
field = this.field.subarray(0, n * 3);
|
|
493
|
-
sampleEffectField(this.options.effect, rect, vals, field);
|
|
568
|
+
sampleEffectField(this.options.effect, rect, vals, field, d);
|
|
494
569
|
}
|
|
495
570
|
this.coverage.accumulate(rect, vals, d.opacity, null, field);
|
|
496
571
|
rects.push(rect);
|
|
@@ -692,6 +767,10 @@ var StickerSpray = class {
|
|
|
692
767
|
const a = rand01(seed, i, 53) * Math.PI * 2;
|
|
693
768
|
const dist = rand01(seed, i, 55) * off;
|
|
694
769
|
const base = this.colorAt(d, Math.cos(a) * dist, Math.sin(a) * dist);
|
|
770
|
+
if (!(base.alpha > 0)) {
|
|
771
|
+
this.next++;
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
695
774
|
const sticker = {
|
|
696
775
|
id: `st${this.next++}`,
|
|
697
776
|
stamp: st.stamps[Math.floor(rand01(seed, i, 57) * st.stamps.length)] ?? "dot",
|
|
@@ -710,6 +789,12 @@ var StickerSpray = class {
|
|
|
710
789
|
sampler;
|
|
711
790
|
/** A sticker's colour: the paint colour, or gradient paint at its dab (`dx`, `dy` from the dab's centre). */
|
|
712
791
|
colorAt(d, dx, dy) {
|
|
792
|
+
const effect = this.init.effect;
|
|
793
|
+
if (effect) {
|
|
794
|
+
const rgb = [0, 0, 0];
|
|
795
|
+
const alpha = sampleEffectAt(effect, d.x + dx, d.y + dy, rgb, d);
|
|
796
|
+
return { color: "#" + rgb.map((c) => Math.round(c * 255).toString(16).padStart(2, "0")).join(""), alpha };
|
|
797
|
+
}
|
|
713
798
|
const g = this.init.paint;
|
|
714
799
|
if (!g) return { color: this.init.color, alpha: 1 };
|
|
715
800
|
this.sampler ??= new PaintGradientSampler(g, this.init.seed);
|
|
@@ -841,10 +926,8 @@ var MarkStroke = class {
|
|
|
841
926
|
return rects;
|
|
842
927
|
}
|
|
843
928
|
step(copy, d, rects) {
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (!this.tipTable) this.sampler.dab(d, this.penColor);
|
|
847
|
-
}
|
|
929
|
+
this.pen = d;
|
|
930
|
+
if (this.sampler && !this.tipTable) this.sampler.dab(d, this.penColor);
|
|
848
931
|
this.advance(copy, d, rects);
|
|
849
932
|
}
|
|
850
933
|
/** The colour of a mark at (x, y): its pen dab's, or across the tip by its offset from the pen. */
|
|
@@ -887,7 +970,7 @@ var MarkStroke = class {
|
|
|
887
970
|
if (this.options.effect) {
|
|
888
971
|
if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
|
|
889
972
|
const field = this.field.subarray(0, n * 3);
|
|
890
|
-
sampleEffectField(this.options.effect, rect, vals, field);
|
|
973
|
+
sampleEffectField(this.options.effect, rect, vals, field, this.pen);
|
|
891
974
|
this.coverage.accumulate(rect, vals, dab.opacity, null, field);
|
|
892
975
|
} else {
|
|
893
976
|
const color = this.markColor(x, y);
|
|
@@ -1888,11 +1971,16 @@ function parseBrushLibrary(raw, problems = []) {
|
|
|
1888
1971
|
}
|
|
1889
1972
|
|
|
1890
1973
|
export {
|
|
1974
|
+
EFFECT_STRIP_HEIGHT,
|
|
1975
|
+
EFFECT_MAP_REPEATS,
|
|
1976
|
+
effectPaintSize,
|
|
1977
|
+
readEffectStrokeMap,
|
|
1891
1978
|
effectPaintKey,
|
|
1892
1979
|
isPaintEffectPaint,
|
|
1893
1980
|
readPaintEffectPaint,
|
|
1894
1981
|
sampleEffectField,
|
|
1895
1982
|
sampleEffectAt,
|
|
1983
|
+
effectDabField,
|
|
1896
1984
|
PAPER_FAMILIES,
|
|
1897
1985
|
PAPER_LIBRARY,
|
|
1898
1986
|
DEFAULT_PAPER,
|
|
@@ -7,7 +7,7 @@ var _chunkPY4GWSE2cjs = require('./chunk-PY4GWSE2.cjs');
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunkGHCSC2WPcjs = require('./chunk-GHCSC2WP.cjs');
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
var _chunkWOFXZ3XUcjs = require('./chunk-WOFXZ3XU.cjs');
|
|
@@ -1316,12 +1316,12 @@ function videoEffectLayerKey(layer, width, height) {
|
|
|
1316
1316
|
}
|
|
1317
1317
|
function mixEffectPixels(a, b, k) {
|
|
1318
1318
|
if (k >= 1) return b;
|
|
1319
|
-
const out =
|
|
1319
|
+
const out = _chunkGHCSC2WPcjs.createEffectPixels.call(void 0, a.width, a.height);
|
|
1320
1320
|
for (let i = 0; i < out.data.length; i++) out.data[i] = a.data[i] + (b.data[i] - a.data[i]) * k;
|
|
1321
1321
|
return out;
|
|
1322
1322
|
}
|
|
1323
1323
|
function overEffectPixels(base, top, opacity = 1) {
|
|
1324
|
-
const out =
|
|
1324
|
+
const out = _chunkGHCSC2WPcjs.createEffectPixels.call(void 0, base.width, base.height);
|
|
1325
1325
|
const o = out.data;
|
|
1326
1326
|
const b = base.data;
|
|
1327
1327
|
const t = top.data;
|
|
@@ -1366,7 +1366,7 @@ var VideoEffectsEngine = (_class2 = class {
|
|
|
1366
1366
|
__init10() {this.generation = 0}
|
|
1367
1367
|
|
|
1368
1368
|
constructor(options = {}) {;_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);
|
|
1369
|
-
this.slot = _nullishCoalesce(options.slot, () => ( new (0,
|
|
1369
|
+
this.slot = _nullishCoalesce(options.slot, () => ( new (0, _chunkGHCSC2WPcjs.EffectsSlot)()));
|
|
1370
1370
|
this.maxEntries = _nullishCoalesce(options.maxEntries, () => ( 120));
|
|
1371
1371
|
this.toImage = _nullishCoalesce(options.toImage, () => ( defaultToImage));
|
|
1372
1372
|
this.pool = new (0, _chunkPY4GWSE2cjs.EffectRendererPool)({ maxRenderers: options.maxRenderers });
|
|
@@ -1508,7 +1508,7 @@ function checkVideoEffectPlace(d, place) {
|
|
|
1508
1508
|
if (d.kind === "motion" || !d.uses.some((u) => allowed.includes(u))) {
|
|
1509
1509
|
const where = place === "clip" ? "on a clip" : place === "transition" ? "as a transition" : "as an effect clip";
|
|
1510
1510
|
const instead = d.uses.includes("transition") ? "set_video_effect_transition" : d.uses.some((u) => VIDEO_EFFECT_PLACES.generator.includes(u)) ? "add_video_effect_clip" : d.uses.some((u) => u === "filter") ? "apply_effect" : null;
|
|
1511
|
-
throw new (0,
|
|
1511
|
+
throw new (0, _chunkGHCSC2WPcjs.EffectUnsupportedError)(
|
|
1512
1512
|
`"${d.id}" (${d.name}) is a ${d.uses.join(" / ")} effect and cannot be used ${where}, which takes ${allowed.join(" or ")} effects${instead ? `; use ${instead} instead` : ""}. search_effects with use "${allowed[0]}" lists ones that fit.`,
|
|
1513
1513
|
d.id
|
|
1514
1514
|
);
|
|
@@ -1529,12 +1529,12 @@ function checkVideoEffectValues(d, values, options = {}) {
|
|
|
1529
1529
|
else if (type !== "number" && type !== "integer") errors.push({ path: `/${k}`, message: "only number controls can be keyframed." });
|
|
1530
1530
|
else for (const [i, key] of v.keys.entries()) for (const e of _chunkWOFXZ3XUcjs.validateJsonSchema.call(void 0, key.v, schema)) errors.push({ path: `/${k}/keys/${i}/v`, message: e.message });
|
|
1531
1531
|
}
|
|
1532
|
-
for (const e of
|
|
1532
|
+
for (const e of _chunkGHCSC2WPcjs.effectValueErrors.call(void 0, d, plain)) {
|
|
1533
1533
|
const name = e.path.split("/")[1];
|
|
1534
1534
|
const about = name && _optionalChain([d, 'access', _46 => _46.controls, 'access', _47 => _47.properties, 'access', _48 => _48[name], 'optionalAccess', _49 => _49.description]);
|
|
1535
1535
|
errors.push(about ? { path: e.path, message: `${e.message} (${about})` } : e);
|
|
1536
1536
|
}
|
|
1537
|
-
if (errors.length) throw new (0,
|
|
1537
|
+
if (errors.length) throw new (0, _chunkGHCSC2WPcjs.EffectControlError)(d.id, errors);
|
|
1538
1538
|
return { ..._nullishCoalesce(options.base, () => ( d.defaults)), ..._nullishCoalesce(values, () => ( {})) };
|
|
1539
1539
|
}
|
|
1540
1540
|
|
package/dist/effects/fake.d.cts
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* - `crossfade` (transition)
|
|
10
10
|
* - `fade-in` (motion, vector only)
|
|
11
11
|
* - `badge` (a vector effect with its own picture: a disc that fades in; vector only)
|
|
12
|
+
* - `trail` (an overlay that draws a line along its `path` control, transparent)
|
|
13
|
+
* - `route` (a vector effect that draws its `path` control as a line; vector only)
|
|
14
|
+
*
|
|
15
|
+
* It writes words as path data (`pathFromText`): a zig-zag per letter, font `zig`.
|
|
12
16
|
*
|
|
13
17
|
* With `camera: true` it also has camera moves (no pixels): `glide` and `whip` (transitions; the
|
|
14
18
|
* whip cuts halfway) and `drift` (a hold that zooms in slowly).
|
package/dist/effects/fake.d.ts
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
* - `crossfade` (transition)
|
|
10
10
|
* - `fade-in` (motion, vector only)
|
|
11
11
|
* - `badge` (a vector effect with its own picture: a disc that fades in; vector only)
|
|
12
|
+
* - `trail` (an overlay that draws a line along its `path` control, transparent)
|
|
13
|
+
* - `route` (a vector effect that draws its `path` control as a line; vector only)
|
|
14
|
+
*
|
|
15
|
+
* It writes words as path data (`pathFromText`): a zig-zag per letter, font `zig`.
|
|
12
16
|
*
|
|
13
17
|
* With `camera: true` it also has camera moves (no pixels): `glide` and `whip` (transitions; the
|
|
14
18
|
* whip cuts halfway) and `drift` (a hold that zooms in slowly).
|
|
@@ -27,6 +27,12 @@ export declare function countEffectTags(effects: readonly Pick<EffectSummary, 't
|
|
|
27
27
|
tag: string;
|
|
28
28
|
count: number;
|
|
29
29
|
}[];
|
|
30
|
+
/**
|
|
31
|
+
* The controls of an effect that draw along a path given at run time: those whose schema (or one
|
|
32
|
+
* of its `anyOf` / `oneOf` choices) has `format: 'svg-path'`. See `EFFECT_PATH_FORMAT` for the
|
|
33
|
+
* values they take.
|
|
34
|
+
*/
|
|
35
|
+
export declare function pathControlsOf(d: Pick<EffectDescription, 'controls'>): string[];
|
|
30
36
|
/**
|
|
31
37
|
* Filters a catalog (kind, category, use and every tag) and ranks it against `query` (every word
|
|
32
38
|
* must appear in the id, name, category, tags or description; name matches rank first). An empty query keeps catalog order.
|
|
@@ -27,6 +27,12 @@ export declare function countEffectTags(effects: readonly Pick<EffectSummary, 't
|
|
|
27
27
|
tag: string;
|
|
28
28
|
count: number;
|
|
29
29
|
}[];
|
|
30
|
+
/**
|
|
31
|
+
* The controls of an effect that draw along a path given at run time: those whose schema (or one
|
|
32
|
+
* of its `anyOf` / `oneOf` choices) has `format: 'svg-path'`. See `EFFECT_PATH_FORMAT` for the
|
|
33
|
+
* values they take.
|
|
34
|
+
*/
|
|
35
|
+
export declare function pathControlsOf(d: Pick<EffectDescription, 'controls'>): string[];
|
|
30
36
|
/**
|
|
31
37
|
* Filters a catalog (kind, category, use and every tag) and ranks it against `query` (every word
|
|
32
38
|
* must appear in the id, name, category, tags or description; name matches rank first). An empty query keeps catalog order.
|