@remotion/effects 4.0.508 → 4.0.509

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ export declare const COLOR_SPACE_GLSL = "\nvec3 srgbToLinear(vec3 color) {\n\tvec3 lower = color / 12.92;\n\tvec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));\n\treturn mix(lower, upper, step(vec3(0.04045), color));\n}\n\nvec3 linearToSrgb(vec3 color) {\n\tvec3 nonNegative = max(color, vec3(0.0));\n\tvec3 lower = nonNegative * 12.92;\n\tvec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;\n\treturn mix(lower, upper, step(vec3(0.0031308), nonNegative));\n}\n";
2
+ export declare const WHITE_BALANCE_GLSL = "\nvec3 applyWhiteBalanceLinear(vec3 linear, float temperature, float tint) {\n\tvec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * temperature;\n\tvec3 tintOffset = vec3(0.15, -0.3, 0.15) * tint;\n\tvec3 gains = exp2(temperatureOffset + tintOffset);\n\tfloat luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));\n\treturn linear * gains / luminanceGain;\n}\n";
3
+ export declare const SHADOWS_HIGHLIGHTS_GLSL = "\nfloat getShadowsHighlightsStops(\n\tvec3 color,\n\tfloat shadows,\n\tfloat highlights\n) {\n\tfloat luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n\tfloat shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);\n\tfloat highlightWeight = smoothstep(0.4, 1.0, luminance);\n\tshadowWeight *= shadowWeight;\n\thighlightWeight *= highlightWeight;\n\treturn shadows * shadowWeight + highlights * highlightWeight;\n}\n";
4
+ export declare const VIBRANCE_GLSL = "\nvec3 applyVibrance(vec3 color, float amount) {\n\tfloat maximum = max(max(color.r, color.g), color.b);\n\tfloat minimum = min(min(color.r, color.g), color.b);\n\tfloat lightness = (maximum + minimum) * 0.5;\n\tfloat chroma = maximum - minimum;\n\tfloat saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);\n\tfloat saturation = saturationDenominator <= 0.0\n\t\t? 0.0\n\t\t: chroma / saturationDenominator;\n\n\tif (saturation <= 0.000001) {\n\t\treturn color;\n\t}\n\n\tfloat targetSaturation = amount >= 0.0\n\t\t? saturation + amount * (1.0 - saturation)\n\t\t: saturation * (1.0 + amount);\n\treturn clamp(\n\t\tvec3(lightness) +\n\t\t\t(color - vec3(lightness)) * (targetSaturation / saturation),\n\t\t0.0,\n\t\t1.0\n\t);\n}\n";
@@ -0,0 +1,27 @@
1
+ export type ColorCorrectionParams = {
2
+ /** Exposure adjustment in stops, from `-5` to `5`. Defaults to `0`. */
3
+ readonly exposure?: number;
4
+ /** Contrast multiplier. Defaults to `1`. */
5
+ readonly contrast?: number;
6
+ /** Contrast pivot from `0` to `1`. Defaults to `0.5`. */
7
+ readonly pivot?: number;
8
+ /** Shadow adjustment from `-1` to `1`. Defaults to `0`. */
9
+ readonly shadows?: number;
10
+ /** Highlight adjustment from `-1` to `1`. Defaults to `0`. */
11
+ readonly highlights?: number;
12
+ /** White-region adjustment from `-1` to `1`. Defaults to `0`. */
13
+ readonly whites?: number;
14
+ /** Black-region adjustment from `-1` to `1`. Defaults to `0`. */
15
+ readonly blacks?: number;
16
+ /** Blue-to-amber temperature adjustment from `-1` to `1`. Defaults to `0`. */
17
+ readonly temperature?: number;
18
+ /** Green-to-magenta tint adjustment from `-1` to `1`. Defaults to `0`. */
19
+ readonly tint?: number;
20
+ /** Saturation multiplier. Defaults to `1`. */
21
+ readonly saturation?: number;
22
+ /** Vibrance adjustment from `-1` to `1`. Defaults to `0`. */
23
+ readonly vibrance?: number;
24
+ };
25
+ export declare const colorCorrection: (params?: (ColorCorrectionParams & {
26
+ readonly disabled?: boolean | undefined;
27
+ }) | undefined) => import("remotion").EffectDescriptor<unknown>;
@@ -0,0 +1,560 @@
1
+ // src/color-correction.ts
2
+ import { Internals } from "remotion";
3
+
4
+ // src/color-correction-shader-utils.ts
5
+ var COLOR_SPACE_GLSL = `
6
+ vec3 srgbToLinear(vec3 color) {
7
+ vec3 lower = color / 12.92;
8
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
9
+ return mix(lower, upper, step(vec3(0.04045), color));
10
+ }
11
+
12
+ vec3 linearToSrgb(vec3 color) {
13
+ vec3 nonNegative = max(color, vec3(0.0));
14
+ vec3 lower = nonNegative * 12.92;
15
+ vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
16
+ return mix(lower, upper, step(vec3(0.0031308), nonNegative));
17
+ }
18
+ `;
19
+ var WHITE_BALANCE_GLSL = `
20
+ vec3 applyWhiteBalanceLinear(vec3 linear, float temperature, float tint) {
21
+ vec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * temperature;
22
+ vec3 tintOffset = vec3(0.15, -0.3, 0.15) * tint;
23
+ vec3 gains = exp2(temperatureOffset + tintOffset);
24
+ float luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));
25
+ return linear * gains / luminanceGain;
26
+ }
27
+ `;
28
+ var SHADOWS_HIGHLIGHTS_GLSL = `
29
+ float getShadowsHighlightsStops(
30
+ vec3 color,
31
+ float shadows,
32
+ float highlights
33
+ ) {
34
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
35
+ float shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);
36
+ float highlightWeight = smoothstep(0.4, 1.0, luminance);
37
+ shadowWeight *= shadowWeight;
38
+ highlightWeight *= highlightWeight;
39
+ return shadows * shadowWeight + highlights * highlightWeight;
40
+ }
41
+ `;
42
+ var VIBRANCE_GLSL = `
43
+ vec3 applyVibrance(vec3 color, float amount) {
44
+ float maximum = max(max(color.r, color.g), color.b);
45
+ float minimum = min(min(color.r, color.g), color.b);
46
+ float lightness = (maximum + minimum) * 0.5;
47
+ float chroma = maximum - minimum;
48
+ float saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);
49
+ float saturation = saturationDenominator <= 0.0
50
+ ? 0.0
51
+ : chroma / saturationDenominator;
52
+
53
+ if (saturation <= 0.000001) {
54
+ return color;
55
+ }
56
+
57
+ float targetSaturation = amount >= 0.0
58
+ ? saturation + amount * (1.0 - saturation)
59
+ : saturation * (1.0 + amount);
60
+ return clamp(
61
+ vec3(lightness) +
62
+ (color - vec3(lightness)) * (targetSaturation / saturation),
63
+ 0.0,
64
+ 1.0
65
+ );
66
+ }
67
+ `;
68
+
69
+ // src/validate-effect-param.ts
70
+ var assertEffectParamsObject = (params, effectLabel) => {
71
+ if (params === null || typeof params !== "object") {
72
+ throw new TypeError(`${effectLabel} effect requires a parameters object, but got ${JSON.stringify(params)}`);
73
+ }
74
+ };
75
+ var assertRequiredFiniteNumber = (value, name) => {
76
+ if (typeof value !== "number" || !Number.isFinite(value)) {
77
+ throw new TypeError(`"${name}" must be a finite number, but got ${JSON.stringify(value)}`);
78
+ }
79
+ };
80
+ var assertRequiredColor = (value, name) => {
81
+ if (typeof value !== "string" || value.length === 0) {
82
+ throw new TypeError(`"${name}" must be a non-empty string, but got ${JSON.stringify(value)}`);
83
+ }
84
+ };
85
+ var assertOptionalColor = (value, name) => {
86
+ if (value === undefined) {
87
+ return;
88
+ }
89
+ assertRequiredColor(value, name);
90
+ };
91
+ var assertOptionalBoolean = (value, name) => {
92
+ if (value === undefined) {
93
+ return;
94
+ }
95
+ if (typeof value !== "boolean") {
96
+ throw new TypeError(`"${name}" must be a boolean, but got ${JSON.stringify(value)}`);
97
+ }
98
+ };
99
+
100
+ // src/color-utils.ts
101
+ var DEFAULT_AMOUNT = 1;
102
+ var DEFAULT_BRIGHTNESS_AMOUNT = 0;
103
+ var DEFAULT_HUE_DEGREES = 0;
104
+ var colorAmountSchema = {
105
+ type: "number",
106
+ min: 0,
107
+ max: 1,
108
+ step: 0.01,
109
+ default: DEFAULT_AMOUNT,
110
+ description: "Amount",
111
+ hiddenFromList: false
112
+ };
113
+ var colorMultiplierSchema = {
114
+ type: "number",
115
+ min: 0,
116
+ step: 0.01,
117
+ default: DEFAULT_AMOUNT,
118
+ description: "Amount",
119
+ hiddenFromList: false
120
+ };
121
+ var brightnessAmountSchema = {
122
+ type: "number",
123
+ min: -1,
124
+ max: 1,
125
+ step: 0.01,
126
+ default: DEFAULT_BRIGHTNESS_AMOUNT,
127
+ description: "Amount",
128
+ hiddenFromList: false
129
+ };
130
+ var hueDegreesSchema = {
131
+ type: "rotation-degrees",
132
+ step: 1,
133
+ default: DEFAULT_HUE_DEGREES,
134
+ description: "Degrees"
135
+ };
136
+ var assertOptionalFiniteNumber = (value, name) => {
137
+ if (value === undefined) {
138
+ return;
139
+ }
140
+ assertRequiredFiniteNumber(value, name);
141
+ };
142
+ var validateUnitInterval = (value, name) => {
143
+ if (value < 0) {
144
+ throw new TypeError(`"${name}" must be >= 0, but got ${JSON.stringify(value)}`);
145
+ }
146
+ if (value > 1) {
147
+ throw new TypeError(`"${name}" must be <= 1, but got ${JSON.stringify(value)}`);
148
+ }
149
+ };
150
+ var validateNonNegative = (value, name) => {
151
+ if (value < 0) {
152
+ throw new TypeError(`"${name}" must be >= 0, but got ${JSON.stringify(value)}`);
153
+ }
154
+ };
155
+ var validateSignedUnitInterval = (value, name) => {
156
+ if (value < -1) {
157
+ throw new TypeError(`"${name}" must be >= -1, but got ${JSON.stringify(value)}`);
158
+ }
159
+ if (value > 1) {
160
+ throw new TypeError(`"${name}" must be <= 1, but got ${JSON.stringify(value)}`);
161
+ }
162
+ };
163
+ var clampColorChannel = (value) => {
164
+ return Math.max(0, Math.min(255, value));
165
+ };
166
+ var parseColorRgba = (ctx, color) => {
167
+ ctx.clearRect(0, 0, 1, 1);
168
+ ctx.fillStyle = color;
169
+ ctx.fillRect(0, 0, 1, 1);
170
+ const { data } = ctx.getImageData(0, 0, 1, 1);
171
+ return [data[0], data[1], data[2], data[3]];
172
+ };
173
+
174
+ // src/color-correction.ts
175
+ var { createEffect, createWebGL2ContextError } = Internals;
176
+ var DEFAULT_EXPOSURE = 0;
177
+ var DEFAULT_CONTRAST = 1;
178
+ var DEFAULT_PIVOT = 0.5;
179
+ var DEFAULT_SHADOWS = 0;
180
+ var DEFAULT_HIGHLIGHTS = 0;
181
+ var DEFAULT_WHITES = 0;
182
+ var DEFAULT_BLACKS = 0;
183
+ var DEFAULT_TEMPERATURE = 0;
184
+ var DEFAULT_TINT = 0;
185
+ var DEFAULT_SATURATION = 1;
186
+ var DEFAULT_VIBRANCE = 0;
187
+ var MIN_EXPOSURE = -5;
188
+ var MAX_EXPOSURE = 5;
189
+ var signedAdjustmentSchema = (description) => ({
190
+ type: "number",
191
+ min: -1,
192
+ max: 1,
193
+ step: 0.01,
194
+ default: 0,
195
+ description,
196
+ hiddenFromList: false
197
+ });
198
+ var colorCorrectionSchema = {
199
+ exposure: {
200
+ type: "number",
201
+ min: MIN_EXPOSURE,
202
+ max: MAX_EXPOSURE,
203
+ step: 0.1,
204
+ default: DEFAULT_EXPOSURE,
205
+ description: "Exposure",
206
+ hiddenFromList: false
207
+ },
208
+ contrast: {
209
+ type: "number",
210
+ min: 0,
211
+ max: 3,
212
+ step: 0.01,
213
+ default: DEFAULT_CONTRAST,
214
+ description: "Contrast",
215
+ hiddenFromList: false
216
+ },
217
+ pivot: {
218
+ type: "number",
219
+ min: 0,
220
+ max: 1,
221
+ step: 0.01,
222
+ default: DEFAULT_PIVOT,
223
+ description: "Contrast pivot",
224
+ hiddenFromList: false
225
+ },
226
+ shadows: signedAdjustmentSchema("Shadows"),
227
+ highlights: signedAdjustmentSchema("Highlights"),
228
+ whites: signedAdjustmentSchema("Whites"),
229
+ blacks: signedAdjustmentSchema("Blacks"),
230
+ temperature: signedAdjustmentSchema("Temperature"),
231
+ tint: signedAdjustmentSchema("Tint"),
232
+ saturation: {
233
+ type: "number",
234
+ min: 0,
235
+ max: 3,
236
+ step: 0.01,
237
+ default: DEFAULT_SATURATION,
238
+ description: "Saturation",
239
+ hiddenFromList: false
240
+ },
241
+ vibrance: signedAdjustmentSchema("Vibrance")
242
+ };
243
+ var resolve = (params) => ({
244
+ exposure: params.exposure ?? DEFAULT_EXPOSURE,
245
+ contrast: params.contrast ?? DEFAULT_CONTRAST,
246
+ pivot: params.pivot ?? DEFAULT_PIVOT,
247
+ shadows: params.shadows ?? DEFAULT_SHADOWS,
248
+ highlights: params.highlights ?? DEFAULT_HIGHLIGHTS,
249
+ whites: params.whites ?? DEFAULT_WHITES,
250
+ blacks: params.blacks ?? DEFAULT_BLACKS,
251
+ temperature: params.temperature ?? DEFAULT_TEMPERATURE,
252
+ tint: params.tint ?? DEFAULT_TINT,
253
+ saturation: params.saturation ?? DEFAULT_SATURATION,
254
+ vibrance: params.vibrance ?? DEFAULT_VIBRANCE
255
+ });
256
+ var validateColorCorrectionParams = (params) => {
257
+ assertEffectParamsObject(params, "Color correction");
258
+ assertOptionalFiniteNumber(params.exposure, "exposure");
259
+ assertOptionalFiniteNumber(params.contrast, "contrast");
260
+ assertOptionalFiniteNumber(params.pivot, "pivot");
261
+ assertOptionalFiniteNumber(params.shadows, "shadows");
262
+ assertOptionalFiniteNumber(params.highlights, "highlights");
263
+ assertOptionalFiniteNumber(params.whites, "whites");
264
+ assertOptionalFiniteNumber(params.blacks, "blacks");
265
+ assertOptionalFiniteNumber(params.temperature, "temperature");
266
+ assertOptionalFiniteNumber(params.tint, "tint");
267
+ assertOptionalFiniteNumber(params.saturation, "saturation");
268
+ assertOptionalFiniteNumber(params.vibrance, "vibrance");
269
+ const resolved = resolve(params);
270
+ if (resolved.exposure < MIN_EXPOSURE) {
271
+ throw new TypeError(`"exposure" must be >= ${MIN_EXPOSURE}, but got ${JSON.stringify(resolved.exposure)}`);
272
+ }
273
+ if (resolved.exposure > MAX_EXPOSURE) {
274
+ throw new TypeError(`"exposure" must be <= ${MAX_EXPOSURE}, but got ${JSON.stringify(resolved.exposure)}`);
275
+ }
276
+ validateNonNegative(resolved.contrast, "contrast");
277
+ validateUnitInterval(resolved.pivot, "pivot");
278
+ validateSignedUnitInterval(resolved.shadows, "shadows");
279
+ validateSignedUnitInterval(resolved.highlights, "highlights");
280
+ validateSignedUnitInterval(resolved.whites, "whites");
281
+ validateSignedUnitInterval(resolved.blacks, "blacks");
282
+ validateSignedUnitInterval(resolved.temperature, "temperature");
283
+ validateSignedUnitInterval(resolved.tint, "tint");
284
+ validateNonNegative(resolved.saturation, "saturation");
285
+ validateSignedUnitInterval(resolved.vibrance, "vibrance");
286
+ };
287
+ var VERTEX_SHADER = `#version 300 es
288
+ in vec2 aPos;
289
+ in vec2 aUv;
290
+ out vec2 vUv;
291
+
292
+ void main() {
293
+ vUv = aUv;
294
+ gl_Position = vec4(aPos, 0.0, 1.0);
295
+ }
296
+ `;
297
+ var FRAGMENT_SHADER = `#version 300 es
298
+ precision highp float;
299
+
300
+ in vec2 vUv;
301
+ out vec4 fragColor;
302
+
303
+ uniform sampler2D uSource;
304
+ uniform float uExposure;
305
+ uniform float uContrast;
306
+ uniform float uPivot;
307
+ uniform float uShadows;
308
+ uniform float uHighlights;
309
+ uniform float uWhites;
310
+ uniform float uBlacks;
311
+ uniform float uTemperature;
312
+ uniform float uTint;
313
+ uniform float uSaturation;
314
+ uniform float uVibrance;
315
+
316
+ ${COLOR_SPACE_GLSL}
317
+ ${WHITE_BALANCE_GLSL}
318
+ ${SHADOWS_HIGHLIGHTS_GLSL}
319
+ ${VIBRANCE_GLSL}
320
+
321
+ vec3 applyEndpointTones(vec3 color, float blacks, float whites) {
322
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
323
+ float blackWeight = 1.0 - smoothstep(0.0, 0.35, luminance);
324
+ float whiteWeight = smoothstep(0.65, 1.0, luminance);
325
+ blackWeight *= blackWeight;
326
+ whiteWeight *= whiteWeight;
327
+
328
+ // Moving tones toward their named endpoint uses the full adjustment.
329
+ // Moving them away is damped to avoid flattening the image too aggressively.
330
+ if (blacks >= 0.0) {
331
+ color += (vec3(1.0) - color) * blacks * blackWeight * 0.2;
332
+ } else {
333
+ color *= 1.0 + blacks * blackWeight;
334
+ }
335
+
336
+ if (whites >= 0.0) {
337
+ color += (vec3(1.0) - color) * whites * whiteWeight;
338
+ } else {
339
+ color *= 1.0 + whites * whiteWeight * 0.2;
340
+ }
341
+
342
+ return clamp(color, 0.0, 1.0);
343
+ }
344
+
345
+ void main() {
346
+ vec4 sourceColor = texture(uSource, vUv);
347
+ float alpha = sourceColor.a;
348
+
349
+ if (alpha <= 0.0) {
350
+ fragColor = vec4(0.0);
351
+ return;
352
+ }
353
+
354
+ if (
355
+ uExposure == 0.0 &&
356
+ uContrast == 1.0 &&
357
+ uPivot == 0.5 &&
358
+ uShadows == 0.0 &&
359
+ uHighlights == 0.0 &&
360
+ uWhites == 0.0 &&
361
+ uBlacks == 0.0 &&
362
+ uTemperature == 0.0 &&
363
+ uTint == 0.0 &&
364
+ uSaturation == 1.0 &&
365
+ uVibrance == 0.0
366
+ ) {
367
+ fragColor = sourceColor;
368
+ return;
369
+ }
370
+
371
+ vec3 color = sourceColor.rgb / alpha;
372
+ vec3 linear = srgbToLinear(color) * exp2(uExposure);
373
+ linear = applyWhiteBalanceLinear(linear, uTemperature, uTint);
374
+ color = linearToSrgb(linear);
375
+
376
+ float tonalStops = getShadowsHighlightsStops(
377
+ color,
378
+ uShadows,
379
+ uHighlights
380
+ );
381
+ linear = srgbToLinear(color) * exp2(tonalStops);
382
+ color = linearToSrgb(linear);
383
+ color = applyEndpointTones(color, uBlacks, uWhites);
384
+ color = clamp((color - vec3(uPivot)) * uContrast + vec3(uPivot), 0.0, 1.0);
385
+
386
+ float luminance = dot(color, vec3(0.213, 0.715, 0.072));
387
+ color = clamp(
388
+ vec3(luminance) + (color - vec3(luminance)) * uSaturation,
389
+ 0.0,
390
+ 1.0
391
+ );
392
+ color = applyVibrance(color, uVibrance);
393
+
394
+ fragColor = vec4(color * alpha, alpha);
395
+ }
396
+ `;
397
+ var compileShader = (gl, type, source) => {
398
+ const shader = gl.createShader(type);
399
+ if (!shader) {
400
+ throw new Error("Failed to create color correction shader");
401
+ }
402
+ gl.shaderSource(shader, source);
403
+ gl.compileShader(shader);
404
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
405
+ const log = gl.getShaderInfoLog(shader);
406
+ gl.deleteShader(shader);
407
+ throw new Error(`Color correction shader compile failed: ${log ?? "(no log)"}`);
408
+ }
409
+ return shader;
410
+ };
411
+ var createProgram = (gl) => {
412
+ const vertexShader = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);
413
+ const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER);
414
+ const program = gl.createProgram();
415
+ if (!program) {
416
+ throw new Error("Failed to create color correction shader program");
417
+ }
418
+ gl.attachShader(program, vertexShader);
419
+ gl.attachShader(program, fragmentShader);
420
+ gl.linkProgram(program);
421
+ gl.deleteShader(vertexShader);
422
+ gl.deleteShader(fragmentShader);
423
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
424
+ const log = gl.getProgramInfoLog(program);
425
+ gl.deleteProgram(program);
426
+ throw new Error(`Color correction shader link failed: ${log ?? "(no log)"}`);
427
+ }
428
+ return program;
429
+ };
430
+ var createTexture = (gl) => {
431
+ const texture = gl.createTexture();
432
+ if (!texture) {
433
+ throw new Error("Failed to create color correction texture");
434
+ }
435
+ gl.bindTexture(gl.TEXTURE_2D, texture);
436
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
437
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
438
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
439
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
440
+ gl.bindTexture(gl.TEXTURE_2D, null);
441
+ return texture;
442
+ };
443
+ var setupColorCorrection = (target) => {
444
+ const gl = target.getContext("webgl2", {
445
+ premultipliedAlpha: true,
446
+ alpha: true,
447
+ preserveDrawingBuffer: true
448
+ });
449
+ if (!gl) {
450
+ throw createWebGL2ContextError("color correction effect");
451
+ }
452
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
453
+ const program = createProgram(gl);
454
+ const vao = gl.createVertexArray();
455
+ if (!vao) {
456
+ throw new Error("Failed to create color correction vertex array");
457
+ }
458
+ const vbo = gl.createBuffer();
459
+ if (!vbo) {
460
+ throw new Error("Failed to create color correction vertex buffer");
461
+ }
462
+ gl.bindVertexArray(vao);
463
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
464
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1]), gl.STATIC_DRAW);
465
+ const aPos = gl.getAttribLocation(program, "aPos");
466
+ const aUv = gl.getAttribLocation(program, "aUv");
467
+ gl.enableVertexAttribArray(aPos);
468
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
469
+ gl.enableVertexAttribArray(aUv);
470
+ gl.vertexAttribPointer(aUv, 2, gl.FLOAT, false, 16, 8);
471
+ gl.bindVertexArray(null);
472
+ return {
473
+ gl,
474
+ program,
475
+ vao,
476
+ vbo,
477
+ textureSource: createTexture(gl),
478
+ uniforms: {
479
+ uSource: gl.getUniformLocation(program, "uSource"),
480
+ uExposure: gl.getUniformLocation(program, "uExposure"),
481
+ uContrast: gl.getUniformLocation(program, "uContrast"),
482
+ uPivot: gl.getUniformLocation(program, "uPivot"),
483
+ uShadows: gl.getUniformLocation(program, "uShadows"),
484
+ uHighlights: gl.getUniformLocation(program, "uHighlights"),
485
+ uWhites: gl.getUniformLocation(program, "uWhites"),
486
+ uBlacks: gl.getUniformLocation(program, "uBlacks"),
487
+ uTemperature: gl.getUniformLocation(program, "uTemperature"),
488
+ uTint: gl.getUniformLocation(program, "uTint"),
489
+ uSaturation: gl.getUniformLocation(program, "uSaturation"),
490
+ uVibrance: gl.getUniformLocation(program, "uVibrance")
491
+ }
492
+ };
493
+ };
494
+ var colorCorrection = createEffect({
495
+ type: "remotion/color-correction",
496
+ label: "colorCorrection()",
497
+ documentationLink: "https://www.remotion.dev/docs/effects/color-correction",
498
+ backend: "webgl2",
499
+ calculateKey: (params) => {
500
+ const r = resolve(params);
501
+ return `color-correction-${r.exposure}-${r.contrast}-${r.pivot}-${r.shadows}-${r.highlights}-${r.whites}-${r.blacks}-${r.temperature}-${r.tint}-${r.saturation}-${r.vibrance}`;
502
+ },
503
+ setup: setupColorCorrection,
504
+ apply: ({ source, width, height, params, state, flipSourceY }) => {
505
+ const r = resolve(params);
506
+ const { gl, program, textureSource, uniforms, vao } = state;
507
+ gl.viewport(0, 0, width, height);
508
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
509
+ gl.clearColor(0, 0, 0, 0);
510
+ gl.clear(gl.COLOR_BUFFER_BIT);
511
+ gl.activeTexture(gl.TEXTURE0);
512
+ gl.bindTexture(gl.TEXTURE_2D, textureSource);
513
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
514
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
515
+ gl.useProgram(program);
516
+ if (uniforms.uSource)
517
+ gl.uniform1i(uniforms.uSource, 0);
518
+ if (uniforms.uExposure)
519
+ gl.uniform1f(uniforms.uExposure, r.exposure);
520
+ if (uniforms.uContrast)
521
+ gl.uniform1f(uniforms.uContrast, r.contrast);
522
+ if (uniforms.uPivot)
523
+ gl.uniform1f(uniforms.uPivot, r.pivot);
524
+ if (uniforms.uShadows)
525
+ gl.uniform1f(uniforms.uShadows, r.shadows);
526
+ if (uniforms.uHighlights) {
527
+ gl.uniform1f(uniforms.uHighlights, r.highlights);
528
+ }
529
+ if (uniforms.uWhites)
530
+ gl.uniform1f(uniforms.uWhites, r.whites);
531
+ if (uniforms.uBlacks)
532
+ gl.uniform1f(uniforms.uBlacks, r.blacks);
533
+ if (uniforms.uTemperature) {
534
+ gl.uniform1f(uniforms.uTemperature, r.temperature);
535
+ }
536
+ if (uniforms.uTint)
537
+ gl.uniform1f(uniforms.uTint, r.tint);
538
+ if (uniforms.uSaturation) {
539
+ gl.uniform1f(uniforms.uSaturation, r.saturation);
540
+ }
541
+ if (uniforms.uVibrance)
542
+ gl.uniform1f(uniforms.uVibrance, r.vibrance);
543
+ gl.bindVertexArray(vao);
544
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
545
+ gl.bindVertexArray(null);
546
+ gl.bindTexture(gl.TEXTURE_2D, null);
547
+ gl.useProgram(null);
548
+ },
549
+ cleanup: ({ gl, program, vao, vbo, textureSource }) => {
550
+ gl.deleteTexture(textureSource);
551
+ gl.deleteBuffer(vbo);
552
+ gl.deleteProgram(program);
553
+ gl.deleteVertexArray(vao);
554
+ },
555
+ schema: colorCorrectionSchema,
556
+ validateParams: validateColorCorrectionParams
557
+ });
558
+ export {
559
+ colorCorrection
560
+ };
@@ -1,6 +1,71 @@
1
1
  // src/exposure.ts
