@framefields/node-kenburns 2.0.2
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/.turbo/turbo-build.log +20 -0
- package/CHANGELOG.md +23 -0
- package/LICENCE +202 -0
- package/dist/index.d.mts +256 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +122 -0
- package/dist/index.mjs.map +1 -0
- package/dist/renderer.d.mts +7 -0
- package/dist/renderer.d.mts.map +1 -0
- package/dist/renderer.mjs +3 -0
- package/dist/renderers-BoufaXfn.mjs +336 -0
- package/dist/renderers-BoufaXfn.mjs.map +1 -0
- package/package.json +46 -0
- package/src/index.ts +2 -0
- package/src/renderers/index.ts +6 -0
- package/src/renderers/webgpu-renderer.ts +500 -0
- package/src/shared/config.ts +107 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/processor-logic.ts +108 -0
- package/tsconfig.json +13 -0
- package/tsdown.config.ts +13 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { defineRenderer } from "@framefields/node-sdk/renderer";
|
|
2
|
+
|
|
3
|
+
//#region src/renderers/webgpu-renderer.ts
|
|
4
|
+
const easeIn = (t) => t * t;
|
|
5
|
+
const easeOut = (t) => t * (2 - t);
|
|
6
|
+
const easeInOut = (t) => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
7
|
+
const springEase = (t) => {
|
|
8
|
+
const c4 = 2 * Math.PI / 3;
|
|
9
|
+
return t === 0 ? 0 : t === 1 ? 1 : 2 ** (-10 * t) * Math.sin((t * 10 - .75) * c4) + 1;
|
|
10
|
+
};
|
|
11
|
+
const applyEasing = (t, easing) => {
|
|
12
|
+
switch (easing) {
|
|
13
|
+
case "ease-in": return easeIn(t);
|
|
14
|
+
case "ease-out": return easeOut(t);
|
|
15
|
+
case "ease-in-out": return easeInOut(t);
|
|
16
|
+
case "spring": return springEase(t);
|
|
17
|
+
default: return t;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const catmullRom = (p0, p1, p2, p3, t) => {
|
|
21
|
+
const t2 = t * t;
|
|
22
|
+
const t3 = t2 * t;
|
|
23
|
+
return .5 * (2 * p1 + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 + (-p0 + 3 * p1 - 3 * p2 + p3) * t3);
|
|
24
|
+
};
|
|
25
|
+
function interpolateKenBurns(keyframes, timeMs, movementStyle = "spline") {
|
|
26
|
+
if (keyframes.length === 0) return {
|
|
27
|
+
x: 50,
|
|
28
|
+
y: 50,
|
|
29
|
+
scale: 1
|
|
30
|
+
};
|
|
31
|
+
if (keyframes.length === 1) {
|
|
32
|
+
const kf = keyframes[0];
|
|
33
|
+
return {
|
|
34
|
+
x: kf.x ?? 50,
|
|
35
|
+
y: kf.y ?? 50,
|
|
36
|
+
scale: kf.scale ?? 1
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
let elapsed = 0;
|
|
40
|
+
for (let i = 0; i < keyframes.length; i++) {
|
|
41
|
+
const kf = keyframes[i];
|
|
42
|
+
const hold = kf.holdMs ?? 0;
|
|
43
|
+
const dur = kf.durationMs ?? 0;
|
|
44
|
+
if (timeMs <= elapsed + hold) return {
|
|
45
|
+
x: kf.x ?? 50,
|
|
46
|
+
y: kf.y ?? 50,
|
|
47
|
+
scale: kf.scale ?? 1
|
|
48
|
+
};
|
|
49
|
+
elapsed += hold;
|
|
50
|
+
if (i < keyframes.length - 1 && dur > 0) {
|
|
51
|
+
if (timeMs <= elapsed + dur) {
|
|
52
|
+
const rawT = (timeMs - elapsed) / dur;
|
|
53
|
+
const t = applyEasing(Math.max(0, Math.min(1, rawT)), kf.easing);
|
|
54
|
+
const next = keyframes[i + 1];
|
|
55
|
+
if (movementStyle === "spline") {
|
|
56
|
+
const prev = keyframes[i - 1] ?? kf;
|
|
57
|
+
const afterNext = keyframes[i + 2] ?? next;
|
|
58
|
+
return {
|
|
59
|
+
x: catmullRom(prev.x ?? 50, kf.x ?? 50, next.x ?? 50, afterNext.x ?? 50, t),
|
|
60
|
+
y: catmullRom(prev.y ?? 50, kf.y ?? 50, next.y ?? 50, afterNext.y ?? 50, t),
|
|
61
|
+
scale: catmullRom(prev.scale ?? 1, kf.scale ?? 1, next.scale ?? 1, afterNext.scale ?? 1, t)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
x: (kf.x ?? 50) + ((next.x ?? 50) - (kf.x ?? 50)) * t,
|
|
66
|
+
y: (kf.y ?? 50) + ((next.y ?? 50) - (kf.y ?? 50)) * t,
|
|
67
|
+
scale: (kf.scale ?? 1) + ((next.scale ?? 1) - (kf.scale ?? 1)) * t
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
elapsed += dur;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const last = keyframes[keyframes.length - 1];
|
|
74
|
+
return {
|
|
75
|
+
x: last.x ?? 50,
|
|
76
|
+
y: last.y ?? 50,
|
|
77
|
+
scale: last.scale ?? 1
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function calculateSrcRect(state, sourceWidth, sourceHeight, targetAspectRatio) {
|
|
81
|
+
let baseBoxW_pct = 100;
|
|
82
|
+
let baseBoxH_pct = 100;
|
|
83
|
+
if (targetAspectRatio !== "input") {
|
|
84
|
+
const parts = targetAspectRatio.split(":");
|
|
85
|
+
const tW = Number(parts[0]);
|
|
86
|
+
const tH = Number(parts[1]);
|
|
87
|
+
if (!Number.isNaN(tW) && !Number.isNaN(tH) && tW > 0 && tH > 0) {
|
|
88
|
+
const desiredAR = tW / tH;
|
|
89
|
+
const sourceAR = sourceWidth / sourceHeight;
|
|
90
|
+
if (sourceAR > desiredAR) {
|
|
91
|
+
baseBoxH_pct = 100;
|
|
92
|
+
baseBoxW_pct = desiredAR / sourceAR * 100;
|
|
93
|
+
} else {
|
|
94
|
+
baseBoxW_pct = 100;
|
|
95
|
+
baseBoxH_pct = sourceAR / desiredAR * 100;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const baseBoxPxW = baseBoxW_pct / 100 * sourceWidth;
|
|
100
|
+
const baseBoxPxH = baseBoxH_pct / 100 * sourceHeight;
|
|
101
|
+
const clampedScale = Math.max(state.scale, 1);
|
|
102
|
+
const currentBoxPxW = baseBoxPxW / clampedScale;
|
|
103
|
+
const currentBoxPxH = baseBoxPxH / clampedScale;
|
|
104
|
+
const rawFocalX = state.x / 100 * sourceWidth;
|
|
105
|
+
const rawFocalY = state.y / 100 * sourceHeight;
|
|
106
|
+
const halfBoxW = currentBoxPxW / 2;
|
|
107
|
+
const halfBoxH = currentBoxPxH / 2;
|
|
108
|
+
const focalX = Math.max(halfBoxW, Math.min(sourceWidth - halfBoxW, rawFocalX));
|
|
109
|
+
const focalY = Math.max(halfBoxH, Math.min(sourceHeight - halfBoxH, rawFocalY));
|
|
110
|
+
return {
|
|
111
|
+
x: focalX - halfBoxW,
|
|
112
|
+
y: focalY - halfBoxH,
|
|
113
|
+
width: currentBoxPxW,
|
|
114
|
+
height: currentBoxPxH
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const motionBlurWgsl = `
|
|
118
|
+
struct MotionBlurUniforms {
|
|
119
|
+
prev_origin : vec2<f32>,
|
|
120
|
+
prev_size : vec2<f32>,
|
|
121
|
+
curr_origin : vec2<f32>,
|
|
122
|
+
curr_size : vec2<f32>,
|
|
123
|
+
samples : f32,
|
|
124
|
+
blurSize : f32,
|
|
125
|
+
padding : vec2<f32>,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
@group(0) @binding(0) var<uniform> u : MotionBlurUniforms;
|
|
129
|
+
@group(1) @binding(0) var tex : texture_2d<f32>;
|
|
130
|
+
@group(1) @binding(1) var samp : sampler;
|
|
131
|
+
|
|
132
|
+
struct VSOut {
|
|
133
|
+
@builtin(position) pos : vec4<f32>,
|
|
134
|
+
@location(0) uv : vec2<f32>,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
@vertex fn vs(@builtin(vertex_index) vi: u32) -> VSOut {
|
|
138
|
+
var pos = array<vec2<f32>, 4>(
|
|
139
|
+
vec2<f32>(-1.0, 1.0),
|
|
140
|
+
vec2<f32>(1.0, 1.0),
|
|
141
|
+
vec2<f32>(-1.0, -1.0),
|
|
142
|
+
vec2<f32>(1.0, -1.0)
|
|
143
|
+
);
|
|
144
|
+
var uv = array<vec2<f32>, 4>(
|
|
145
|
+
vec2<f32>(0.0, 0.0),
|
|
146
|
+
vec2<f32>(1.0, 0.0),
|
|
147
|
+
vec2<f32>(0.0, 1.0),
|
|
148
|
+
vec2<f32>(1.0, 1.0)
|
|
149
|
+
);
|
|
150
|
+
return VSOut(vec4<f32>(pos[vi], 0.0, 1.0), uv[vi]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {
|
|
154
|
+
let uv = in.uv;
|
|
155
|
+
let n = i32(u.samples);
|
|
156
|
+
|
|
157
|
+
if (n <= 1) {
|
|
158
|
+
let p_curr = u.curr_origin + uv * u.curr_size;
|
|
159
|
+
return textureSampleLevel(tex, samp, p_curr, 0.0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
var color = vec4<f32>(0.0);
|
|
163
|
+
for (var i = 0; i < n; i = i + 1) {
|
|
164
|
+
// Use blurSize to control the shutter interval width.
|
|
165
|
+
// t=1.0 is current frame, t=0.0 is previous frame.
|
|
166
|
+
let t = 1.0 + (f32(i) / f32(n - 1) - 0.5) * u.blurSize;
|
|
167
|
+
|
|
168
|
+
let origin = mix(u.prev_origin, u.curr_origin, t);
|
|
169
|
+
let size = mix(u.prev_size, u.curr_size, t);
|
|
170
|
+
let p_sample = origin + uv * size;
|
|
171
|
+
|
|
172
|
+
color += textureSampleLevel(tex, samp, p_sample, 0.0);
|
|
173
|
+
}
|
|
174
|
+
return color / f32(n);
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
const deviceResourceCache = /* @__PURE__ */ new WeakMap();
|
|
178
|
+
const motionBlurData = new Float32Array(12);
|
|
179
|
+
function getMotionBlurResources(device, format) {
|
|
180
|
+
let res = deviceResourceCache.get(device);
|
|
181
|
+
if (!res) {
|
|
182
|
+
const motionBlurUniformLayout = device.createBindGroupLayout({ entries: [{
|
|
183
|
+
binding: 0,
|
|
184
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
185
|
+
buffer: { type: "uniform" }
|
|
186
|
+
}] });
|
|
187
|
+
const singleTextureLayout = device.createBindGroupLayout({ entries: [{
|
|
188
|
+
binding: 0,
|
|
189
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
190
|
+
texture: { sampleType: "float" }
|
|
191
|
+
}, {
|
|
192
|
+
binding: 1,
|
|
193
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
194
|
+
sampler: { type: "filtering" }
|
|
195
|
+
}] });
|
|
196
|
+
const module = device.createShaderModule({
|
|
197
|
+
label: "kenburns_motionblur.wgsl",
|
|
198
|
+
code: motionBlurWgsl
|
|
199
|
+
});
|
|
200
|
+
res = {
|
|
201
|
+
motionBlurPipeline: device.createRenderPipeline({
|
|
202
|
+
label: "MotionBlurPipeline",
|
|
203
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [motionBlurUniformLayout, singleTextureLayout] }),
|
|
204
|
+
vertex: {
|
|
205
|
+
module,
|
|
206
|
+
entryPoint: "vs"
|
|
207
|
+
},
|
|
208
|
+
fragment: {
|
|
209
|
+
module,
|
|
210
|
+
entryPoint: "fs",
|
|
211
|
+
targets: [{ format }]
|
|
212
|
+
},
|
|
213
|
+
primitive: { topology: "triangle-strip" }
|
|
214
|
+
}),
|
|
215
|
+
motionBlurUniformLayout,
|
|
216
|
+
singleTextureLayout
|
|
217
|
+
};
|
|
218
|
+
deviceResourceCache.set(device, res);
|
|
219
|
+
}
|
|
220
|
+
return res;
|
|
221
|
+
}
|
|
222
|
+
const KenBurnsWebGPURenderer = async (args) => {
|
|
223
|
+
const { ctx, encoder, pass, targetView, targetTexture, targetWidth, targetHeight, props, drawChild } = args;
|
|
224
|
+
const { virtualMedia, containerWidth, containerHeight, frame, fps, trimStartOverride, opacity = 1, inheritedSeekOffset = 0 } = props;
|
|
225
|
+
const op = virtualMedia.operation;
|
|
226
|
+
if (op?.op !== "KenBurns") return;
|
|
227
|
+
const childMedia = virtualMedia.children?.[0];
|
|
228
|
+
if (!childMedia) return;
|
|
229
|
+
const sourceWidth = op.originalWidth ?? childMedia.metadata?.width ?? virtualMedia.metadata?.width ?? containerWidth ?? targetWidth;
|
|
230
|
+
const sourceHeight = op.originalHeight ?? childMedia.metadata?.height ?? virtualMedia.metadata?.height ?? containerHeight ?? targetHeight;
|
|
231
|
+
const keyframes = op.keyframes ?? [];
|
|
232
|
+
const movementStyle = op.movementStyle ?? "spline";
|
|
233
|
+
const targetAspectRatio = op.aspectRatio || "input";
|
|
234
|
+
const offsetMs = ((trimStartOverride ?? 0) + inheritedSeekOffset) * 1e3;
|
|
235
|
+
const fpsVal = Math.max(1, fps ?? 30);
|
|
236
|
+
const frameDurationMs = 1e3 / fpsVal;
|
|
237
|
+
const timeMs = offsetMs + (frame ?? 0) / fpsVal * 1e3;
|
|
238
|
+
const prevTimeMs = timeMs - frameDurationMs;
|
|
239
|
+
const state = interpolateKenBurns(keyframes, timeMs, movementStyle);
|
|
240
|
+
const prevState = interpolateKenBurns(keyframes, prevTimeMs, movementStyle);
|
|
241
|
+
const srcRect = calculateSrcRect(state, sourceWidth, sourceHeight, targetAspectRatio);
|
|
242
|
+
const prevSrcRect = calculateSrcRect(prevState, sourceWidth, sourceHeight, targetAspectRatio);
|
|
243
|
+
const dstWidth = containerWidth ?? targetWidth;
|
|
244
|
+
const dstHeight = containerHeight ?? targetHeight;
|
|
245
|
+
const dstRect = {
|
|
246
|
+
x: 0,
|
|
247
|
+
y: 0,
|
|
248
|
+
width: dstWidth,
|
|
249
|
+
height: dstHeight
|
|
250
|
+
};
|
|
251
|
+
const tempTex = ctx.renderer.getTemporaryTexture(sourceWidth, sourceHeight, [...props.excludeTextures || [], targetTexture]);
|
|
252
|
+
const tempView = tempTex.createView();
|
|
253
|
+
pass.end();
|
|
254
|
+
ctx.renderer.beginFrame(encoder, tempView, {
|
|
255
|
+
r: 0,
|
|
256
|
+
g: 0,
|
|
257
|
+
b: 0,
|
|
258
|
+
a: 0
|
|
259
|
+
}, sourceWidth, sourceHeight, "clear").end();
|
|
260
|
+
ctx.renderer.pushScissor({
|
|
261
|
+
x: 0,
|
|
262
|
+
y: 0,
|
|
263
|
+
width: sourceWidth,
|
|
264
|
+
height: sourceHeight
|
|
265
|
+
});
|
|
266
|
+
ctx.renderer.pushIdentity();
|
|
267
|
+
await drawChild(childMedia, {
|
|
268
|
+
...props,
|
|
269
|
+
containerWidth: sourceWidth,
|
|
270
|
+
containerHeight: sourceHeight
|
|
271
|
+
}, tempView, tempTex, sourceWidth, sourceHeight);
|
|
272
|
+
ctx.renderer.popTransform();
|
|
273
|
+
ctx.renderer.popScissor();
|
|
274
|
+
const motionBlurSize = op.motionBlurSize ?? 1.5;
|
|
275
|
+
let blurTex = null;
|
|
276
|
+
if (motionBlurSize > 0) {
|
|
277
|
+
const { motionBlurPipeline: pipeline, motionBlurUniformLayout: uLayout, singleTextureLayout: tLayout } = getMotionBlurResources(ctx.device, ctx.renderer.format);
|
|
278
|
+
const sampler = ctx.renderer.samplerCache.getSampler(ctx.device);
|
|
279
|
+
motionBlurData[0] = prevSrcRect.x / sourceWidth;
|
|
280
|
+
motionBlurData[1] = prevSrcRect.y / sourceHeight;
|
|
281
|
+
motionBlurData[2] = prevSrcRect.width / sourceWidth;
|
|
282
|
+
motionBlurData[3] = prevSrcRect.height / sourceHeight;
|
|
283
|
+
motionBlurData[4] = srcRect.x / sourceWidth;
|
|
284
|
+
motionBlurData[5] = srcRect.y / sourceHeight;
|
|
285
|
+
motionBlurData[6] = srcRect.width / sourceWidth;
|
|
286
|
+
motionBlurData[7] = srcRect.height / sourceHeight;
|
|
287
|
+
motionBlurData[8] = 12;
|
|
288
|
+
motionBlurData[9] = motionBlurSize;
|
|
289
|
+
const uBuffer = ctx.renderer.getTemporaryBuffer(motionBlurData);
|
|
290
|
+
blurTex = ctx.renderer.getTemporaryTexture(dstWidth, dstHeight, [
|
|
291
|
+
targetTexture,
|
|
292
|
+
tempTex,
|
|
293
|
+
...props.excludeTextures || []
|
|
294
|
+
]);
|
|
295
|
+
const blurView = blurTex.createView();
|
|
296
|
+
const blurPass = encoder.beginRenderPass({ colorAttachments: [{
|
|
297
|
+
view: blurView,
|
|
298
|
+
loadOp: "clear",
|
|
299
|
+
storeOp: "store",
|
|
300
|
+
clearValue: {
|
|
301
|
+
r: 0,
|
|
302
|
+
g: 0,
|
|
303
|
+
b: 0,
|
|
304
|
+
a: 0
|
|
305
|
+
}
|
|
306
|
+
}] });
|
|
307
|
+
blurPass.setPipeline(pipeline);
|
|
308
|
+
blurPass.setBindGroup(0, ctx.device.createBindGroup({
|
|
309
|
+
layout: uLayout,
|
|
310
|
+
entries: [{
|
|
311
|
+
binding: 0,
|
|
312
|
+
resource: { buffer: uBuffer }
|
|
313
|
+
}]
|
|
314
|
+
}));
|
|
315
|
+
blurPass.setBindGroup(1, ctx.renderer.bindGroupCache.getBindGroup(ctx.device, tLayout, tempTex, sampler));
|
|
316
|
+
blurPass.draw(4);
|
|
317
|
+
blurPass.end();
|
|
318
|
+
}
|
|
319
|
+
const outPass = ctx.renderer.beginFrame(encoder, targetView, {
|
|
320
|
+
r: 0,
|
|
321
|
+
g: 0,
|
|
322
|
+
b: 0,
|
|
323
|
+
a: 0
|
|
324
|
+
}, targetWidth, targetHeight, "load");
|
|
325
|
+
if (blurTex) ctx.renderer.drawTexture(outPass, blurTex, dstRect, { opacity });
|
|
326
|
+
else ctx.renderer.drawTextureRegion(outPass, tempTex, srcRect, dstRect, { opacity });
|
|
327
|
+
args.pass = outPass;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/renderers/index.ts
|
|
332
|
+
var renderers_default = defineRenderer({ WebGPURenderer: KenBurnsWebGPURenderer });
|
|
333
|
+
|
|
334
|
+
//#endregion
|
|
335
|
+
export { renderers_default as t };
|
|
336
|
+
//# sourceMappingURL=renderers-BoufaXfn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderers-BoufaXfn.mjs","names":[],"sources":["../src/renderers/webgpu-renderer.ts","../src/renderers/index.ts"],"sourcesContent":["import type { WebGPUNodeRenderer } from \"@framefields/node-sdk\";\nimport type { KenBurnsKeyframe } from \"../shared/config.js\";\n\nconst easeIn = (t: number): number => t * t;\nconst easeOut = (t: number): number => t * (2 - t);\nconst easeInOut = (t: number): number =>\n\tt < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\nconst springEase = (t: number): number => {\n\tconst c4 = (2 * Math.PI) / 3;\n\treturn t === 0\n\t\t? 0\n\t\t: t === 1\n\t\t\t? 1\n\t\t\t: 2 ** (-10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;\n};\n\nconst applyEasing = (t: number, easing?: string): number => {\n\tswitch (easing) {\n\t\tcase \"ease-in\":\n\t\t\treturn easeIn(t);\n\t\tcase \"ease-out\":\n\t\t\treturn easeOut(t);\n\t\tcase \"ease-in-out\":\n\t\t\treturn easeInOut(t);\n\t\tcase \"spring\":\n\t\t\treturn springEase(t);\n\t\tdefault:\n\t\t\treturn t;\n\t}\n};\n\nconst catmullRom = (\n\tp0: number,\n\tp1: number,\n\tp2: number,\n\tp3: number,\n\tt: number,\n): number => {\n\tconst t2 = t * t;\n\tconst t3 = t2 * t;\n\treturn (\n\t\t0.5 *\n\t\t(2 * p1 +\n\t\t\t(-p0 + p2) * t +\n\t\t\t(2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +\n\t\t\t(-p0 + 3 * p1 - 3 * p2 + p3) * t3)\n\t);\n};\n\ninterface InterpolatedState {\n\tx: number;\n\ty: number;\n\tscale: number;\n}\n\nfunction interpolateKenBurns(\n\tkeyframes: KenBurnsKeyframe[],\n\ttimeMs: number,\n\tmovementStyle = \"spline\",\n): InterpolatedState {\n\tif (keyframes.length === 0) return { x: 50, y: 50, scale: 1 };\n\tif (keyframes.length === 1) {\n\t\tconst kf = keyframes[0];\n\t\treturn { x: kf.x ?? 50, y: kf.y ?? 50, scale: kf.scale ?? 1 };\n\t}\n\tlet elapsed = 0;\n\tfor (let i = 0; i < keyframes.length; i++) {\n\t\tconst kf = keyframes[i];\n\t\tconst hold = kf.holdMs ?? 0;\n\t\tconst dur = kf.durationMs ?? 0;\n\t\tif (timeMs <= elapsed + hold)\n\t\t\treturn { x: kf.x ?? 50, y: kf.y ?? 50, scale: kf.scale ?? 1 };\n\t\telapsed += hold;\n\t\tif (i < keyframes.length - 1 && dur > 0) {\n\t\t\tif (timeMs <= elapsed + dur) {\n\t\t\t\tconst rawT = (timeMs - elapsed) / dur;\n\t\t\t\tconst t = applyEasing(Math.max(0, Math.min(1, rawT)), kf.easing);\n\t\t\t\tconst next = keyframes[i + 1];\n\t\t\t\tif (movementStyle === \"spline\") {\n\t\t\t\t\tconst prev = keyframes[i - 1] ?? kf;\n\t\t\t\t\tconst afterNext = keyframes[i + 2] ?? next;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tx: catmullRom(\n\t\t\t\t\t\t\tprev.x ?? 50,\n\t\t\t\t\t\t\tkf.x ?? 50,\n\t\t\t\t\t\t\tnext.x ?? 50,\n\t\t\t\t\t\t\tafterNext.x ?? 50,\n\t\t\t\t\t\t\tt,\n\t\t\t\t\t\t),\n\t\t\t\t\t\ty: catmullRom(\n\t\t\t\t\t\t\tprev.y ?? 50,\n\t\t\t\t\t\t\tkf.y ?? 50,\n\t\t\t\t\t\t\tnext.y ?? 50,\n\t\t\t\t\t\t\tafterNext.y ?? 50,\n\t\t\t\t\t\t\tt,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tscale: catmullRom(\n\t\t\t\t\t\t\tprev.scale ?? 1,\n\t\t\t\t\t\t\tkf.scale ?? 1,\n\t\t\t\t\t\t\tnext.scale ?? 1,\n\t\t\t\t\t\t\tafterNext.scale ?? 1,\n\t\t\t\t\t\t\tt,\n\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tx: (kf.x ?? 50) + ((next.x ?? 50) - (kf.x ?? 50)) * t,\n\t\t\t\t\ty: (kf.y ?? 50) + ((next.y ?? 50) - (kf.y ?? 50)) * t,\n\t\t\t\t\tscale: (kf.scale ?? 1) + ((next.scale ?? 1) - (kf.scale ?? 1)) * t,\n\t\t\t\t};\n\t\t\t}\n\t\t\telapsed += dur;\n\t\t}\n\t}\n\tconst last = keyframes[keyframes.length - 1];\n\treturn { x: last.x ?? 50, y: last.y ?? 50, scale: last.scale ?? 1 };\n}\n\nfunction calculateSrcRect(\n\tstate: InterpolatedState,\n\tsourceWidth: number,\n\tsourceHeight: number,\n\ttargetAspectRatio: string,\n) {\n\tlet baseBoxW_pct = 100;\n\tlet baseBoxH_pct = 100;\n\n\tif (targetAspectRatio !== \"input\") {\n\t\tconst parts = targetAspectRatio.split(\":\");\n\t\tconst tW = Number(parts[0]);\n\t\tconst tH = Number(parts[1]);\n\t\tif (!Number.isNaN(tW) && !Number.isNaN(tH) && tW > 0 && tH > 0) {\n\t\t\tconst desiredAR = tW / tH;\n\t\t\tconst sourceAR = sourceWidth / sourceHeight;\n\t\t\tif (sourceAR > desiredAR) {\n\t\t\t\tbaseBoxH_pct = 100;\n\t\t\t\tbaseBoxW_pct = (desiredAR / sourceAR) * 100;\n\t\t\t} else {\n\t\t\t\tbaseBoxW_pct = 100;\n\t\t\t\tbaseBoxH_pct = (sourceAR / desiredAR) * 100;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst baseBoxPxW = (baseBoxW_pct / 100) * sourceWidth;\n\tconst baseBoxPxH = (baseBoxH_pct / 100) * sourceHeight;\n\tconst clampedScale = Math.max(state.scale, 1);\n\tconst currentBoxPxW = baseBoxPxW / clampedScale;\n\tconst currentBoxPxH = baseBoxPxH / clampedScale;\n\n\tconst rawFocalX = (state.x / 100) * sourceWidth;\n\tconst rawFocalY = (state.y / 100) * sourceHeight;\n\tconst halfBoxW = currentBoxPxW / 2;\n\tconst halfBoxH = currentBoxPxH / 2;\n\tconst focalX = Math.max(\n\t\thalfBoxW,\n\t\tMath.min(sourceWidth - halfBoxW, rawFocalX),\n\t);\n\tconst focalY = Math.max(\n\t\thalfBoxH,\n\t\tMath.min(sourceHeight - halfBoxH, rawFocalY),\n\t);\n\n\treturn {\n\t\tx: focalX - halfBoxW,\n\t\ty: focalY - halfBoxH,\n\t\twidth: currentBoxPxW,\n\t\theight: currentBoxPxH,\n\t};\n}\n\nconst motionBlurWgsl = `\nstruct MotionBlurUniforms {\n\tprev_origin : vec2<f32>,\n\tprev_size : vec2<f32>,\n\tcurr_origin : vec2<f32>,\n\tcurr_size : vec2<f32>,\n\tsamples : f32,\n\tblurSize : f32,\n\tpadding : vec2<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u : MotionBlurUniforms;\n@group(1) @binding(0) var tex : texture_2d<f32>;\n@group(1) @binding(1) var samp : sampler;\n\nstruct VSOut {\n\t@builtin(position) pos : vec4<f32>,\n\t@location(0) uv : vec2<f32>,\n};\n\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VSOut {\n\tvar pos = array<vec2<f32>, 4>(\n\t\tvec2<f32>(-1.0, 1.0),\n\t\tvec2<f32>(1.0, 1.0),\n\t\tvec2<f32>(-1.0, -1.0),\n\t\tvec2<f32>(1.0, -1.0)\n\t);\n\tvar uv = array<vec2<f32>, 4>(\n\t\tvec2<f32>(0.0, 0.0),\n\t\tvec2<f32>(1.0, 0.0),\n\t\tvec2<f32>(0.0, 1.0),\n\t\tvec2<f32>(1.0, 1.0)\n\t);\n\treturn VSOut(vec4<f32>(pos[vi], 0.0, 1.0), uv[vi]);\n}\n\n@fragment fn fs(in : VSOut) -> @location(0) vec4<f32> {\n\tlet uv = in.uv;\n\tlet n = i32(u.samples);\n\t\n\tif (n <= 1) {\n\t\tlet p_curr = u.curr_origin + uv * u.curr_size;\n\t\treturn textureSampleLevel(tex, samp, p_curr, 0.0);\n\t}\n\t\n\tvar color = vec4<f32>(0.0);\n\tfor (var i = 0; i < n; i = i + 1) {\n\t\t// Use blurSize to control the shutter interval width. \n\t\t// t=1.0 is current frame, t=0.0 is previous frame.\n\t\tlet t = 1.0 + (f32(i) / f32(n - 1) - 0.5) * u.blurSize;\n\t\t\n\t\tlet origin = mix(u.prev_origin, u.curr_origin, t);\n\t\tlet size = mix(u.prev_size, u.curr_size, t);\n\t\tlet p_sample = origin + uv * size;\n\t\t\n\t\tcolor += textureSampleLevel(tex, samp, p_sample, 0.0);\n\t}\n\treturn color / f32(n);\n}\n`;\n\ninterface DeviceKenBurnsResources {\n\tmotionBlurPipeline: GPURenderPipeline;\n\tmotionBlurUniformLayout: GPUBindGroupLayout;\n\tsingleTextureLayout: GPUBindGroupLayout;\n}\n\nconst deviceResourceCache = new WeakMap<GPUDevice, DeviceKenBurnsResources>();\nconst motionBlurData = new Float32Array(12);\n\nfunction getMotionBlurResources(device: GPUDevice, format: GPUTextureFormat) {\n\tlet res = deviceResourceCache.get(device);\n\tif (!res) {\n\t\tconst motionBlurUniformLayout = device.createBindGroupLayout({\n\t\t\tentries: [\n\t\t\t\t{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\t\tbuffer: { type: \"uniform\" },\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tconst singleTextureLayout = device.createBindGroupLayout({\n\t\t\tentries: [\n\t\t\t\t{\n\t\t\t\t\tbinding: 0,\n\t\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\t\ttexture: { sampleType: \"float\" },\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tbinding: 1,\n\t\t\t\t\tvisibility: GPUShaderStage.FRAGMENT,\n\t\t\t\t\tsampler: { type: \"filtering\" },\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tconst module = device.createShaderModule({\n\t\t\tlabel: \"kenburns_motionblur.wgsl\",\n\t\t\tcode: motionBlurWgsl,\n\t\t});\n\t\tconst motionBlurPipeline = device.createRenderPipeline({\n\t\t\tlabel: \"MotionBlurPipeline\",\n\t\t\tlayout: device.createPipelineLayout({\n\t\t\t\tbindGroupLayouts: [motionBlurUniformLayout, singleTextureLayout],\n\t\t\t}),\n\t\t\tvertex: { module, entryPoint: \"vs\" },\n\t\t\tfragment: { module, entryPoint: \"fs\", targets: [{ format }] },\n\t\t\tprimitive: { topology: \"triangle-strip\" },\n\t\t});\n\t\tres = { motionBlurPipeline, motionBlurUniformLayout, singleTextureLayout };\n\t\tdeviceResourceCache.set(device, res);\n\t}\n\treturn res;\n}\n\nexport const KenBurnsWebGPURenderer: WebGPUNodeRenderer = async (args) => {\n\tconst {\n\t\tctx,\n\t\tencoder,\n\t\tpass,\n\t\ttargetView,\n\t\ttargetTexture,\n\t\ttargetWidth,\n\t\ttargetHeight,\n\t\tprops,\n\t\tdrawChild,\n\t} = args;\n\n\tconst {\n\t\tvirtualMedia,\n\t\tcontainerWidth,\n\t\tcontainerHeight,\n\t\tframe,\n\t\tfps,\n\t\ttrimStartOverride,\n\t\topacity = 1,\n\t\tinheritedSeekOffset = 0,\n\t} = props;\n\n\tconst op = virtualMedia.operation as any;\n\tif (op?.op !== \"KenBurns\") return;\n\n\tconst childMedia = virtualMedia.children?.[0];\n\tif (!childMedia) return;\n\n\t// ── Source dimensions ────────────────────────────────────────────────────\n\tconst sourceWidth =\n\t\top.originalWidth ??\n\t\tchildMedia.metadata?.width ??\n\t\tvirtualMedia.metadata?.width ??\n\t\tcontainerWidth ??\n\t\ttargetWidth;\n\tconst sourceHeight =\n\t\top.originalHeight ??\n\t\tchildMedia.metadata?.height ??\n\t\tvirtualMedia.metadata?.height ??\n\t\tcontainerHeight ??\n\t\ttargetHeight;\n\n\tconst keyframes = op.keyframes ?? [];\n\tconst movementStyle = op.movementStyle ?? \"spline\";\n\tconst targetAspectRatio = op.aspectRatio || \"input\";\n\n\t// ── Interpolate Ken Burns state ──────────────────────────────────────────\n\tconst offsetMs = ((trimStartOverride ?? 0) + inheritedSeekOffset) * 1000;\n\tconst fpsVal = Math.max(1, fps ?? 30);\n\tconst frameDurationMs = 1000 / fpsVal;\n\tconst timeMs = offsetMs + ((frame ?? 0) / fpsVal) * 1000;\n\tconst prevTimeMs = timeMs - frameDurationMs;\n\n\tconst state = interpolateKenBurns(keyframes, timeMs, movementStyle);\n\tconst prevState = interpolateKenBurns(keyframes, prevTimeMs, movementStyle);\n\n\tconst srcRect = calculateSrcRect(\n\t\tstate,\n\t\tsourceWidth,\n\t\tsourceHeight,\n\t\ttargetAspectRatio,\n\t);\n\tconst prevSrcRect = calculateSrcRect(\n\t\tprevState,\n\t\tsourceWidth,\n\t\tsourceHeight,\n\t\ttargetAspectRatio,\n\t);\n\tconst dstWidth = containerWidth ?? targetWidth;\n\tconst dstHeight = containerHeight ?? targetHeight;\n\tconst dstRect = { x: 0, y: 0, width: dstWidth, height: dstHeight };\n\n\t// ── 5. Render child into a temporary offscreen texture ───────────────────\n\t//\n\t// ROOT CAUSE FIX: drawChild receives `matrix: new DOMMatrix()` which resets\n\t// the renderer's transform stack internally, so pushTransform() is silently\n\t// ignored by the child. We therefore render the child to a temp texture at\n\t// its native size (no Ken Burns transform applied), then composite the\n\t// cropped region ourselves using drawTextureRegion — which does not depend\n\t// on the transform stack at all.\n\t//\n\tconst tempTex = ctx.renderer.getTemporaryTexture(sourceWidth, sourceHeight, [\n\t\t...(props.excludeTextures || []),\n\t\ttargetTexture,\n\t]);\n\tconst tempView = tempTex.createView();\n\n\t// End the current pass before starting child rendering.\n\tpass.end();\n\n\t// Clear the temporary texture to avoid garbage from previous frames\n\tconst clearPass = ctx.renderer.beginFrame(\n\t\tencoder,\n\t\ttempView,\n\t\t{ r: 0, g: 0, b: 0, a: 0 },\n\t\tsourceWidth,\n\t\tsourceHeight,\n\t\t\"clear\",\n\t);\n\tclearPass.end();\n\n\tctx.renderer.pushScissor({\n\t\tx: 0,\n\t\ty: 0,\n\t\twidth: sourceWidth,\n\t\theight: sourceHeight,\n\t});\n\tctx.renderer.pushIdentity();\n\n\tawait drawChild(\n\t\tchildMedia,\n\t\t{\n\t\t\t...props,\n\t\t\tcontainerWidth: sourceWidth,\n\t\t\tcontainerHeight: sourceHeight,\n\t\t},\n\t\ttempView,\n\t\ttempTex,\n\t\tsourceWidth,\n\t\tsourceHeight,\n\t);\n\n\tctx.renderer.popTransform();\n\tctx.renderer.popScissor();\n\n\tconst motionBlurSize = op.motionBlurSize ?? 1.5;\n\tlet blurTex: GPUTexture | null = null;\n\n\tif (motionBlurSize > 0) {\n\t\tconst {\n\t\t\tmotionBlurPipeline: pipeline,\n\t\t\tmotionBlurUniformLayout: uLayout,\n\t\t\tsingleTextureLayout: tLayout,\n\t\t} = getMotionBlurResources(ctx.device, ctx.renderer.format);\n\t\tconst sampler = ctx.renderer.samplerCache.getSampler(ctx.device);\n\n\t\tmotionBlurData[0] = prevSrcRect.x / sourceWidth;\n\t\tmotionBlurData[1] = prevSrcRect.y / sourceHeight;\n\t\tmotionBlurData[2] = prevSrcRect.width / sourceWidth;\n\t\tmotionBlurData[3] = prevSrcRect.height / sourceHeight;\n\t\tmotionBlurData[4] = srcRect.x / sourceWidth;\n\t\tmotionBlurData[5] = srcRect.y / sourceHeight;\n\t\tmotionBlurData[6] = srcRect.width / sourceWidth;\n\t\tmotionBlurData[7] = srcRect.height / sourceHeight;\n\t\tmotionBlurData[8] = 12; // samples\n\t\tmotionBlurData[9] = motionBlurSize;\n\n\t\tconst uBuffer = ctx.renderer.getTemporaryBuffer(motionBlurData);\n\n\t\t// 1. Create a temporary texture for the blurred result at layer size\n\t\tblurTex = ctx.renderer.getTemporaryTexture(dstWidth, dstHeight, [\n\t\t\ttargetTexture,\n\t\t\ttempTex,\n\t\t\t...(props.excludeTextures || []),\n\t\t]);\n\t\tconst blurView = blurTex.createView();\n\n\t\t// 2. Render blurred result into blurTex\n\t\tconst blurPass = encoder.beginRenderPass({\n\t\t\tcolorAttachments: [\n\t\t\t\t{\n\t\t\t\t\tview: blurView,\n\t\t\t\t\tloadOp: \"clear\",\n\t\t\t\t\tstoreOp: \"store\",\n\t\t\t\t\tclearValue: { r: 0, g: 0, b: 0, a: 0 },\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\n\t\tblurPass.setPipeline(pipeline!);\n\t\tblurPass.setBindGroup(\n\t\t\t0,\n\t\t\tctx.device.createBindGroup({\n\t\t\t\tlayout: uLayout!,\n\t\t\t\tentries: [{ binding: 0, resource: { buffer: uBuffer } }],\n\t\t\t}),\n\t\t);\n\t\tblurPass.setBindGroup(\n\t\t\t1,\n\t\t\tctx.renderer.bindGroupCache.getBindGroup(\n\t\t\t\tctx.device,\n\t\t\t\ttLayout!,\n\t\t\t\ttempTex,\n\t\t\t\tsampler,\n\t\t\t),\n\t\t);\n\t\tblurPass.draw(4);\n\t\tblurPass.end();\n\t}\n\n\t// ── 6. Composite the cropped source onto the output ──────────────────────\n\t// Begin a new pass on the real target, preserving any existing content.\n\tconst outPass = ctx.renderer.beginFrame(\n\t\tencoder,\n\t\ttargetView,\n\t\t{ r: 0, g: 0, b: 0, a: 0 },\n\t\ttargetWidth,\n\t\ttargetHeight,\n\t\t\"load\",\n\t);\n\n\tif (blurTex) {\n\t\t// 3. Draw the blurred result to the target (respecting parent transform)\n\t\tctx.renderer.drawTexture(outPass, blurTex, dstRect, { opacity });\n\t} else {\n\t\tctx.renderer.drawTextureRegion(outPass, tempTex, srcRect, dstRect, {\n\t\t\topacity,\n\t\t});\n\t}\n\n\targs.pass = outPass;\n};\n","import { defineRenderer } from \"@framefields/node-sdk/renderer\";\nimport { KenBurnsWebGPURenderer } from \"./webgpu-renderer.js\";\n\nexport default defineRenderer({\n\tWebGPURenderer: KenBurnsWebGPURenderer,\n});\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@framefields/node-kenburns",
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"private": false,
|
|
6
|
+
"framefields": {
|
|
7
|
+
"type": "KenBurns",
|
|
8
|
+
"kind": "effect"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"module": "./dist/index.mjs",
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.mts",
|
|
16
|
+
"development": "./src/index.ts",
|
|
17
|
+
"import": "./dist/index.mjs",
|
|
18
|
+
"default": "./dist/index.mjs"
|
|
19
|
+
},
|
|
20
|
+
"./renderer": {
|
|
21
|
+
"types": "./dist/renderer.d.mts",
|
|
22
|
+
"development": "./src/renderers/index.ts",
|
|
23
|
+
"import": "./dist/renderer.mjs",
|
|
24
|
+
"default": "./dist/renderer.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"zod": "^4.3.6",
|
|
29
|
+
"@framefields/core": "2.0.2",
|
|
30
|
+
"@framefields/node-sdk": "2.0.2"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^24.1.0",
|
|
34
|
+
"tsdown": "^0.19.0",
|
|
35
|
+
"typescript": "^5.8.3",
|
|
36
|
+
"@framefields/tsconfig": "2.0.2"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsdown",
|
|
43
|
+
"dev": "tsdown --watch",
|
|
44
|
+
"clean": "rm -rf dist"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/index.ts
ADDED