@remotion/effects 4.0.540 → 4.0.542
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/THIRD_PARTY_LICENSES.md +10 -0
- package/dist/esm/lightning.mjs +603 -0
- package/dist/esm/magnify.mjs +403 -0
- package/dist/esm/preview/barrel-distortion.mjs +303 -0
- package/dist/esm/preview/bevel-alpha.mjs +525 -0
- package/dist/esm/preview/blur.mjs +435 -0
- package/dist/esm/preview/brightness.mjs +95 -0
- package/dist/esm/preview/burlap.mjs +481 -0
- package/dist/esm/preview/checkerboard.mjs +8 -0
- package/dist/esm/preview/chromatic-aberration.mjs +308 -0
- package/dist/esm/preview/color-correction.mjs +522 -0
- package/dist/esm/preview/color-key.mjs +440 -0
- package/dist/esm/preview/contour-lines.mjs +608 -0
- package/dist/esm/preview/contrast.mjs +95 -0
- package/dist/esm/preview/corner-pin.mjs +6 -0
- package/dist/esm/preview/curves.mjs +357 -0
- package/dist/esm/preview/cylinder.mjs +273 -0
- package/dist/esm/preview/dot-grid.mjs +333 -0
- package/dist/esm/preview/drop-shadow.mjs +607 -0
- package/dist/esm/preview/duotone.mjs +344 -0
- package/dist/esm/preview/emboss.mjs +479 -0
- package/dist/esm/preview/evolve.mjs +485 -0
- package/dist/esm/preview/exposure.mjs +264 -0
- package/dist/esm/preview/film-burn.mjs +298 -0
- package/dist/esm/preview/fisheye.mjs +422 -0
- package/dist/esm/preview/flannel.mjs +401 -0
- package/dist/esm/preview/fractal-noise.mjs +642 -0
- package/dist/esm/preview/glow.mjs +598 -0
- package/dist/esm/preview/grayscale.mjs +86 -0
- package/dist/esm/preview/gridlines.mjs +8 -0
- package/dist/esm/preview/halftone-linear-gradient.mjs +523 -0
- package/dist/esm/preview/halftone.mjs +552 -0
- package/dist/esm/preview/hue.mjs +83 -0
- package/dist/esm/preview/index-00v3hr66.mjs +299 -0
- package/dist/esm/preview/index-0w4eqht8.mjs +351 -0
- package/dist/esm/preview/index-18y0zcy2.mjs +440 -0
- package/dist/esm/preview/index-1m1b1ad7.mjs +520 -0
- package/dist/esm/preview/index-34fxm3ax.mjs +518 -0
- package/dist/esm/preview/index-4fvp93ps.mjs +32 -0
- package/dist/esm/preview/index-4scpn12x.mjs +224 -0
- package/dist/esm/preview/index-4z6pt3ka.mjs +104 -0
- package/dist/esm/preview/index-618ky75w.mjs +51 -0
- package/dist/esm/preview/index-6agxdrvr.mjs +411 -0
- package/dist/esm/preview/index-6jkps947.mjs +120 -0
- package/dist/esm/preview/index-8agtsbw4.mjs +617 -0
- package/dist/esm/preview/index-8jx11jyn.mjs +29 -0
- package/dist/esm/preview/index-8p04sssx.mjs +123 -0
- package/dist/esm/preview/index-afdtmrh2.mjs +596 -0
- package/dist/esm/preview/index-b86qstcs.mjs +18 -0
- package/dist/esm/preview/index-cb77qg3g.mjs +587 -0
- package/dist/esm/preview/index-cdp7tr8x.mjs +122 -0
- package/dist/esm/preview/index-evy8hyah.mjs +55 -0
- package/dist/esm/preview/index-gnsce796.mjs +6 -0
- package/dist/esm/preview/index-kkvzt8b7.mjs +199 -0
- package/dist/esm/preview/index-r0v18b2n.mjs +425 -0
- package/dist/esm/preview/index-rswpzmm5.mjs +926 -0
- package/dist/esm/preview/index-s9mrx750.mjs +97 -0
- package/dist/esm/preview/index-tvfexb7s.mjs +164 -0
- package/dist/esm/preview/index-vp56k1gj.mjs +215 -0
- package/dist/esm/preview/index-wxhvgv2s.mjs +79 -0
- package/dist/esm/preview/index-wy95a41r.mjs +407 -0
- package/dist/esm/preview/index-ysnwfc4t.mjs +306 -0
- package/dist/esm/preview/index.mjs +51 -0
- package/dist/esm/preview/invert.mjs +84 -0
- package/dist/esm/preview/iris-wipe.mjs +315 -0
- package/dist/esm/preview/lens-blur.mjs +563 -0
- package/dist/esm/preview/levels.mjs +400 -0
- package/dist/esm/preview/light-leak.mjs +8 -0
- package/dist/esm/preview/light-trail.mjs +516 -0
- package/dist/esm/preview/lightning.mjs +603 -0
- package/dist/esm/preview/linear-gradient-tint.mjs +6 -0
- package/dist/esm/preview/linear-gradient.mjs +6 -0
- package/dist/esm/preview/linear-progressive-blur.mjs +516 -0
- package/dist/esm/preview/linear-progressive-pixelate.mjs +176 -0
- package/dist/esm/preview/lines.mjs +536 -0
- package/dist/esm/preview/liquid-contours.mjs +512 -0
- package/dist/esm/preview/long-shadows.mjs +437 -0
- package/dist/esm/preview/lut.mjs +601 -0
- package/dist/esm/preview/magnify.mjs +403 -0
- package/dist/esm/preview/mirror.mjs +341 -0
- package/dist/esm/preview/noise-displacement.mjs +584 -0
- package/dist/esm/preview/noise.mjs +318 -0
- package/dist/esm/preview/outline.mjs +725 -0
- package/dist/esm/preview/page-turn.mjs +302 -0
- package/dist/esm/preview/paper.mjs +1249 -0
- package/dist/esm/preview/pattern.mjs +6 -0
- package/dist/esm/preview/pixel-dissolve.mjs +360 -0
- package/dist/esm/preview/pixelate.mjs +249 -0
- package/dist/esm/preview/radial-progressive-blur.mjs +582 -0
- package/dist/esm/preview/radial-progressive-pixelate.mjs +220 -0
- package/dist/esm/preview/radial-wipe.mjs +224 -0
- package/dist/esm/preview/rain-fall.mjs +114 -0
- package/dist/esm/preview/region-blur.mjs +500 -0
- package/dist/esm/preview/rings.mjs +8 -0
- package/dist/esm/preview/roughen-edges.mjs +590 -0
- package/dist/esm/preview/saturation.mjs +86 -0
- package/dist/esm/preview/scale.mjs +109 -0
- package/dist/esm/preview/scanlines.mjs +362 -0
- package/dist/esm/preview/shadows-highlights.mjs +284 -0
- package/dist/esm/preview/shatter.mjs +868 -0
- package/dist/esm/preview/shine.mjs +368 -0
- package/dist/esm/preview/shrinkwrap.mjs +769 -0
- package/dist/esm/preview/skew.mjs +300 -0
- package/dist/esm/preview/snow-fall.mjs +170 -0
- package/dist/esm/preview/speckle.mjs +356 -0
- package/dist/esm/preview/sphere.mjs +252 -0
- package/dist/esm/preview/starburst.mjs +8 -0
- package/dist/esm/preview/tear.mjs +375 -0
- package/dist/esm/preview/thermal-vision.mjs +387 -0
- package/dist/esm/preview/tile.mjs +7 -0
- package/dist/esm/preview/tint.mjs +125 -0
- package/dist/esm/preview/translate.mjs +151 -0
- package/dist/esm/preview/turbulent-displace.mjs +613 -0
- package/dist/esm/preview/tv-signal-off.mjs +308 -0
- package/dist/esm/preview/venetian-blinds.mjs +341 -0
- package/dist/esm/preview/vibrance.mjs +254 -0
- package/dist/esm/preview/vignette.mjs +458 -0
- package/dist/esm/preview/wave.mjs +384 -0
- package/dist/esm/preview/waves.mjs +599 -0
- package/dist/esm/preview/white-balance.mjs +280 -0
- package/dist/esm/preview/white-noise.mjs +244 -0
- package/dist/esm/preview/zigzag.mjs +8 -0
- package/dist/esm/preview/zoom-blur.mjs +389 -0
- package/dist/lightning/geometry.d.ts +10 -0
- package/dist/lightning/index.d.ts +4 -0
- package/dist/lightning/mask.d.ts +2 -0
- package/dist/lightning/params.d.ts +259 -0
- package/dist/lightning/shaders.d.ts +2 -0
- package/dist/lightning.d.ts +2 -0
- package/dist/magnify/shader.d.ts +1 -0
- package/dist/magnify/webgpu-shader.d.ts +1 -0
- package/dist/magnify.d.ts +28 -0
- package/package.json +30 -6
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WEBGPU_SINGLE_PASS_VERTEX,
|
|
3
|
+
setupWebGpuSinglePass,
|
|
4
|
+
cleanupWebGpuSinglePass,
|
|
5
|
+
applyWebGpuSinglePass
|
|
6
|
+
} from "./index-tvfexb7s.mjs";
|
|
7
|
+
|
|
8
|
+
// src/lut/index.ts
|
|
9
|
+
import { Internals as Internals2 } from "remotion";
|
|
10
|
+
|
|
11
|
+
// src/lut/lut-runtime.ts
|
|
12
|
+
import { Internals } from "remotion";
|
|
13
|
+
|
|
14
|
+
// src/lut/lut-shaders.ts
|
|
15
|
+
var LUT_VERTEX_SHADER = `#version 300 es
|
|
16
|
+
layout(location = 0) in vec2 aPos;
|
|
17
|
+
layout(location = 1) in vec2 aUv;
|
|
18
|
+
out vec2 vUv;
|
|
19
|
+
|
|
20
|
+
void main() {
|
|
21
|
+
vUv = aUv;
|
|
22
|
+
gl_Position = vec4(aPos, 0.0, 1.0);
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
var LUT_FRAGMENT_SHADER = `#version 300 es
|
|
26
|
+
precision highp float;
|
|
27
|
+
precision highp sampler3D;
|
|
28
|
+
|
|
29
|
+
in vec2 vUv;
|
|
30
|
+
out vec4 fragColor;
|
|
31
|
+
|
|
32
|
+
uniform sampler2D uSource;
|
|
33
|
+
uniform sampler3D uLut;
|
|
34
|
+
uniform int uLutSize;
|
|
35
|
+
uniform vec3 uDomainMin;
|
|
36
|
+
uniform vec3 uDomainMax;
|
|
37
|
+
|
|
38
|
+
vec3 sampleLut(vec3 color) {
|
|
39
|
+
vec3 normalizedColor = clamp(
|
|
40
|
+
(color - uDomainMin) / (uDomainMax - uDomainMin),
|
|
41
|
+
vec3(0.0),
|
|
42
|
+
vec3(1.0)
|
|
43
|
+
);
|
|
44
|
+
vec3 position = normalizedColor * float(uLutSize - 1);
|
|
45
|
+
ivec3 lower = ivec3(floor(position));
|
|
46
|
+
ivec3 upper = min(lower + ivec3(1), ivec3(uLutSize - 1));
|
|
47
|
+
vec3 fraction = fract(position);
|
|
48
|
+
|
|
49
|
+
vec3 c000 = texelFetch(uLut, ivec3(lower.x, lower.y, lower.z), 0).rgb;
|
|
50
|
+
vec3 c100 = texelFetch(uLut, ivec3(upper.x, lower.y, lower.z), 0).rgb;
|
|
51
|
+
vec3 c010 = texelFetch(uLut, ivec3(lower.x, upper.y, lower.z), 0).rgb;
|
|
52
|
+
vec3 c110 = texelFetch(uLut, ivec3(upper.x, upper.y, lower.z), 0).rgb;
|
|
53
|
+
vec3 c001 = texelFetch(uLut, ivec3(lower.x, lower.y, upper.z), 0).rgb;
|
|
54
|
+
vec3 c101 = texelFetch(uLut, ivec3(upper.x, lower.y, upper.z), 0).rgb;
|
|
55
|
+
vec3 c011 = texelFetch(uLut, ivec3(lower.x, upper.y, upper.z), 0).rgb;
|
|
56
|
+
vec3 c111 = texelFetch(uLut, ivec3(upper.x, upper.y, upper.z), 0).rgb;
|
|
57
|
+
|
|
58
|
+
vec3 c00 = mix(c000, c100, fraction.x);
|
|
59
|
+
vec3 c10 = mix(c010, c110, fraction.x);
|
|
60
|
+
vec3 c01 = mix(c001, c101, fraction.x);
|
|
61
|
+
vec3 c11 = mix(c011, c111, fraction.x);
|
|
62
|
+
vec3 c0 = mix(c00, c10, fraction.y);
|
|
63
|
+
vec3 c1 = mix(c01, c11, fraction.y);
|
|
64
|
+
return mix(c0, c1, fraction.z);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void main() {
|
|
68
|
+
vec4 sourceColor = texture(uSource, vUv);
|
|
69
|
+
float alpha = sourceColor.a;
|
|
70
|
+
|
|
71
|
+
if (alpha <= 0.0) {
|
|
72
|
+
fragColor = vec4(0.0);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
vec3 straightColor = sourceColor.rgb / alpha;
|
|
77
|
+
vec3 transformedColor = sampleLut(straightColor);
|
|
78
|
+
fragColor = vec4(transformedColor * alpha, alpha);
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
// src/lut/lut-runtime.ts
|
|
83
|
+
var { createWebGL2ContextError } = Internals;
|
|
84
|
+
var compileShader = (gl, type, source) => {
|
|
85
|
+
const shader = gl.createShader(type);
|
|
86
|
+
if (!shader) {
|
|
87
|
+
throw new Error("Failed to create LUT shader");
|
|
88
|
+
}
|
|
89
|
+
gl.shaderSource(shader, source);
|
|
90
|
+
gl.compileShader(shader);
|
|
91
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
92
|
+
const log = gl.getShaderInfoLog(shader);
|
|
93
|
+
gl.deleteShader(shader);
|
|
94
|
+
throw new Error(`LUT shader compile failed: ${log ?? "(no log)"}`);
|
|
95
|
+
}
|
|
96
|
+
return shader;
|
|
97
|
+
};
|
|
98
|
+
var createProgram = (gl) => {
|
|
99
|
+
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, LUT_VERTEX_SHADER);
|
|
100
|
+
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, LUT_FRAGMENT_SHADER);
|
|
101
|
+
const program = gl.createProgram();
|
|
102
|
+
if (!program) {
|
|
103
|
+
throw new Error("Failed to create LUT shader program");
|
|
104
|
+
}
|
|
105
|
+
gl.attachShader(program, vertexShader);
|
|
106
|
+
gl.attachShader(program, fragmentShader);
|
|
107
|
+
gl.linkProgram(program);
|
|
108
|
+
gl.deleteShader(vertexShader);
|
|
109
|
+
gl.deleteShader(fragmentShader);
|
|
110
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
111
|
+
const log = gl.getProgramInfoLog(program);
|
|
112
|
+
gl.deleteProgram(program);
|
|
113
|
+
throw new Error(`LUT shader link failed: ${log ?? "(no log)"}`);
|
|
114
|
+
}
|
|
115
|
+
return program;
|
|
116
|
+
};
|
|
117
|
+
var createSourceTexture = (gl) => {
|
|
118
|
+
const texture = gl.createTexture();
|
|
119
|
+
if (!texture) {
|
|
120
|
+
throw new Error("Failed to create LUT source texture");
|
|
121
|
+
}
|
|
122
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
123
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
124
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
125
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
126
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
127
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
128
|
+
return texture;
|
|
129
|
+
};
|
|
130
|
+
var createLutTexture = (gl) => {
|
|
131
|
+
const texture = gl.createTexture();
|
|
132
|
+
if (!texture) {
|
|
133
|
+
throw new Error("Failed to create LUT texture");
|
|
134
|
+
}
|
|
135
|
+
gl.bindTexture(gl.TEXTURE_3D, texture);
|
|
136
|
+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
137
|
+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
138
|
+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
139
|
+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
140
|
+
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
|
|
141
|
+
gl.bindTexture(gl.TEXTURE_3D, null);
|
|
142
|
+
return texture;
|
|
143
|
+
};
|
|
144
|
+
var setupLut = (target) => {
|
|
145
|
+
const gl = target.getContext("webgl2", {
|
|
146
|
+
premultipliedAlpha: true,
|
|
147
|
+
alpha: true,
|
|
148
|
+
preserveDrawingBuffer: true
|
|
149
|
+
});
|
|
150
|
+
if (!gl) {
|
|
151
|
+
throw createWebGL2ContextError("LUT effect");
|
|
152
|
+
}
|
|
153
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
|
154
|
+
const program = createProgram(gl);
|
|
155
|
+
const vao = gl.createVertexArray();
|
|
156
|
+
if (!vao) {
|
|
157
|
+
throw new Error("Failed to create LUT vertex array");
|
|
158
|
+
}
|
|
159
|
+
const vbo = gl.createBuffer();
|
|
160
|
+
if (!vbo) {
|
|
161
|
+
throw new Error("Failed to create LUT vertex buffer");
|
|
162
|
+
}
|
|
163
|
+
gl.bindVertexArray(vao);
|
|
164
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
|
165
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1]), gl.STATIC_DRAW);
|
|
166
|
+
gl.enableVertexAttribArray(0);
|
|
167
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0);
|
|
168
|
+
gl.enableVertexAttribArray(1);
|
|
169
|
+
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8);
|
|
170
|
+
gl.bindVertexArray(null);
|
|
171
|
+
return {
|
|
172
|
+
gl,
|
|
173
|
+
program,
|
|
174
|
+
vao,
|
|
175
|
+
vbo,
|
|
176
|
+
sourceTexture: createSourceTexture(gl),
|
|
177
|
+
lutTexture: createLutTexture(gl),
|
|
178
|
+
uniforms: {
|
|
179
|
+
uSource: gl.getUniformLocation(program, "uSource"),
|
|
180
|
+
uLut: gl.getUniformLocation(program, "uLut"),
|
|
181
|
+
uLutSize: gl.getUniformLocation(program, "uLutSize"),
|
|
182
|
+
uDomainMin: gl.getUniformLocation(program, "uDomainMin"),
|
|
183
|
+
uDomainMax: gl.getUniformLocation(program, "uDomainMax")
|
|
184
|
+
},
|
|
185
|
+
cachedContent: null
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
var applyLut = ({
|
|
189
|
+
state,
|
|
190
|
+
source,
|
|
191
|
+
width,
|
|
192
|
+
height,
|
|
193
|
+
content,
|
|
194
|
+
parsed,
|
|
195
|
+
flipSourceY
|
|
196
|
+
}) => {
|
|
197
|
+
const { gl, program, sourceTexture, lutTexture, uniforms, vao } = state;
|
|
198
|
+
if (state.cachedContent !== content) {
|
|
199
|
+
const maxSize = gl.getParameter(gl.MAX_3D_TEXTURE_SIZE);
|
|
200
|
+
if (parsed.size > maxSize) {
|
|
201
|
+
throw new Error(`LUT_3D_SIZE ${parsed.size} exceeds this browser's maximum 3D texture size of ${maxSize}`);
|
|
202
|
+
}
|
|
203
|
+
const previousError = gl.getError();
|
|
204
|
+
if (previousError !== gl.NO_ERROR) {
|
|
205
|
+
throw new Error(`WebGL error ${previousError} was present before uploading the 3D LUT texture`);
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
209
|
+
gl.bindTexture(gl.TEXTURE_3D, lutTexture);
|
|
210
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
211
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
212
|
+
gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA16F, parsed.size, parsed.size, parsed.size, 0, gl.RGBA, gl.FLOAT, parsed.data);
|
|
213
|
+
const uploadError = gl.getError();
|
|
214
|
+
if (uploadError !== gl.NO_ERROR) {
|
|
215
|
+
throw new Error(`Failed to upload the 3D LUT texture (WebGL error ${uploadError})`);
|
|
216
|
+
}
|
|
217
|
+
state.cachedContent = content;
|
|
218
|
+
} finally {
|
|
219
|
+
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
|
220
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
|
|
221
|
+
gl.bindTexture(gl.TEXTURE_3D, null);
|
|
222
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
gl.viewport(0, 0, width, height);
|
|
226
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
227
|
+
gl.clearColor(0, 0, 0, 0);
|
|
228
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
229
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
230
|
+
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
|
|
231
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
|
|
232
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
233
|
+
gl.activeTexture(gl.TEXTURE1);
|
|
234
|
+
gl.bindTexture(gl.TEXTURE_3D, lutTexture);
|
|
235
|
+
gl.useProgram(program);
|
|
236
|
+
if (uniforms.uSource)
|
|
237
|
+
gl.uniform1i(uniforms.uSource, 0);
|
|
238
|
+
if (uniforms.uLut)
|
|
239
|
+
gl.uniform1i(uniforms.uLut, 1);
|
|
240
|
+
if (uniforms.uLutSize)
|
|
241
|
+
gl.uniform1i(uniforms.uLutSize, parsed.size);
|
|
242
|
+
if (uniforms.uDomainMin) {
|
|
243
|
+
gl.uniform3f(uniforms.uDomainMin, ...parsed.domainMin);
|
|
244
|
+
}
|
|
245
|
+
if (uniforms.uDomainMax) {
|
|
246
|
+
gl.uniform3f(uniforms.uDomainMax, ...parsed.domainMax);
|
|
247
|
+
}
|
|
248
|
+
gl.bindVertexArray(vao);
|
|
249
|
+
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
|
250
|
+
gl.bindVertexArray(null);
|
|
251
|
+
gl.bindTexture(gl.TEXTURE_3D, null);
|
|
252
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
253
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
254
|
+
gl.useProgram(null);
|
|
255
|
+
};
|
|
256
|
+
var cleanupLut = ({
|
|
257
|
+
gl,
|
|
258
|
+
program,
|
|
259
|
+
vao,
|
|
260
|
+
vbo,
|
|
261
|
+
sourceTexture,
|
|
262
|
+
lutTexture
|
|
263
|
+
}) => {
|
|
264
|
+
gl.deleteTexture(sourceTexture);
|
|
265
|
+
gl.deleteTexture(lutTexture);
|
|
266
|
+
gl.deleteBuffer(vbo);
|
|
267
|
+
gl.deleteProgram(program);
|
|
268
|
+
gl.deleteVertexArray(vao);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// src/lut/parse-cube-lut.ts
|
|
272
|
+
var stripComment = (line) => {
|
|
273
|
+
let insideQuotes = false;
|
|
274
|
+
for (let i = 0;i < line.length; i++) {
|
|
275
|
+
const character = line[i];
|
|
276
|
+
if (character === '"') {
|
|
277
|
+
insideQuotes = !insideQuotes;
|
|
278
|
+
} else if (character === "#" && !insideQuotes) {
|
|
279
|
+
return line.slice(0, i);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return line;
|
|
283
|
+
};
|
|
284
|
+
var parseTriplet = ({
|
|
285
|
+
values,
|
|
286
|
+
lineNumber,
|
|
287
|
+
label
|
|
288
|
+
}) => {
|
|
289
|
+
if (values.length !== 3) {
|
|
290
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: ${label} must contain exactly 3 numbers`);
|
|
291
|
+
}
|
|
292
|
+
const parsed = values.map((value) => Number(value));
|
|
293
|
+
if (parsed.some((value) => !Number.isFinite(value))) {
|
|
294
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: ${label} must contain only finite numbers`);
|
|
295
|
+
}
|
|
296
|
+
return [parsed[0], parsed[1], parsed[2]];
|
|
297
|
+
};
|
|
298
|
+
var HEADER_DIRECTIVES = new Set([
|
|
299
|
+
"TITLE",
|
|
300
|
+
"LUT_3D_SIZE",
|
|
301
|
+
"DOMAIN_MIN",
|
|
302
|
+
"DOMAIN_MAX"
|
|
303
|
+
]);
|
|
304
|
+
var parseCubeLut = (content) => {
|
|
305
|
+
const lines = content.replace(/^\uFEFF/, "").split(/\r?\n/);
|
|
306
|
+
let size = null;
|
|
307
|
+
let domainMin = [0, 0, 0];
|
|
308
|
+
let domainMax = [1, 1, 1];
|
|
309
|
+
let hasDomainMin = false;
|
|
310
|
+
let hasDomainMax = false;
|
|
311
|
+
let hasTitle = false;
|
|
312
|
+
let dataRows = 0;
|
|
313
|
+
let dataStarted = false;
|
|
314
|
+
for (let index = 0;index < lines.length; index++) {
|
|
315
|
+
const lineNumber = index + 1;
|
|
316
|
+
const line = stripComment(lines[index]).trim();
|
|
317
|
+
if (line.length === 0) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const [keyword, ...values] = line.split(/\s+/);
|
|
321
|
+
if (keyword === "LUT_1D_SIZE") {
|
|
322
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: 1D LUTs are not supported`);
|
|
323
|
+
}
|
|
324
|
+
if (HEADER_DIRECTIVES.has(keyword)) {
|
|
325
|
+
if (dataStarted) {
|
|
326
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: ${keyword} must appear before the color data`);
|
|
327
|
+
}
|
|
328
|
+
if (keyword === "TITLE") {
|
|
329
|
+
if (hasTitle) {
|
|
330
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: TITLE may only be declared once`);
|
|
331
|
+
}
|
|
332
|
+
hasTitle = true;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (keyword === "LUT_3D_SIZE") {
|
|
336
|
+
if (size !== null) {
|
|
337
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: LUT_3D_SIZE may only be declared once`);
|
|
338
|
+
}
|
|
339
|
+
if (values.length !== 1) {
|
|
340
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: LUT_3D_SIZE must contain exactly one integer`);
|
|
341
|
+
}
|
|
342
|
+
const parsedSize = Number(values[0]);
|
|
343
|
+
if (!Number.isInteger(parsedSize) || parsedSize < 2 || parsedSize > 256) {
|
|
344
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: LUT_3D_SIZE must be an integer from 2 to 256`);
|
|
345
|
+
}
|
|
346
|
+
size = parsedSize;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (keyword === "DOMAIN_MIN") {
|
|
350
|
+
if (hasDomainMin) {
|
|
351
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: DOMAIN_MIN may only be declared once`);
|
|
352
|
+
}
|
|
353
|
+
domainMin = parseTriplet({
|
|
354
|
+
values,
|
|
355
|
+
lineNumber,
|
|
356
|
+
label: "DOMAIN_MIN"
|
|
357
|
+
});
|
|
358
|
+
hasDomainMin = true;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (hasDomainMax) {
|
|
362
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: DOMAIN_MAX may only be declared once`);
|
|
363
|
+
}
|
|
364
|
+
domainMax = parseTriplet({
|
|
365
|
+
values,
|
|
366
|
+
lineNumber,
|
|
367
|
+
label: "DOMAIN_MAX"
|
|
368
|
+
});
|
|
369
|
+
hasDomainMax = true;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
if (/^[A-Z_]/i.test(keyword)) {
|
|
373
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: unsupported directive "${keyword}"`);
|
|
374
|
+
}
|
|
375
|
+
if (size === null) {
|
|
376
|
+
throw new TypeError(`Invalid LUT content on line ${lineNumber}: LUT_3D_SIZE must be declared before the color data`);
|
|
377
|
+
}
|
|
378
|
+
parseTriplet({
|
|
379
|
+
values: [keyword, ...values],
|
|
380
|
+
lineNumber,
|
|
381
|
+
label: "color row"
|
|
382
|
+
});
|
|
383
|
+
dataStarted = true;
|
|
384
|
+
dataRows++;
|
|
385
|
+
const maxDataRows = size ** 3;
|
|
386
|
+
if (dataRows > maxDataRows) {
|
|
387
|
+
throw new TypeError(`Invalid LUT content: expected ${maxDataRows} color rows for LUT_3D_SIZE ${size}, but got more`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (size === null) {
|
|
391
|
+
throw new TypeError("Invalid LUT content: LUT_3D_SIZE is missing");
|
|
392
|
+
}
|
|
393
|
+
for (let channel = 0;channel < 3; channel++) {
|
|
394
|
+
if (domainMin[channel] >= domainMax[channel]) {
|
|
395
|
+
throw new TypeError(`Invalid LUT content: DOMAIN_MIN must be smaller than DOMAIN_MAX for every channel`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const expectedRows = size ** 3;
|
|
399
|
+
if (dataRows !== expectedRows) {
|
|
400
|
+
throw new TypeError(`Invalid LUT content: expected ${expectedRows} color rows for LUT_3D_SIZE ${size}, but got ${dataRows}`);
|
|
401
|
+
}
|
|
402
|
+
const data = new Float32Array(expectedRows * 4);
|
|
403
|
+
let dataIndex = 0;
|
|
404
|
+
for (let index = 0;index < lines.length; index++) {
|
|
405
|
+
const line = stripComment(lines[index]).trim();
|
|
406
|
+
if (line.length === 0) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const [keyword, ...values] = line.split(/\s+/);
|
|
410
|
+
if (HEADER_DIRECTIVES.has(keyword)) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const [red, green, blue] = parseTriplet({
|
|
414
|
+
values: [keyword, ...values],
|
|
415
|
+
lineNumber: index + 1,
|
|
416
|
+
label: "color row"
|
|
417
|
+
});
|
|
418
|
+
const offset = dataIndex * 4;
|
|
419
|
+
data[offset] = red;
|
|
420
|
+
data[offset + 1] = green;
|
|
421
|
+
data[offset + 2] = blue;
|
|
422
|
+
data[offset + 3] = 1;
|
|
423
|
+
dataIndex++;
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
size,
|
|
427
|
+
domainMin,
|
|
428
|
+
domainMax,
|
|
429
|
+
data
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/lut/webgpu-shader.ts
|
|
434
|
+
var LUT_WEBGPU_SHADER = `
|
|
435
|
+
${WEBGPU_SINGLE_PASS_VERTEX}
|
|
436
|
+
struct Params {domainMin: vec4f, domainMax: vec4f,}
|
|
437
|
+
@group(0) @binding(0) var source: texture_2d<f32>;
|
|
438
|
+
@group(0) @binding(1) var sourceSampler: sampler;
|
|
439
|
+
@group(0) @binding(2) var<uniform> params: Params;
|
|
440
|
+
@group(0) @binding(3) var lut: texture_2d<f32>;
|
|
441
|
+
fn lookup(p: vec3u) -> vec3f {
|
|
442
|
+
let size = u32(params.domainMin.w);
|
|
443
|
+
let index = p.x + size * (p.y + size * p.z);
|
|
444
|
+
let width = textureDimensions(lut).x;
|
|
445
|
+
// The WebGL LUT is RGBA16F. Preserve its rounding before interpolation.
|
|
446
|
+
return quantizeToF16(textureLoad(lut, vec2u(index % width, index / width), 0).rgb);
|
|
447
|
+
}
|
|
448
|
+
@fragment fn fragment(input: VertexOutput) -> @location(0) vec4f {
|
|
449
|
+
let color = textureSampleLevel(source, sourceSampler, input.uv, 0);
|
|
450
|
+
if (color.a <= 0.0) { return vec4f(0); }
|
|
451
|
+
let normalized = clamp((color.rgb / color.a - params.domainMin.xyz) / (params.domainMax.xyz - params.domainMin.xyz), vec3f(0), vec3f(1));
|
|
452
|
+
let position = normalized * (params.domainMin.w - 1.0);
|
|
453
|
+
let lower = vec3u(floor(position));
|
|
454
|
+
let upper = min(lower + vec3u(1), vec3u(u32(params.domainMin.w) - 1u));
|
|
455
|
+
let fraction = fract(position);
|
|
456
|
+
let c00 = mix(lookup(lower), lookup(vec3u(upper.x, lower.yz)), fraction.x);
|
|
457
|
+
let c10 = mix(lookup(vec3u(lower.x, upper.y, lower.z)), lookup(vec3u(upper.xy, lower.z)), fraction.x);
|
|
458
|
+
let c01 = mix(lookup(vec3u(lower.xy, upper.z)), lookup(vec3u(upper.x, lower.y, upper.z)), fraction.x);
|
|
459
|
+
let c11 = mix(lookup(vec3u(lower.x, upper.yz)), lookup(upper), fraction.x);
|
|
460
|
+
return vec4f(mix(mix(c00, c10, fraction.y), mix(c01, c11, fraction.y), fraction.z) * color.a, color.a);
|
|
461
|
+
}
|
|
462
|
+
`;
|
|
463
|
+
|
|
464
|
+
// src/lut/index.ts
|
|
465
|
+
var { createEffect } = Internals2;
|
|
466
|
+
var DEFAULT_LUT_CONTENT = `TITLE "Identity"
|
|
467
|
+
LUT_3D_SIZE 2
|
|
468
|
+
|
|
469
|
+
0 0 0
|
|
470
|
+
1 0 0
|
|
471
|
+
0 1 0
|
|
472
|
+
1 1 0
|
|
473
|
+
0 0 1
|
|
474
|
+
1 0 1
|
|
475
|
+
0 1 1
|
|
476
|
+
1 1 1`;
|
|
477
|
+
var lutSchema = {
|
|
478
|
+
content: {
|
|
479
|
+
type: "string",
|
|
480
|
+
default: DEFAULT_LUT_CONTENT,
|
|
481
|
+
description: "Cube LUT content",
|
|
482
|
+
keyframable: false
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
var MAX_CACHED_LUTS = 4;
|
|
486
|
+
var parsedLuts = new Map;
|
|
487
|
+
var getCachedLut = (content) => {
|
|
488
|
+
const cached = parsedLuts.get(content);
|
|
489
|
+
if (cached) {
|
|
490
|
+
parsedLuts.delete(content);
|
|
491
|
+
parsedLuts.set(content, cached);
|
|
492
|
+
return cached;
|
|
493
|
+
}
|
|
494
|
+
let firstHash = 2166136261;
|
|
495
|
+
let secondHash = 5381;
|
|
496
|
+
for (let i = 0;i < content.length; i++) {
|
|
497
|
+
const character = content.charCodeAt(i);
|
|
498
|
+
firstHash ^= character;
|
|
499
|
+
firstHash = Math.imul(firstHash, 16777619);
|
|
500
|
+
secondHash = Math.imul(secondHash, 33) ^ character;
|
|
501
|
+
}
|
|
502
|
+
const created = {
|
|
503
|
+
parsed: parseCubeLut(content),
|
|
504
|
+
effectKey: `lut-${content.length}-${firstHash >>> 0}-${secondHash >>> 0}`
|
|
505
|
+
};
|
|
506
|
+
parsedLuts.set(content, created);
|
|
507
|
+
if (parsedLuts.size > MAX_CACHED_LUTS) {
|
|
508
|
+
const oldestContent = parsedLuts.keys().next().value;
|
|
509
|
+
if (oldestContent !== undefined) {
|
|
510
|
+
parsedLuts.delete(oldestContent);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return created;
|
|
514
|
+
};
|
|
515
|
+
var validateLutParams = (params) => {
|
|
516
|
+
if (params === null || typeof params !== "object") {
|
|
517
|
+
throw new TypeError(`LUT effect requires a parameters object, but got ${JSON.stringify(params)}`);
|
|
518
|
+
}
|
|
519
|
+
if (typeof params.content !== "string" || params.content.trim().length === 0) {
|
|
520
|
+
throw new TypeError(`"content" must be a non-empty string, but got ${JSON.stringify(params.content)}`);
|
|
521
|
+
}
|
|
522
|
+
getCachedLut(params.content);
|
|
523
|
+
};
|
|
524
|
+
var lut = createEffect({
|
|
525
|
+
type: "remotion/lut",
|
|
526
|
+
label: "lut()",
|
|
527
|
+
documentationLink: "https://www.remotion.dev/docs/effects/lut",
|
|
528
|
+
calculateKey: (params) => getCachedLut(params.content).effectKey,
|
|
529
|
+
implementations: {
|
|
530
|
+
webgpu: Internals2.createEffectImplementation({
|
|
531
|
+
setup: (target) => ({
|
|
532
|
+
...setupWebGpuSinglePass(target, "lut"),
|
|
533
|
+
cached: null
|
|
534
|
+
}),
|
|
535
|
+
apply: ({ source, width, height, params, state, gpuDevice }) => {
|
|
536
|
+
if (!gpuDevice)
|
|
537
|
+
throw new Error("A WebGPU device is required for the LUT effect");
|
|
538
|
+
const device = gpuDevice;
|
|
539
|
+
const { parsed } = getCachedLut(params.content);
|
|
540
|
+
const textureWidth = Math.min(parsed.size * parsed.size, device.limits.maxTextureDimension2D);
|
|
541
|
+
const textureHeight = Math.ceil(parsed.size ** 3 / textureWidth);
|
|
542
|
+
if (textureHeight > device.limits.maxTextureDimension2D)
|
|
543
|
+
throw new Error(`LUT_3D_SIZE ${parsed.size} exceeds this browser's maximum LUT texture size`);
|
|
544
|
+
if (state.cached?.content !== params.content || state.cached.width !== textureWidth) {
|
|
545
|
+
const data = new Float32Array(textureWidth * textureHeight * 4);
|
|
546
|
+
data.set(parsed.data);
|
|
547
|
+
state.cached = {
|
|
548
|
+
content: params.content,
|
|
549
|
+
data,
|
|
550
|
+
width: textureWidth,
|
|
551
|
+
height: textureHeight
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
applyWebGpuSinglePass({
|
|
555
|
+
state,
|
|
556
|
+
source,
|
|
557
|
+
width,
|
|
558
|
+
height,
|
|
559
|
+
device,
|
|
560
|
+
shader: LUT_WEBGPU_SHADER,
|
|
561
|
+
uniformData: new Float32Array([
|
|
562
|
+
...parsed.domainMin,
|
|
563
|
+
parsed.size,
|
|
564
|
+
...parsed.domainMax,
|
|
565
|
+
0
|
|
566
|
+
])
|
|
567
|
+
}, {
|
|
568
|
+
sourceFilter: null,
|
|
569
|
+
auxiliaryTexture: {
|
|
570
|
+
data: state.cached.data,
|
|
571
|
+
width: textureWidth,
|
|
572
|
+
height: textureHeight,
|
|
573
|
+
format: "rgba32float"
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
},
|
|
577
|
+
cleanup: cleanupWebGpuSinglePass
|
|
578
|
+
}),
|
|
579
|
+
webgl2: Internals2.createEffectImplementation({
|
|
580
|
+
setup: setupLut,
|
|
581
|
+
apply: ({ source, width, height, params, state, flipSourceY }) => {
|
|
582
|
+
applyLut({
|
|
583
|
+
state,
|
|
584
|
+
source,
|
|
585
|
+
width,
|
|
586
|
+
height,
|
|
587
|
+
content: params.content,
|
|
588
|
+
parsed: getCachedLut(params.content).parsed,
|
|
589
|
+
flipSourceY
|
|
590
|
+
});
|
|
591
|
+
},
|
|
592
|
+
cleanup: cleanupLut
|
|
593
|
+
})
|
|
594
|
+
},
|
|
595
|
+
schema: lutSchema,
|
|
596
|
+
validateParams: validateLutParams
|
|
597
|
+
});
|
|
598
|
+
export {
|
|
599
|
+
lut,
|
|
600
|
+
lutSchema
|
|
601
|
+
};
|