2
2
  import { Internals } from "remotion";
3
3
 
4
+ // src/color-correction-shader-utils.ts
5
+ var COLOR_SPACE_GLSL = `
6
+ vec3 srgbToLinear(vec3 color) {
7
+ vec3 lower = color / 12.92;
8
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
9
+ return mix(lower, upper, step(vec3(0.04045), color));
10
+ }
11
+
12
+ vec3 linearToSrgb(vec3 color) {
13
+ vec3 nonNegative = max(color, vec3(0.0));
14
+ vec3 lower = nonNegative * 12.92;
15
+ vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
16
+ return mix(lower, upper, step(vec3(0.0031308), nonNegative));
17
+ }
18
+ `;
19
+ var WHITE_BALANCE_GLSL = `
20
+ vec3 applyWhiteBalanceLinear(vec3 linear, float temperature, float tint) {
21
+ vec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * temperature;
22
+ vec3 tintOffset = vec3(0.15, -0.3, 0.15) * tint;
23
+ vec3 gains = exp2(temperatureOffset + tintOffset);
24
+ float luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));
25
+ return linear * gains / luminanceGain;
26
+ }
27
+ `;
28
+ var SHADOWS_HIGHLIGHTS_GLSL = `
29
+ float getShadowsHighlightsStops(
30
+ vec3 color,
31
+ float shadows,
32
+ float highlights
33
+ ) {
34
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
35
+ float shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);
36
+ float highlightWeight = smoothstep(0.4, 1.0, luminance);
37
+ shadowWeight *= shadowWeight;
38
+ highlightWeight *= highlightWeight;
39
+ return shadows * shadowWeight + highlights * highlightWeight;
40
+ }
41
+ `;
42
+ var VIBRANCE_GLSL = `
43
+ vec3 applyVibrance(vec3 color, float amount) {
44
+ float maximum = max(max(color.r, color.g), color.b);
45
+ float minimum = min(min(color.r, color.g), color.b);
46
+ float lightness = (maximum + minimum) * 0.5;
47
+ float chroma = maximum - minimum;
48
+ float saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);
49
+ float saturation = saturationDenominator <= 0.0
50
+ ? 0.0
51
+ : chroma / saturationDenominator;
52
+
53
+ if (saturation <= 0.000001) {
54
+ return color;
55
+ }
56
+
57
+ float targetSaturation = amount >= 0.0
58
+ ? saturation + amount * (1.0 - saturation)
59
+ : saturation * (1.0 + amount);
60
+ return clamp(
61
+ vec3(lightness) +
62
+ (color - vec3(lightness)) * (targetSaturation / saturation),
63
+ 0.0,
64
+ 1.0
65
+ );
66
+ }
67
+ `;
68
+
4
69
  // src/validate-effect-param.ts
