@remotion/effects 4.0.536 → 4.0.537

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.
@@ -0,0 +1,30 @@
1
+ # Third-party licenses
2
+
3
+ ## EffectCraft
4
+
5
+ The Fractal Noise, Turbulent Displace, Lens Blur, and Bevel Alpha shaders are
6
+ adapted from the noise, distortion, blur, and perspective engines in
7
+ [EffectCraft](https://github.com/storytold/effectcraft), revision
8
+ `8f02f52a44a4b1ea807d8824b92301b96f896a06`. It is used under the MIT license.
9
+
10
+ MIT License
11
+
12
+ Copyright (c) 2026 ArtCraft Team and the EffectCraft contributors
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
@@ -0,0 +1,13 @@
1
+ export type BevelAlphaParams = {
2
+ /** Width of the rounded bevel in pixels, from 0 to 200. Defaults to `2`. */
3
+ readonly edgeThickness?: number;
4
+ /** Light direction in degrees, clockwise from above. Defaults to `-60`. */
5
+ readonly lightAngle?: number;
6
+ /** CSS color of the highlights. Defaults to `white`. */
7
+ readonly lightColor?: string;
8
+ /** Strength of highlights and shadows, from 0 to 1. Defaults to `0.4`. */
9
+ readonly lightIntensity?: number;
10
+ };
11
+ export declare const bevelAlpha: (params?: (BevelAlphaParams & {
12
+ readonly disabled?: boolean | undefined;
13
+ }) | undefined) => import("remotion").EffectDescriptor<unknown>;
@@ -0,0 +1,3 @@
1
+ export declare const BEVEL_ALPHA_VERTEX_SHADER = "#version 300 es\nlayout(location = 0) in vec2 aPos;\nlayout(location = 1) in vec2 aUv;\nout vec2 vUv;\nvoid main() {\n\tvUv = aUv;\n\tgl_Position = vec4(aPos, 0.0, 1.0);\n}\n";
2
+ export declare const buildBevelAlphaBlurShader: (direction: "horizontal" | "vertical") => string;
3
+ export declare const BEVEL_ALPHA_SHADE_SHADER = "#version 300 es\nprecision highp float;\nin vec2 vUv;\nout vec4 fragColor;\nuniform sampler2D uSource;\nuniform sampler2D uHeight;\nuniform vec2 uResolution;\nuniform float uEdgeThickness;\nuniform vec3 uLightDirection;\nuniform vec3 uLightColor;\nuniform float uLightIntensity;\n\n// Store a 16-bit height in two 8-bit channels. This avoids banded normals\n// without requiring floating-point render targets or WebGL extensions.\nvec4 encodeHeight(float height) {\n\tfloat value = floor(clamp(height, 0.0, 1.0) * 65535.0 + 0.5);\n\treturn vec4(floor(value / 256.0), mod(value, 256.0), 0.0, 255.0) / 255.0;\n}\nfloat decodeHeight(vec4 value) {\n\treturn dot(value.rg, vec2(65280.0, 255.0)) / 65535.0;\n}\n\nfloat heightAt(ivec2 pixel) {\n\treturn decodeHeight(texelFetch(uHeight, clamp(pixel, ivec2(0), ivec2(uResolution) - 1), 0));\n}\nvoid main() {\n\tvec4 source = texelFetch(uSource, ivec2(gl_FragCoord.xy), 0);\n\tif (uEdgeThickness == 0.0 || uLightIntensity == 0.0 || source.a == 0.0) {\n\t\tfragColor = source;\n\t\treturn;\n\t}\n\tivec2 pixel = ivec2(gl_FragCoord.xy);\n\tfloat dx = (heightAt(pixel + ivec2(1, 0)) - heightAt(pixel - ivec2(1, 0))) * 0.5;\n\t// Screen-space Y points down; texture-space Y points up.\n\tfloat dy = (heightAt(pixel - ivec2(0, 1)) - heightAt(pixel + ivec2(0, 1))) * 0.5;\n\tvec3 normal = normalize(vec3(-dx * uEdgeThickness * 1.25, -dy * uEdgeThickness * 1.25, 1.0));\n\tfloat shade = (dot(normal, uLightDirection) - uLightDirection.z) * uLightIntensity * 2.0;\n\tvec3 color = source.rgb;\n\tif (shade > 0.0) {\n\t\tcolor += uLightColor * shade * source.a;\n\t} else {\n\t\tcolor *= max(1.0 + shade, 0.0);\n\t}\n\tfragColor = vec4(clamp(color, vec3(0.0), vec3(source.a)), source.a);\n}\n";
@@ -0,0 +1 @@
1
+ export { bevelAlpha, type BevelAlphaParams } from './bevel-alpha/index.js';
@@ -0,0 +1,20 @@
1
+ export type CylinderRender = 'both' | 'front' | 'back';
2
+ export type CylinderParams = {
3
+ /** Cylinder radius in pixels. Defaults to 250. */
4
+ readonly radius?: number;
5
+ /** Texture rotation around the cylinder, in degrees. Defaults to 0. */
6
+ readonly rotation?: number;
7
+ /** Cylinder orientation in degrees, applied in X, Y, Z order. */
8
+ readonly rotationX?: number;
9
+ readonly rotationY?: number;
10
+ readonly rotationZ?: number;
11
+ /** Center as fractions of the canvas width and height. Defaults to [0.5, 0.5]. */
12
+ readonly position?: readonly [number, number];
13
+ /** Which faces to show. Defaults to 'both'. */
14
+ readonly render?: CylinderRender;
15
+ /** Darkening at the silhouette, from 0 to 1. Defaults to 0.35. */
16
+ readonly shading?: number;
17
+ };
18
+ export declare const cylinder: (params?: (CylinderParams & {
19
+ readonly disabled?: boolean | undefined;
20
+ }) | undefined) => import("remotion").EffectDescriptor<unknown>;
@@ -0,0 +1 @@
1
+ export declare const CYLINDER_SHADER = "\nstruct Params {\n resolution: vec2f,\n center: vec2f,\n radius: f32,\n spin: f32,\n shading: f32,\n faces: f32,\n rotation: vec4f,\n}\n@group(0) @binding(0) var source: texture_2d<f32>;\n@group(0) @binding(1) var sourceSampler: sampler;\n@group(0) @binding(2) var<uniform> params: Params;\n\n@vertex fn vertex(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {\n let positions = array<vec2f, 3>(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3));\n return vec4f(positions[index], 0, 1);\n}\n\nfn sampleFace(point: vec3f, inverse: mat3x3f, rotation: mat3x3f) -> vec4f {\n let local = inverse * point;\n let uv = vec2f(fract((atan2(local.x, local.z) + params.spin) / 6.28318530718 + 0.5), local.y / params.resolution.y + 0.5);\n let color = textureSampleLevel(source, sourceSampler, uv, 0);\n if (uv.y < 0 || uv.y > 1) { return vec4f(0); }\n let normal = rotation * vec3f(local.x / params.radius, 0, local.z / params.radius);\n let shade = mix(1.0, abs(normal.z), params.shading);\n return vec4f(color.rgb * shade, color.a);\n}\n\n@fragment fn fragment(@builtin(position) position: vec4f) -> @location(0) vec4f {\n let s = sin(params.rotation.xyz);\n let c = cos(params.rotation.xyz);\n let rx = mat3x3f(vec3f(1, 0, 0), vec3f(0, c.x, s.x), vec3f(0, -s.x, c.x));\n let ry = mat3x3f(vec3f(c.y, 0, -s.y), vec3f(0, 1, 0), vec3f(s.y, 0, c.y));\n let rz = mat3x3f(vec3f(c.z, s.z, 0), vec3f(-s.z, c.z, 0), vec3f(0, 0, 1));\n let rotation = rz * ry * rx;\n let axis = rotation[1];\n let p = position.xy - params.center;\n // Orthographic viewing ray intersecting the rotated, open cylinder wall.\n let a = 1.0 - axis.z * axis.z;\n let da = dot(p, axis.xy);\n let b = -da * axis.z;\n let discriminant = b * b - a * (dot(p, p) - da * da - params.radius * params.radius);\n if (a < 0.000001 || discriminant <= 0) { return vec4f(0); }\n let root = sqrt(discriminant);\n let inverse = transpose(rotation);\n let front = sampleFace(vec3f(p, (-b + root) / a), inverse, rotation);\n let back = sampleFace(vec3f(p, (-b - root) / a), inverse, rotation);\n if (params.faces == 1) { return front; }\n if (params.faces == 2) { return back; }\n // Both the texture and output use premultiplied alpha.\n return front + back * (1.0 - front.a);\n}\n";
@@ -0,0 +1 @@
1
+ export { cylinder, type CylinderParams, type CylinderRender, } from './cylinder/index.js';
@@ -0,0 +1,474 @@
1
+ // src/bevel-alpha/index.ts
2
+ import { Internals } from "remotion";
3
+
4
+ // src/validate-effect-param.ts
5
+ var assertEffectParamsObject = (params, effectLabel) => {
6
+ if (params === null || typeof params !== "object") {
7
+ throw new TypeError(`${effectLabel} effect requires a parameters object, but got ${JSON.stringify(params)}`);
8
+ }
9
+ };
10
+ var assertRequiredFiniteNumber = (value, name) => {
11
+ if (typeof value !== "number" || !Number.isFinite(value)) {
12
+ throw new TypeError(`"${name}" must be a finite number, but got ${JSON.stringify(value)}`);
13
+ }
14
+ };
15
+ var assertRequiredColor = (value, name) => {
16
+ if (typeof value !== "string" || value.length === 0) {
17
+ throw new TypeError(`"${name}" must be a non-empty string, but got ${JSON.stringify(value)}`);
18
+ }
19
+ };
20
+ var assertOptionalColor = (value, name) => {
21
+ if (value === undefined) {
22
+ return;
23
+ }
24
+ assertRequiredColor(value, name);
25
+ };
26
+ var assertOptionalBoolean = (value, name) => {
27
+ if (value === undefined) {
28
+ return;
29
+ }
30
+ if (typeof value !== "boolean") {
31
+ throw new TypeError(`"${name}" must be a boolean, but got ${JSON.stringify(value)}`);
32
+ }
33
+ };
34
+
35
+ // src/color-utils.ts
36
+ var DEFAULT_AMOUNT = 1;
37
+ var DEFAULT_BRIGHTNESS_AMOUNT = 0;
38
+ var DEFAULT_HUE_DEGREES = 0;
39
+ var colorAmountSchema = {
40
+ type: "number",
41
+ min: 0,
42
+ max: 1,
43
+ step: 0.01,
44
+ default: DEFAULT_AMOUNT,
45
+ description: "Amount",
46
+ hiddenFromList: false
47
+ };
48
+ var colorMultiplierSchema = {
49
+ type: "number",
50
+ min: 0,
51
+ step: 0.01,
52
+ default: DEFAULT_AMOUNT,
53
+ description: "Amount",
54
+ hiddenFromList: false
55
+ };
56
+ var brightnessAmountSchema = {
57
+ type: "number",
58
+ min: -1,
59
+ max: 1,
60
+ step: 0.01,
61
+ default: DEFAULT_BRIGHTNESS_AMOUNT,
62
+ description: "Amount",
63
+ hiddenFromList: false
64
+ };
65
+ var hueDegreesSchema = {
66
+ type: "rotation-degrees",
67
+ step: 1,
68
+ default: DEFAULT_HUE_DEGREES,
69
+ description: "Degrees"
70
+ };
71
+ var assertOptionalFiniteNumber = (value, name) => {
72
+ if (value === undefined) {
73
+ return;
74
+ }
75
+ assertRequiredFiniteNumber(value, name);
76
+ };
77
+ var validateUnitInterval = (value, name) => {
78
+ if (value < 0) {
79
+ throw new TypeError(`"${name}" must be >= 0, but got ${JSON.stringify(value)}`);
80
+ }
81
+ if (value > 1) {
82
+ throw new TypeError(`"${name}" must be <= 1, but got ${JSON.stringify(value)}`);
83
+ }
84
+ };
85
+ var validateNonNegative = (value, name) => {
86
+ if (value < 0) {
87
+ throw new TypeError(`"${name}" must be >= 0, but got ${JSON.stringify(value)}`);
88
+ }
89
+ };
90
+ var validateSignedUnitInterval = (value, name) => {
91
+ if (value < -1) {
92
+ throw new TypeError(`"${name}" must be >= -1, but got ${JSON.stringify(value)}`);
93
+ }
94
+ if (value > 1) {
95
+ throw new TypeError(`"${name}" must be <= 1, but got ${JSON.stringify(value)}`);
96
+ }
97
+ };
98
+ var clampColorChannel = (value) => {
99
+ return Math.max(0, Math.min(255, value));
100
+ };
101
+ var parseColorRgba = (ctx, color) => {
102
+ ctx.clearRect(0, 0, 1, 1);
103
+ ctx.fillStyle = color;
104
+ ctx.fillRect(0, 0, 1, 1);
105
+ const { data } = ctx.getImageData(0, 0, 1, 1);
106
+ return [data[0], data[1], data[2], data[3]];
107
+ };
108
+
109
+ // src/bevel-alpha/shaders.ts
110
+ var BEVEL_ALPHA_VERTEX_SHADER = `#version 300 es
111
+ layout(location = 0) in vec2 aPos;
112
+ layout(location = 1) in vec2 aUv;
113
+ out vec2 vUv;
114
+ void main() {
115
+ vUv = aUv;
116
+ gl_Position = vec4(aPos, 0.0, 1.0);
117
+ }
118
+ `;
119
+ var HEIGHT_ENCODING = `
120
+ // Store a 16-bit height in two 8-bit channels. This avoids banded normals
121
+ // without requiring floating-point render targets or WebGL extensions.
122
+ vec4 encodeHeight(float height) {
123
+ float value = floor(clamp(height, 0.0, 1.0) * 65535.0 + 0.5);
124
+ return vec4(floor(value / 256.0), mod(value, 256.0), 0.0, 255.0) / 255.0;
125
+ }
126
+ float decodeHeight(vec4 value) {
127
+ return dot(value.rg, vec2(65280.0, 255.0)) / 65535.0;
128
+ }
129
+ `;
130
+ var buildBevelAlphaBlurShader = (direction) => `#version 300 es
131
+ precision highp float;
132
+ in vec2 vUv;
133
+ out vec4 fragColor;
134
+ uniform sampler2D uSource;
135
+ uniform vec2 uResolution;
136
+ uniform float uRadius;
137
+ ${HEIGHT_ENCODING}
138
+ float readHeight(vec2 uv) {
139
+ ${direction === "horizontal" ? "return texture(uSource, uv).a;" : `
140
+ // Decode before interpolating: packed channel values cannot be filtered.
141
+ vec2 pixel = uv * uResolution - 0.5;
142
+ ivec2 lower = ivec2(int(gl_FragCoord.x), int(floor(pixel.y)));
143
+ ivec2 maximum = ivec2(uResolution) - 1;
144
+ float a = decodeHeight(texelFetch(uSource, clamp(lower, ivec2(0), maximum), 0));
145
+ float b = decodeHeight(texelFetch(uSource, clamp(lower + ivec2(0, 1), ivec2(0), maximum), 0));
146
+ return mix(a, b, fract(pixel.y));`}
147
+ }
148
+ void main() {
149
+ float radius = max(uRadius, 0.0001);
150
+ int halfKernel = int(clamp(ceil(radius), 4.0, 64.0));
151
+ float sigma = radius / 3.0;
152
+ float sum = 0.0;
153
+ float total = 0.0;
154
+ for (int i = -64; i <= 64; i++) {
155
+ if (abs(i) > halfKernel) continue;
156
+ float offset = float(i) * radius / float(halfKernel);
157
+ float weight = exp(-0.5 * offset * offset / (sigma * sigma));
158
+ vec2 uv = vUv + ${direction === "horizontal" ? "vec2(offset, 0.0)" : "vec2(0.0, offset)"} / uResolution;
159
+ sum += readHeight(uv) * weight;
160
+ total += weight;
161
+ }
162
+ fragColor = encodeHeight(sum / total);
163
+ }
164
+ `;
165
+ var BEVEL_ALPHA_SHADE_SHADER = `#version 300 es
166
+ precision highp float;
167
+ in vec2 vUv;
168
+ out vec4 fragColor;
169
+ uniform sampler2D uSource;
170
+ uniform sampler2D uHeight;
171
+ uniform vec2 uResolution;
172
+ uniform float uEdgeThickness;
173
+ uniform vec3 uLightDirection;
174
+ uniform vec3 uLightColor;
175
+ uniform float uLightIntensity;
176
+ ${HEIGHT_ENCODING}
177
+ float heightAt(ivec2 pixel) {
178
+ return decodeHeight(texelFetch(uHeight, clamp(pixel, ivec2(0), ivec2(uResolution) - 1), 0));
179
+ }
180
+ void main() {
181
+ vec4 source = texelFetch(uSource, ivec2(gl_FragCoord.xy), 0);
182
+ if (uEdgeThickness == 0.0 || uLightIntensity == 0.0 || source.a == 0.0) {
183
+ fragColor = source;
184
+ return;
185
+ }
186
+ ivec2 pixel = ivec2(gl_FragCoord.xy);
187
+ float dx = (heightAt(pixel + ivec2(1, 0)) - heightAt(pixel - ivec2(1, 0))) * 0.5;
188
+ // Screen-space Y points down; texture-space Y points up.
189
+ float dy = (heightAt(pixel - ivec2(0, 1)) - heightAt(pixel + ivec2(0, 1))) * 0.5;
190
+ vec3 normal = normalize(vec3(-dx * uEdgeThickness * 1.25, -dy * uEdgeThickness * 1.25, 1.0));
191
+ float shade = (dot(normal, uLightDirection) - uLightDirection.z) * uLightIntensity * 2.0;
192
+ vec3 color = source.rgb;
193
+ if (shade > 0.0) {
194
+ color += uLightColor * shade * source.a;
195
+ } else {
196
+ color *= max(1.0 + shade, 0.0);
197
+ }
198
+ fragColor = vec4(clamp(color, vec3(0.0), vec3(source.a)), source.a);
199
+ }
200
+ `;
201
+
202
+ // src/bevel-alpha/index.ts
203
+ var { createEffect, createWebGL2ContextError } = Internals;
204
+ var DEFAULT_EDGE_THICKNESS = 2;
205
+ var DEFAULT_LIGHT_ANGLE = -60;
206
+ var DEFAULT_LIGHT_COLOR = "white";
207
+ var DEFAULT_LIGHT_INTENSITY = 0.4;
208
+ var bevelAlphaSchema = {
209
+ edgeThickness: {
210
+ type: "number",
211
+ min: 0,
212
+ max: 200,
213
+ step: 0.1,
214
+ default: DEFAULT_EDGE_THICKNESS,
215
+ description: "Edge thickness",
216
+ hiddenFromList: false
217
+ },
218
+ lightAngle: {
219
+ type: "rotation-degrees",
220
+ step: 1,
221
+ default: DEFAULT_LIGHT_ANGLE,
222
+ description: "Light angle"
223
+ },
224
+ lightColor: {
225
+ type: "color",
226
+ default: DEFAULT_LIGHT_COLOR,
227
+ description: "Light color"
228
+ },
229
+ lightIntensity: {
230
+ type: "number",
231
+ min: 0,
232
+ max: 1,
233
+ step: 0.01,
234
+ default: DEFAULT_LIGHT_INTENSITY,
235
+ description: "Light intensity",
236
+ hiddenFromList: false
237
+ }
238
+ };
239
+ var resolve = (p) => ({
240
+ edgeThickness: p.edgeThickness ?? DEFAULT_EDGE_THICKNESS,
241
+ lightAngle: p.lightAngle ?? DEFAULT_LIGHT_ANGLE,
242
+ lightColor: p.lightColor ?? DEFAULT_LIGHT_COLOR,
243
+ lightIntensity: p.lightIntensity ?? DEFAULT_LIGHT_INTENSITY
244
+ });
245
+ var validateBevelAlphaParams = (params) => {
246
+ assertEffectParamsObject(params, "Bevel alpha");
247
+ assertOptionalFiniteNumber(params.edgeThickness, "edgeThickness");
248
+ assertOptionalFiniteNumber(params.lightAngle, "lightAngle");
249
+ assertOptionalColor(params.lightColor, "lightColor");
250
+ assertOptionalFiniteNumber(params.lightIntensity, "lightIntensity");
251
+ const r = resolve(params);
252
+ validateNonNegative(r.edgeThickness, "edgeThickness");
253
+ if (r.edgeThickness > 200)
254
+ throw new TypeError('"edgeThickness" must be at most 200');
255
+ validateUnitInterval(r.lightIntensity, "lightIntensity");
256
+ };
257
+ var compileShader = (gl, type, source) => {
258
+ const shader = gl.createShader(type);
259
+ if (!shader) {
260
+ throw new Error("Failed to create WebGL shader");
261
+ }
262
+ gl.shaderSource(shader, source);
263
+ gl.compileShader(shader);
264
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
265
+ const log = gl.getShaderInfoLog(shader);
266
+ gl.deleteShader(shader);
267
+ throw new Error(`BevelAlpha shader compile failed: ${log ?? "(no log)"}`);
268
+ }
269
+ return shader;
270
+ };
271
+ var linkProgram = (gl, vs, fs) => {
272
+ const program = gl.createProgram();
273
+ if (!program) {
274
+ throw new Error("Failed to create WebGL program");
275
+ }
276
+ gl.attachShader(program, vs);
277
+ gl.attachShader(program, fs);
278
+ gl.linkProgram(program);
279
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
280
+ const log = gl.getProgramInfoLog(program);
281
+ gl.deleteProgram(program);
282
+ throw new Error(`BevelAlpha program link failed: ${log ?? "(no log)"}`);
283
+ }
284
+ return program;
285
+ };
286
+ var setupBevelAlpha = (target) => {
287
+ const gl = target.getContext("webgl2", {
288
+ premultipliedAlpha: true,
289
+ alpha: true,
290
+ preserveDrawingBuffer: true
291
+ });
292
+ if (!gl)
293
+ throw createWebGL2ContextError("bevel alpha effect");
294
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
295
+ const vs = compileShader(gl, gl.VERTEX_SHADER, BEVEL_ALPHA_VERTEX_SHADER);
296
+ const programs = [
297
+ buildBevelAlphaBlurShader("horizontal"),
298
+ buildBevelAlphaBlurShader("vertical"),
299
+ BEVEL_ALPHA_SHADE_SHADER
300
+ ].map((source) => {
301
+ const fs = compileShader(gl, gl.FRAGMENT_SHADER, source);
302
+ const shaderProgram = linkProgram(gl, vs, fs);
303
+ gl.deleteShader(fs);
304
+ return shaderProgram;
305
+ });
306
+ gl.deleteShader(vs);
307
+ const [horizontalProgram, verticalProgram, program] = programs;
308
+ const passes = [horizontalProgram, verticalProgram].map((p) => ({
309
+ program: p,
310
+ uSource: gl.getUniformLocation(p, "uSource"),
311
+ uResolution: gl.getUniformLocation(p, "uResolution"),
312
+ uRadius: gl.getUniformLocation(p, "uRadius")
313
+ }));
314
+ const vao = gl.createVertexArray();
315
+ const vbo = gl.createBuffer();
316
+ const framebuffer = gl.createFramebuffer();
317
+ if (!vao || !vbo || !framebuffer)
318
+ throw new Error("Could not allocate bevel alpha WebGL resources");
319
+ gl.bindVertexArray(vao);
320
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
321
+ 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);
322
+ gl.enableVertexAttribArray(0);
323
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0);
324
+ gl.enableVertexAttribArray(1);
325
+ gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8);
326
+ gl.bindVertexArray(null);
327
+ const textures = [gl.LINEAR, gl.NEAREST, gl.NEAREST].map((filter) => {
328
+ const texture = gl.createTexture();
329
+ if (!texture)
330
+ throw new Error("Could not allocate bevel alpha texture");
331
+ gl.bindTexture(gl.TEXTURE_2D, texture);
332
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
333
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
334
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
335
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
336
+ return texture;
337
+ });
338
+ gl.bindTexture(gl.TEXTURE_2D, null);
339
+ const colorCanvas = document.createElement("canvas");
340
+ colorCanvas.width = 1;
341
+ colorCanvas.height = 1;
342
+ const colorCtx = colorCanvas.getContext("2d", { willReadFrequently: true });
343
+ if (!colorCtx)
344
+ throw new Error("Failed to acquire 2D context for color parsing");
345
+ return {
346
+ gl,
347
+ horizontal: passes[0],
348
+ vertical: passes[1],
349
+ program,
350
+ vao,
351
+ vbo,
352
+ framebuffer,
353
+ texture: textures[0],
354
+ intermediate: textures[1],
355
+ heightTexture: textures[2],
356
+ uSource: gl.getUniformLocation(program, "uSource"),
357
+ uHeight: gl.getUniformLocation(program, "uHeight"),
358
+ uResolution: gl.getUniformLocation(program, "uResolution"),
359
+ uEdgeThickness: gl.getUniformLocation(program, "uEdgeThickness"),
360
+ uLightDirection: gl.getUniformLocation(program, "uLightDirection"),
361
+ uLightColor: gl.getUniformLocation(program, "uLightColor"),
362
+ uLightIntensity: gl.getUniformLocation(program, "uLightIntensity"),
363
+ colorCtx,
364
+ cachedColor: "",
365
+ cachedColorRgba: [255, 255, 255, 255],
366
+ width: 0,
367
+ height: 0
368
+ };
369
+ };
370
+ var bevelAlpha = createEffect({
371
+ type: "remotion/bevel-alpha",
372
+ label: "bevelAlpha()",
373
+ documentationLink: "https://www.remotion.dev/docs/effects/bevel-alpha",
374
+ backend: "webgl2",
375
+ calculateKey: (params) => `bevel-alpha-${JSON.stringify(resolve(params))}`,
376
+ setup: setupBevelAlpha,
377
+ apply: ({ source, width, height, params, state, flipSourceY }) => {
378
+ const r = resolve(params);
379
+ const {
380
+ gl,
381
+ texture,
382
+ intermediate,
383
+ heightTexture,
384
+ program,
385
+ vao,
386
+ framebuffer
387
+ } = state;
388
+ if (state.cachedColor !== r.lightColor) {
389
+ state.cachedColor = r.lightColor;
390
+ state.cachedColorRgba = parseColorRgba(state.colorCtx, r.lightColor);
391
+ }
392
+ gl.viewport(0, 0, width, height);
393
+ gl.activeTexture(gl.TEXTURE0);
394
+ gl.bindTexture(gl.TEXTURE_2D, texture);
395
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
396
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
397
+ if (state.width !== width || state.height !== height) {
398
+ for (const output of [intermediate, heightTexture]) {
399
+ gl.bindTexture(gl.TEXTURE_2D, output);
400
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
401
+ }
402
+ state.width = width;
403
+ state.height = height;
404
+ }
405
+ gl.bindVertexArray(vao);
406
+ if (r.edgeThickness > 0 && r.lightIntensity > 0) {
407
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
408
+ const ditherEnabled = gl.isEnabled(gl.DITHER);
409
+ gl.disable(gl.DITHER);
410
+ try {
411
+ for (const [pass, input, output] of [
412
+ [state.horizontal, texture, intermediate],
413
+ [state.vertical, intermediate, heightTexture]
414
+ ]) {
415
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, output, 0);
416
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE)
417
+ throw new Error("Bevel alpha framebuffer is incomplete");
418
+ gl.bindTexture(gl.TEXTURE_2D, input);
419
+ gl.useProgram(pass.program);
420
+ gl.uniform1i(pass.uSource, 0);
421
+ gl.uniform2f(pass.uResolution, width, height);
422
+ gl.uniform1f(pass.uRadius, r.edgeThickness * 1.5);
423
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
424
+ }
425
+ } finally {
426
+ if (ditherEnabled)
427
+ gl.enable(gl.DITHER);
428
+ }
429
+ }
430
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
431
+ gl.bindTexture(gl.TEXTURE_2D, texture);
432
+ gl.activeTexture(gl.TEXTURE1);
433
+ gl.bindTexture(gl.TEXTURE_2D, heightTexture);
434
+ gl.useProgram(program);
435
+ gl.uniform1i(state.uSource, 0);
436
+ gl.uniform1i(state.uHeight, 1);
437
+ gl.uniform2f(state.uResolution, width, height);
438
+ gl.uniform1f(state.uEdgeThickness, r.edgeThickness);
439
+ const angle = r.lightAngle % 360 * Math.PI / 180;
440
+ gl.uniform3f(state.uLightDirection, Math.sin(angle) * Math.SQRT1_2, -Math.cos(angle) * Math.SQRT1_2, Math.SQRT1_2);
441
+ const [red, green, blue, alpha] = state.cachedColorRgba;
442
+ gl.uniform3f(state.uLightColor, red / 255 * alpha / 255, green / 255 * alpha / 255, blue / 255 * alpha / 255);
443
+ gl.uniform1f(state.uLightIntensity, r.lightIntensity);
444
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
445
+ gl.bindVertexArray(null);
446
+ gl.bindTexture(gl.TEXTURE_2D, null);
447
+ gl.activeTexture(gl.TEXTURE0);
448
+ gl.bindTexture(gl.TEXTURE_2D, null);
449
+ gl.useProgram(null);
450
+ },
451
+ cleanup: (state) => {
452
+ const { gl } = state;
453
+ for (const texture of [
454
+ state.texture,
455
+ state.intermediate,
456
+ state.heightTexture
457
+ ])
458
+ gl.deleteTexture(texture);
459
+ for (const program of [
460
+ state.horizontal.program,
461
+ state.vertical.program,
462
+ state.program
463
+ ])
464
+ gl.deleteProgram(program);
465
+ gl.deleteFramebuffer(state.framebuffer);
466
+ gl.deleteBuffer(state.vbo);
467
+ gl.deleteVertexArray(state.vao);
468
+ },
469
+ schema: bevelAlphaSchema,
470
+ validateParams: validateBevelAlphaParams
471
+ });
472
+ export {
473
+ bevelAlpha
474
+ };