@elah/core 0.1.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/actions/index.d.ts +3 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/splitClipAtPlayhead.d.ts +8 -0
- package/dist/actions/splitClipAtPlayhead.js +34 -0
- package/dist/actions/types.d.ts +8 -0
- package/dist/actions/types.js +1 -0
- package/dist/assets/importFiles.d.ts +29 -0
- package/dist/assets/importFiles.js +351 -0
- package/dist/assets/index.d.ts +10 -0
- package/dist/assets/index.js +13 -0
- package/dist/assets/store.d.ts +13 -0
- package/dist/assets/store.js +20 -0
- package/dist/assets/types.d.ts +23 -0
- package/dist/assets/types.js +1 -0
- package/dist/debug/trace.d.ts +20 -0
- package/dist/debug/trace.js +98 -0
- package/dist/editor/TimelineEngine.d.ts +65 -0
- package/dist/editor/TimelineEngine.js +413 -0
- package/dist/editor-context.d.ts +10 -0
- package/dist/editor-context.js +10 -0
- package/dist/elements/audio.d.ts +13 -0
- package/dist/elements/audio.js +4 -0
- package/dist/elements/base.d.ts +40 -0
- package/dist/elements/base.js +39 -0
- package/dist/elements/image.d.ts +13 -0
- package/dist/elements/image.js +4 -0
- package/dist/elements/text.d.ts +12 -0
- package/dist/elements/text.js +8 -0
- package/dist/elements/video.d.ts +13 -0
- package/dist/elements/video.js +4 -0
- package/dist/export/ExportWorker.d.ts +1 -0
- package/dist/export/ExportWorker.js +371 -0
- package/dist/export/exportVideo.d.ts +3 -0
- package/dist/export/exportVideo.js +118 -0
- package/dist/export/index.d.ts +2 -0
- package/dist/export/index.js +1 -0
- package/dist/export/lazyExport.d.ts +4 -0
- package/dist/export/lazyExport.js +4 -0
- package/dist/export/types.d.ts +37 -0
- package/dist/export/types.js +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +28 -0
- package/dist/media/audio/AudioPlaybackController.d.ts +26 -0
- package/dist/media/audio/AudioPlaybackController.js +125 -0
- package/dist/media/video/DecoderBackedVideoFrameProvider.d.ts +51 -0
- package/dist/media/video/DecoderBackedVideoFrameProvider.js +125 -0
- package/dist/media/video/FrameCache.d.ts +28 -0
- package/dist/media/video/FrameCache.js +88 -0
- package/dist/media/video/StreamingFrameProducer.d.ts +57 -0
- package/dist/media/video/StreamingFrameProducer.js +356 -0
- package/dist/media/video/VideoDecoderManager.d.ts +59 -0
- package/dist/media/video/VideoDecoderManager.js +342 -0
- package/dist/media/video/VideoFrameProvider.d.ts +101 -0
- package/dist/media/video/VideoFrameProvider.js +257 -0
- package/dist/media/video/demuxer/MediabunnyDemuxer.d.ts +23 -0
- package/dist/media/video/demuxer/MediabunnyDemuxer.js +88 -0
- package/dist/media/video/demuxer/createMediabunnyBackend.d.ts +32 -0
- package/dist/media/video/demuxer/createMediabunnyBackend.js +156 -0
- package/dist/media/video/index.d.ts +8 -0
- package/dist/media/video/index.js +5 -0
- package/dist/playback/PlaybackEngine.d.ts +50 -0
- package/dist/playback/PlaybackEngine.js +188 -0
- package/dist/renderer/gpu/GpuRenderer.d.ts +38 -0
- package/dist/renderer/gpu/GpuRenderer.js +208 -0
- package/dist/renderer/gpu/RenderGraph.d.ts +10 -0
- package/dist/renderer/gpu/RenderGraph.js +80 -0
- package/dist/renderer/gpu/ShaderProgram.d.ts +14 -0
- package/dist/renderer/gpu/ShaderProgram.js +76 -0
- package/dist/renderer/gpu/TexturePool.d.ts +25 -0
- package/dist/renderer/gpu/TexturePool.js +93 -0
- package/dist/renderer/gpu/VideoTexture.d.ts +13 -0
- package/dist/renderer/gpu/VideoTexture.js +54 -0
- package/dist/renderer/gpu/WebGLContext.d.ts +28 -0
- package/dist/renderer/gpu/WebGLContext.js +102 -0
- package/dist/renderer/gpu/debug/DebugGpuRenderer.d.ts +27 -0
- package/dist/renderer/gpu/debug/DebugGpuRenderer.js +108 -0
- package/dist/renderer/gpu/debug/DebugOverlay.d.ts +17 -0
- package/dist/renderer/gpu/debug/DebugOverlay.js +83 -0
- package/dist/renderer/gpu/debug/GpuDebugCounters.d.ts +38 -0
- package/dist/renderer/gpu/debug/GpuDebugCounters.js +72 -0
- package/dist/renderer/gpu/debug/GpuDebugGlobal.d.ts +16 -0
- package/dist/renderer/gpu/debug/GpuDebugGlobal.js +14 -0
- package/dist/renderer/gpu/debug/GpuRendererDebugPanel.d.ts +31 -0
- package/dist/renderer/gpu/debug/GpuRendererDebugPanel.js +128 -0
- package/dist/renderer/gpu/debug/RecordingGl.d.ts +88 -0
- package/dist/renderer/gpu/debug/RecordingGl.js +214 -0
- package/dist/renderer/gpu/debug/playground.d.ts +20 -0
- package/dist/renderer/gpu/debug/playground.js +64 -0
- package/dist/renderer/gpu/debug/scenarios.d.ts +7 -0
- package/dist/renderer/gpu/debug/scenarios.js +145 -0
- package/dist/renderer/gpu/debug/types.d.ts +16 -0
- package/dist/renderer/gpu/debug/types.js +1 -0
- package/dist/renderer/gpu/layers/FrameProbeLayer.d.ts +16 -0
- package/dist/renderer/gpu/layers/FrameProbeLayer.js +127 -0
- package/dist/renderer/gpu/layers/ImageLayer.d.ts +23 -0
- package/dist/renderer/gpu/layers/ImageLayer.js +124 -0
- package/dist/renderer/gpu/layers/TestLayer.d.ts +16 -0
- package/dist/renderer/gpu/layers/TestLayer.js +109 -0
- package/dist/renderer/gpu/layers/TextLayer.d.ts +19 -0
- package/dist/renderer/gpu/layers/TextLayer.js +166 -0
- package/dist/renderer/gpu/layers/VideoLayer.d.ts +38 -0
- package/dist/renderer/gpu/layers/VideoLayer.js +194 -0
- package/dist/renderer/gpu/layers/drawRect.d.ts +13 -0
- package/dist/renderer/gpu/layers/drawRect.js +55 -0
- package/dist/renderer/gpu/layers/objectFit.d.ts +7 -0
- package/dist/renderer/gpu/layers/objectFit.js +26 -0
- package/dist/renderer/gpu/layers/textLayout.d.ts +47 -0
- package/dist/renderer/gpu/layers/textLayout.js +82 -0
- package/dist/renderer/gpu/layers/types.d.ts +18 -0
- package/dist/renderer/gpu/layers/types.js +1 -0
- package/dist/renderer/gpu/shaders/quad.frag.d.ts +1 -0
- package/dist/renderer/gpu/shaders/quad.frag.js +14 -0
- package/dist/renderer/gpu/shaders/quad.vert.d.ts +1 -0
- package/dist/renderer/gpu/shaders/quad.vert.js +28 -0
- package/dist/renderer/gpu/types.d.ts +21 -0
- package/dist/renderer/gpu/types.js +1 -0
- package/dist/renderer/gpu/viewport.d.ts +7 -0
- package/dist/renderer/gpu/viewport.js +33 -0
- package/dist/renderer/types.d.ts +7 -0
- package/dist/renderer/types.js +1 -0
- package/dist/resolver/resolveTimeline.d.ts +3 -0
- package/dist/resolver/resolveTimeline.js +249 -0
- package/dist/resolver/scene.d.ts +54 -0
- package/dist/resolver/scene.js +1 -0
- package/dist/stores/playback.store.d.ts +50 -0
- package/dist/stores/playback.store.js +42 -0
- package/dist/stores/selection.store.d.ts +12 -0
- package/dist/stores/selection.store.js +19 -0
- package/dist/stores/tracks.store.d.ts +19 -0
- package/dist/stores/tracks.store.js +27 -0
- package/dist/stores/transitions.store.d.ts +9 -0
- package/dist/stores/transitions.store.js +7 -0
- package/dist/track/track.d.ts +8 -0
- package/dist/track/track.js +19 -0
- package/dist/types/index.d.ts +117 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/frames.d.ts +20 -0
- package/dist/utils/frames.js +40 -0
- package/dist/utils/id.d.ts +1 -0
- package/dist/utils/id.js +3 -0
- package/dist/utils/snap.d.ts +5 -0
- package/dist/utils/snap.js +79 -0
- package/dist/visitor/add.d.ts +3 -0
- package/dist/visitor/add.js +16 -0
- package/dist/visitor/clone.d.ts +3 -0
- package/dist/visitor/clone.js +25 -0
- package/dist/visitor/remove.d.ts +5 -0
- package/dist/visitor/remove.js +29 -0
- package/dist/visitor/split.d.ts +3 -0
- package/dist/visitor/split.js +31 -0
- package/dist/visitor/update.d.ts +4 -0
- package/dist/visitor/update.js +31 -0
- package/package.json +31 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { ShaderProgram } from '../ShaderProgram';
|
|
2
|
+
import { QUAD_FRAG_SRC } from '../shaders/quad.frag';
|
|
3
|
+
import { QUAD_VERT_SRC } from '../shaders/quad.vert';
|
|
4
|
+
import { buildVideoTransformMatrix } from './VideoLayer';
|
|
5
|
+
const PROBE_WIDTH = 640;
|
|
6
|
+
const PROBE_HEIGHT = 360;
|
|
7
|
+
function frameColor(frame) {
|
|
8
|
+
const hue = (frame * 37) % 360;
|
|
9
|
+
return `hsl(${hue}, 70%, 45%)`;
|
|
10
|
+
}
|
|
11
|
+
export class FrameProbeLayer {
|
|
12
|
+
constructor() {
|
|
13
|
+
this._program = null;
|
|
14
|
+
this._vao = null;
|
|
15
|
+
this._gl = null;
|
|
16
|
+
this._resources = new Map();
|
|
17
|
+
}
|
|
18
|
+
acquire(item, ctx) {
|
|
19
|
+
const { gl } = ctx;
|
|
20
|
+
this._gl = gl;
|
|
21
|
+
this._ensurePipeline(gl);
|
|
22
|
+
const texture = gl.createTexture();
|
|
23
|
+
if (!texture) {
|
|
24
|
+
throw new Error('FrameProbeLayer: gl.createTexture() returned null');
|
|
25
|
+
}
|
|
26
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
27
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
28
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
29
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
30
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
31
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
32
|
+
const canvas = document.createElement('canvas');
|
|
33
|
+
canvas.width = PROBE_WIDTH;
|
|
34
|
+
canvas.height = PROBE_HEIGHT;
|
|
35
|
+
const ctx2d = canvas.getContext('2d');
|
|
36
|
+
if (!ctx2d) {
|
|
37
|
+
throw new Error('FrameProbeLayer: 2D context unavailable');
|
|
38
|
+
}
|
|
39
|
+
this._resources.set(item.id, {
|
|
40
|
+
texture,
|
|
41
|
+
canvas,
|
|
42
|
+
ctx2d,
|
|
43
|
+
lastPaintedFrame: -1,
|
|
44
|
+
});
|
|
45
|
+
console.log(`[PROBE-ACQUIRE] clip=${item.id} sourceFrame=${item.sourceFrame}`);
|
|
46
|
+
}
|
|
47
|
+
release(itemId) {
|
|
48
|
+
const res = this._resources.get(itemId);
|
|
49
|
+
if (!res)
|
|
50
|
+
return;
|
|
51
|
+
if (this._gl) {
|
|
52
|
+
this._gl.deleteTexture(res.texture);
|
|
53
|
+
}
|
|
54
|
+
this._resources.delete(itemId);
|
|
55
|
+
console.log(`[PROBE-RELEASE] clip=${itemId}`);
|
|
56
|
+
}
|
|
57
|
+
draw(item, ctx) {
|
|
58
|
+
this._gl = ctx.gl;
|
|
59
|
+
const res = this._resources.get(item.id);
|
|
60
|
+
if (!res || !this._program || !this._vao)
|
|
61
|
+
return;
|
|
62
|
+
const { gl } = ctx;
|
|
63
|
+
const frame = item.sourceFrame;
|
|
64
|
+
if (res.lastPaintedFrame !== frame) {
|
|
65
|
+
this._paint(res, frame);
|
|
66
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
67
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, res.canvas);
|
|
68
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
69
|
+
res.lastPaintedFrame = frame;
|
|
70
|
+
console.log(`[PROBE-UPLOAD] clip=${item.id} frame=${frame}`);
|
|
71
|
+
}
|
|
72
|
+
const opacity = item.opacity ?? 1;
|
|
73
|
+
this._program.use(gl);
|
|
74
|
+
gl.bindVertexArray(this._vao);
|
|
75
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
76
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
77
|
+
this._program.setUniform1i(gl, 'uTexture', 0);
|
|
78
|
+
this._program.setUniform1f(gl, 'uOpacity', opacity);
|
|
79
|
+
this._program.setUniformMatrix3fv(gl, 'uTransform', false, buildVideoTransformMatrix(item, ctx.stage.width, ctx.stage.height));
|
|
80
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
81
|
+
console.log(`[PROBE-DRAW] clip=${item.id} frame=${frame}`);
|
|
82
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
83
|
+
gl.bindVertexArray(null);
|
|
84
|
+
}
|
|
85
|
+
dispose() {
|
|
86
|
+
this._resources.clear();
|
|
87
|
+
this._program = null;
|
|
88
|
+
this._vao = null;
|
|
89
|
+
this._gl = null;
|
|
90
|
+
}
|
|
91
|
+
notifyContextLost() {
|
|
92
|
+
this._resources.clear();
|
|
93
|
+
this._program = null;
|
|
94
|
+
this._vao = null;
|
|
95
|
+
this._gl = null;
|
|
96
|
+
}
|
|
97
|
+
disposeGL(gl) {
|
|
98
|
+
for (const res of this._resources.values()) {
|
|
99
|
+
gl.deleteTexture(res.texture);
|
|
100
|
+
}
|
|
101
|
+
if (this._vao)
|
|
102
|
+
gl.deleteVertexArray(this._vao);
|
|
103
|
+
if (this._program)
|
|
104
|
+
this._program.dispose(gl);
|
|
105
|
+
this.dispose();
|
|
106
|
+
}
|
|
107
|
+
_paint(res, frame) {
|
|
108
|
+
const { ctx2d } = res;
|
|
109
|
+
ctx2d.fillStyle = frameColor(frame);
|
|
110
|
+
ctx2d.fillRect(0, 0, PROBE_WIDTH, PROBE_HEIGHT);
|
|
111
|
+
ctx2d.fillStyle = '#ffffff';
|
|
112
|
+
ctx2d.font = 'bold 96px monospace';
|
|
113
|
+
ctx2d.textAlign = 'center';
|
|
114
|
+
ctx2d.textBaseline = 'middle';
|
|
115
|
+
ctx2d.fillText(`frame ${frame}`, PROBE_WIDTH / 2, PROBE_HEIGHT / 2);
|
|
116
|
+
}
|
|
117
|
+
_ensurePipeline(gl) {
|
|
118
|
+
if (this._program && this._vao)
|
|
119
|
+
return;
|
|
120
|
+
this._program = ShaderProgram.create(gl, QUAD_VERT_SRC, QUAD_FRAG_SRC);
|
|
121
|
+
const vao = gl.createVertexArray();
|
|
122
|
+
if (!vao) {
|
|
123
|
+
throw new Error('FrameProbeLayer: gl.createVertexArray() returned null');
|
|
124
|
+
}
|
|
125
|
+
this._vao = vao;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ActiveImageClip } from '../../../resolver/scene';
|
|
2
|
+
import type { Layer, LayerContext } from './types';
|
|
3
|
+
export interface LoadedImage {
|
|
4
|
+
source: TexImageSource;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
export type ImageLoader = (src: string) => Promise<LoadedImage>;
|
|
9
|
+
export declare class ImageLayer implements Layer<ActiveImageClip> {
|
|
10
|
+
private _program;
|
|
11
|
+
private _vao;
|
|
12
|
+
private _gl;
|
|
13
|
+
private readonly _resources;
|
|
14
|
+
private readonly _loadImage;
|
|
15
|
+
constructor(loadImage?: ImageLoader);
|
|
16
|
+
acquire(item: ActiveImageClip, ctx: LayerContext): void;
|
|
17
|
+
release(itemId: string): void;
|
|
18
|
+
draw(item: ActiveImageClip, ctx: LayerContext): void;
|
|
19
|
+
dispose(): void;
|
|
20
|
+
notifyContextLost(): void;
|
|
21
|
+
getTextureCount(): number;
|
|
22
|
+
private _ensurePipeline;
|
|
23
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { ShaderProgram } from '../ShaderProgram';
|
|
2
|
+
import { QUAD_FRAG_SRC } from '../shaders/quad.frag';
|
|
3
|
+
import { QUAD_VERT_SRC } from '../shaders/quad.vert';
|
|
4
|
+
import { buildDrawTransformMatrix } from './drawRect';
|
|
5
|
+
const defaultImageLoader = (src) => new Promise((resolve, reject) => {
|
|
6
|
+
const img = document.createElement('img');
|
|
7
|
+
img.onload = () => {
|
|
8
|
+
img.onload = null;
|
|
9
|
+
img.onerror = null;
|
|
10
|
+
resolve({ source: img, width: img.naturalWidth, height: img.naturalHeight });
|
|
11
|
+
};
|
|
12
|
+
img.onerror = () => {
|
|
13
|
+
img.onload = null;
|
|
14
|
+
img.onerror = null;
|
|
15
|
+
reject(new Error(`ImageLayer: failed to load image "${src}"`));
|
|
16
|
+
};
|
|
17
|
+
img.src = src;
|
|
18
|
+
});
|
|
19
|
+
export class ImageLayer {
|
|
20
|
+
constructor(loadImage) {
|
|
21
|
+
this._program = null;
|
|
22
|
+
this._vao = null;
|
|
23
|
+
this._gl = null;
|
|
24
|
+
this._resources = new Map();
|
|
25
|
+
this._loadImage = loadImage ?? defaultImageLoader;
|
|
26
|
+
}
|
|
27
|
+
acquire(item, ctx) {
|
|
28
|
+
const { gl } = ctx;
|
|
29
|
+
this._gl = gl;
|
|
30
|
+
this._ensurePipeline(gl);
|
|
31
|
+
const texture = gl.createTexture();
|
|
32
|
+
if (!texture) {
|
|
33
|
+
throw new Error('ImageLayer: gl.createTexture() returned null');
|
|
34
|
+
}
|
|
35
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
36
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
37
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
38
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
39
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
40
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
41
|
+
const res = { texture, image: null, uploaded: false };
|
|
42
|
+
this._resources.set(item.id, res);
|
|
43
|
+
void this._loadImage(item.src)
|
|
44
|
+
.then((image) => {
|
|
45
|
+
if (this._resources.get(item.id) === res) {
|
|
46
|
+
res.image = image;
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
.catch((err) => {
|
|
50
|
+
console.warn(`[ImageLayer] ${err.message}`);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
release(itemId) {
|
|
54
|
+
const res = this._resources.get(itemId);
|
|
55
|
+
if (!res)
|
|
56
|
+
return;
|
|
57
|
+
if (this._gl) {
|
|
58
|
+
this._gl.deleteTexture(res.texture);
|
|
59
|
+
}
|
|
60
|
+
this._resources.delete(itemId);
|
|
61
|
+
}
|
|
62
|
+
draw(item, ctx) {
|
|
63
|
+
this._gl = ctx.gl;
|
|
64
|
+
const res = this._resources.get(item.id);
|
|
65
|
+
if (!res || !this._program || !this._vao)
|
|
66
|
+
return;
|
|
67
|
+
const { gl } = ctx;
|
|
68
|
+
if (res.image && !res.uploaded) {
|
|
69
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
70
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
|
71
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, res.image.source);
|
|
72
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
73
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
74
|
+
res.uploaded = true;
|
|
75
|
+
}
|
|
76
|
+
if (!res.uploaded || !res.image)
|
|
77
|
+
return;
|
|
78
|
+
const opacity = item.opacity ?? 1;
|
|
79
|
+
this._program.use(gl);
|
|
80
|
+
gl.bindVertexArray(this._vao);
|
|
81
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
82
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
83
|
+
this._program.setUniform1i(gl, 'uTexture', 0);
|
|
84
|
+
this._program.setUniform1f(gl, 'uOpacity', opacity);
|
|
85
|
+
this._program.setUniformMatrix3fv(gl, 'uTransform', false, buildDrawTransformMatrix(item.transform, ctx.stage.width, ctx.stage.height, res.image.width, res.image.height));
|
|
86
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
87
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
88
|
+
gl.bindVertexArray(null);
|
|
89
|
+
}
|
|
90
|
+
dispose() {
|
|
91
|
+
if (this._gl) {
|
|
92
|
+
for (const res of this._resources.values()) {
|
|
93
|
+
this._gl.deleteTexture(res.texture);
|
|
94
|
+
}
|
|
95
|
+
if (this._vao)
|
|
96
|
+
this._gl.deleteVertexArray(this._vao);
|
|
97
|
+
if (this._program)
|
|
98
|
+
this._program.dispose(this._gl);
|
|
99
|
+
}
|
|
100
|
+
this._resources.clear();
|
|
101
|
+
this._program = null;
|
|
102
|
+
this._vao = null;
|
|
103
|
+
this._gl = null;
|
|
104
|
+
}
|
|
105
|
+
notifyContextLost() {
|
|
106
|
+
this._resources.clear();
|
|
107
|
+
this._program = null;
|
|
108
|
+
this._vao = null;
|
|
109
|
+
this._gl = null;
|
|
110
|
+
}
|
|
111
|
+
getTextureCount() {
|
|
112
|
+
return this._resources.size;
|
|
113
|
+
}
|
|
114
|
+
_ensurePipeline(gl) {
|
|
115
|
+
if (this._program && this._vao)
|
|
116
|
+
return;
|
|
117
|
+
this._program = ShaderProgram.create(gl, QUAD_VERT_SRC, QUAD_FRAG_SRC);
|
|
118
|
+
const vao = gl.createVertexArray();
|
|
119
|
+
if (!vao) {
|
|
120
|
+
throw new Error('ImageLayer: gl.createVertexArray() returned null');
|
|
121
|
+
}
|
|
122
|
+
this._vao = vao;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DebugRenderItem } from '../debug/types';
|
|
2
|
+
import type { Layer, LayerContext } from './types';
|
|
3
|
+
export declare function buildTransformMatrix(item: DebugRenderItem, stageWidth: number, stageHeight: number): Float32Array;
|
|
4
|
+
export declare class TestLayer implements Layer<DebugRenderItem> {
|
|
5
|
+
private _program;
|
|
6
|
+
private _vao;
|
|
7
|
+
private _gl;
|
|
8
|
+
private readonly _resources;
|
|
9
|
+
acquire(item: DebugRenderItem, ctx: LayerContext): void;
|
|
10
|
+
release(itemId: string): void;
|
|
11
|
+
draw(item: DebugRenderItem, ctx: LayerContext): void;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
notifyContextLost(): void;
|
|
14
|
+
disposeGL(gl: WebGL2RenderingContext): void;
|
|
15
|
+
private _ensurePipeline;
|
|
16
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ShaderProgram } from '../ShaderProgram';
|
|
2
|
+
import { QUAD_FRAG_SRC } from '../shaders/quad.frag';
|
|
3
|
+
import { QUAD_VERT_SRC } from '../shaders/quad.vert';
|
|
4
|
+
export function buildTransformMatrix(item, stageWidth, stageHeight) {
|
|
5
|
+
const xs = item.x / stageWidth;
|
|
6
|
+
const ys = item.y / stageHeight;
|
|
7
|
+
const ws = item.width / stageWidth;
|
|
8
|
+
const hs = item.height / stageHeight;
|
|
9
|
+
const rotation = item.rotation ?? 0;
|
|
10
|
+
const sc = Math.cos(rotation);
|
|
11
|
+
const ss = Math.sin(rotation);
|
|
12
|
+
const tx = 2 * (xs + ws / 2 - 0.5 * sc * ws + 0.5 * ss * hs) - 1;
|
|
13
|
+
const ty = 2 * (ys + hs / 2 - 0.5 * ss * ws - 0.5 * sc * hs) - 1;
|
|
14
|
+
return new Float32Array([
|
|
15
|
+
2 * sc * ws, 2 * ss * ws, 0,
|
|
16
|
+
-2 * ss * hs, 2 * sc * hs, 0,
|
|
17
|
+
tx, ty, 1,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
export class TestLayer {
|
|
21
|
+
constructor() {
|
|
22
|
+
this._program = null;
|
|
23
|
+
this._vao = null;
|
|
24
|
+
this._gl = null;
|
|
25
|
+
this._resources = new Map();
|
|
26
|
+
}
|
|
27
|
+
acquire(item, ctx) {
|
|
28
|
+
const { gl } = ctx;
|
|
29
|
+
this._gl = gl;
|
|
30
|
+
this._ensurePipeline(gl);
|
|
31
|
+
const texture = gl.createTexture();
|
|
32
|
+
if (!texture) {
|
|
33
|
+
throw new Error('TestLayer: gl.createTexture() returned null');
|
|
34
|
+
}
|
|
35
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
36
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
37
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
38
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
39
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
40
|
+
const [r, g, b, a] = item.color;
|
|
41
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([
|
|
42
|
+
Math.round(r * 255),
|
|
43
|
+
Math.round(g * 255),
|
|
44
|
+
Math.round(b * 255),
|
|
45
|
+
Math.round(a * 255),
|
|
46
|
+
]));
|
|
47
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
48
|
+
this._resources.set(item.id, { texture, color: item.color });
|
|
49
|
+
}
|
|
50
|
+
release(itemId) {
|
|
51
|
+
const res = this._resources.get(itemId);
|
|
52
|
+
if (!res)
|
|
53
|
+
return;
|
|
54
|
+
if (this._gl) {
|
|
55
|
+
this._gl.deleteTexture(res.texture);
|
|
56
|
+
}
|
|
57
|
+
this._resources.delete(itemId);
|
|
58
|
+
}
|
|
59
|
+
draw(item, ctx) {
|
|
60
|
+
this._gl = ctx.gl;
|
|
61
|
+
const res = this._resources.get(item.id);
|
|
62
|
+
if (!res || !this._program || !this._vao)
|
|
63
|
+
return;
|
|
64
|
+
const { gl } = ctx;
|
|
65
|
+
const opacity = item.opacity ?? 1;
|
|
66
|
+
this._program.use(gl);
|
|
67
|
+
gl.bindVertexArray(this._vao);
|
|
68
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
69
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
70
|
+
this._program.setUniform1i(gl, 'uTexture', 0);
|
|
71
|
+
this._program.setUniform1f(gl, 'uOpacity', opacity);
|
|
72
|
+
this._program.setUniformMatrix3fv(gl, 'uTransform', false, buildTransformMatrix(item, ctx.stage.width, ctx.stage.height));
|
|
73
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
74
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
75
|
+
gl.bindVertexArray(null);
|
|
76
|
+
}
|
|
77
|
+
dispose() {
|
|
78
|
+
this._resources.clear();
|
|
79
|
+
this._program = null;
|
|
80
|
+
this._vao = null;
|
|
81
|
+
this._gl = null;
|
|
82
|
+
}
|
|
83
|
+
notifyContextLost() {
|
|
84
|
+
this._resources.clear();
|
|
85
|
+
this._program = null;
|
|
86
|
+
this._vao = null;
|
|
87
|
+
this._gl = null;
|
|
88
|
+
}
|
|
89
|
+
disposeGL(gl) {
|
|
90
|
+
for (const res of this._resources.values()) {
|
|
91
|
+
gl.deleteTexture(res.texture);
|
|
92
|
+
}
|
|
93
|
+
if (this._vao)
|
|
94
|
+
gl.deleteVertexArray(this._vao);
|
|
95
|
+
if (this._program)
|
|
96
|
+
this._program.dispose(gl);
|
|
97
|
+
this.dispose();
|
|
98
|
+
}
|
|
99
|
+
_ensurePipeline(gl) {
|
|
100
|
+
if (this._program && this._vao)
|
|
101
|
+
return;
|
|
102
|
+
this._program = ShaderProgram.create(gl, QUAD_VERT_SRC, QUAD_FRAG_SRC);
|
|
103
|
+
const vao = gl.createVertexArray();
|
|
104
|
+
if (!vao) {
|
|
105
|
+
throw new Error('TestLayer: gl.createVertexArray() returned null');
|
|
106
|
+
}
|
|
107
|
+
this._vao = vao;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ActiveTextClip } from '../../../resolver/scene';
|
|
2
|
+
import type { Layer, LayerContext } from './types';
|
|
3
|
+
export type TextCanvasFactory = () => HTMLCanvasElement;
|
|
4
|
+
export declare class TextLayer implements Layer<ActiveTextClip> {
|
|
5
|
+
private _program;
|
|
6
|
+
private _vao;
|
|
7
|
+
private _gl;
|
|
8
|
+
private readonly _resources;
|
|
9
|
+
private readonly _createCanvas;
|
|
10
|
+
constructor(createCanvas?: TextCanvasFactory);
|
|
11
|
+
acquire(item: ActiveTextClip, ctx: LayerContext): void;
|
|
12
|
+
release(itemId: string): void;
|
|
13
|
+
draw(item: ActiveTextClip, ctx: LayerContext): void;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
notifyContextLost(): void;
|
|
16
|
+
getTextureCount(): number;
|
|
17
|
+
private _paint;
|
|
18
|
+
private _ensurePipeline;
|
|
19
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { ShaderProgram } from '../ShaderProgram';
|
|
2
|
+
import { QUAD_FRAG_SRC } from '../shaders/quad.frag';
|
|
3
|
+
import { QUAD_VERT_SRC } from '../shaders/quad.vert';
|
|
4
|
+
import { computeTextLayout } from './textLayout';
|
|
5
|
+
const FULL_STAGE_MAT3 = new Float32Array([2, 0, 0, 0, 2, 0, -1, -1, 1]);
|
|
6
|
+
function buildTextTransformMatrix(transform, stage) {
|
|
7
|
+
if (!transform || transform.rotation === 0) {
|
|
8
|
+
return FULL_STAGE_MAT3;
|
|
9
|
+
}
|
|
10
|
+
const a = Math.cos(transform.rotation);
|
|
11
|
+
const b = Math.sin(transform.rotation);
|
|
12
|
+
const px = transform.x;
|
|
13
|
+
const py = transform.y;
|
|
14
|
+
const wOverH = stage.width / stage.height;
|
|
15
|
+
const hOverW = stage.height / stage.width;
|
|
16
|
+
const txx = 2 * (px - a * px + b * hOverW * py) - 1;
|
|
17
|
+
const tyy = 2 * (py - b * wOverH * px - a * py) - 1;
|
|
18
|
+
return new Float32Array([
|
|
19
|
+
2 * a, 2 * b * wOverH, 0,
|
|
20
|
+
-2 * b * hOverW, 2 * a, 0,
|
|
21
|
+
txx, tyy, 1,
|
|
22
|
+
]);
|
|
23
|
+
}
|
|
24
|
+
function paintSignature(item, stage) {
|
|
25
|
+
return JSON.stringify([
|
|
26
|
+
item.content,
|
|
27
|
+
item.fontSize,
|
|
28
|
+
item.color,
|
|
29
|
+
item.fontFamily,
|
|
30
|
+
item.fontWeight,
|
|
31
|
+
item.textAlign,
|
|
32
|
+
item.transform?.x,
|
|
33
|
+
item.transform?.y,
|
|
34
|
+
item.transform?.scale,
|
|
35
|
+
stage.width,
|
|
36
|
+
stage.height,
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
export class TextLayer {
|
|
40
|
+
constructor(createCanvas) {
|
|
41
|
+
this._program = null;
|
|
42
|
+
this._vao = null;
|
|
43
|
+
this._gl = null;
|
|
44
|
+
this._resources = new Map();
|
|
45
|
+
this._createCanvas = createCanvas ?? (() => document.createElement('canvas'));
|
|
46
|
+
}
|
|
47
|
+
acquire(item, ctx) {
|
|
48
|
+
const { gl } = ctx;
|
|
49
|
+
this._gl = gl;
|
|
50
|
+
this._ensurePipeline(gl);
|
|
51
|
+
const texture = gl.createTexture();
|
|
52
|
+
if (!texture) {
|
|
53
|
+
throw new Error('TextLayer: gl.createTexture() returned null');
|
|
54
|
+
}
|
|
55
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
56
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
57
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
58
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
59
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
60
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
61
|
+
const canvas = this._createCanvas();
|
|
62
|
+
canvas.width = ctx.stage.width;
|
|
63
|
+
canvas.height = ctx.stage.height;
|
|
64
|
+
const ctx2d = canvas.getContext('2d');
|
|
65
|
+
if (!ctx2d) {
|
|
66
|
+
throw new Error('TextLayer: 2D context unavailable');
|
|
67
|
+
}
|
|
68
|
+
this._resources.set(item.id, {
|
|
69
|
+
texture,
|
|
70
|
+
canvas,
|
|
71
|
+
ctx2d,
|
|
72
|
+
width: canvas.width,
|
|
73
|
+
height: canvas.height,
|
|
74
|
+
lastSignature: '',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
release(itemId) {
|
|
78
|
+
const res = this._resources.get(itemId);
|
|
79
|
+
if (!res)
|
|
80
|
+
return;
|
|
81
|
+
if (this._gl) {
|
|
82
|
+
this._gl.deleteTexture(res.texture);
|
|
83
|
+
}
|
|
84
|
+
this._resources.delete(itemId);
|
|
85
|
+
}
|
|
86
|
+
draw(item, ctx) {
|
|
87
|
+
this._gl = ctx.gl;
|
|
88
|
+
const res = this._resources.get(item.id);
|
|
89
|
+
if (!res || !this._program || !this._vao)
|
|
90
|
+
return;
|
|
91
|
+
const { gl } = ctx;
|
|
92
|
+
if (res.width !== ctx.stage.width || res.height !== ctx.stage.height) {
|
|
93
|
+
res.canvas.width = ctx.stage.width;
|
|
94
|
+
res.canvas.height = ctx.stage.height;
|
|
95
|
+
res.width = ctx.stage.width;
|
|
96
|
+
res.height = ctx.stage.height;
|
|
97
|
+
res.lastSignature = '';
|
|
98
|
+
}
|
|
99
|
+
const sig = paintSignature(item, ctx.stage);
|
|
100
|
+
if (res.lastSignature !== sig) {
|
|
101
|
+
this._paint(res, item, ctx.stage);
|
|
102
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
103
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
|
104
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, res.canvas);
|
|
105
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
106
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
107
|
+
res.lastSignature = sig;
|
|
108
|
+
}
|
|
109
|
+
const opacity = item.opacity ?? 1;
|
|
110
|
+
this._program.use(gl);
|
|
111
|
+
gl.bindVertexArray(this._vao);
|
|
112
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
113
|
+
gl.bindTexture(gl.TEXTURE_2D, res.texture);
|
|
114
|
+
this._program.setUniform1i(gl, 'uTexture', 0);
|
|
115
|
+
this._program.setUniform1f(gl, 'uOpacity', opacity);
|
|
116
|
+
this._program.setUniformMatrix3fv(gl, 'uTransform', false, buildTextTransformMatrix(item.transform, ctx.stage));
|
|
117
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
118
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
119
|
+
gl.bindVertexArray(null);
|
|
120
|
+
}
|
|
121
|
+
dispose() {
|
|
122
|
+
if (this._gl) {
|
|
123
|
+
for (const res of this._resources.values()) {
|
|
124
|
+
this._gl.deleteTexture(res.texture);
|
|
125
|
+
}
|
|
126
|
+
if (this._vao)
|
|
127
|
+
this._gl.deleteVertexArray(this._vao);
|
|
128
|
+
if (this._program)
|
|
129
|
+
this._program.dispose(this._gl);
|
|
130
|
+
}
|
|
131
|
+
this._resources.clear();
|
|
132
|
+
this._program = null;
|
|
133
|
+
this._vao = null;
|
|
134
|
+
this._gl = null;
|
|
135
|
+
}
|
|
136
|
+
notifyContextLost() {
|
|
137
|
+
this._resources.clear();
|
|
138
|
+
this._program = null;
|
|
139
|
+
this._vao = null;
|
|
140
|
+
this._gl = null;
|
|
141
|
+
}
|
|
142
|
+
getTextureCount() {
|
|
143
|
+
return this._resources.size;
|
|
144
|
+
}
|
|
145
|
+
_paint(res, item, stage) {
|
|
146
|
+
const { ctx2d } = res;
|
|
147
|
+
ctx2d.clearRect(0, 0, stage.width, stage.height);
|
|
148
|
+
const layout = computeTextLayout(ctx2d, item, stage);
|
|
149
|
+
ctx2d.fillStyle = layout.style.color;
|
|
150
|
+
ctx2d.textBaseline = 'middle';
|
|
151
|
+
ctx2d.textAlign = layout.style.textAlign;
|
|
152
|
+
layout.lines.forEach((line, i) => {
|
|
153
|
+
ctx2d.fillText(line, layout.anchorX, layout.firstLineY + i * layout.lineAdvance);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
_ensurePipeline(gl) {
|
|
157
|
+
if (this._program && this._vao)
|
|
158
|
+
return;
|
|
159
|
+
this._program = ShaderProgram.create(gl, QUAD_VERT_SRC, QUAD_FRAG_SRC);
|
|
160
|
+
const vao = gl.createVertexArray();
|
|
161
|
+
if (!vao) {
|
|
162
|
+
throw new Error('TextLayer: gl.createVertexArray() returned null');
|
|
163
|
+
}
|
|
164
|
+
this._vao = vao;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ActiveVideoClip } from '../../../resolver/scene';
|
|
2
|
+
import type { TexturePool } from '../TexturePool';
|
|
3
|
+
import { type VideoFrameProvider, type VideoFrameProviderDeps } from '../../../media/video';
|
|
4
|
+
import { type DrawRect } from './drawRect';
|
|
5
|
+
import type { Layer, LayerContext } from './types';
|
|
6
|
+
export { buildTransformMatrixFromRect, resolveTransformRect, resolveDrawRect, buildDrawTransformMatrix, } from './drawRect';
|
|
7
|
+
export type { DrawRect } from './drawRect';
|
|
8
|
+
export type VideoDrawRect = DrawRect;
|
|
9
|
+
export declare function buildVideoTransformMatrix(item: ActiveVideoClip, stageWidth: number, stageHeight: number, contentWidth?: number, contentHeight?: number): Float32Array;
|
|
10
|
+
export type VideoFrameProviderFactory = (src: string) => VideoFrameProvider;
|
|
11
|
+
export declare class VideoLayer implements Layer<ActiveVideoClip> {
|
|
12
|
+
private readonly _pool;
|
|
13
|
+
private readonly _providerFactory;
|
|
14
|
+
private readonly _deps;
|
|
15
|
+
private _program;
|
|
16
|
+
private _vao;
|
|
17
|
+
private _gl;
|
|
18
|
+
private readonly _providers;
|
|
19
|
+
private readonly _textures;
|
|
20
|
+
private readonly _srcByItemId;
|
|
21
|
+
private readonly _contentSizeByItemId;
|
|
22
|
+
private readonly _lastLoggedSourceFrameByItemId;
|
|
23
|
+
private _holdoverTexture;
|
|
24
|
+
constructor(pool: TexturePool, providerFactoryOrDeps?: VideoFrameProviderFactory | VideoFrameProviderDeps);
|
|
25
|
+
acquire(item: ActiveVideoClip, ctx: LayerContext): void;
|
|
26
|
+
release(itemId: string): void;
|
|
27
|
+
draw(item: ActiveVideoClip, ctx: LayerContext): void;
|
|
28
|
+
dispose(): void;
|
|
29
|
+
notifyContextLost(): void;
|
|
30
|
+
disposeGL(gl: WebGL2RenderingContext): void;
|
|
31
|
+
getProviderForItemId(itemId: string): VideoFrameProvider | undefined;
|
|
32
|
+
getProviderForSrc(src: string): VideoFrameProvider | undefined;
|
|
33
|
+
getProviderRefCount(itemId: string): number;
|
|
34
|
+
getTextureCount(): number;
|
|
35
|
+
getProviderCount(): number;
|
|
36
|
+
getDecoderStates(): Record<string, string>;
|
|
37
|
+
private _ensurePipeline;
|
|
38
|
+
}
|