5
70
  var assertEffectParamsObject = (params, effectLabel) => {
6
71
  if (params === null || typeof params !== "object") {
@@ -155,18 +220,7 @@ out vec4 fragColor;
155
220
  uniform sampler2D uSource;
156
221
  uniform float uStops;
157
222
 
158
- vec3 srgbToLinear(vec3 color) {
159
- vec3 lower = color / 12.92;
160
- vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
161
- return mix(lower, upper, step(vec3(0.04045), color));
162
- }
163
-
164
- vec3 linearToSrgb(vec3 color) {
165
- vec3 nonNegative = max(color, vec3(0.0));
166
- vec3 lower = nonNegative * 12.92;
167
- vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
168
- return mix(lower, upper, step(vec3(0.0031308), nonNegative));
169
- }
223
+ ${COLOR_SPACE_GLSL}
170
224
 
171
225
  void main() {
172
226
  vec4 sourceColor = texture(uSource, vUv);
@@ -1,6 +1,71 @@
1
1
  // src/shadows-highlights.ts
2
2
  import { Internals } from "remotion";
3
3
 
4
+ // src/color-correction-shader-utils.ts
5
+ var COLOR_SPACE_GLSL = `
6
+ vec3 srgbToLinear(vec3 color) {
7
+ vec3 lower = color / 12.92;
8
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
9
+ return mix(lower, upper, step(vec3(0.04045), color));
10
+ }
11
+
12
+ vec3 linearToSrgb(vec3 color) {
13
+ vec3 nonNegative = max(color, vec3(0.0));
14
+ vec3 lower = nonNegative * 12.92;
15
+ vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
16
+ return mix(lower, upper, step(vec3(0.0031308), nonNegative));
17
+ }
18
+ `;
19
+ var WHITE_BALANCE_GLSL = `
20
+ vec3 applyWhiteBalanceLinear(vec3 linear, float temperature, float tint) {
21
+ vec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * temperature;
22
+ vec3 tintOffset = vec3(0.15, -0.3, 0.15) * tint;
23
+ vec3 gains = exp2(temperatureOffset + tintOffset);
24
+ float luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));
25
+ return linear * gains / luminanceGain;
26
+ }
27
+ `;
28
+ var SHADOWS_HIGHLIGHTS_GLSL = `
29
+ float getShadowsHighlightsStops(
30
+ vec3 color,
31
+ float shadows,
32
+ float highlights
33
+ ) {
34
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
35
+ float shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);
36
+ float highlightWeight = smoothstep(0.4, 1.0, luminance);
37
+ shadowWeight *= shadowWeight;
38
+ highlightWeight *= highlightWeight;
39
+ return shadows * shadowWeight + highlights * highlightWeight;
40
+ }
41
+ `;
42
+ var VIBRANCE_GLSL = `
43
+ vec3 applyVibrance(vec3 color, float amount) {
44
+ float maximum = max(max(color.r, color.g), color.b);
45
+ float minimum = min(min(color.r, color.g), color.b);
46
+ float lightness = (maximum + minimum) * 0.5;
47
+ float chroma = maximum - minimum;
48
+ float saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);
49
+ float saturation = saturationDenominator <= 0.0
50
+ ? 0.0
51
+ : chroma / saturationDenominator;
52
+
53
+ if (saturation <= 0.000001) {
54
+ return color;
55
+ }
56
+
57
+ float targetSaturation = amount >= 0.0
58
+ ? saturation + amount * (1.0 - saturation)
59
+ : saturation * (1.0 + amount);
60
+ return clamp(
61
+ vec3(lightness) +
62
+ (color - vec3(lightness)) * (targetSaturation / saturation),
63
+ 0.0,
64
+ 1.0
65
+ );
66
+ }
67
+ `;
68
+
4
69
  // src/validate-effect-param.ts
5
70
  var assertEffectParamsObject = (params, effectLabel) => {
6
71
  if (params === null || typeof params !== "object") {
@@ -162,18 +227,8 @@ uniform sampler2D uSource;
162
227
  uniform float uShadows;
163
228
  uniform float uHighlights;
164
229
 
165
- vec3 srgbToLinear(vec3 color) {
166
- vec3 lower = color / 12.92;
167
- vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
168
- return mix(lower, upper, step(vec3(0.04045), color));
169
- }
170
-
171
- vec3 linearToSrgb(vec3 color) {
172
- vec3 nonNegative = max(color, vec3(0.0));
173
- vec3 lower = nonNegative * 12.92;
174
- vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
175
- return mix(lower, upper, step(vec3(0.0031308), nonNegative));
176
- }
230
+ ${COLOR_SPACE_GLSL}
231
+ ${SHADOWS_HIGHLIGHTS_GLSL}
177
232
 
178
233
  void main() {
179
234
  vec4 sourceColor = texture(uSource, vUv);
@@ -185,13 +240,7 @@ void main() {
185
240
  }
186
241
 
187
242
  vec3 color = sourceColor.rgb / alpha;
188
- float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
189
- float shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);
190
- float highlightWeight = smoothstep(0.4, 1.0, luminance);
191
- shadowWeight *= shadowWeight;
192
- highlightWeight *= highlightWeight;
193
-
194
- float stops = uShadows * shadowWeight + uHighlights * highlightWeight;
243
+ float stops = getShadowsHighlightsStops(color, uShadows, uHighlights);
195
244
  vec3 linear = srgbToLinear(color);
196
245
  vec3 adjusted = linear * exp2(stops);
197
246
  vec3 corrected = linearToSrgb(adjusted);
@@ -1,6 +1,71 @@
1
1
  // src/vibrance.ts
2
2
  import { Internals } from "remotion";
3
3
 
4
+ // src/color-correction-shader-utils.ts
5
+ var COLOR_SPACE_GLSL = `
6
+ vec3 srgbToLinear(vec3 color) {
7
+ vec3 lower = color / 12.92;
8
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
9
+ return mix(lower, upper, step(vec3(0.04045), color));
10
+ }
11
+
12
+ vec3 linearToSrgb(vec3 color) {
13
+ vec3 nonNegative = max(color, vec3(0.0));
14
+ vec3 lower = nonNegative * 12.92;
15
+ vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
16
+ return mix(lower, upper, step(vec3(0.0031308), nonNegative));
17
+ }
18
+ `;
19
+ var WHITE_BALANCE_GLSL = `
20
+ vec3 applyWhiteBalanceLinear(vec3 linear, float temperature, float tint) {
21
+ vec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * temperature;
22
+ vec3 tintOffset = vec3(0.15, -0.3, 0.15) * tint;
23
+ vec3 gains = exp2(temperatureOffset + tintOffset);
24
+ float luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));
25
+ return linear * gains / luminanceGain;
26
+ }
27
+ `;
28
+ var SHADOWS_HIGHLIGHTS_GLSL = `
29
+ float getShadowsHighlightsStops(
30
+ vec3 color,
31
+ float shadows,
32
+ float highlights
33
+ ) {
34
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
35
+ float shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);
36
+ float highlightWeight = smoothstep(0.4, 1.0, luminance);
37
+ shadowWeight *= shadowWeight;
38
+ highlightWeight *= highlightWeight;
39
+ return shadows * shadowWeight + highlights * highlightWeight;
40
+ }
41
+ `;
42
+ var VIBRANCE_GLSL = `
43
+ vec3 applyVibrance(vec3 color, float amount) {
44
+ float maximum = max(max(color.r, color.g), color.b);
45
+ float minimum = min(min(color.r, color.g), color.b);
46
+ float lightness = (maximum + minimum) * 0.5;
47
+ float chroma = maximum - minimum;
48
+ float saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);
49
+ float saturation = saturationDenominator <= 0.0
50
+ ? 0.0
51
+ : chroma / saturationDenominator;
52
+
53
+ if (saturation <= 0.000001) {
54
+ return color;
55
+ }
56
+
57
+ float targetSaturation = amount >= 0.0
58
+ ? saturation + amount * (1.0 - saturation)
59
+ : saturation * (1.0 + amount);
60
+ return clamp(
61
+ vec3(lightness) +
62
+ (color - vec3(lightness)) * (targetSaturation / saturation),
63
+ 0.0,
64
+ 1.0
65
+ );
66
+ }
67
+ `;
68
+
4
69
  // src/validate-effect-param.ts
5
70
  var assertEffectParamsObject = (params, effectLabel) => {
6
71
  if (params === null || typeof params !== "object") {
@@ -148,6 +213,8 @@ out vec4 fragColor;
148
213
  uniform sampler2D uSource;
149
214
  uniform float uAmount;
150
215
 
216
+ ${VIBRANCE_GLSL}
217
+
151
218
  void main() {
152
219
  vec4 sourceColor = texture(uSource, vUv);
153
220
  float alpha = sourceColor.a;
@@ -158,27 +225,9 @@ void main() {
158
225
  }
159
226
 
160
227
  vec3 color = sourceColor.rgb / alpha;
161
- float maximum = max(max(color.r, color.g), color.b);
162
- float minimum = min(min(color.r, color.g), color.b);
163
- float lightness = (maximum + minimum) * 0.5;
164
- float chroma = maximum - minimum;
165
- float saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);
166
- float saturation = saturationDenominator <= 0.0
167
- ? 0.0
168
- : chroma / saturationDenominator;
169
-
170
- if (saturation <= 0.000001) {
171
- fragColor = sourceColor;
172
- return;
173
- }
174
-
175
- float targetSaturation = uAmount >= 0.0
176
- ? saturation + uAmount * (1.0 - saturation)
177
- : saturation * (1.0 + uAmount);
178
- vec3 adjusted = vec3(lightness) +
179
- (color - vec3(lightness)) * (targetSaturation / saturation);
228
+ vec3 adjusted = applyVibrance(color, uAmount);
180
229
 
181
- fragColor = vec4(clamp(adjusted, 0.0, 1.0) * alpha, alpha);
230
+ fragColor = vec4(adjusted * alpha, alpha);
182
231
  }
183
232
  `;
184
233
  var compileShader = (gl, type, source) => {
@@ -1,6 +1,71 @@
1
1
  // src/white-balance.ts
2
2
  import { Internals } from "remotion";
3
3
 
4
+ // src/color-correction-shader-utils.ts
5
+ var COLOR_SPACE_GLSL = `
6
+ vec3 srgbToLinear(vec3 color) {
7
+ vec3 lower = color / 12.92;
8
+ vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
9
+ return mix(lower, upper, step(vec3(0.04045), color));
10
+ }
11
+
12
+ vec3 linearToSrgb(vec3 color) {
13
+ vec3 nonNegative = max(color, vec3(0.0));
14
+ vec3 lower = nonNegative * 12.92;
15
+ vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
16
+ return mix(lower, upper, step(vec3(0.0031308), nonNegative));
17
+ }
18
+ `;
19
+ var WHITE_BALANCE_GLSL = `
20
+ vec3 applyWhiteBalanceLinear(vec3 linear, float temperature, float tint) {
21
+ vec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * temperature;
22
+ vec3 tintOffset = vec3(0.15, -0.3, 0.15) * tint;
23
+ vec3 gains = exp2(temperatureOffset + tintOffset);
24
+ float luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));
25
+ return linear * gains / luminanceGain;
26
+ }
27
+ `;
28
+ var SHADOWS_HIGHLIGHTS_GLSL = `
29
+ float getShadowsHighlightsStops(
30
+ vec3 color,
31
+ float shadows,
32
+ float highlights
33
+ ) {
34
+ float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
35
+ float shadowWeight = 1.0 - smoothstep(0.0, 0.6, luminance);
36
+ float highlightWeight = smoothstep(0.4, 1.0, luminance);
37
+ shadowWeight *= shadowWeight;
38
+ highlightWeight *= highlightWeight;
39
+ return shadows * shadowWeight + highlights * highlightWeight;
40
+ }
41
+ `;
42
+ var VIBRANCE_GLSL = `
43
+ vec3 applyVibrance(vec3 color, float amount) {
44
+ float maximum = max(max(color.r, color.g), color.b);
45
+ float minimum = min(min(color.r, color.g), color.b);
46
+ float lightness = (maximum + minimum) * 0.5;
47
+ float chroma = maximum - minimum;
48
+ float saturationDenominator = 1.0 - abs(2.0 * lightness - 1.0);
49
+ float saturation = saturationDenominator <= 0.0
50
+ ? 0.0
51
+ : chroma / saturationDenominator;
52
+
53
+ if (saturation <= 0.000001) {
54
+ return color;
55
+ }
56
+
57
+ float targetSaturation = amount >= 0.0
58
+ ? saturation + amount * (1.0 - saturation)
59
+ : saturation * (1.0 + amount);
60
+ return clamp(
61
+ vec3(lightness) +
62
+ (color - vec3(lightness)) * (targetSaturation / saturation),
63
+ 0.0,
64
+ 1.0
65
+ );
66
+ }
67
+ `;
68
+
4
69
  // src/validate-effect-param.ts
5
70
  var assertEffectParamsObject = (params, effectLabel) => {
6
71
  if (params === null || typeof params !== "object") {
@@ -162,18 +227,8 @@ uniform sampler2D uSource;
162
227
  uniform float uTemperature;
163
228
  uniform float uTint;
164
229
 
165
- vec3 srgbToLinear(vec3 color) {
166
- vec3 lower = color / 12.92;
167
- vec3 upper = pow((color + 0.055) / 1.055, vec3(2.4));
168
- return mix(lower, upper, step(vec3(0.04045), color));
169
- }
170
-
171
- vec3 linearToSrgb(vec3 color) {
172
- vec3 nonNegative = max(color, vec3(0.0));
173
- vec3 lower = nonNegative * 12.92;
174
- vec3 upper = 1.055 * pow(nonNegative, vec3(1.0 / 2.4)) - 0.055;
175
- return mix(lower, upper, step(vec3(0.0031308), nonNegative));
176
- }
230
+ ${COLOR_SPACE_GLSL}
231
+ ${WHITE_BALANCE_GLSL}
177
232
 
178
233
  void main() {
179
234
  vec4 sourceColor = texture(uSource, vUv);
@@ -186,11 +241,7 @@ void main() {
186
241
 
187
242
  vec3 unpremultiplied = sourceColor.rgb / alpha;
188
243
  vec3 linear = srgbToLinear(unpremultiplied);
189
- vec3 temperatureOffset = vec3(0.3, 0.0, -0.3) * uTemperature;
190
- vec3 tintOffset = vec3(0.15, -0.3, 0.15) * uTint;
191
- vec3 gains = exp2(temperatureOffset + tintOffset);
192
- float luminanceGain = dot(gains, vec3(0.2126, 0.7152, 0.0722));
193
- vec3 balanced = linear * gains / luminanceGain;
244
+ vec3 balanced = applyWhiteBalanceLinear(linear, uTemperature, uTint);
194
245
  vec3 corrected = linearToSrgb(balanced);
195
246
 
196
247
  fragColor = vec4(corrected * alpha, alpha);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/effects",
3
- "version": "4.0.508",
3
+ "version": "4.0.509",
4
4
  "description": "Effects that can be applied to Remotion-based canvas components",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,7 +26,7 @@
26
26
  "url": "https://github.com/remotion-dev/remotion/issues"
27
27
  },
28
28
  "dependencies": {
29
- "remotion": "4.0.508"
29
+ "remotion": "4.0.509"
30
30
  },
31
31
  "exports": {
32
32
  ".": {
@@ -69,6 +69,11 @@
69
69
  "module": "./dist/esm/color-key.mjs",
70
70
  "import": "./dist/esm/color-key.mjs"
71
71
  },
72
+ "./color-correction": {
73
+ "types": "./dist/color-correction.d.ts",
74
+ "module": "./dist/esm/color-correction.mjs",
75
+ "import": "./dist/esm/color-correction.mjs"
76
+ },
72
77
  "./brightness": {
73
78
  "types": "./dist/brightness.d.ts",
74
79
  "module": "./dist/esm/brightness.mjs",
@@ -399,6 +404,9 @@
399
404
  "color-key": [
400
405
  "dist/color-key.d.ts"
401
406
  ],
407
+ "color-correction": [
408
+ "dist/color-correction.d.ts"
409
+ ],
402
410
  "brightness": [
403
411
  "dist/brightness.d.ts"
404
412
  ],
@@ -586,7 +594,7 @@
586
594
  },
587
595
  "homepage": "https://www.remotion.dev/docs/effects/api",
588
596
  "devDependencies": {
589
- "@remotion/eslint-config-internal": "4.0.508",
597
+ "@remotion/eslint-config-internal": "4.0.509",
590
598
  "@vitest/browser-playwright": "4.0.9",
591
599
  "eslint": "9.19.0",
592
600
  "vitest": "4.0.9",