@remotion/effects 4.0.479 → 4.0.482

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.
@@ -1,4 +1,4 @@
1
- // src/pattern.ts
1
+ // src/checkerboard.ts
2
2
  import { Internals } from "remotion";
3
3
 
4
4
  // src/validate-effect-param.ts
@@ -106,195 +106,120 @@ var parseColorRgba = (ctx, color) => {
106
106
  return [data[0], data[1], data[2], data[3]];
107
107
  };
108
108
 
109
- // src/pattern.ts
109
+ // src/checkerboard.ts
110
110
  var { createEffect, createWebGL2ContextError } = Internals;
111
- var DEFAULT_SCALE = 0.1;
112
- var DEFAULT_CROP = 0;
111
+ var DEFAULT_COLORS = ["#dff4ff", "#7cc6ff"];
112
+ var DEFAULT_CELL_SIZE = 80;
113
113
  var DEFAULT_GAP = 0;
114
- var DEFAULT_OFFSET = 0;
115
- var DEFAULT_OFFSET_EVERY = 0;
116
- var DEFAULT_ORIGIN = [0, 0];
117
- var DEFAULT_WRAP = true;
118
- var patternSchema = {
119
- scale: {
120
- type: "number",
121
- min: 0.001,
122
- max: 2,
123
- step: 0.01,
124
- default: DEFAULT_SCALE,
125
- description: "Scale",
126
- hiddenFromList: false
127
- },
128
- cropLeft: {
129
- type: "number",
130
- step: 1,
131
- default: DEFAULT_CROP,
132
- description: "Crop left",
133
- hiddenFromList: false
134
- },
135
- cropTop: {
136
- type: "number",
137
- step: 1,
138
- default: DEFAULT_CROP,
139
- description: "Crop top",
140
- hiddenFromList: false
141
- },
142
- cropRight: {
143
- type: "number",
144
- step: 1,
145
- default: DEFAULT_CROP,
146
- description: "Crop right",
147
- hiddenFromList: false
148
- },
149
- cropBottom: {
150
- type: "number",
151
- step: 1,
152
- default: DEFAULT_CROP,
153
- description: "Crop bottom",
154
- hiddenFromList: false
114
+ var DEFAULT_ANGLE = 0;
115
+ var DEFAULT_OFFSET_X = 0;
116
+ var DEFAULT_OFFSET_Y = 0;
117
+ var DEFAULT_MASK_TO_SOURCE_ALPHA = false;
118
+ var checkerboardSchema = {
119
+ colors: {
120
+ type: "array",
121
+ item: {
122
+ type: "color"
123
+ },
124
+ default: DEFAULT_COLORS,
125
+ minLength: 2,
126
+ newItemDefault: "#ff0000",
127
+ description: "Colors",
128
+ keyframable: false
155
129
  },
156
- gapX: {
130
+ cellSize: {
157
131
  type: "number",
158
- step: 1,
159
- default: DEFAULT_GAP,
160
- description: "Gap X",
132
+ min: 0.1,
133
+ max: 800,
134
+ step: 0.1,
135
+ default: DEFAULT_CELL_SIZE,
136
+ description: "Cell size",
161
137
  hiddenFromList: false
162
138
  },
163
- gapY: {
139
+ gap: {
164
140
  type: "number",
165
- step: 1,
141
+ min: 0,
142
+ max: 400,
143
+ step: 0.1,
166
144
  default: DEFAULT_GAP,
167
- description: "Gap Y",
168
- hiddenFromList: false
169
- },
170
- offsetU: {
171
- type: "number",
172
- step: 0.01,
173
- default: DEFAULT_OFFSET,
174
- description: "Offset U",
175
- hiddenFromList: false
176
- },
177
- offsetV: {
178
- type: "number",
179
- step: 0.01,
180
- default: DEFAULT_OFFSET,
181
- description: "Offset V",
182
- hiddenFromList: false
183
- },
184
- rowOffset: {
185
- type: "number",
186
- step: 1,
187
- default: DEFAULT_OFFSET,
188
- description: "Row offset",
145
+ description: "Gap",
189
146
  hiddenFromList: false
190
147
  },
191
- rowOffsetEvery: {
192
- type: "number",
193
- min: 0,
148
+ angle: {
149
+ type: "rotation-degrees",
194
150
  step: 1,
195
- default: DEFAULT_OFFSET_EVERY,
196
- description: "Row offset every",
197
- hiddenFromList: false
151
+ default: DEFAULT_ANGLE,
152
+ description: "Angle"
198
153
  },
199
- columnOffset: {
154
+ offsetX: {
200
155
  type: "number",
201
- step: 1,
202
- default: DEFAULT_OFFSET,
203
- description: "Column offset",
156
+ step: 0.1,
157
+ default: DEFAULT_OFFSET_X,
158
+ description: "Offset X",
204
159
  hiddenFromList: false
205
160
  },
206
- columnOffsetEvery: {
161
+ offsetY: {
207
162
  type: "number",
208
- min: 0,
209
- step: 1,
210
- default: DEFAULT_OFFSET_EVERY,
211
- description: "Column offset every",
163
+ step: 0.1,
164
+ default: DEFAULT_OFFSET_Y,
165
+ description: "Offset Y",
212
166
  hiddenFromList: false
213
167
  },
214
- origin: {
215
- type: "uv-coordinate",
216
- min: 0,
217
- max: 1,
218
- step: 0.01,
219
- default: DEFAULT_ORIGIN,
220
- description: "Origin"
221
- },
222
- wrap: {
168
+ maskToSourceAlpha: {
223
169
  type: "boolean",
224
- default: DEFAULT_WRAP,
225
- description: "Wrap"
170
+ default: DEFAULT_MASK_TO_SOURCE_ALPHA,
171
+ description: "Mask to source alpha"
226
172
  }
227
173
  };
228
- var resolve = (p) => ({
229
- scale: p.scale ?? DEFAULT_SCALE,
230
- cropLeft: p.cropLeft ?? DEFAULT_CROP,
231
- cropTop: p.cropTop ?? DEFAULT_CROP,
232
- cropRight: p.cropRight ?? DEFAULT_CROP,
233
- cropBottom: p.cropBottom ?? DEFAULT_CROP,
234
- gapX: p.gapX ?? DEFAULT_GAP,
235
- gapY: p.gapY ?? DEFAULT_GAP,
236
- offsetU: p.offsetU ?? DEFAULT_OFFSET,
237
- offsetV: p.offsetV ?? DEFAULT_OFFSET,
238
- rowOffset: p.rowOffset ?? DEFAULT_OFFSET,
239
- rowOffsetEvery: p.rowOffsetEvery ?? DEFAULT_OFFSET_EVERY,
240
- columnOffset: p.columnOffset ?? DEFAULT_OFFSET,
241
- columnOffsetEvery: p.columnOffsetEvery ?? DEFAULT_OFFSET_EVERY,
242
- origin: [...p.origin ?? DEFAULT_ORIGIN],
243
- wrap: p.wrap ?? DEFAULT_WRAP
244
- });
245
- var assertOptionalUvCoordinate = (value, name) => {
246
- if (value === undefined) {
247
- return;
174
+ var resolve = (p) => {
175
+ const cellSize = p.cellSize ?? DEFAULT_CELL_SIZE;
176
+ const gap = p.gap ?? DEFAULT_GAP;
177
+ return {
178
+ colors: p.colors ?? DEFAULT_COLORS,
179
+ cellSize,
180
+ spacing: cellSize + gap,
181
+ angle: p.angle ?? DEFAULT_ANGLE,
182
+ offsetX: p.offsetX ?? DEFAULT_OFFSET_X,
183
+ offsetY: p.offsetY ?? DEFAULT_OFFSET_Y,
184
+ maskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA
185
+ };
186
+ };
187
+ var validatePositive = (value, name) => {
188
+ if (value <= 0) {
189
+ throw new TypeError(`"${name}" must be greater than 0, but got ${JSON.stringify(value)}`);
248
190
  }
249
- if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
250
- throw new TypeError(`"${name}" must be a [number, number] tuple`);
191
+ };
192
+ var validateNonNegative2 = (value, name) => {
193
+ if (value < 0) {
194
+ throw new TypeError(`"${name}" must be greater than or equal to 0, but got ${JSON.stringify(value)}`);
251
195
  }
252
196
  };
253
- var assertOptionalInteger = (value, name) => {
254
- if (value === undefined) {
197
+ var validateColors = (colors) => {
198
+ if (colors === undefined) {
255
199
  return;
256
200
  }
257
- if (!Number.isInteger(value)) {
258
- throw new TypeError(`"${name}" must be an integer, but got ${JSON.stringify(value)}`);
259
- }
260
- };
261
- var validatePositive = (value, name) => {
262
- if (value <= 0) {
263
- throw new TypeError(`"${name}" must be > 0`);
201
+ if (!Array.isArray(colors) || colors.length < 2) {
202
+ throw new TypeError(`"colors" must be an array with at least 2 colors, but got ${JSON.stringify(colors)}`);
264
203
  }
265
- };
266
- var validateAtLeast = (value, min, name) => {
267
- if (value < min) {
268
- throw new TypeError(`"${name}" must be >= ${min}, but got ${JSON.stringify(value)}`);
204
+ for (let i = 0;i < colors.length; i++) {
205
+ assertRequiredColor(colors[i], `colors[${i}]`);
269
206
  }
270
207
  };
271
- var validatePatternParams = (params) => {
272
- assertEffectParamsObject(params, "Pattern");
273
- assertOptionalFiniteNumber(params.scale, "scale");
274
- assertOptionalFiniteNumber(params.cropLeft, "cropLeft");
275
- assertOptionalFiniteNumber(params.cropTop, "cropTop");
276
- assertOptionalFiniteNumber(params.cropRight, "cropRight");
277
- assertOptionalFiniteNumber(params.cropBottom, "cropBottom");
278
- assertOptionalFiniteNumber(params.gapX, "gapX");
279
- assertOptionalFiniteNumber(params.gapY, "gapY");
280
- assertOptionalFiniteNumber(params.offsetU, "offsetU");
281
- assertOptionalFiniteNumber(params.offsetV, "offsetV");
282
- assertOptionalFiniteNumber(params.rowOffset, "rowOffset");
283
- assertOptionalFiniteNumber(params.rowOffsetEvery, "rowOffsetEvery");
284
- assertOptionalFiniteNumber(params.columnOffset, "columnOffset");
285
- assertOptionalFiniteNumber(params.columnOffsetEvery, "columnOffsetEvery");
286
- assertOptionalUvCoordinate(params.origin, "origin");
287
- assertOptionalBoolean(params.wrap, "wrap");
288
- assertOptionalInteger(params.rowOffsetEvery, "rowOffsetEvery");
289
- assertOptionalInteger(params.columnOffsetEvery, "columnOffsetEvery");
290
- const r = resolve(params);
291
- validatePositive(r.scale, "scale");
292
- validateAtLeast(r.rowOffsetEvery, 0, "rowOffsetEvery");
293
- validateAtLeast(r.columnOffsetEvery, 0, "columnOffsetEvery");
294
- validateUnitInterval(r.origin[0], "origin[0]");
295
- validateUnitInterval(r.origin[1], "origin[1]");
208
+ var validateCheckerboardParams = (params) => {
209
+ assertEffectParamsObject(params, "Checkerboard");
210
+ validateColors(params.colors);
211
+ assertOptionalFiniteNumber(params.cellSize, "cellSize");
212
+ assertOptionalFiniteNumber(params.gap, "gap");
213
+ assertOptionalFiniteNumber(params.angle, "angle");
214
+ assertOptionalFiniteNumber(params.offsetX, "offsetX");
215
+ assertOptionalFiniteNumber(params.offsetY, "offsetY");
216
+ assertOptionalBoolean(params.maskToSourceAlpha, "maskToSourceAlpha");
217
+ const cellSize = params.cellSize ?? DEFAULT_CELL_SIZE;
218
+ const gap = params.gap ?? DEFAULT_GAP;
219
+ validatePositive(cellSize, "cellSize");
220
+ validateNonNegative2(gap, "gap");
296
221
  };
297
- var PATTERN_VS = `#version 300 es
222
+ var CHECKERBOARD_VS = `#version 300 es
298
223
  in vec2 aPos;
299
224
  in vec2 aUv;
300
225
  out vec2 vUv;
@@ -304,101 +229,936 @@ void main() {
304
229
  gl_Position = vec4(aPos, 0.0, 1.0);
305
230
  }
306
231
  `;
307
- var PATTERN_FS = `#version 300 es
232
+ var CHECKERBOARD_FS = `#version 300 es
308
233
  precision highp float;
309
234
 
310
235
  in vec2 vUv;
311
236
  out vec4 fragColor;
312
237
 
313
238
  uniform sampler2D uSource;
239
+ uniform sampler2D uPalette;
314
240
  uniform vec2 uResolution;
315
- uniform float uScale;
316
- uniform vec4 uCrop;
317
- uniform vec2 uGap;
241
+ uniform float uNumColors;
242
+ uniform float uCellSize;
243
+ uniform float uSpacing;
244
+ uniform float uAngle;
318
245
  uniform vec2 uOffset;
319
- uniform vec2 uRowOffset;
320
- uniform vec2 uColumnOffset;
321
- uniform vec2 uOrigin;
322
- uniform bool uWrap;
323
-
324
- float positiveModulo(float value, float modulo) {
325
- return mod(mod(value, modulo) + modulo, modulo);
326
- }
246
+ uniform bool uMaskToSourceAlpha;
327
247
 
328
- float getPhase(float index, float every) {
329
- if (every == 0.0) {
330
- return index;
248
+ void main() {
249
+ vec4 texColor = texture(uSource, vUv);
250
+ float cellSize = max(uCellSize, 0.001);
251
+ float spacing = max(uSpacing, 0.001);
252
+ vec2 centered = vUv * uResolution - uResolution * 0.5;
253
+ float s = sin(uAngle);
254
+ float c = cos(uAngle);
255
+ vec2 rotated = vec2(
256
+ centered.x * c - centered.y * s,
257
+ centered.x * s + centered.y * c
258
+ );
259
+ vec2 position = rotated + uOffset;
260
+ vec2 localPosition = mod(position, spacing);
261
+ if (localPosition.x < 0.0) {
262
+ localPosition.x += spacing;
331
263
  }
332
-
333
- return positiveModulo(index, every);
334
- }
335
-
336
- bool sampleCell(vec2 cell, vec2 fragPx, vec2 cropStart, vec2 tileSize, vec2 pitch, vec2 originPx, out vec4 color) {
337
- if (!uWrap && (cell.x < 0.0 || cell.y < 0.0)) {
338
- return false;
264
+ if (localPosition.y < 0.0) {
265
+ localPosition.y += spacing;
339
266
  }
340
267
 
341
- float rowPhase = getPhase(cell.y, uRowOffset.y);
342
- float columnPhase = getPhase(cell.x, uColumnOffset.y);
343
- vec2 stagger = vec2(rowPhase * uRowOffset.x, columnPhase * uColumnOffset.x);
344
- vec2 localPx = fragPx - originPx - stagger - cell * pitch;
268
+ if (localPosition.x > cellSize || localPosition.y > cellSize) {
269
+ fragColor = texColor;
270
+ return;
271
+ }
345
272
 
346
- if (localPx.x < 0.0 || localPx.y < 0.0 || localPx.x >= tileSize.x || localPx.y >= tileSize.y) {
347
- return false;
273
+ vec2 cell = floor(position / spacing);
274
+ float colorIndex = mod(cell.x + cell.y, uNumColors);
275
+ if (colorIndex < 0.0) {
276
+ colorIndex += uNumColors;
348
277
  }
349
278
 
350
- vec2 sourcePx = cropStart + localPx / uScale;
351
- vec2 sourceUv = vec2(sourcePx.x / uResolution.x, 1.0 - sourcePx.y / uResolution.y);
279
+ float texCoord = (colorIndex + 0.5) / uNumColors;
280
+ vec4 checkerColor = texture(uPalette, vec2(texCoord, 0.5));
281
+ float checkerAlpha = checkerColor.a;
282
+ vec3 premultipliedChecker = checkerColor.rgb * checkerAlpha;
283
+
284
+ if (uMaskToSourceAlpha) {
285
+ fragColor = vec4(
286
+ premultipliedChecker * texColor.a + texColor.rgb * (1.0 - checkerAlpha),
287
+ texColor.a
288
+ );
289
+ return;
290
+ }
291
+
292
+ fragColor = vec4(
293
+ premultipliedChecker + texColor.rgb * (1.0 - checkerAlpha),
294
+ checkerAlpha + texColor.a * (1.0 - checkerAlpha)
295
+ );
296
+ }
297
+ `;
298
+ var compileShader = (gl, type, source) => {
299
+ const shader = gl.createShader(type);
300
+ if (!shader) {
301
+ throw new Error("Failed to create WebGL shader");
302
+ }
303
+ gl.shaderSource(shader, source);
304
+ gl.compileShader(shader);
305
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
306
+ const log = gl.getShaderInfoLog(shader);
307
+ gl.deleteShader(shader);
308
+ throw new Error(`Checkerboard shader compile failed: ${log ?? "(no log)"}`);
309
+ }
310
+ return shader;
311
+ };
312
+ var linkProgram = (gl, vs, fs) => {
313
+ const program = gl.createProgram();
314
+ if (!program) {
315
+ throw new Error("Failed to create WebGL program");
316
+ }
317
+ gl.attachShader(program, vs);
318
+ gl.attachShader(program, fs);
319
+ gl.linkProgram(program);
320
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
321
+ const log = gl.getProgramInfoLog(program);
322
+ gl.deleteProgram(program);
323
+ throw new Error(`Checkerboard program link failed: ${log ?? "(no log)"}`);
324
+ }
325
+ return program;
326
+ };
327
+ var createProgram = (gl, vertexSource, fragmentSource) => {
328
+ const vs = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
329
+ const fs = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
330
+ const program = linkProgram(gl, vs, fs);
331
+ gl.deleteShader(vs);
332
+ gl.deleteShader(fs);
333
+ return program;
334
+ };
335
+ var createTexture = (gl, filter) => {
336
+ const texture = gl.createTexture();
337
+ if (!texture) {
338
+ throw new Error("Failed to create WebGL texture");
339
+ }
340
+ gl.bindTexture(gl.TEXTURE_2D, texture);
341
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
342
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
343
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
344
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
345
+ gl.bindTexture(gl.TEXTURE_2D, null);
346
+ return texture;
347
+ };
348
+ var setupCheckerboard = (target) => {
349
+ const gl = target.getContext("webgl2", {
350
+ premultipliedAlpha: true,
351
+ alpha: true,
352
+ preserveDrawingBuffer: true
353
+ });
354
+ if (!gl) {
355
+ throw createWebGL2ContextError("checkerboard effect");
356
+ }
357
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
358
+ const program = createProgram(gl, CHECKERBOARD_VS, CHECKERBOARD_FS);
359
+ const vao = gl.createVertexArray();
360
+ if (!vao) {
361
+ throw new Error("Failed to create WebGL vertex array");
362
+ }
363
+ gl.bindVertexArray(vao);
364
+ const data = new Float32Array([
365
+ -1,
366
+ -1,
367
+ 0,
368
+ 0,
369
+ 1,
370
+ -1,
371
+ 1,
372
+ 0,
373
+ -1,
374
+ 1,
375
+ 0,
376
+ 1,
377
+ 1,
378
+ 1,
379
+ 1,
380
+ 1
381
+ ]);
382
+ const vbo = gl.createBuffer();
383
+ if (!vbo) {
384
+ throw new Error("Failed to create WebGL buffer");
385
+ }
386
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
387
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
388
+ const aPos = gl.getAttribLocation(program, "aPos");
389
+ const aUv = gl.getAttribLocation(program, "aUv");
390
+ gl.enableVertexAttribArray(aPos);
391
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
392
+ gl.enableVertexAttribArray(aUv);
393
+ gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
394
+ gl.bindVertexArray(null);
395
+ const colorCanvas = document.createElement("canvas");
396
+ colorCanvas.width = 1;
397
+ colorCanvas.height = 1;
398
+ const colorCtx = colorCanvas.getContext("2d", { willReadFrequently: true });
399
+ if (!colorCtx) {
400
+ throw new Error("Failed to acquire 2D context for color parsing");
401
+ }
402
+ return {
403
+ gl,
404
+ program,
405
+ vao,
406
+ vbo,
407
+ sourceTexture: createTexture(gl, gl.LINEAR),
408
+ paletteTexture: createTexture(gl, gl.NEAREST),
409
+ colorCtx,
410
+ uniforms: {
411
+ uSource: gl.getUniformLocation(program, "uSource"),
412
+ uPalette: gl.getUniformLocation(program, "uPalette"),
413
+ uResolution: gl.getUniformLocation(program, "uResolution"),
414
+ uNumColors: gl.getUniformLocation(program, "uNumColors"),
415
+ uCellSize: gl.getUniformLocation(program, "uCellSize"),
416
+ uSpacing: gl.getUniformLocation(program, "uSpacing"),
417
+ uAngle: gl.getUniformLocation(program, "uAngle"),
418
+ uOffset: gl.getUniformLocation(program, "uOffset"),
419
+ uMaskToSourceAlpha: gl.getUniformLocation(program, "uMaskToSourceAlpha")
420
+ },
421
+ cachedPaletteKey: "",
422
+ palettePixelData: new Uint8Array(0)
423
+ };
424
+ };
425
+ var updatePalette = (state, colors) => {
426
+ const paletteKey = colors.join("|");
427
+ const paletteDirty = state.cachedPaletteKey !== paletteKey;
428
+ if (!paletteDirty) {
429
+ return false;
430
+ }
431
+ state.cachedPaletteKey = paletteKey;
432
+ const len = colors.length * 4;
433
+ if (state.palettePixelData.length !== len) {
434
+ state.palettePixelData = new Uint8Array(len);
435
+ }
436
+ const { palettePixelData } = state;
437
+ for (let i = 0;i < colors.length; i++) {
438
+ const color = parseColorRgba(state.colorCtx, colors[i]);
439
+ palettePixelData[i * 4] = color[0];
440
+ palettePixelData[i * 4 + 1] = color[1];
441
+ palettePixelData[i * 4 + 2] = color[2];
442
+ palettePixelData[i * 4 + 3] = color[3];
443
+ }
444
+ return true;
445
+ };
446
+ var checkerboard = createEffect({
447
+ type: "remotion/checkerboard",
448
+ label: "checkerboard()",
449
+ documentationLink: "https://www.remotion.dev/docs/effects/checkerboard",
450
+ backend: "webgl2",
451
+ calculateKey: (params) => {
452
+ const r = resolve(params);
453
+ const maskSuffix = r.maskToSourceAlpha ? "-mask-to-source-alpha" : "";
454
+ return `checkerboard-${r.colors.join("|")}-${r.cellSize}-${r.spacing}-${r.angle}-${r.offsetX}-${r.offsetY}${maskSuffix}`;
455
+ },
456
+ setup: (target) => setupCheckerboard(target),
457
+ apply: ({ source, width, height, params, state, flipSourceY }) => {
458
+ const r = resolve(params);
459
+ const paletteDirty = updatePalette(state, r.colors);
460
+ const { gl, program, sourceTexture, paletteTexture, uniforms, vao } = state;
461
+ gl.viewport(0, 0, width, height);
462
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
463
+ gl.clearColor(0, 0, 0, 0);
464
+ gl.clear(gl.COLOR_BUFFER_BIT);
465
+ gl.activeTexture(gl.TEXTURE0);
466
+ gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
467
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
468
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
469
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
470
+ gl.activeTexture(gl.TEXTURE1);
471
+ gl.bindTexture(gl.TEXTURE_2D, paletteTexture);
472
+ if (paletteDirty) {
473
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
474
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
475
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, r.colors.length, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, state.palettePixelData);
476
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
477
+ }
478
+ gl.useProgram(program);
479
+ if (uniforms.uSource)
480
+ gl.uniform1i(uniforms.uSource, 0);
481
+ if (uniforms.uPalette)
482
+ gl.uniform1i(uniforms.uPalette, 1);
483
+ if (uniforms.uResolution)
484
+ gl.uniform2f(uniforms.uResolution, width, height);
485
+ if (uniforms.uNumColors)
486
+ gl.uniform1f(uniforms.uNumColors, r.colors.length);
487
+ if (uniforms.uCellSize)
488
+ gl.uniform1f(uniforms.uCellSize, r.cellSize);
489
+ if (uniforms.uSpacing)
490
+ gl.uniform1f(uniforms.uSpacing, r.spacing);
491
+ if (uniforms.uAngle)
492
+ gl.uniform1f(uniforms.uAngle, r.angle * Math.PI / 180);
493
+ if (uniforms.uOffset)
494
+ gl.uniform2f(uniforms.uOffset, r.offsetX, r.offsetY);
495
+ if (uniforms.uMaskToSourceAlpha)
496
+ gl.uniform1i(uniforms.uMaskToSourceAlpha, r.maskToSourceAlpha ? 1 : 0);
497
+ gl.bindVertexArray(vao);
498
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
499
+ gl.bindVertexArray(null);
500
+ gl.bindTexture(gl.TEXTURE_2D, null);
501
+ gl.useProgram(null);
502
+ },
503
+ cleanup: ({ gl, program, vao, vbo, sourceTexture, paletteTexture }) => {
504
+ gl.deleteTexture(sourceTexture);
505
+ gl.deleteTexture(paletteTexture);
506
+ gl.deleteBuffer(vbo);
507
+ gl.deleteProgram(program);
508
+ gl.deleteVertexArray(vao);
509
+ },
510
+ schema: checkerboardSchema,
511
+ validateParams: validateCheckerboardParams
512
+ });
513
+
514
+ // src/pattern.ts
515
+ import { Internals as Internals2 } from "remotion";
516
+ var { createEffect: createEffect2, createWebGL2ContextError: createWebGL2ContextError2 } = Internals2;
517
+ var DEFAULT_SCALE = 0.1;
518
+ var DEFAULT_CROP = 0;
519
+ var DEFAULT_GAP2 = 0;
520
+ var DEFAULT_OFFSET = 0;
521
+ var DEFAULT_OFFSET_EVERY = 0;
522
+ var DEFAULT_ORIGIN = [0, 0];
523
+ var DEFAULT_WRAP = true;
524
+ var patternSchema = {
525
+ scale: {
526
+ type: "number",
527
+ min: 0.001,
528
+ max: 2,
529
+ step: 0.01,
530
+ default: DEFAULT_SCALE,
531
+ description: "Scale",
532
+ hiddenFromList: false
533
+ },
534
+ cropLeft: {
535
+ type: "number",
536
+ step: 1,
537
+ default: DEFAULT_CROP,
538
+ description: "Crop left",
539
+ hiddenFromList: false
540
+ },
541
+ cropTop: {
542
+ type: "number",
543
+ step: 1,
544
+ default: DEFAULT_CROP,
545
+ description: "Crop top",
546
+ hiddenFromList: false
547
+ },
548
+ cropRight: {
549
+ type: "number",
550
+ step: 1,
551
+ default: DEFAULT_CROP,
552
+ description: "Crop right",
553
+ hiddenFromList: false
554
+ },
555
+ cropBottom: {
556
+ type: "number",
557
+ step: 1,
558
+ default: DEFAULT_CROP,
559
+ description: "Crop bottom",
560
+ hiddenFromList: false
561
+ },
562
+ gapX: {
563
+ type: "number",
564
+ step: 1,
565
+ default: DEFAULT_GAP2,
566
+ description: "Gap X",
567
+ hiddenFromList: false
568
+ },
569
+ gapY: {
570
+ type: "number",
571
+ step: 1,
572
+ default: DEFAULT_GAP2,
573
+ description: "Gap Y",
574
+ hiddenFromList: false
575
+ },
576
+ offsetU: {
577
+ type: "number",
578
+ step: 0.01,
579
+ default: DEFAULT_OFFSET,
580
+ description: "Offset U",
581
+ hiddenFromList: false
582
+ },
583
+ offsetV: {
584
+ type: "number",
585
+ step: 0.01,
586
+ default: DEFAULT_OFFSET,
587
+ description: "Offset V",
588
+ hiddenFromList: false
589
+ },
590
+ rowOffset: {
591
+ type: "number",
592
+ step: 1,
593
+ default: DEFAULT_OFFSET,
594
+ description: "Row offset",
595
+ hiddenFromList: false
596
+ },
597
+ rowOffsetEvery: {
598
+ type: "number",
599
+ min: 0,
600
+ step: 1,
601
+ default: DEFAULT_OFFSET_EVERY,
602
+ description: "Row offset every",
603
+ hiddenFromList: false
604
+ },
605
+ columnOffset: {
606
+ type: "number",
607
+ step: 1,
608
+ default: DEFAULT_OFFSET,
609
+ description: "Column offset",
610
+ hiddenFromList: false
611
+ },
612
+ columnOffsetEvery: {
613
+ type: "number",
614
+ min: 0,
615
+ step: 1,
616
+ default: DEFAULT_OFFSET_EVERY,
617
+ description: "Column offset every",
618
+ hiddenFromList: false
619
+ },
620
+ origin: {
621
+ type: "uv-coordinate",
622
+ min: 0,
623
+ max: 1,
624
+ step: 0.01,
625
+ default: DEFAULT_ORIGIN,
626
+ description: "Origin"
627
+ },
628
+ wrap: {
629
+ type: "boolean",
630
+ default: DEFAULT_WRAP,
631
+ description: "Wrap"
632
+ }
633
+ };
634
+ var resolve2 = (p) => ({
635
+ scale: p.scale ?? DEFAULT_SCALE,
636
+ cropLeft: p.cropLeft ?? DEFAULT_CROP,
637
+ cropTop: p.cropTop ?? DEFAULT_CROP,
638
+ cropRight: p.cropRight ?? DEFAULT_CROP,
639
+ cropBottom: p.cropBottom ?? DEFAULT_CROP,
640
+ gapX: p.gapX ?? DEFAULT_GAP2,
641
+ gapY: p.gapY ?? DEFAULT_GAP2,
642
+ offsetU: p.offsetU ?? DEFAULT_OFFSET,
643
+ offsetV: p.offsetV ?? DEFAULT_OFFSET,
644
+ rowOffset: p.rowOffset ?? DEFAULT_OFFSET,
645
+ rowOffsetEvery: p.rowOffsetEvery ?? DEFAULT_OFFSET_EVERY,
646
+ columnOffset: p.columnOffset ?? DEFAULT_OFFSET,
647
+ columnOffsetEvery: p.columnOffsetEvery ?? DEFAULT_OFFSET_EVERY,
648
+ origin: [...p.origin ?? DEFAULT_ORIGIN],
649
+ wrap: p.wrap ?? DEFAULT_WRAP
650
+ });
651
+ var assertOptionalUvCoordinate = (value, name) => {
652
+ if (value === undefined) {
653
+ return;
654
+ }
655
+ if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
656
+ throw new TypeError(`"${name}" must be a [number, number] tuple`);
657
+ }
658
+ };
659
+ var assertOptionalInteger = (value, name) => {
660
+ if (value === undefined) {
661
+ return;
662
+ }
663
+ if (!Number.isInteger(value)) {
664
+ throw new TypeError(`"${name}" must be an integer, but got ${JSON.stringify(value)}`);
665
+ }
666
+ };
667
+ var validatePositive2 = (value, name) => {
668
+ if (value <= 0) {
669
+ throw new TypeError(`"${name}" must be > 0`);
670
+ }
671
+ };
672
+ var validateAtLeast = (value, min, name) => {
673
+ if (value < min) {
674
+ throw new TypeError(`"${name}" must be >= ${min}, but got ${JSON.stringify(value)}`);
675
+ }
676
+ };
677
+ var validatePatternParams = (params) => {
678
+ assertEffectParamsObject(params, "Pattern");
679
+ assertOptionalFiniteNumber(params.scale, "scale");
680
+ assertOptionalFiniteNumber(params.cropLeft, "cropLeft");
681
+ assertOptionalFiniteNumber(params.cropTop, "cropTop");
682
+ assertOptionalFiniteNumber(params.cropRight, "cropRight");
683
+ assertOptionalFiniteNumber(params.cropBottom, "cropBottom");
684
+ assertOptionalFiniteNumber(params.gapX, "gapX");
685
+ assertOptionalFiniteNumber(params.gapY, "gapY");
686
+ assertOptionalFiniteNumber(params.offsetU, "offsetU");
687
+ assertOptionalFiniteNumber(params.offsetV, "offsetV");
688
+ assertOptionalFiniteNumber(params.rowOffset, "rowOffset");
689
+ assertOptionalFiniteNumber(params.rowOffsetEvery, "rowOffsetEvery");
690
+ assertOptionalFiniteNumber(params.columnOffset, "columnOffset");
691
+ assertOptionalFiniteNumber(params.columnOffsetEvery, "columnOffsetEvery");
692
+ assertOptionalUvCoordinate(params.origin, "origin");
693
+ assertOptionalBoolean(params.wrap, "wrap");
694
+ assertOptionalInteger(params.rowOffsetEvery, "rowOffsetEvery");
695
+ assertOptionalInteger(params.columnOffsetEvery, "columnOffsetEvery");
696
+ const r = resolve2(params);
697
+ validatePositive2(r.scale, "scale");
698
+ validateAtLeast(r.rowOffsetEvery, 0, "rowOffsetEvery");
699
+ validateAtLeast(r.columnOffsetEvery, 0, "columnOffsetEvery");
700
+ validateUnitInterval(r.origin[0], "origin[0]");
701
+ validateUnitInterval(r.origin[1], "origin[1]");
702
+ };
703
+ var PATTERN_VS = `#version 300 es
704
+ in vec2 aPos;
705
+ in vec2 aUv;
706
+ out vec2 vUv;
707
+
708
+ void main() {
709
+ vUv = aUv;
710
+ gl_Position = vec4(aPos, 0.0, 1.0);
711
+ }
712
+ `;
713
+ var PATTERN_FS = `#version 300 es
714
+ precision highp float;
715
+
716
+ in vec2 vUv;
717
+ out vec4 fragColor;
718
+
719
+ uniform sampler2D uSource;
720
+ uniform vec2 uResolution;
721
+ uniform float uScale;
722
+ uniform vec4 uCrop;
723
+ uniform vec2 uGap;
724
+ uniform vec2 uOffset;
725
+ uniform vec2 uRowOffset;
726
+ uniform vec2 uColumnOffset;
727
+ uniform vec2 uOrigin;
728
+ uniform bool uWrap;
729
+
730
+ float positiveModulo(float value, float modulo) {
731
+ return mod(mod(value, modulo) + modulo, modulo);
732
+ }
733
+
734
+ float getPhase(float index, float every) {
735
+ if (every == 0.0) {
736
+ return index;
737
+ }
738
+
739
+ return positiveModulo(index, every);
740
+ }
741
+
742
+ bool sampleCell(vec2 cell, vec2 fragPx, vec2 cropStart, vec2 tileSize, vec2 pitch, vec2 originPx, out vec4 color) {
743
+ if (!uWrap && (cell.x < 0.0 || cell.y < 0.0)) {
744
+ return false;
745
+ }
746
+
747
+ float rowPhase = getPhase(cell.y, uRowOffset.y);
748
+ float columnPhase = getPhase(cell.x, uColumnOffset.y);
749
+ vec2 stagger = vec2(rowPhase * uRowOffset.x, columnPhase * uColumnOffset.x);
750
+ vec2 localPx = fragPx - originPx - stagger - cell * pitch;
751
+
752
+ if (localPx.x < 0.0 || localPx.y < 0.0 || localPx.x >= tileSize.x || localPx.y >= tileSize.y) {
753
+ return false;
754
+ }
755
+
756
+ vec2 sourcePx = cropStart + localPx / uScale;
757
+ vec2 sourceUv = vec2(sourcePx.x / uResolution.x, 1.0 - sourcePx.y / uResolution.y);
758
+
759
+ if (sourceUv.x < 0.0 || sourceUv.y < 0.0 || sourceUv.x > 1.0 || sourceUv.y > 1.0) {
760
+ return false;
761
+ }
762
+
763
+ color = texture(uSource, sourceUv);
764
+ return true;
765
+ }
766
+
767
+ void main() {
768
+ vec2 cropStart = uCrop.xy;
769
+ vec2 cropEnd = uResolution - uCrop.zw;
770
+ vec2 cropSize = max(cropEnd - cropStart, vec2(0.001));
771
+ vec2 tileSize = max(cropSize * uScale, vec2(0.001));
772
+ vec2 pitch = max(tileSize + uGap, vec2(0.001));
773
+ vec2 patternUv = vec2(vUv.x, 1.0 - vUv.y);
774
+ vec2 originPx = (uOrigin + uOffset) * uResolution;
775
+ vec2 fragPx = patternUv * uResolution;
776
+
777
+ vec2 gridPx = fragPx - originPx;
778
+ vec2 cell = floor(gridPx / pitch);
779
+
780
+ for (int i = 0; i < 4; i++) {
781
+ float rowPhase = getPhase(cell.y, uRowOffset.y);
782
+ float columnPhase = getPhase(cell.x, uColumnOffset.y);
783
+ vec2 stagger = vec2(rowPhase * uRowOffset.x, columnPhase * uColumnOffset.x);
784
+ vec2 adjustedGridPx = fragPx - originPx - stagger;
785
+ vec2 nextCell = floor(adjustedGridPx / pitch);
352
786
 
353
- if (sourceUv.x < 0.0 || sourceUv.y < 0.0 || sourceUv.x > 1.0 || sourceUv.y > 1.0) {
354
- return false;
787
+ if (all(equal(nextCell, cell))) {
788
+ break;
789
+ }
790
+
791
+ cell = nextCell;
355
792
  }
356
793
 
357
- color = texture(uSource, sourceUv);
358
- return true;
794
+ vec4 color = vec4(0.0);
795
+ for (int y = -4; y <= 4; y++) {
796
+ for (int x = -4; x <= 4; x++) {
797
+ if (sampleCell(cell + vec2(float(x), float(y)), fragPx, cropStart, tileSize, pitch, originPx, color)) {
798
+ fragColor = color;
799
+ return;
800
+ }
801
+ }
802
+ }
803
+
804
+ fragColor = vec4(0.0);
359
805
  }
806
+ `;
807
+ var compileShader2 = (gl, type, source) => {
808
+ const shader = gl.createShader(type);
809
+ if (!shader) {
810
+ throw new Error("Failed to create WebGL shader");
811
+ }
812
+ gl.shaderSource(shader, source);
813
+ gl.compileShader(shader);
814
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
815
+ const log = gl.getShaderInfoLog(shader);
816
+ gl.deleteShader(shader);
817
+ throw new Error(`Pattern shader compile failed: ${log ?? "(no log)"}`);
818
+ }
819
+ return shader;
820
+ };
821
+ var linkProgram2 = (gl, vs, fs) => {
822
+ const program = gl.createProgram();
823
+ if (!program) {
824
+ throw new Error("Failed to create WebGL program");
825
+ }
826
+ gl.attachShader(program, vs);
827
+ gl.attachShader(program, fs);
828
+ gl.linkProgram(program);
829
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
830
+ const log = gl.getProgramInfoLog(program);
831
+ gl.deleteProgram(program);
832
+ throw new Error(`Pattern program link failed: ${log ?? "(no log)"}`);
833
+ }
834
+ return program;
835
+ };
836
+ var pattern = createEffect2({
837
+ type: "dev.remotion.effects.pattern",
838
+ label: "pattern()",
839
+ documentationLink: "https://www.remotion.dev/docs/effects/pattern",
840
+ backend: "webgl2",
841
+ calculateKey: (params) => {
842
+ const r = resolve2(params);
843
+ return `pattern-${r.scale}-${r.cropLeft}-${r.cropTop}-${r.cropRight}-${r.cropBottom}-${r.gapX}-${r.gapY}-${r.offsetU}-${r.offsetV}-${r.rowOffset}-${r.rowOffsetEvery}-${r.columnOffset}-${r.columnOffsetEvery}-${r.origin[0]}-${r.origin[1]}-${r.wrap ? 1 : 0}`;
844
+ },
845
+ setup: (target) => {
846
+ const gl = target.getContext("webgl2", {
847
+ premultipliedAlpha: true,
848
+ alpha: true,
849
+ preserveDrawingBuffer: true
850
+ });
851
+ if (!gl) {
852
+ throw createWebGL2ContextError2("pattern effect");
853
+ }
854
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
855
+ const vs = compileShader2(gl, gl.VERTEX_SHADER, PATTERN_VS);
856
+ const fs = compileShader2(gl, gl.FRAGMENT_SHADER, PATTERN_FS);
857
+ const program = linkProgram2(gl, vs, fs);
858
+ gl.deleteShader(vs);
859
+ gl.deleteShader(fs);
860
+ const vao = gl.createVertexArray();
861
+ if (!vao) {
862
+ throw new Error("Failed to create WebGL vertex array");
863
+ }
864
+ gl.bindVertexArray(vao);
865
+ const data = new Float32Array([
866
+ -1,
867
+ -1,
868
+ 0,
869
+ 0,
870
+ 1,
871
+ -1,
872
+ 1,
873
+ 0,
874
+ -1,
875
+ 1,
876
+ 0,
877
+ 1,
878
+ 1,
879
+ 1,
880
+ 1,
881
+ 1
882
+ ]);
883
+ const vbo = gl.createBuffer();
884
+ if (!vbo) {
885
+ throw new Error("Failed to create WebGL buffer");
886
+ }
887
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
888
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
889
+ const aPos = gl.getAttribLocation(program, "aPos");
890
+ const aUv = gl.getAttribLocation(program, "aUv");
891
+ gl.enableVertexAttribArray(aPos);
892
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
893
+ gl.enableVertexAttribArray(aUv);
894
+ gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
895
+ gl.bindVertexArray(null);
896
+ const texture = gl.createTexture();
897
+ if (!texture) {
898
+ throw new Error("Failed to create WebGL texture");
899
+ }
900
+ gl.bindTexture(gl.TEXTURE_2D, texture);
901
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
902
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
903
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
904
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
905
+ gl.bindTexture(gl.TEXTURE_2D, null);
906
+ return {
907
+ gl,
908
+ program,
909
+ vao,
910
+ vbo,
911
+ texture,
912
+ uniforms: {
913
+ uSource: gl.getUniformLocation(program, "uSource"),
914
+ uResolution: gl.getUniformLocation(program, "uResolution"),
915
+ uScale: gl.getUniformLocation(program, "uScale"),
916
+ uCrop: gl.getUniformLocation(program, "uCrop"),
917
+ uGap: gl.getUniformLocation(program, "uGap"),
918
+ uOffset: gl.getUniformLocation(program, "uOffset"),
919
+ uRowOffset: gl.getUniformLocation(program, "uRowOffset"),
920
+ uColumnOffset: gl.getUniformLocation(program, "uColumnOffset"),
921
+ uOrigin: gl.getUniformLocation(program, "uOrigin"),
922
+ uWrap: gl.getUniformLocation(program, "uWrap")
923
+ }
924
+ };
925
+ },
926
+ apply: ({ source, width, height, params, state, flipSourceY }) => {
927
+ const r = resolve2(params);
928
+ const { gl, program, vao, texture, uniforms } = state;
929
+ gl.viewport(0, 0, width, height);
930
+ gl.clearColor(0, 0, 0, 0);
931
+ gl.clear(gl.COLOR_BUFFER_BIT);
932
+ gl.useProgram(program);
933
+ gl.bindVertexArray(vao);
934
+ gl.activeTexture(gl.TEXTURE0);
935
+ gl.bindTexture(gl.TEXTURE_2D, texture);
936
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
937
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
938
+ if (uniforms.uSource)
939
+ gl.uniform1i(uniforms.uSource, 0);
940
+ if (uniforms.uResolution)
941
+ gl.uniform2f(uniforms.uResolution, width, height);
942
+ if (uniforms.uScale)
943
+ gl.uniform1f(uniforms.uScale, r.scale);
944
+ if (uniforms.uCrop)
945
+ gl.uniform4f(uniforms.uCrop, r.cropLeft, r.cropTop, r.cropRight, r.cropBottom);
946
+ if (uniforms.uGap)
947
+ gl.uniform2f(uniforms.uGap, r.gapX, r.gapY);
948
+ if (uniforms.uOffset)
949
+ gl.uniform2f(uniforms.uOffset, r.offsetU, r.offsetV);
950
+ if (uniforms.uRowOffset)
951
+ gl.uniform2f(uniforms.uRowOffset, r.rowOffset, r.rowOffsetEvery);
952
+ if (uniforms.uColumnOffset)
953
+ gl.uniform2f(uniforms.uColumnOffset, r.columnOffset, r.columnOffsetEvery);
954
+ if (uniforms.uOrigin)
955
+ gl.uniform2f(uniforms.uOrigin, r.origin[0], r.origin[1]);
956
+ if (uniforms.uWrap)
957
+ gl.uniform1i(uniforms.uWrap, r.wrap ? 1 : 0);
958
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
959
+ gl.bindVertexArray(null);
960
+ gl.bindTexture(gl.TEXTURE_2D, null);
961
+ gl.useProgram(null);
962
+ },
963
+ cleanup: ({ gl, program, vao, vbo, texture }) => {
964
+ gl.deleteBuffer(vbo);
965
+ gl.deleteProgram(program);
966
+ gl.deleteVertexArray(vao);
967
+ gl.deleteTexture(texture);
968
+ },
969
+ schema: patternSchema,
970
+ validateParams: validatePatternParams
971
+ });
972
+
973
+ // src/rings.ts
974
+ import { Internals as Internals3 } from "remotion";
975
+
976
+ // src/uv-coordinate.ts
977
+ var publicUvToShaderUv = (uv) => {
978
+ return [uv[0], 1 - uv[1]];
979
+ };
980
+
981
+ // src/rings.ts
982
+ var { createEffect: createEffect3, createWebGL2ContextError: createWebGL2ContextError3 } = Internals3;
983
+ var DEFAULT_COLORS2 = ["#dff4ff", "#7cc6ff"];
984
+ var DEFAULT_CENTER = [0.5, 0.5];
985
+ var DEFAULT_THICKNESS = 40;
986
+ var DEFAULT_GAP3 = 0;
987
+ var DEFAULT_OFFSET2 = 0;
988
+ var DEFAULT_MASK_TO_SOURCE_ALPHA2 = false;
989
+ var ringsSchema = {
990
+ colors: {
991
+ type: "array",
992
+ item: {
993
+ type: "color"
994
+ },
995
+ default: DEFAULT_COLORS2,
996
+ minLength: 2,
997
+ newItemDefault: "#ff0000",
998
+ description: "Colors",
999
+ keyframable: false
1000
+ },
1001
+ center: {
1002
+ type: "uv-coordinate",
1003
+ min: 0,
1004
+ max: 1,
1005
+ step: 0.01,
1006
+ default: DEFAULT_CENTER,
1007
+ description: "Center"
1008
+ },
1009
+ thickness: {
1010
+ type: "number",
1011
+ min: 0.1,
1012
+ max: 400,
1013
+ step: 0.1,
1014
+ default: DEFAULT_THICKNESS,
1015
+ description: "Thickness",
1016
+ hiddenFromList: false
1017
+ },
1018
+ gap: {
1019
+ type: "number",
1020
+ min: 0,
1021
+ max: 400,
1022
+ step: 0.1,
1023
+ default: DEFAULT_GAP3,
1024
+ description: "Gap",
1025
+ hiddenFromList: false
1026
+ },
1027
+ offset: {
1028
+ type: "number",
1029
+ step: 0.1,
1030
+ default: DEFAULT_OFFSET2,
1031
+ description: "Offset",
1032
+ hiddenFromList: false
1033
+ },
1034
+ maskToSourceAlpha: {
1035
+ type: "boolean",
1036
+ default: DEFAULT_MASK_TO_SOURCE_ALPHA2,
1037
+ description: "Mask to source alpha"
1038
+ }
1039
+ };
1040
+ var resolve3 = (p) => {
1041
+ const thickness = p.thickness ?? DEFAULT_THICKNESS;
1042
+ const gap = p.gap ?? DEFAULT_GAP3;
1043
+ return {
1044
+ colors: p.colors ?? DEFAULT_COLORS2,
1045
+ center: [...p.center ?? DEFAULT_CENTER],
1046
+ thickness,
1047
+ spacing: thickness + gap,
1048
+ offset: p.offset ?? DEFAULT_OFFSET2,
1049
+ maskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA2
1050
+ };
1051
+ };
1052
+ var validatePositive3 = (value, name) => {
1053
+ if (value <= 0) {
1054
+ throw new TypeError(`"${name}" must be greater than 0, but got ${JSON.stringify(value)}`);
1055
+ }
1056
+ };
1057
+ var validateNonNegative3 = (value, name) => {
1058
+ if (value < 0) {
1059
+ throw new TypeError(`"${name}" must be greater than or equal to 0, but got ${JSON.stringify(value)}`);
1060
+ }
1061
+ };
1062
+ var validateColors2 = (colors) => {
1063
+ if (colors === undefined) {
1064
+ return;
1065
+ }
1066
+ if (!Array.isArray(colors) || colors.length < 2) {
1067
+ throw new TypeError(`"colors" must be an array with at least 2 colors, but got ${JSON.stringify(colors)}`);
1068
+ }
1069
+ for (let i = 0;i < colors.length; i++) {
1070
+ assertRequiredColor(colors[i], `colors[${i}]`);
1071
+ }
1072
+ };
1073
+ var assertOptionalCenter = (value, name) => {
1074
+ if (value === undefined) {
1075
+ return;
1076
+ }
1077
+ if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
1078
+ throw new TypeError(`"${name}" must be a [number, number] tuple`);
1079
+ }
1080
+ validateUnitInterval(value[0], `${name}[0]`);
1081
+ validateUnitInterval(value[1], `${name}[1]`);
1082
+ };
1083
+ var validateRingsParams = (params) => {
1084
+ assertEffectParamsObject(params, "Rings");
1085
+ validateColors2(params.colors);
1086
+ assertOptionalCenter(params.center, "center");
1087
+ assertOptionalFiniteNumber(params.thickness, "thickness");
1088
+ assertOptionalFiniteNumber(params.gap, "gap");
1089
+ assertOptionalFiniteNumber(params.offset, "offset");
1090
+ assertOptionalBoolean(params.maskToSourceAlpha, "maskToSourceAlpha");
1091
+ const thickness = params.thickness ?? DEFAULT_THICKNESS;
1092
+ const gap = params.gap ?? DEFAULT_GAP3;
1093
+ validatePositive3(thickness, "thickness");
1094
+ validateNonNegative3(gap, "gap");
1095
+ };
1096
+ var RINGS_VS = `#version 300 es
1097
+ in vec2 aPos;
1098
+ in vec2 aUv;
1099
+ out vec2 vUv;
360
1100
 
361
1101
  void main() {
362
- vec2 cropStart = uCrop.xy;
363
- vec2 cropEnd = uResolution - uCrop.zw;
364
- vec2 cropSize = max(cropEnd - cropStart, vec2(0.001));
365
- vec2 tileSize = max(cropSize * uScale, vec2(0.001));
366
- vec2 pitch = max(tileSize + uGap, vec2(0.001));
367
- vec2 patternUv = vec2(vUv.x, 1.0 - vUv.y);
368
- vec2 originPx = (uOrigin + uOffset) * uResolution;
369
- vec2 fragPx = patternUv * uResolution;
1102
+ vUv = aUv;
1103
+ gl_Position = vec4(aPos, 0.0, 1.0);
1104
+ }
1105
+ `;
1106
+ var RINGS_FS = `#version 300 es
1107
+ precision highp float;
370
1108
 
371
- vec2 gridPx = fragPx - originPx;
372
- vec2 cell = floor(gridPx / pitch);
1109
+ in vec2 vUv;
1110
+ out vec4 fragColor;
373
1111
 
374
- for (int i = 0; i < 4; i++) {
375
- float rowPhase = getPhase(cell.y, uRowOffset.y);
376
- float columnPhase = getPhase(cell.x, uColumnOffset.y);
377
- vec2 stagger = vec2(rowPhase * uRowOffset.x, columnPhase * uColumnOffset.x);
378
- vec2 adjustedGridPx = fragPx - originPx - stagger;
379
- vec2 nextCell = floor(adjustedGridPx / pitch);
1112
+ uniform sampler2D uSource;
1113
+ uniform sampler2D uPalette;
1114
+ uniform vec2 uResolution;
1115
+ uniform float uNumColors;
1116
+ uniform vec2 uCenter;
1117
+ uniform float uThickness;
1118
+ uniform float uSpacing;
1119
+ uniform float uOffset;
1120
+ uniform bool uMaskToSourceAlpha;
380
1121
 
381
- if (all(equal(nextCell, cell))) {
382
- break;
383
- }
1122
+ void main() {
1123
+ vec4 texColor = texture(uSource, vUv);
1124
+ float thickness = max(uThickness, 0.001);
1125
+ float spacing = max(uSpacing, 0.001);
1126
+ float cycle = spacing * uNumColors;
1127
+ vec2 pixelPosition = vUv * uResolution;
1128
+ vec2 centerPosition = uCenter * uResolution;
1129
+ float radius = length(pixelPosition - centerPosition);
1130
+ float position = mod(radius + uOffset, cycle);
1131
+ if (position < 0.0) {
1132
+ position += cycle;
1133
+ }
384
1134
 
385
- cell = nextCell;
1135
+ float colorIndex = floor(position / spacing);
1136
+ float inStripe = mod(position, spacing);
1137
+ if (inStripe > thickness) {
1138
+ fragColor = texColor;
1139
+ return;
386
1140
  }
387
1141
 
388
- vec4 color = vec4(0.0);
389
- for (int y = -4; y <= 4; y++) {
390
- for (int x = -4; x <= 4; x++) {
391
- if (sampleCell(cell + vec2(float(x), float(y)), fragPx, cropStart, tileSize, pitch, originPx, color)) {
392
- fragColor = color;
393
- return;
394
- }
395
- }
1142
+ float texCoord = (colorIndex + 0.5) / uNumColors;
1143
+ vec4 ringColor = texture(uPalette, vec2(texCoord, 0.5));
1144
+ float ringAlpha = ringColor.a;
1145
+ vec3 premultipliedRing = ringColor.rgb * ringAlpha;
1146
+
1147
+ if (uMaskToSourceAlpha) {
1148
+ fragColor = vec4(
1149
+ premultipliedRing * texColor.a + texColor.rgb * (1.0 - ringAlpha),
1150
+ texColor.a
1151
+ );
1152
+ return;
396
1153
  }
397
1154
 
398
- fragColor = vec4(0.0);
1155
+ fragColor = vec4(
1156
+ premultipliedRing + texColor.rgb * (1.0 - ringAlpha),
1157
+ ringAlpha + texColor.a * (1.0 - ringAlpha)
1158
+ );
399
1159
  }
400
1160
  `;
401
- var compileShader = (gl, type, source) => {
1161
+ var compileShader3 = (gl, type, source) => {
402
1162
  const shader = gl.createShader(type);
403
1163
  if (!shader) {
404
1164
  throw new Error("Failed to create WebGL shader");
@@ -408,11 +1168,11 @@ var compileShader = (gl, type, source) => {
408
1168
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
409
1169
  const log = gl.getShaderInfoLog(shader);
410
1170
  gl.deleteShader(shader);
411
- throw new Error(`Pattern shader compile failed: ${log ?? "(no log)"}`);
1171
+ throw new Error(`Rings shader compile failed: ${log ?? "(no log)"}`);
412
1172
  }
413
1173
  return shader;
414
1174
  };
415
- var linkProgram = (gl, vs, fs) => {
1175
+ var linkProgram3 = (gl, vs, fs) => {
416
1176
  const program = gl.createProgram();
417
1177
  if (!program) {
418
1178
  throw new Error("Failed to create WebGL program");
@@ -423,271 +1183,340 @@ var linkProgram = (gl, vs, fs) => {
423
1183
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
424
1184
  const log = gl.getProgramInfoLog(program);
425
1185
  gl.deleteProgram(program);
426
- throw new Error(`Pattern program link failed: ${log ?? "(no log)"}`);
1186
+ throw new Error(`Rings program link failed: ${log ?? "(no log)"}`);
1187
+ }
1188
+ return program;
1189
+ };
1190
+ var createProgram2 = (gl, vertexSource, fragmentSource) => {
1191
+ const vs = compileShader3(gl, gl.VERTEX_SHADER, vertexSource);
1192
+ const fs = compileShader3(gl, gl.FRAGMENT_SHADER, fragmentSource);
1193
+ const program = linkProgram3(gl, vs, fs);
1194
+ gl.deleteShader(vs);
1195
+ gl.deleteShader(fs);
1196
+ return program;
1197
+ };
1198
+ var createTexture2 = (gl, filter) => {
1199
+ const texture = gl.createTexture();
1200
+ if (!texture) {
1201
+ throw new Error("Failed to create WebGL texture");
1202
+ }
1203
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1204
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
1205
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
1206
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1207
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1208
+ gl.bindTexture(gl.TEXTURE_2D, null);
1209
+ return texture;
1210
+ };
1211
+ var setupRings = (target) => {
1212
+ const gl = target.getContext("webgl2", {
1213
+ premultipliedAlpha: true,
1214
+ alpha: true,
1215
+ preserveDrawingBuffer: true
1216
+ });
1217
+ if (!gl) {
1218
+ throw createWebGL2ContextError3("rings effect");
1219
+ }
1220
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
1221
+ const program = createProgram2(gl, RINGS_VS, RINGS_FS);
1222
+ const vao = gl.createVertexArray();
1223
+ if (!vao) {
1224
+ throw new Error("Failed to create WebGL vertex array");
1225
+ }
1226
+ gl.bindVertexArray(vao);
1227
+ const data = new Float32Array([
1228
+ -1,
1229
+ -1,
1230
+ 0,
1231
+ 0,
1232
+ 1,
1233
+ -1,
1234
+ 1,
1235
+ 0,
1236
+ -1,
1237
+ 1,
1238
+ 0,
1239
+ 1,
1240
+ 1,
1241
+ 1,
1242
+ 1,
1243
+ 1
1244
+ ]);
1245
+ const vbo = gl.createBuffer();
1246
+ if (!vbo) {
1247
+ throw new Error("Failed to create WebGL buffer");
1248
+ }
1249
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
1250
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
1251
+ const aPos = gl.getAttribLocation(program, "aPos");
1252
+ const aUv = gl.getAttribLocation(program, "aUv");
1253
+ gl.enableVertexAttribArray(aPos);
1254
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
1255
+ gl.enableVertexAttribArray(aUv);
1256
+ gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
1257
+ gl.bindVertexArray(null);
1258
+ const colorCanvas = document.createElement("canvas");
1259
+ colorCanvas.width = 1;
1260
+ colorCanvas.height = 1;
1261
+ const colorCtx = colorCanvas.getContext("2d", { willReadFrequently: true });
1262
+ if (!colorCtx) {
1263
+ throw new Error("Failed to acquire 2D context for color parsing");
1264
+ }
1265
+ return {
1266
+ gl,
1267
+ program,
1268
+ vao,
1269
+ vbo,
1270
+ sourceTexture: createTexture2(gl, gl.LINEAR),
1271
+ paletteTexture: createTexture2(gl, gl.NEAREST),
1272
+ colorCtx,
1273
+ uniforms: {
1274
+ uSource: gl.getUniformLocation(program, "uSource"),
1275
+ uPalette: gl.getUniformLocation(program, "uPalette"),
1276
+ uResolution: gl.getUniformLocation(program, "uResolution"),
1277
+ uNumColors: gl.getUniformLocation(program, "uNumColors"),
1278
+ uCenter: gl.getUniformLocation(program, "uCenter"),
1279
+ uThickness: gl.getUniformLocation(program, "uThickness"),
1280
+ uSpacing: gl.getUniformLocation(program, "uSpacing"),
1281
+ uOffset: gl.getUniformLocation(program, "uOffset"),
1282
+ uMaskToSourceAlpha: gl.getUniformLocation(program, "uMaskToSourceAlpha")
1283
+ },
1284
+ cachedPaletteKey: "",
1285
+ palettePixelData: new Uint8Array(0)
1286
+ };
1287
+ };
1288
+ var updatePalette2 = (state, colors) => {
1289
+ const paletteKey = colors.join("|");
1290
+ const paletteDirty = state.cachedPaletteKey !== paletteKey;
1291
+ if (!paletteDirty) {
1292
+ return false;
1293
+ }
1294
+ state.cachedPaletteKey = paletteKey;
1295
+ const len = colors.length * 4;
1296
+ if (state.palettePixelData.length !== len) {
1297
+ state.palettePixelData = new Uint8Array(len);
1298
+ }
1299
+ const { palettePixelData } = state;
1300
+ for (let i = 0;i < colors.length; i++) {
1301
+ const color = parseColorRgba(state.colorCtx, colors[i]);
1302
+ palettePixelData[i * 4] = color[0];
1303
+ palettePixelData[i * 4 + 1] = color[1];
1304
+ palettePixelData[i * 4 + 2] = color[2];
1305
+ palettePixelData[i * 4 + 3] = color[3];
427
1306
  }
428
- return program;
1307
+ return true;
429
1308
  };
430
- var pattern = createEffect({
431
- type: "dev.remotion.effects.pattern",
432
- label: "pattern()",
433
- documentationLink: "https://www.remotion.dev/docs/effects/pattern",
1309
+ var rings = createEffect3({
1310
+ type: "dev.remotion.effects.rings",
1311
+ label: "rings()",
1312
+ documentationLink: "https://www.remotion.dev/docs/effects/rings",
434
1313
  backend: "webgl2",
435
1314
  calculateKey: (params) => {
436
- const r = resolve(params);
437
- return `pattern-${r.scale}-${r.cropLeft}-${r.cropTop}-${r.cropRight}-${r.cropBottom}-${r.gapX}-${r.gapY}-${r.offsetU}-${r.offsetV}-${r.rowOffset}-${r.rowOffsetEvery}-${r.columnOffset}-${r.columnOffsetEvery}-${r.origin[0]}-${r.origin[1]}-${r.wrap ? 1 : 0}`;
438
- },
439
- setup: (target) => {
440
- const gl = target.getContext("webgl2", {
441
- premultipliedAlpha: true,
442
- alpha: true,
443
- preserveDrawingBuffer: true
444
- });
445
- if (!gl) {
446
- throw createWebGL2ContextError("pattern effect");
447
- }
448
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
449
- const vs = compileShader(gl, gl.VERTEX_SHADER, PATTERN_VS);
450
- const fs = compileShader(gl, gl.FRAGMENT_SHADER, PATTERN_FS);
451
- const program = linkProgram(gl, vs, fs);
452
- gl.deleteShader(vs);
453
- gl.deleteShader(fs);
454
- const vao = gl.createVertexArray();
455
- if (!vao) {
456
- throw new Error("Failed to create WebGL vertex array");
457
- }
458
- gl.bindVertexArray(vao);
459
- const data = new Float32Array([
460
- -1,
461
- -1,
462
- 0,
463
- 0,
464
- 1,
465
- -1,
466
- 1,
467
- 0,
468
- -1,
469
- 1,
470
- 0,
471
- 1,
472
- 1,
473
- 1,
474
- 1,
475
- 1
476
- ]);
477
- const vbo = gl.createBuffer();
478
- if (!vbo) {
479
- throw new Error("Failed to create WebGL buffer");
480
- }
481
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
482
- gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
483
- const aPos = gl.getAttribLocation(program, "aPos");
484
- const aUv = gl.getAttribLocation(program, "aUv");
485
- gl.enableVertexAttribArray(aPos);
486
- gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
487
- gl.enableVertexAttribArray(aUv);
488
- gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
489
- gl.bindVertexArray(null);
490
- const texture = gl.createTexture();
491
- if (!texture) {
492
- throw new Error("Failed to create WebGL texture");
493
- }
494
- gl.bindTexture(gl.TEXTURE_2D, texture);
495
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
496
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
497
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
498
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
499
- gl.bindTexture(gl.TEXTURE_2D, null);
500
- return {
501
- gl,
502
- program,
503
- vao,
504
- vbo,
505
- texture,
506
- uniforms: {
507
- uSource: gl.getUniformLocation(program, "uSource"),
508
- uResolution: gl.getUniformLocation(program, "uResolution"),
509
- uScale: gl.getUniformLocation(program, "uScale"),
510
- uCrop: gl.getUniformLocation(program, "uCrop"),
511
- uGap: gl.getUniformLocation(program, "uGap"),
512
- uOffset: gl.getUniformLocation(program, "uOffset"),
513
- uRowOffset: gl.getUniformLocation(program, "uRowOffset"),
514
- uColumnOffset: gl.getUniformLocation(program, "uColumnOffset"),
515
- uOrigin: gl.getUniformLocation(program, "uOrigin"),
516
- uWrap: gl.getUniformLocation(program, "uWrap")
517
- }
518
- };
1315
+ const r = resolve3(params);
1316
+ const maskSuffix = r.maskToSourceAlpha ? "-mask-to-source-alpha" : "";
1317
+ return `rings-${r.colors.join("|")}-${r.center.join(":")}-${r.thickness}-${r.spacing}-${r.offset}${maskSuffix}`;
519
1318
  },
1319
+ setup: (target) => setupRings(target),
520
1320
  apply: ({ source, width, height, params, state, flipSourceY }) => {
521
- const r = resolve(params);
522
- const { gl, program, vao, texture, uniforms } = state;
1321
+ const r = resolve3(params);
1322
+ const paletteDirty = updatePalette2(state, r.colors);
1323
+ const { gl, program, sourceTexture, paletteTexture, uniforms, vao } = state;
523
1324
  gl.viewport(0, 0, width, height);
1325
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
524
1326
  gl.clearColor(0, 0, 0, 0);
525
1327
  gl.clear(gl.COLOR_BUFFER_BIT);
526
- gl.useProgram(program);
527
- gl.bindVertexArray(vao);
528
1328
  gl.activeTexture(gl.TEXTURE0);
529
- gl.bindTexture(gl.TEXTURE_2D, texture);
1329
+ gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
1330
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
530
1331
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
531
1332
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1333
+ gl.activeTexture(gl.TEXTURE1);
1334
+ gl.bindTexture(gl.TEXTURE_2D, paletteTexture);
1335
+ if (paletteDirty) {
1336
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
1337
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
1338
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, r.colors.length, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, state.palettePixelData);
1339
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
1340
+ }
1341
+ gl.useProgram(program);
532
1342
  if (uniforms.uSource)
533
1343
  gl.uniform1i(uniforms.uSource, 0);
1344
+ if (uniforms.uPalette)
1345
+ gl.uniform1i(uniforms.uPalette, 1);
534
1346
  if (uniforms.uResolution)
535
1347
  gl.uniform2f(uniforms.uResolution, width, height);
536
- if (uniforms.uScale)
537
- gl.uniform1f(uniforms.uScale, r.scale);
538
- if (uniforms.uCrop)
539
- gl.uniform4f(uniforms.uCrop, r.cropLeft, r.cropTop, r.cropRight, r.cropBottom);
540
- if (uniforms.uGap)
541
- gl.uniform2f(uniforms.uGap, r.gapX, r.gapY);
1348
+ if (uniforms.uNumColors)
1349
+ gl.uniform1f(uniforms.uNumColors, r.colors.length);
1350
+ if (uniforms.uCenter) {
1351
+ const shaderCenter = publicUvToShaderUv(r.center);
1352
+ gl.uniform2f(uniforms.uCenter, shaderCenter[0], shaderCenter[1]);
1353
+ }
1354
+ if (uniforms.uThickness)
1355
+ gl.uniform1f(uniforms.uThickness, r.thickness);
1356
+ if (uniforms.uSpacing)
1357
+ gl.uniform1f(uniforms.uSpacing, r.spacing);
542
1358
  if (uniforms.uOffset)
543
- gl.uniform2f(uniforms.uOffset, r.offsetU, r.offsetV);
544
- if (uniforms.uRowOffset)
545
- gl.uniform2f(uniforms.uRowOffset, r.rowOffset, r.rowOffsetEvery);
546
- if (uniforms.uColumnOffset)
547
- gl.uniform2f(uniforms.uColumnOffset, r.columnOffset, r.columnOffsetEvery);
548
- if (uniforms.uOrigin)
549
- gl.uniform2f(uniforms.uOrigin, r.origin[0], r.origin[1]);
550
- if (uniforms.uWrap)
551
- gl.uniform1i(uniforms.uWrap, r.wrap ? 1 : 0);
1359
+ gl.uniform1f(uniforms.uOffset, r.offset);
1360
+ if (uniforms.uMaskToSourceAlpha)
1361
+ gl.uniform1i(uniforms.uMaskToSourceAlpha, r.maskToSourceAlpha ? 1 : 0);
1362
+ gl.bindVertexArray(vao);
552
1363
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
553
1364
  gl.bindVertexArray(null);
554
1365
  gl.bindTexture(gl.TEXTURE_2D, null);
555
1366
  gl.useProgram(null);
556
1367
  },
557
- cleanup: ({ gl, program, vao, vbo, texture }) => {
1368
+ cleanup: ({ gl, program, vao, vbo, sourceTexture, paletteTexture }) => {
1369
+ gl.deleteTexture(sourceTexture);
1370
+ gl.deleteTexture(paletteTexture);
558
1371
  gl.deleteBuffer(vbo);
559
1372
  gl.deleteProgram(program);
560
1373
  gl.deleteVertexArray(vao);
561
- gl.deleteTexture(texture);
562
1374
  },
563
- schema: patternSchema,
564
- validateParams: validatePatternParams
1375
+ schema: ringsSchema,
1376
+ validateParams: validateRingsParams
565
1377
  });
566
1378
 
567
- // src/rings.ts
568
- import { Internals as Internals2 } from "remotion";
569
-
570
- // src/uv-coordinate.ts
571
- var publicUvToShaderUv = (uv) => {
572
- return [uv[0], 1 - uv[1]];
573
- };
574
-
575
- // src/rings.ts
576
- var { createEffect: createEffect2, createWebGL2ContextError: createWebGL2ContextError2 } = Internals2;
577
- var DEFAULT_COLORS = ["#dff4ff", "#7cc6ff"];
578
- var DEFAULT_CENTER = [0.5, 0.5];
579
- var DEFAULT_THICKNESS = 40;
580
- var DEFAULT_GAP2 = 0;
581
- var DEFAULT_OFFSET2 = 0;
582
- var DEFAULT_MASK_TO_SOURCE_ALPHA = false;
583
- var ringsSchema = {
584
- colors: {
585
- type: "array",
586
- item: {
587
- type: "color"
588
- },
589
- default: DEFAULT_COLORS,
590
- minLength: 2,
591
- newItemDefault: "#ff0000",
592
- description: "Colors",
593
- keyframable: false
594
- },
595
- center: {
596
- type: "uv-coordinate",
597
- min: 0,
598
- max: 1,
599
- step: 0.01,
600
- default: DEFAULT_CENTER,
601
- description: "Center"
602
- },
603
- thickness: {
1379
+ // src/gridlines.ts
1380
+ import { Internals as Internals4 } from "remotion";
1381
+ var { createEffect: createEffect4, createWebGL2ContextError: createWebGL2ContextError4 } = Internals4;
1382
+ var DEFAULT_GRID_SIZE = 64;
1383
+ var DEFAULT_LINE_WIDTH = 2;
1384
+ var DEFAULT_LINE_COLOR = "#ffffff";
1385
+ var DEFAULT_BACKGROUND_COLOR = "transparent";
1386
+ var DEFAULT_ROTATION = 0;
1387
+ var DEFAULT_ROTATION_X = 0;
1388
+ var DEFAULT_ROTATION_Y = 0;
1389
+ var DEFAULT_PERSPECTIVE = 0;
1390
+ var DEFAULT_OFFSET_X2 = 0;
1391
+ var DEFAULT_OFFSET_Y2 = 0;
1392
+ var DEFAULT_MASK_TO_SOURCE_ALPHA3 = false;
1393
+ var gridlinesSchema = {
1394
+ gridSize: {
604
1395
  type: "number",
605
- min: 0.1,
1396
+ min: 1,
606
1397
  max: 400,
1398
+ step: 1,
1399
+ default: DEFAULT_GRID_SIZE,
1400
+ description: "Grid size",
1401
+ hiddenFromList: false
1402
+ },
1403
+ lineWidth: {
1404
+ type: "number",
1405
+ min: 0,
1406
+ max: 100,
607
1407
  step: 0.1,
608
- default: DEFAULT_THICKNESS,
609
- description: "Thickness",
1408
+ default: DEFAULT_LINE_WIDTH,
1409
+ description: "Line width",
610
1410
  hiddenFromList: false
611
1411
  },
612
- gap: {
1412
+ lineColor: {
1413
+ type: "color",
1414
+ default: DEFAULT_LINE_COLOR,
1415
+ description: "Line color"
1416
+ },
1417
+ backgroundColor: {
1418
+ type: "color",
1419
+ default: DEFAULT_BACKGROUND_COLOR,
1420
+ description: "Background color"
1421
+ },
1422
+ rotation: {
1423
+ type: "rotation-degrees",
1424
+ min: -180,
1425
+ max: 180,
1426
+ step: 1,
1427
+ default: DEFAULT_ROTATION,
1428
+ description: "Rotation"
1429
+ },
1430
+ rotationX: {
1431
+ type: "rotation-degrees",
1432
+ min: -180,
1433
+ max: 180,
1434
+ step: 0.1,
1435
+ default: DEFAULT_ROTATION_X,
1436
+ description: "Rotation X"
1437
+ },
1438
+ rotationY: {
1439
+ type: "rotation-degrees",
1440
+ min: -180,
1441
+ max: 180,
1442
+ step: 0.1,
1443
+ default: DEFAULT_ROTATION_Y,
1444
+ description: "Rotation Y"
1445
+ },
1446
+ perspective: {
613
1447
  type: "number",
614
1448
  min: 0,
1449
+ max: 4000,
1450
+ step: 1,
1451
+ default: DEFAULT_PERSPECTIVE,
1452
+ description: "Perspective",
1453
+ hiddenFromList: false
1454
+ },
1455
+ offsetX: {
1456
+ type: "number",
1457
+ min: -400,
615
1458
  max: 400,
616
1459
  step: 0.1,
617
- default: DEFAULT_GAP2,
618
- description: "Gap",
1460
+ default: DEFAULT_OFFSET_X2,
1461
+ description: "Offset X",
619
1462
  hiddenFromList: false
620
1463
  },
621
- offset: {
1464
+ offsetY: {
622
1465
  type: "number",
1466
+ min: -400,
1467
+ max: 400,
623
1468
  step: 0.1,
624
- default: DEFAULT_OFFSET2,
625
- description: "Offset",
1469
+ default: DEFAULT_OFFSET_Y2,
1470
+ description: "Offset Y",
626
1471
  hiddenFromList: false
627
1472
  },
628
1473
  maskToSourceAlpha: {
629
1474
  type: "boolean",
630
- default: DEFAULT_MASK_TO_SOURCE_ALPHA,
631
- description: "Mask to source alpha"
632
- }
633
- };
634
- var resolve2 = (p) => {
635
- const thickness = p.thickness ?? DEFAULT_THICKNESS;
636
- const gap = p.gap ?? DEFAULT_GAP2;
637
- return {
638
- colors: p.colors ?? DEFAULT_COLORS,
639
- center: [...p.center ?? DEFAULT_CENTER],
640
- thickness,
641
- spacing: thickness + gap,
642
- offset: p.offset ?? DEFAULT_OFFSET2,
643
- maskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA
644
- };
645
- };
646
- var validatePositive2 = (value, name) => {
647
- if (value <= 0) {
648
- throw new TypeError(`"${name}" must be greater than 0, but got ${JSON.stringify(value)}`);
649
- }
650
- };
651
- var validateNonNegative2 = (value, name) => {
652
- if (value < 0) {
653
- throw new TypeError(`"${name}" must be greater than or equal to 0, but got ${JSON.stringify(value)}`);
654
- }
655
- };
656
- var validateColors = (colors) => {
657
- if (colors === undefined) {
658
- return;
659
- }
660
- if (!Array.isArray(colors) || colors.length < 2) {
661
- throw new TypeError(`"colors" must be an array with at least 2 colors, but got ${JSON.stringify(colors)}`);
662
- }
663
- for (let i = 0;i < colors.length; i++) {
664
- assertRequiredColor(colors[i], `colors[${i}]`);
1475
+ default: DEFAULT_MASK_TO_SOURCE_ALPHA3,
1476
+ description: "Mask to source alpha"
665
1477
  }
666
1478
  };
667
- var assertOptionalCenter = (value, name) => {
668
- if (value === undefined) {
669
- return;
1479
+ var resolve4 = (p) => ({
1480
+ gridSize: p.gridSize ?? DEFAULT_GRID_SIZE,
1481
+ lineWidth: p.lineWidth ?? DEFAULT_LINE_WIDTH,
1482
+ lineColor: p.lineColor ?? DEFAULT_LINE_COLOR,
1483
+ backgroundColor: p.backgroundColor ?? DEFAULT_BACKGROUND_COLOR,
1484
+ rotation: p.rotation ?? DEFAULT_ROTATION,
1485
+ rotationX: p.rotationX ?? DEFAULT_ROTATION_X,
1486
+ rotationY: p.rotationY ?? DEFAULT_ROTATION_Y,
1487
+ perspective: p.perspective ?? DEFAULT_PERSPECTIVE,
1488
+ offsetX: p.offsetX ?? DEFAULT_OFFSET_X2,
1489
+ offsetY: p.offsetY ?? DEFAULT_OFFSET_Y2,
1490
+ maskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA3
1491
+ });
1492
+ var validatePositive4 = (value, name) => {
1493
+ if (value <= 0) {
1494
+ throw new TypeError(`"${name}" must be greater than 0, but got ${JSON.stringify(value)}`);
670
1495
  }
671
- if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
672
- throw new TypeError(`"${name}" must be a [number, number] tuple`);
1496
+ };
1497
+ var validateNonNegative4 = (value, name) => {
1498
+ if (value < 0) {
1499
+ throw new TypeError(`"${name}" must be greater than or equal to 0, but got ${JSON.stringify(value)}`);
673
1500
  }
674
- validateUnitInterval(value[0], `${name}[0]`);
675
- validateUnitInterval(value[1], `${name}[1]`);
676
1501
  };
677
- var validateRingsParams = (params) => {
678
- assertEffectParamsObject(params, "Rings");
679
- validateColors(params.colors);
680
- assertOptionalCenter(params.center, "center");
681
- assertOptionalFiniteNumber(params.thickness, "thickness");
682
- assertOptionalFiniteNumber(params.gap, "gap");
683
- assertOptionalFiniteNumber(params.offset, "offset");
1502
+ var validateGridlinesParams = (params) => {
1503
+ assertEffectParamsObject(params, "Gridlines");
1504
+ assertOptionalFiniteNumber(params.gridSize, "gridSize");
1505
+ assertOptionalFiniteNumber(params.lineWidth, "lineWidth");
1506
+ assertOptionalFiniteNumber(params.rotation, "rotation");
1507
+ assertOptionalFiniteNumber(params.rotationX, "rotationX");
1508
+ assertOptionalFiniteNumber(params.rotationY, "rotationY");
1509
+ assertOptionalFiniteNumber(params.perspective, "perspective");
1510
+ assertOptionalFiniteNumber(params.offsetX, "offsetX");
1511
+ assertOptionalFiniteNumber(params.offsetY, "offsetY");
1512
+ assertOptionalColor(params.lineColor, "lineColor");
1513
+ assertOptionalColor(params.backgroundColor, "backgroundColor");
684
1514
  assertOptionalBoolean(params.maskToSourceAlpha, "maskToSourceAlpha");
685
- const thickness = params.thickness ?? DEFAULT_THICKNESS;
686
- const gap = params.gap ?? DEFAULT_GAP2;
687
- validatePositive2(thickness, "thickness");
688
- validateNonNegative2(gap, "gap");
1515
+ validatePositive4(params.gridSize ?? DEFAULT_GRID_SIZE, "gridSize");
1516
+ validateNonNegative4(params.lineWidth ?? DEFAULT_LINE_WIDTH, "lineWidth");
1517
+ validateNonNegative4(params.perspective ?? DEFAULT_PERSPECTIVE, "perspective");
689
1518
  };
690
- var RINGS_VS = `#version 300 es
1519
+ var GRIDLINES_VS = `#version 300 es
691
1520
  in vec2 aPos;
692
1521
  in vec2 aUv;
693
1522
  out vec2 vUv;
@@ -697,62 +1526,123 @@ void main() {
697
1526
  gl_Position = vec4(aPos, 0.0, 1.0);
698
1527
  }
699
1528
  `;
700
- var RINGS_FS = `#version 300 es
1529
+ var GRIDLINES_FS = `#version 300 es
701
1530
  precision highp float;
702
1531
 
703
1532
  in vec2 vUv;
704
1533
  out vec4 fragColor;
705
1534
 
706
1535
  uniform sampler2D uSource;
707
- uniform sampler2D uPalette;
708
1536
  uniform vec2 uResolution;
709
- uniform float uNumColors;
710
- uniform vec2 uCenter;
711
- uniform float uThickness;
712
- uniform float uSpacing;
713
- uniform float uOffset;
1537
+ uniform float uGridSize;
1538
+ uniform float uLineWidth;
1539
+ uniform vec4 uLineColor;
1540
+ uniform vec4 uBackgroundColor;
1541
+ uniform float uRotation;
1542
+ uniform float uRotationX;
1543
+ uniform float uRotationY;
1544
+ uniform float uPerspective;
1545
+ uniform vec2 uOffset;
714
1546
  uniform bool uMaskToSourceAlpha;
715
1547
 
1548
+ vec4 sourceOver(vec4 backdrop, vec4 overlay) {
1549
+ return overlay + backdrop * (1.0 - overlay.a);
1550
+ }
1551
+
1552
+ vec4 sourceAtop(vec4 backdrop, vec4 overlay) {
1553
+ return vec4(
1554
+ overlay.rgb * backdrop.a + backdrop.rgb * (1.0 - overlay.a),
1555
+ backdrop.a
1556
+ );
1557
+ }
1558
+
1559
+ mat3 rotationMatrix(float rotationX, float rotationY, float rotationZ) {
1560
+ float sx = sin(rotationX);
1561
+ float cx = cos(rotationX);
1562
+ float sy = sin(rotationY);
1563
+ float cy = cos(rotationY);
1564
+ float sz = sin(rotationZ);
1565
+ float cz = cos(rotationZ);
1566
+
1567
+ mat3 rx = mat3(
1568
+ 1.0, 0.0, 0.0,
1569
+ 0.0, cx, sx,
1570
+ 0.0, -sx, cx
1571
+ );
1572
+ mat3 ry = mat3(
1573
+ cy, 0.0, -sy,
1574
+ 0.0, 1.0, 0.0,
1575
+ sy, 0.0, cy
1576
+ );
1577
+ mat3 rz = mat3(
1578
+ cz, sz, 0.0,
1579
+ -sz, cz, 0.0,
1580
+ 0.0, 0.0, 1.0
1581
+ );
1582
+
1583
+ return rz * ry * rx;
1584
+ }
1585
+
716
1586
  void main() {
717
1587
  vec4 texColor = texture(uSource, vUv);
718
- float thickness = max(uThickness, 0.001);
719
- float spacing = max(uSpacing, 0.001);
720
- float cycle = spacing * uNumColors;
721
- vec2 pixelPosition = vUv * uResolution;
722
- vec2 centerPosition = uCenter * uResolution;
723
- float radius = length(pixelPosition - centerPosition);
724
- float position = mod(radius + uOffset, cycle);
725
- if (position < 0.0) {
726
- position += cycle;
727
- }
1588
+ vec2 centered = vUv * uResolution - uResolution * 0.5;
1589
+ mat3 rotation = rotationMatrix(uRotationX, uRotationY, uRotation);
1590
+ vec2 gridCoord;
728
1591
 
729
- float colorIndex = floor(position / spacing);
730
- float inStripe = mod(position, spacing);
731
- if (inStripe > thickness) {
732
- fragColor = texColor;
733
- return;
1592
+ if (uPerspective <= 0.0) {
1593
+ vec3 rotated = rotation * vec3(centered, 0.0);
1594
+ gridCoord = rotated.xy + uOffset;
1595
+ } else {
1596
+ vec3 camera = vec3(0.0, 0.0, uPerspective);
1597
+ vec3 ray = normalize(vec3(centered, -uPerspective));
1598
+ vec3 normal = rotation * vec3(0.0, 0.0, 1.0);
1599
+ float denominator = dot(ray, normal);
1600
+ float t = -dot(camera, normal) / denominator;
1601
+
1602
+ if (abs(denominator) < 0.0001 || t <= 0.0) {
1603
+ fragColor = sourceOver(texColor, uBackgroundColor);
1604
+ return;
1605
+ }
1606
+
1607
+ vec3 worldPosition = camera + ray * t;
1608
+ vec3 localPosition = transpose(rotation) * worldPosition;
1609
+ gridCoord = localPosition.xy + uOffset;
734
1610
  }
735
1611
 
736
- float texCoord = (colorIndex + 0.5) / uNumColors;
737
- vec4 ringColor = texture(uPalette, vec2(texCoord, 0.5));
738
- float ringAlpha = ringColor.a;
739
- vec3 premultipliedRing = ringColor.rgb * ringAlpha;
1612
+ float gridSize = max(uGridSize, 0.001);
1613
+ vec2 cell = abs(fract(gridCoord / gridSize + 0.5) - 0.5) * gridSize;
1614
+ float halfLineWidth = uLineWidth * 0.5;
1615
+ vec2 gridUnitsPerPixel = max(fwidth(gridCoord), vec2(0.001));
1616
+ vec2 halfWidthInGridUnits = halfLineWidth * gridUnitsPerPixel;
1617
+ vec2 antialiasInGridUnits = gridUnitsPerPixel;
1618
+ vec2 projectedCellSize = gridSize / gridUnitsPerPixel;
1619
+ vec2 densityFade = smoothstep(
1620
+ vec2(max(uLineWidth * 1.5, 1.0)),
1621
+ vec2(max(uLineWidth * 4.0, 2.0)),
1622
+ projectedCellSize
1623
+ );
1624
+ vec2 lineCoverage = halfLineWidth <= 0.001
1625
+ ? vec2(0.0)
1626
+ : 1.0 - smoothstep(
1627
+ halfWidthInGridUnits - antialiasInGridUnits,
1628
+ halfWidthInGridUnits + antialiasInGridUnits,
1629
+ cell
1630
+ );
1631
+ lineCoverage *= densityFade;
1632
+ float coverage = max(lineCoverage.x, lineCoverage.y);
1633
+
1634
+ vec4 background = uBackgroundColor;
1635
+ vec4 line = uLineColor * coverage;
740
1636
 
741
1637
  if (uMaskToSourceAlpha) {
742
- fragColor = vec4(
743
- premultipliedRing * texColor.a + texColor.rgb * (1.0 - ringAlpha),
744
- texColor.a
745
- );
1638
+ fragColor = sourceAtop(sourceAtop(texColor, background), line);
746
1639
  return;
747
1640
  }
748
1641
 
749
- fragColor = vec4(
750
- premultipliedRing + texColor.rgb * (1.0 - ringAlpha),
751
- ringAlpha + texColor.a * (1.0 - ringAlpha)
752
- );
1642
+ fragColor = sourceOver(sourceOver(texColor, background), line);
753
1643
  }
754
1644
  `;
755
- var compileShader2 = (gl, type, source) => {
1645
+ var compileShader4 = (gl, type, source) => {
756
1646
  const shader = gl.createShader(type);
757
1647
  if (!shader) {
758
1648
  throw new Error("Failed to create WebGL shader");
@@ -762,11 +1652,11 @@ var compileShader2 = (gl, type, source) => {
762
1652
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
763
1653
  const log = gl.getShaderInfoLog(shader);
764
1654
  gl.deleteShader(shader);
765
- throw new Error(`Rings shader compile failed: ${log ?? "(no log)"}`);
1655
+ throw new Error(`Gridlines shader compile failed: ${log ?? "(no log)"}`);
766
1656
  }
767
1657
  return shader;
768
1658
  };
769
- var linkProgram2 = (gl, vs, fs) => {
1659
+ var linkProgram4 = (gl, vs, fs) => {
770
1660
  const program = gl.createProgram();
771
1661
  if (!program) {
772
1662
  throw new Error("Failed to create WebGL program");
@@ -777,219 +1667,202 @@ var linkProgram2 = (gl, vs, fs) => {
777
1667
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
778
1668
  const log = gl.getProgramInfoLog(program);
779
1669
  gl.deleteProgram(program);
780
- throw new Error(`Rings program link failed: ${log ?? "(no log)"}`);
1670
+ throw new Error(`Gridlines program link failed: ${log ?? "(no log)"}`);
781
1671
  }
782
1672
  return program;
783
1673
  };
784
- var createProgram = (gl, vertexSource, fragmentSource) => {
785
- const vs = compileShader2(gl, gl.VERTEX_SHADER, vertexSource);
786
- const fs = compileShader2(gl, gl.FRAGMENT_SHADER, fragmentSource);
787
- const program = linkProgram2(gl, vs, fs);
788
- gl.deleteShader(vs);
789
- gl.deleteShader(fs);
790
- return program;
791
- };
792
- var createTexture = (gl, filter) => {
793
- const texture = gl.createTexture();
794
- if (!texture) {
795
- throw new Error("Failed to create WebGL texture");
796
- }
797
- gl.bindTexture(gl.TEXTURE_2D, texture);
798
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
799
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
800
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
801
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
802
- gl.bindTexture(gl.TEXTURE_2D, null);
803
- return texture;
804
- };
805
- var setupRings = (target) => {
806
- const gl = target.getContext("webgl2", {
807
- premultipliedAlpha: true,
808
- alpha: true,
809
- preserveDrawingBuffer: true
810
- });
811
- if (!gl) {
812
- throw createWebGL2ContextError2("rings effect");
813
- }
814
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
815
- const program = createProgram(gl, RINGS_VS, RINGS_FS);
816
- const vao = gl.createVertexArray();
817
- if (!vao) {
818
- throw new Error("Failed to create WebGL vertex array");
819
- }
820
- gl.bindVertexArray(vao);
821
- const data = new Float32Array([
822
- -1,
823
- -1,
824
- 0,
825
- 0,
826
- 1,
827
- -1,
828
- 1,
829
- 0,
830
- -1,
831
- 1,
832
- 0,
833
- 1,
834
- 1,
835
- 1,
836
- 1,
837
- 1
838
- ]);
839
- const vbo = gl.createBuffer();
840
- if (!vbo) {
841
- throw new Error("Failed to create WebGL buffer");
842
- }
843
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
844
- gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
845
- const aPos = gl.getAttribLocation(program, "aPos");
846
- const aUv = gl.getAttribLocation(program, "aUv");
847
- gl.enableVertexAttribArray(aPos);
848
- gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
849
- gl.enableVertexAttribArray(aUv);
850
- gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
851
- gl.bindVertexArray(null);
852
- const colorCanvas = document.createElement("canvas");
853
- colorCanvas.width = 1;
854
- colorCanvas.height = 1;
855
- const colorCtx = colorCanvas.getContext("2d", { willReadFrequently: true });
856
- if (!colorCtx) {
857
- throw new Error("Failed to acquire 2D context for color parsing");
858
- }
859
- return {
860
- gl,
861
- program,
862
- vao,
863
- vbo,
864
- sourceTexture: createTexture(gl, gl.LINEAR),
865
- paletteTexture: createTexture(gl, gl.NEAREST),
866
- colorCtx,
867
- uniforms: {
868
- uSource: gl.getUniformLocation(program, "uSource"),
869
- uPalette: gl.getUniformLocation(program, "uPalette"),
870
- uResolution: gl.getUniformLocation(program, "uResolution"),
871
- uNumColors: gl.getUniformLocation(program, "uNumColors"),
872
- uCenter: gl.getUniformLocation(program, "uCenter"),
873
- uThickness: gl.getUniformLocation(program, "uThickness"),
874
- uSpacing: gl.getUniformLocation(program, "uSpacing"),
875
- uOffset: gl.getUniformLocation(program, "uOffset"),
876
- uMaskToSourceAlpha: gl.getUniformLocation(program, "uMaskToSourceAlpha")
877
- },
878
- cachedPaletteKey: "",
879
- palettePixelData: new Uint8Array(0)
880
- };
881
- };
882
- var updatePalette = (state, colors) => {
883
- const paletteKey = colors.join("|");
884
- const paletteDirty = state.cachedPaletteKey !== paletteKey;
885
- if (!paletteDirty) {
886
- return false;
887
- }
888
- state.cachedPaletteKey = paletteKey;
889
- const len = colors.length * 4;
890
- if (state.palettePixelData.length !== len) {
891
- state.palettePixelData = new Uint8Array(len);
892
- }
893
- const { palettePixelData } = state;
894
- for (let i = 0;i < colors.length; i++) {
895
- const color = parseColorRgba(state.colorCtx, colors[i]);
896
- palettePixelData[i * 4] = color[0];
897
- palettePixelData[i * 4 + 1] = color[1];
898
- palettePixelData[i * 4 + 2] = color[2];
899
- palettePixelData[i * 4 + 3] = color[3];
900
- }
901
- return true;
1674
+ var rgbaToUniform = (rgba) => {
1675
+ const [r, g, b, a] = rgba;
1676
+ const alpha = a / 255;
1677
+ return [r / 255 * alpha, g / 255 * alpha, b / 255 * alpha, alpha];
902
1678
  };
903
- var rings = createEffect2({
904
- type: "dev.remotion.effects.rings",
905
- label: "rings()",
906
- documentationLink: "https://www.remotion.dev/docs/effects/rings",
1679
+ var gridlines = createEffect4({
1680
+ type: "remotion/gridlines",
1681
+ label: "gridlines()",
1682
+ documentationLink: "https://www.remotion.dev/docs/effects/gridlines",
907
1683
  backend: "webgl2",
908
1684
  calculateKey: (params) => {
909
- const r = resolve2(params);
1685
+ const r = resolve4(params);
910
1686
  const maskSuffix = r.maskToSourceAlpha ? "-mask-to-source-alpha" : "";
911
- return `rings-${r.colors.join("|")}-${r.center.join(":")}-${r.thickness}-${r.spacing}-${r.offset}${maskSuffix}`;
1687
+ return `gridlines-${r.gridSize}-${r.lineWidth}-${r.lineColor}-${r.backgroundColor}-${r.rotation}-${r.rotationX}-${r.rotationY}-${r.perspective}-${r.offsetX}-${r.offsetY}${maskSuffix}`;
1688
+ },
1689
+ setup: (target) => {
1690
+ const gl = target.getContext("webgl2", {
1691
+ premultipliedAlpha: true,
1692
+ alpha: true,
1693
+ preserveDrawingBuffer: true
1694
+ });
1695
+ if (!gl) {
1696
+ throw createWebGL2ContextError4("gridlines effect");
1697
+ }
1698
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
1699
+ const vs = compileShader4(gl, gl.VERTEX_SHADER, GRIDLINES_VS);
1700
+ const fs = compileShader4(gl, gl.FRAGMENT_SHADER, GRIDLINES_FS);
1701
+ const program = linkProgram4(gl, vs, fs);
1702
+ gl.deleteShader(vs);
1703
+ gl.deleteShader(fs);
1704
+ const vao = gl.createVertexArray();
1705
+ if (!vao) {
1706
+ throw new Error("Failed to create WebGL vertex array");
1707
+ }
1708
+ gl.bindVertexArray(vao);
1709
+ const data = new Float32Array([
1710
+ -1,
1711
+ -1,
1712
+ 0,
1713
+ 0,
1714
+ 1,
1715
+ -1,
1716
+ 1,
1717
+ 0,
1718
+ -1,
1719
+ 1,
1720
+ 0,
1721
+ 1,
1722
+ 1,
1723
+ 1,
1724
+ 1,
1725
+ 1
1726
+ ]);
1727
+ const vbo = gl.createBuffer();
1728
+ if (!vbo) {
1729
+ throw new Error("Failed to create WebGL buffer");
1730
+ }
1731
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
1732
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
1733
+ const aPos = gl.getAttribLocation(program, "aPos");
1734
+ const aUv = gl.getAttribLocation(program, "aUv");
1735
+ gl.enableVertexAttribArray(aPos);
1736
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
1737
+ gl.enableVertexAttribArray(aUv);
1738
+ gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
1739
+ gl.bindVertexArray(null);
1740
+ const texture = gl.createTexture();
1741
+ if (!texture) {
1742
+ throw new Error("Failed to create WebGL texture");
1743
+ }
1744
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1745
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1746
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1747
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
1748
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1749
+ gl.bindTexture(gl.TEXTURE_2D, null);
1750
+ const colorCanvas = document.createElement("canvas");
1751
+ colorCanvas.width = 1;
1752
+ colorCanvas.height = 1;
1753
+ const colorCtx = colorCanvas.getContext("2d", { willReadFrequently: true });
1754
+ if (!colorCtx) {
1755
+ throw new Error("Failed to acquire 2D context for color parsing");
1756
+ }
1757
+ return {
1758
+ gl,
1759
+ program,
1760
+ vao,
1761
+ vbo,
1762
+ texture,
1763
+ uniforms: {
1764
+ uSource: gl.getUniformLocation(program, "uSource"),
1765
+ uResolution: gl.getUniformLocation(program, "uResolution"),
1766
+ uGridSize: gl.getUniformLocation(program, "uGridSize"),
1767
+ uLineWidth: gl.getUniformLocation(program, "uLineWidth"),
1768
+ uLineColor: gl.getUniformLocation(program, "uLineColor"),
1769
+ uBackgroundColor: gl.getUniformLocation(program, "uBackgroundColor"),
1770
+ uRotation: gl.getUniformLocation(program, "uRotation"),
1771
+ uRotationX: gl.getUniformLocation(program, "uRotationX"),
1772
+ uRotationY: gl.getUniformLocation(program, "uRotationY"),
1773
+ uPerspective: gl.getUniformLocation(program, "uPerspective"),
1774
+ uOffset: gl.getUniformLocation(program, "uOffset"),
1775
+ uMaskToSourceAlpha: gl.getUniformLocation(program, "uMaskToSourceAlpha")
1776
+ },
1777
+ colorCtx,
1778
+ cachedLineColorStr: "",
1779
+ cachedLineColorRgba: [255, 255, 255, 255],
1780
+ cachedBackgroundColorStr: "",
1781
+ cachedBackgroundColorRgba: [0, 0, 0, 0]
1782
+ };
912
1783
  },
913
- setup: (target) => setupRings(target),
914
1784
  apply: ({ source, width, height, params, state, flipSourceY }) => {
915
- const r = resolve2(params);
916
- const paletteDirty = updatePalette(state, r.colors);
917
- const { gl, program, sourceTexture, paletteTexture, uniforms, vao } = state;
1785
+ const r = resolve4(params);
1786
+ const { gl, program, vao, texture, uniforms } = state;
1787
+ if (state.cachedLineColorStr !== r.lineColor) {
1788
+ state.cachedLineColorStr = r.lineColor;
1789
+ state.cachedLineColorRgba = parseColorRgba(state.colorCtx, r.lineColor);
1790
+ }
1791
+ if (state.cachedBackgroundColorStr !== r.backgroundColor) {
1792
+ state.cachedBackgroundColorStr = r.backgroundColor;
1793
+ state.cachedBackgroundColorRgba = parseColorRgba(state.colorCtx, r.backgroundColor);
1794
+ }
1795
+ const [lineR, lineG, lineB, lineA] = rgbaToUniform(state.cachedLineColorRgba);
1796
+ const [bgR, bgG, bgB, bgA] = rgbaToUniform(state.cachedBackgroundColorRgba);
918
1797
  gl.viewport(0, 0, width, height);
919
1798
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
920
1799
  gl.clearColor(0, 0, 0, 0);
921
1800
  gl.clear(gl.COLOR_BUFFER_BIT);
1801
+ gl.useProgram(program);
1802
+ gl.bindVertexArray(vao);
922
1803
  gl.activeTexture(gl.TEXTURE0);
923
- gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
924
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
1804
+ gl.bindTexture(gl.TEXTURE_2D, texture);
925
1805
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
926
1806
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
927
- gl.activeTexture(gl.TEXTURE1);
928
- gl.bindTexture(gl.TEXTURE_2D, paletteTexture);
929
- if (paletteDirty) {
930
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
931
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
932
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, r.colors.length, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, state.palettePixelData);
933
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
934
- }
935
- gl.useProgram(program);
936
1807
  if (uniforms.uSource)
937
1808
  gl.uniform1i(uniforms.uSource, 0);
938
- if (uniforms.uPalette)
939
- gl.uniform1i(uniforms.uPalette, 1);
940
1809
  if (uniforms.uResolution)
941
1810
  gl.uniform2f(uniforms.uResolution, width, height);
942
- if (uniforms.uNumColors)
943
- gl.uniform1f(uniforms.uNumColors, r.colors.length);
944
- if (uniforms.uCenter) {
945
- const shaderCenter = publicUvToShaderUv(r.center);
946
- gl.uniform2f(uniforms.uCenter, shaderCenter[0], shaderCenter[1]);
947
- }
948
- if (uniforms.uThickness)
949
- gl.uniform1f(uniforms.uThickness, r.thickness);
950
- if (uniforms.uSpacing)
951
- gl.uniform1f(uniforms.uSpacing, r.spacing);
1811
+ if (uniforms.uGridSize)
1812
+ gl.uniform1f(uniforms.uGridSize, r.gridSize);
1813
+ if (uniforms.uLineWidth)
1814
+ gl.uniform1f(uniforms.uLineWidth, r.lineWidth);
1815
+ if (uniforms.uLineColor)
1816
+ gl.uniform4f(uniforms.uLineColor, lineR, lineG, lineB, lineA);
1817
+ if (uniforms.uBackgroundColor)
1818
+ gl.uniform4f(uniforms.uBackgroundColor, bgR, bgG, bgB, bgA);
1819
+ if (uniforms.uRotation)
1820
+ gl.uniform1f(uniforms.uRotation, r.rotation * Math.PI / 180);
1821
+ if (uniforms.uRotationX)
1822
+ gl.uniform1f(uniforms.uRotationX, r.rotationX * Math.PI / 180);
1823
+ if (uniforms.uRotationY)
1824
+ gl.uniform1f(uniforms.uRotationY, r.rotationY * Math.PI / 180);
1825
+ if (uniforms.uPerspective)
1826
+ gl.uniform1f(uniforms.uPerspective, r.perspective);
952
1827
  if (uniforms.uOffset)
953
- gl.uniform1f(uniforms.uOffset, r.offset);
1828
+ gl.uniform2f(uniforms.uOffset, r.offsetX, r.offsetY);
954
1829
  if (uniforms.uMaskToSourceAlpha)
955
1830
  gl.uniform1i(uniforms.uMaskToSourceAlpha, r.maskToSourceAlpha ? 1 : 0);
956
- gl.bindVertexArray(vao);
957
1831
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
958
1832
  gl.bindVertexArray(null);
959
1833
  gl.bindTexture(gl.TEXTURE_2D, null);
960
1834
  gl.useProgram(null);
961
1835
  },
962
- cleanup: ({ gl, program, vao, vbo, sourceTexture, paletteTexture }) => {
963
- gl.deleteTexture(sourceTexture);
964
- gl.deleteTexture(paletteTexture);
1836
+ cleanup: ({ gl, program, vao, vbo, texture }) => {
965
1837
  gl.deleteBuffer(vbo);
966
1838
  gl.deleteProgram(program);
967
1839
  gl.deleteVertexArray(vao);
1840
+ gl.deleteTexture(texture);
968
1841
  },
969
- schema: ringsSchema,
970
- validateParams: validateRingsParams
1842
+ schema: gridlinesSchema,
1843
+ validateParams: validateGridlinesParams
971
1844
  });
972
1845
 
973
1846
  // src/zigzag.ts
974
- import { Internals as Internals3 } from "remotion";
975
- var { createEffect: createEffect3, createWebGL2ContextError: createWebGL2ContextError3 } = Internals3;
1847
+ import { Internals as Internals5 } from "remotion";
1848
+ var { createEffect: createEffect5, createWebGL2ContextError: createWebGL2ContextError5 } = Internals5;
976
1849
  var ZIGZAG_DIRECTIONS = ["horizontal", "vertical"];
977
- var DEFAULT_COLORS2 = ["#dff4ff", "#7cc6ff"];
1850
+ var DEFAULT_COLORS3 = ["#dff4ff", "#7cc6ff"];
978
1851
  var DEFAULT_DIRECTION = "horizontal";
979
1852
  var DEFAULT_THICKNESS2 = 40;
980
- var DEFAULT_GAP3 = 0;
981
- var DEFAULT_ANGLE = 0;
1853
+ var DEFAULT_GAP4 = 0;
1854
+ var DEFAULT_ANGLE2 = 0;
982
1855
  var DEFAULT_OFFSET3 = 0;
983
1856
  var DEFAULT_AMPLITUDE = 40;
984
1857
  var DEFAULT_WAVELENGTH = 160;
985
- var DEFAULT_MASK_TO_SOURCE_ALPHA2 = false;
1858
+ var DEFAULT_MASK_TO_SOURCE_ALPHA4 = false;
986
1859
  var zigzagSchema = {
987
1860
  colors: {
988
1861
  type: "array",
989
1862
  item: {
990
1863
  type: "color"
991
1864
  },
992
- default: DEFAULT_COLORS2,
1865
+ default: DEFAULT_COLORS3,
993
1866
  minLength: 2,
994
1867
  newItemDefault: "#ff0000",
995
1868
  description: "Colors",
@@ -1018,14 +1891,14 @@ var zigzagSchema = {
1018
1891
  min: 0,
1019
1892
  max: 400,
1020
1893
  step: 0.1,
1021
- default: DEFAULT_GAP3,
1894
+ default: DEFAULT_GAP4,
1022
1895
  description: "Gap",
1023
1896
  hiddenFromList: false
1024
1897
  },
1025
1898
  angle: {
1026
1899
  type: "rotation-degrees",
1027
1900
  step: 1,
1028
- default: DEFAULT_ANGLE,
1901
+ default: DEFAULT_ANGLE2,
1029
1902
  description: "Angle"
1030
1903
  },
1031
1904
  offset: {
@@ -1055,23 +1928,23 @@ var zigzagSchema = {
1055
1928
  },
1056
1929
  maskToSourceAlpha: {
1057
1930
  type: "boolean",
1058
- default: DEFAULT_MASK_TO_SOURCE_ALPHA2,
1931
+ default: DEFAULT_MASK_TO_SOURCE_ALPHA4,
1059
1932
  description: "Mask to source alpha"
1060
1933
  }
1061
1934
  };
1062
- var resolve3 = (p) => {
1935
+ var resolve5 = (p) => {
1063
1936
  const thickness = p.thickness ?? DEFAULT_THICKNESS2;
1064
- const gap = p.gap ?? DEFAULT_GAP3;
1937
+ const gap = p.gap ?? DEFAULT_GAP4;
1065
1938
  return {
1066
- colors: p.colors ?? DEFAULT_COLORS2,
1939
+ colors: p.colors ?? DEFAULT_COLORS3,
1067
1940
  direction: p.direction ?? DEFAULT_DIRECTION,
1068
1941
  thickness,
1069
1942
  spacing: thickness + gap,
1070
- angle: p.angle ?? DEFAULT_ANGLE,
1943
+ angle: p.angle ?? DEFAULT_ANGLE2,
1071
1944
  offset: p.offset ?? DEFAULT_OFFSET3,
1072
1945
  amplitude: p.amplitude ?? DEFAULT_AMPLITUDE,
1073
1946
  wavelength: p.wavelength ?? DEFAULT_WAVELENGTH,
1074
- maskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA2
1947
+ maskToSourceAlpha: p.maskToSourceAlpha ?? DEFAULT_MASK_TO_SOURCE_ALPHA4
1075
1948
  };
1076
1949
  };
1077
1950
  var formatEnum = (variants) => {
@@ -1080,17 +1953,17 @@ var formatEnum = (variants) => {
1080
1953
  }
1081
1954
  return `${variants.slice(0, -1).map((variant) => `"${variant}"`).join(", ")} or "${variants[variants.length - 1]}"`;
1082
1955
  };
1083
- var validatePositive3 = (value, name) => {
1956
+ var validatePositive5 = (value, name) => {
1084
1957
  if (value <= 0) {
1085
1958
  throw new TypeError(`"${name}" must be greater than 0, but got ${JSON.stringify(value)}`);
1086
1959
  }
1087
1960
  };
1088
- var validateNonNegative3 = (value, name) => {
1961
+ var validateNonNegative5 = (value, name) => {
1089
1962
  if (value < 0) {
1090
1963
  throw new TypeError(`"${name}" must be greater than or equal to 0, but got ${JSON.stringify(value)}`);
1091
1964
  }
1092
1965
  };
1093
- var validateColors2 = (colors) => {
1966
+ var validateColors3 = (colors) => {
1094
1967
  if (colors === undefined) {
1095
1968
  return;
1096
1969
  }
@@ -1111,7 +1984,7 @@ var validateDirection = (direction) => {
1111
1984
  };
1112
1985
  var validateZigzagParams = (params) => {
1113
1986
  assertEffectParamsObject(params, "Zigzag");
1114
- validateColors2(params.colors);
1987
+ validateColors3(params.colors);
1115
1988
  validateDirection(params.direction);
1116
1989
  assertOptionalFiniteNumber(params.thickness, "thickness");
1117
1990
  assertOptionalFiniteNumber(params.gap, "gap");
@@ -1121,13 +1994,13 @@ var validateZigzagParams = (params) => {
1121
1994
  assertOptionalFiniteNumber(params.wavelength, "wavelength");
1122
1995
  assertOptionalBoolean(params.maskToSourceAlpha, "maskToSourceAlpha");
1123
1996
  const thickness = params.thickness ?? DEFAULT_THICKNESS2;
1124
- const gap = params.gap ?? DEFAULT_GAP3;
1997
+ const gap = params.gap ?? DEFAULT_GAP4;
1125
1998
  const amplitude = params.amplitude ?? DEFAULT_AMPLITUDE;
1126
1999
  const wavelength = params.wavelength ?? DEFAULT_WAVELENGTH;
1127
- validatePositive3(thickness, "thickness");
1128
- validateNonNegative3(gap, "gap");
1129
- validateNonNegative3(amplitude, "amplitude");
1130
- validatePositive3(wavelength, "wavelength");
2000
+ validatePositive5(thickness, "thickness");
2001
+ validateNonNegative5(gap, "gap");
2002
+ validateNonNegative5(amplitude, "amplitude");
2003
+ validatePositive5(wavelength, "wavelength");
1131
2004
  };
1132
2005
  var ZIGZAG_VS = `#version 300 es
1133
2006
  in vec2 aPos;
@@ -1205,7 +2078,7 @@ void main() {
1205
2078
  );
1206
2079
  }
1207
2080
  `;
1208
- var compileShader3 = (gl, type, source) => {
2081
+ var compileShader5 = (gl, type, source) => {
1209
2082
  const shader = gl.createShader(type);
1210
2083
  if (!shader) {
1211
2084
  throw new Error("Failed to create WebGL shader");
@@ -1219,7 +2092,7 @@ var compileShader3 = (gl, type, source) => {
1219
2092
  }
1220
2093
  return shader;
1221
2094
  };
1222
- var linkProgram3 = (gl, vs, fs) => {
2095
+ var linkProgram5 = (gl, vs, fs) => {
1223
2096
  const program = gl.createProgram();
1224
2097
  if (!program) {
1225
2098
  throw new Error("Failed to create WebGL program");
@@ -1234,15 +2107,15 @@ var linkProgram3 = (gl, vs, fs) => {
1234
2107
  }
1235
2108
  return program;
1236
2109
  };
1237
- var createProgram2 = (gl, vertexSource, fragmentSource) => {
1238
- const vs = compileShader3(gl, gl.VERTEX_SHADER, vertexSource);
1239
- const fs = compileShader3(gl, gl.FRAGMENT_SHADER, fragmentSource);
1240
- const program = linkProgram3(gl, vs, fs);
2110
+ var createProgram3 = (gl, vertexSource, fragmentSource) => {
2111
+ const vs = compileShader5(gl, gl.VERTEX_SHADER, vertexSource);
2112
+ const fs = compileShader5(gl, gl.FRAGMENT_SHADER, fragmentSource);
2113
+ const program = linkProgram5(gl, vs, fs);
1241
2114
  gl.deleteShader(vs);
1242
2115
  gl.deleteShader(fs);
1243
2116
  return program;
1244
2117
  };
1245
- var createTexture2 = (gl, filter) => {
2118
+ var createTexture3 = (gl, filter) => {
1246
2119
  const texture = gl.createTexture();
1247
2120
  if (!texture) {
1248
2121
  throw new Error("Failed to create WebGL texture");
@@ -1262,10 +2135,10 @@ var setupZigzag = (target) => {
1262
2135
  preserveDrawingBuffer: true
1263
2136
  });
1264
2137
  if (!gl) {
1265
- throw createWebGL2ContextError3("zigzag effect");
2138
+ throw createWebGL2ContextError5("zigzag effect");
1266
2139
  }
1267
2140
  gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
1268
- const program = createProgram2(gl, ZIGZAG_VS, ZIGZAG_FS);
2141
+ const program = createProgram3(gl, ZIGZAG_VS, ZIGZAG_FS);
1269
2142
  const vao = gl.createVertexArray();
1270
2143
  if (!vao) {
1271
2144
  throw new Error("Failed to create WebGL vertex array");
@@ -1314,8 +2187,8 @@ var setupZigzag = (target) => {
1314
2187
  program,
1315
2188
  vao,
1316
2189
  vbo,
1317
- sourceTexture: createTexture2(gl, gl.LINEAR),
1318
- paletteTexture: createTexture2(gl, gl.NEAREST),
2190
+ sourceTexture: createTexture3(gl, gl.LINEAR),
2191
+ paletteTexture: createTexture3(gl, gl.NEAREST),
1319
2192
  colorCtx,
1320
2193
  uniforms: {
1321
2194
  uSource: gl.getUniformLocation(program, "uSource"),
@@ -1335,7 +2208,7 @@ var setupZigzag = (target) => {
1335
2208
  palettePixelData: new Uint8Array(0)
1336
2209
  };
1337
2210
  };
1338
- var updatePalette2 = (state, colors) => {
2211
+ var updatePalette3 = (state, colors) => {
1339
2212
  const paletteKey = colors.join("|");
1340
2213
  const paletteDirty = state.cachedPaletteKey !== paletteKey;
1341
2214
  if (!paletteDirty) {
@@ -1356,20 +2229,20 @@ var updatePalette2 = (state, colors) => {
1356
2229
  }
1357
2230
  return true;
1358
2231
  };
1359
- var zigzag = createEffect3({
2232
+ var zigzag = createEffect5({
1360
2233
  type: "dev.remotion.effects.zigzag",
1361
2234
  label: "zigzag()",
1362
2235
  documentationLink: "https://www.remotion.dev/docs/effects/zigzag",
1363
2236
  backend: "webgl2",
1364
2237
  calculateKey: (params) => {
1365
- const r = resolve3(params);
2238
+ const r = resolve5(params);
1366
2239
  const maskSuffix = r.maskToSourceAlpha ? "-mask-to-source-alpha" : "";
1367
2240
  return `zigzag-${r.colors.join("|")}-${r.direction}-${r.thickness}-${r.spacing}-${r.angle}-${r.offset}-${r.amplitude}-${r.wavelength}${maskSuffix}`;
1368
2241
  },
1369
2242
  setup: (target) => setupZigzag(target),
1370
2243
  apply: ({ source, width, height, params, state, flipSourceY }) => {
1371
- const r = resolve3(params);
1372
- const paletteDirty = updatePalette2(state, r.colors);
2244
+ const r = resolve5(params);
2245
+ const paletteDirty = updatePalette3(state, r.colors);
1373
2246
  const { gl, program, sourceTexture, paletteTexture, uniforms, vao } = state;
1374
2247
  gl.viewport(0, 0, width, height);
1375
2248
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
@@ -1436,8 +2309,361 @@ var zigzag = createEffect3({
1436
2309
  schema: zigzagSchema,
1437
2310
  validateParams: validateZigzagParams
1438
2311
  });
2312
+ // src/corner-pin/index.ts
2313
+ import { Internals as Internals7 } from "remotion";
2314
+
2315
+ // src/corner-pin/corner-pin-runtime.ts
2316
+ import { Internals as Internals6 } from "remotion";
2317
+
2318
+ // src/corner-pin/corner-pin-shaders.ts
2319
+ var CORNER_PIN_VS = `#version 300 es
2320
+ layout(location = 0) in vec2 aPos;
2321
+ layout(location = 1) in vec2 aUv;
2322
+ out vec2 vUv;
2323
+
2324
+ void main() {
2325
+ vUv = aUv;
2326
+ gl_Position = vec4(aPos, 0.0, 1.0);
2327
+ }
2328
+ `;
2329
+ var CORNER_PIN_FS = `#version 300 es
2330
+ precision highp float;
2331
+
2332
+ in vec2 vUv;
2333
+ out vec4 fragColor;
2334
+
2335
+ uniform sampler2D uSource;
2336
+ uniform vec2 uTopLeft;
2337
+ uniform vec2 uTopRight;
2338
+ uniform vec2 uBottomRight;
2339
+ uniform vec2 uBottomLeft;
2340
+
2341
+ float cross2(vec2 a, vec2 b) {
2342
+ return a.x * b.y - a.y * b.x;
2343
+ }
2344
+
2345
+ bool solveBilinearInverse(vec2 p, out vec2 uv) {
2346
+ vec2 a = uBottomLeft;
2347
+ vec2 b = uBottomRight - uBottomLeft;
2348
+ vec2 c = uTopLeft - uBottomLeft;
2349
+ vec2 d = uBottomLeft - uBottomRight + uTopRight - uTopLeft;
2350
+ vec2 q = p - a;
2351
+
2352
+ float linearDenominator = cross2(b, c);
2353
+ if (abs(cross2(b, d)) < 0.000001) {
2354
+ if (abs(linearDenominator) < 0.000001) {
2355
+ return false;
2356
+ }
2357
+
2358
+ uv.x = cross2(q, c) / linearDenominator;
2359
+ uv.y = cross2(q, b) / cross2(c, b);
2360
+ return true;
2361
+ }
2362
+
2363
+ float qa = -cross2(b, d);
2364
+ float qb = cross2(q, d) - cross2(b, c);
2365
+ float qc = cross2(q, c);
2366
+ float discriminant = qb * qb - 4.0 * qa * qc;
2367
+
2368
+ if (discriminant < 0.0) {
2369
+ return false;
2370
+ }
2371
+
2372
+ float sqrtDiscriminant = sqrt(discriminant);
2373
+ float u1 = (-qb + sqrtDiscriminant) / (2.0 * qa);
2374
+ float u2 = (-qb - sqrtDiscriminant) / (2.0 * qa);
2375
+ float u = u1;
2376
+ if (u1 < -0.001 || u1 > 1.001) {
2377
+ u = u2;
2378
+ }
2379
+
2380
+ vec2 denominator = c + d * u;
2381
+ float v = abs(denominator.x) > abs(denominator.y)
2382
+ ? (q.x - b.x * u) / denominator.x
2383
+ : (q.y - b.y * u) / denominator.y;
2384
+
2385
+ uv = vec2(u, v);
2386
+ return true;
2387
+ }
2388
+
2389
+ void main() {
2390
+ vec2 sourceUv;
2391
+ if (!solveBilinearInverse(vUv, sourceUv)) {
2392
+ fragColor = vec4(0.0);
2393
+ return;
2394
+ }
2395
+
2396
+ if (
2397
+ sourceUv.x < 0.0 ||
2398
+ sourceUv.x > 1.0 ||
2399
+ sourceUv.y < 0.0 ||
2400
+ sourceUv.y > 1.0
2401
+ ) {
2402
+ fragColor = vec4(0.0);
2403
+ return;
2404
+ }
2405
+
2406
+ fragColor = texture(uSource, sourceUv);
2407
+ }
2408
+ `;
2409
+
2410
+ // src/corner-pin/corner-pin-runtime.ts
2411
+ var { createWebGL2ContextError: createWebGL2ContextError6 } = Internals6;
2412
+ var compileShader6 = (gl, type, source) => {
2413
+ const shader = gl.createShader(type);
2414
+ if (!shader) {
2415
+ throw new Error("Failed to create WebGL shader");
2416
+ }
2417
+ gl.shaderSource(shader, source);
2418
+ gl.compileShader(shader);
2419
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
2420
+ const log = gl.getShaderInfoLog(shader);
2421
+ gl.deleteShader(shader);
2422
+ throw new Error(`Shader compile failed: ${log ?? "(no log)"}`);
2423
+ }
2424
+ return shader;
2425
+ };
2426
+ var linkProgram6 = (gl, vs, fs) => {
2427
+ const program = gl.createProgram();
2428
+ if (!program) {
2429
+ throw new Error("Failed to create WebGL program");
2430
+ }
2431
+ gl.attachShader(program, vs);
2432
+ gl.attachShader(program, fs);
2433
+ gl.linkProgram(program);
2434
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
2435
+ const log = gl.getProgramInfoLog(program);
2436
+ gl.deleteProgram(program);
2437
+ throw new Error(`Program link failed: ${log ?? "(no log)"}`);
2438
+ }
2439
+ return program;
2440
+ };
2441
+ var createProgram4 = (gl, vertexSource, fragmentSource) => {
2442
+ const vs = compileShader6(gl, gl.VERTEX_SHADER, vertexSource);
2443
+ const fs = compileShader6(gl, gl.FRAGMENT_SHADER, fragmentSource);
2444
+ const program = linkProgram6(gl, vs, fs);
2445
+ gl.deleteShader(vs);
2446
+ gl.deleteShader(fs);
2447
+ return program;
2448
+ };
2449
+ var createRgbaTexture = (gl) => {
2450
+ const texture = gl.createTexture();
2451
+ if (!texture) {
2452
+ throw new Error("Failed to create WebGL texture");
2453
+ }
2454
+ gl.bindTexture(gl.TEXTURE_2D, texture);
2455
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
2456
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
2457
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
2458
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
2459
+ gl.bindTexture(gl.TEXTURE_2D, null);
2460
+ return texture;
2461
+ };
2462
+ var setupCornerPin = (target) => {
2463
+ const gl = target.getContext("webgl2", {
2464
+ premultipliedAlpha: true,
2465
+ alpha: true,
2466
+ preserveDrawingBuffer: true
2467
+ });
2468
+ if (!gl) {
2469
+ throw createWebGL2ContextError6("corner pin effect");
2470
+ }
2471
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
2472
+ const program = createProgram4(gl, CORNER_PIN_VS, CORNER_PIN_FS);
2473
+ const vao = gl.createVertexArray();
2474
+ if (!vao) {
2475
+ throw new Error("Failed to create WebGL vertex array");
2476
+ }
2477
+ gl.bindVertexArray(vao);
2478
+ const data = new Float32Array([
2479
+ -1,
2480
+ -1,
2481
+ 0,
2482
+ 0,
2483
+ 1,
2484
+ -1,
2485
+ 1,
2486
+ 0,
2487
+ -1,
2488
+ 1,
2489
+ 0,
2490
+ 1,
2491
+ 1,
2492
+ 1,
2493
+ 1,
2494
+ 1
2495
+ ]);
2496
+ const vbo = gl.createBuffer();
2497
+ if (!vbo) {
2498
+ throw new Error("Failed to create WebGL buffer");
2499
+ }
2500
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
2501
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
2502
+ gl.enableVertexAttribArray(0);
2503
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0);
2504
+ gl.enableVertexAttribArray(1);
2505
+ gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8);
2506
+ gl.bindVertexArray(null);
2507
+ const textureSource = createRgbaTexture(gl);
2508
+ return {
2509
+ gl,
2510
+ program,
2511
+ vao,
2512
+ vbo,
2513
+ textureSource,
2514
+ uniforms: {
2515
+ uSource: gl.getUniformLocation(program, "uSource"),
2516
+ uTopLeft: gl.getUniformLocation(program, "uTopLeft"),
2517
+ uTopRight: gl.getUniformLocation(program, "uTopRight"),
2518
+ uBottomRight: gl.getUniformLocation(program, "uBottomRight"),
2519
+ uBottomLeft: gl.getUniformLocation(program, "uBottomLeft")
2520
+ }
2521
+ };
2522
+ };
2523
+ var cleanupCornerPin = (state) => {
2524
+ const { gl, program, vao, vbo, textureSource } = state;
2525
+ gl.deleteTexture(textureSource);
2526
+ gl.deleteBuffer(vbo);
2527
+ gl.deleteProgram(program);
2528
+ gl.deleteVertexArray(vao);
2529
+ };
2530
+ var drawFullscreenQuad = (state) => {
2531
+ const { gl, vao } = state;
2532
+ gl.bindVertexArray(vao);
2533
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
2534
+ gl.bindVertexArray(null);
2535
+ };
2536
+ var setUvUniform = (gl, location, uv) => {
2537
+ if (!location) {
2538
+ return;
2539
+ }
2540
+ const shaderUv = publicUvToShaderUv(uv);
2541
+ gl.uniform2f(location, shaderUv[0], shaderUv[1]);
2542
+ };
2543
+ var applyCornerPin = ({
2544
+ state,
2545
+ source,
2546
+ width,
2547
+ height,
2548
+ topLeft,
2549
+ topRight,
2550
+ bottomRight,
2551
+ bottomLeft,
2552
+ flipSourceY
2553
+ }) => {
2554
+ const { gl, program, textureSource, uniforms } = state;
2555
+ gl.viewport(0, 0, width, height);
2556
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
2557
+ gl.bindTexture(gl.TEXTURE_2D, textureSource);
2558
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
2559
+ gl.bindTexture(gl.TEXTURE_2D, null);
2560
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
2561
+ gl.clearColor(0, 0, 0, 0);
2562
+ gl.clear(gl.COLOR_BUFFER_BIT);
2563
+ gl.useProgram(program);
2564
+ if (uniforms.uSource)
2565
+ gl.uniform1i(uniforms.uSource, 0);
2566
+ setUvUniform(gl, uniforms.uTopLeft, topLeft);
2567
+ setUvUniform(gl, uniforms.uTopRight, topRight);
2568
+ setUvUniform(gl, uniforms.uBottomRight, bottomRight);
2569
+ setUvUniform(gl, uniforms.uBottomLeft, bottomLeft);
2570
+ gl.activeTexture(gl.TEXTURE0);
2571
+ gl.bindTexture(gl.TEXTURE_2D, textureSource);
2572
+ drawFullscreenQuad(state);
2573
+ gl.bindTexture(gl.TEXTURE_2D, null);
2574
+ gl.useProgram(null);
2575
+ };
2576
+
2577
+ // src/corner-pin/index.ts
2578
+ var { createEffect: createEffect6 } = Internals7;
2579
+ var DEFAULT_TOP_LEFT = [0, 0];
2580
+ var DEFAULT_TOP_RIGHT = [1, 0];
2581
+ var DEFAULT_BOTTOM_RIGHT = [1, 1];
2582
+ var DEFAULT_BOTTOM_LEFT = [0, 1];
2583
+ var cornerSchema = (defaultValue) => ({
2584
+ type: "uv-coordinate",
2585
+ min: -1,
2586
+ max: 2,
2587
+ step: 0.01,
2588
+ default: defaultValue,
2589
+ description: "Corner"
2590
+ });
2591
+ var cornerPinSchema = {
2592
+ topLeft: {
2593
+ ...cornerSchema(DEFAULT_TOP_LEFT),
2594
+ description: "Top left"
2595
+ },
2596
+ topRight: {
2597
+ ...cornerSchema(DEFAULT_TOP_RIGHT),
2598
+ description: "Top right"
2599
+ },
2600
+ bottomRight: {
2601
+ ...cornerSchema(DEFAULT_BOTTOM_RIGHT),
2602
+ description: "Bottom right"
2603
+ },
2604
+ bottomLeft: {
2605
+ ...cornerSchema(DEFAULT_BOTTOM_LEFT),
2606
+ description: "Bottom left"
2607
+ }
2608
+ };
2609
+ var resolve6 = (p) => ({
2610
+ topLeft: [...p.topLeft ?? DEFAULT_TOP_LEFT],
2611
+ topRight: [...p.topRight ?? DEFAULT_TOP_RIGHT],
2612
+ bottomRight: [
2613
+ ...p.bottomRight ?? DEFAULT_BOTTOM_RIGHT
2614
+ ],
2615
+ bottomLeft: [
2616
+ ...p.bottomLeft ?? DEFAULT_BOTTOM_LEFT
2617
+ ]
2618
+ });
2619
+ var assertOptionalUvCoordinate2 = (value, name) => {
2620
+ if (value === undefined) {
2621
+ return;
2622
+ }
2623
+ if (!Array.isArray(value) || value.length !== 2 || value.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
2624
+ throw new TypeError(`"${name}" must be a [number, number] tuple`);
2625
+ }
2626
+ };
2627
+ var validateCornerPinParams = (params) => {
2628
+ assertEffectParamsObject(params, "Corner pin");
2629
+ assertOptionalUvCoordinate2(params.topLeft, "topLeft");
2630
+ assertOptionalUvCoordinate2(params.topRight, "topRight");
2631
+ assertOptionalUvCoordinate2(params.bottomRight, "bottomRight");
2632
+ assertOptionalUvCoordinate2(params.bottomLeft, "bottomLeft");
2633
+ };
2634
+ var cornerPin = createEffect6({
2635
+ type: "dev.remotion.effects.cornerPin",
2636
+ label: "cornerPin()",
2637
+ documentationLink: "https://www.remotion.dev/docs/effects/corner-pin",
2638
+ backend: "webgl2",
2639
+ calculateKey: (params) => {
2640
+ const r = resolve6(params);
2641
+ return `corner-pin-${r.topLeft.join(":")}-${r.topRight.join(":")}-${r.bottomRight.join(":")}-${r.bottomLeft.join(":")}`;
2642
+ },
2643
+ setup: (target) => setupCornerPin(target),
2644
+ apply: ({ source, width, height, params, state, flipSourceY }) => {
2645
+ const r = resolve6(params);
2646
+ applyCornerPin({
2647
+ state,
2648
+ source,
2649
+ width,
2650
+ height,
2651
+ topLeft: r.topLeft,
2652
+ topRight: r.topRight,
2653
+ bottomRight: r.bottomRight,
2654
+ bottomLeft: r.bottomLeft,
2655
+ flipSourceY
2656
+ });
2657
+ },
2658
+ cleanup: (state) => cleanupCornerPin(state),
2659
+ schema: cornerPinSchema,
2660
+ validateParams: validateCornerPinParams
2661
+ });
1439
2662
  export {
1440
2663
  zigzag,
1441
2664
  rings,
1442
- pattern
2665
+ pattern,
2666
+ gridlines,
2667
+ cornerPin,
2668
+ checkerboard
1443
2669
  };