@livekit/track-processors 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -251,14 +251,90 @@ var dependencies = {
251
251
  "@mediapipe/tasks-vision": "0.10.14"
252
252
  };
253
253
 
254
- // src/webgl/index.ts
255
- var blurFragmentShader = `
254
+ // src/webgl/utils.ts
255
+ function initTexture(gl, texIndex) {
256
+ const texRef = gl.TEXTURE0 + texIndex;
257
+ gl.activeTexture(texRef);
258
+ const texture = gl.createTexture();
259
+ gl.bindTexture(gl.TEXTURE_2D, texture);
260
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
261
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
262
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
263
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
264
+ gl.bindTexture(gl.TEXTURE_2D, texture);
265
+ return texture;
266
+ }
267
+ function createFramebuffer(gl, texture, width, height) {
268
+ const framebuffer = gl.createFramebuffer();
269
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
270
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
271
+ gl.bindTexture(gl.TEXTURE_2D, texture);
272
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
273
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
274
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
275
+ throw new Error("Framebuffer not complete");
276
+ }
277
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
278
+ return framebuffer;
279
+ }
280
+ function createVertexBuffer(gl) {
281
+ const vertexBuffer = gl.createBuffer();
282
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
283
+ gl.bufferData(
284
+ gl.ARRAY_BUFFER,
285
+ new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]),
286
+ gl.STATIC_DRAW
287
+ );
288
+ return vertexBuffer;
289
+ }
290
+ async function resizeImageToCover(image, targetWidth, targetHeight) {
291
+ const imgAspect = image.width / image.height;
292
+ const targetAspect = targetWidth / targetHeight;
293
+ let sx = 0;
294
+ let sy = 0;
295
+ let sWidth = image.width;
296
+ let sHeight = image.height;
297
+ if (imgAspect > targetAspect) {
298
+ sWidth = Math.round(image.height * targetAspect);
299
+ sx = Math.round((image.width - sWidth) / 2);
300
+ } else if (imgAspect < targetAspect) {
301
+ sHeight = Math.round(image.width / targetAspect);
302
+ sy = Math.round((image.height - sHeight) / 2);
303
+ }
304
+ return createImageBitmap(image, sx, sy, sWidth, sHeight, {
305
+ resizeWidth: targetWidth,
306
+ resizeHeight: targetHeight,
307
+ resizeQuality: "medium"
308
+ });
309
+ }
310
+ var emptyImageData = new ImageData(2, 2);
311
+ emptyImageData.data[0] = 0;
312
+ emptyImageData.data[1] = 0;
313
+ emptyImageData.data[2] = 0;
314
+ emptyImageData.data[3] = 0;
315
+ var glsl = (source) => source;
316
+
317
+ // src/webgl/shader-programs/vertexShader.ts
318
+ var vertexShaderSource = (flipY = true) => `#version 300 es
319
+ in vec2 position;
320
+ out vec2 texCoords;
321
+
322
+ void main() {
323
+ texCoords = (position + 1.0) / 2.0;
324
+ texCoords.y = ${flipY ? "1.0 - texCoords.y" : "texCoords.y"};
325
+ gl_Position = vec4(position, 0, 1.0);
326
+ }
327
+ `;
328
+
329
+ // src/webgl/shader-programs/blurShader.ts
330
+ var blurFragmentShader = glsl`#version 300 es
256
331
  precision highp float;
257
- varying vec2 texCoords;
332
+ in vec2 texCoords;
258
333
  uniform sampler2D u_texture;
259
334
  uniform vec2 u_texelSize;
260
335
  uniform vec2 u_direction;
261
336
  uniform float u_radius;
337
+ out vec4 fragColor;
262
338
 
263
339
  void main() {
264
340
  float sigma = u_radius;
@@ -273,68 +349,17 @@ var blurFragmentShader = `
273
349
  if (abs(offset) > float(radius)) continue;
274
350
  float weight = exp(-(offset * offset) / twoSigmaSq);
275
351
  vec2 sampleCoord = texCoords + u_direction * u_texelSize * offset;
276
- result += texture2D(u_texture, sampleCoord).rgb * weight;
352
+ result += texture(u_texture, sampleCoord).rgb * weight;
277
353
  totalWeight += weight;
278
354
  }
279
355
 
280
- gl_FragColor = vec4(result / totalWeight, 1.0);
356
+ fragColor = vec4(result / totalWeight, 1.0);
281
357
  }
282
358
  `;
283
- var createShaderProgram = (gl) => {
284
- const vs = `
285
- attribute vec2 position;
286
- varying vec2 texCoords;
287
-
288
- void main() {
289
- texCoords = (position + 1.0) / 2.0;
290
- texCoords.y = 1.0 - texCoords.y;
291
- gl_Position = vec4(position, 0, 1.0);
292
- }
293
- `;
294
- const cS = `
295
- precision highp float;
296
- varying vec2 texCoords;
297
- uniform sampler2D background;
298
- uniform sampler2D frame;
299
- uniform sampler2D mask;
300
- void main() {
301
- vec4 maskTex = texture2D(mask, texCoords);
302
- vec4 frameTex = texture2D(frame, texCoords);
303
- vec4 bgTex = texture2D(background, texCoords);
304
-
305
-
306
- float a = maskTex.r;
307
-
308
- gl_FragColor = mix(bgTex, vec4(frameTex.rgb, 1.0), 1.0 - a);
309
-
310
- }
311
- `;
312
- const vertexShader = gl.createShader(gl.VERTEX_SHADER);
313
- if (!vertexShader) {
314
- throw Error("can not create vertex shader");
315
- }
316
- gl.shaderSource(vertexShader, vs);
317
- gl.compileShader(vertexShader);
318
- const compositeShader = gl.createShader(gl.FRAGMENT_SHADER);
319
- if (!compositeShader) {
320
- throw Error("can not create fragment shader");
321
- }
322
- gl.shaderSource(compositeShader, cS);
323
- gl.compileShader(compositeShader);
324
- const compositeProgram = gl.createProgram();
325
- if (!compositeProgram) {
326
- throw Error("can not create composite program");
327
- }
328
- gl.attachShader(compositeProgram, vertexShader);
329
- gl.attachShader(compositeProgram, compositeShader);
330
- gl.linkProgram(compositeProgram);
331
- let blurProgram = null;
332
- let blurVertexShader = null;
333
- let blurFrag = null;
334
- let blurUniforms = null;
335
- blurFrag = gl.createShader(gl.FRAGMENT_SHADER);
359
+ function createBlurProgram(gl) {
360
+ const blurFrag = gl.createShader(gl.FRAGMENT_SHADER);
336
361
  if (!blurFrag) {
337
- throw Error("can not create blur shader");
362
+ throw Error("cannot create blur shader");
338
363
  }
339
364
  gl.shaderSource(blurFrag, blurFragmentShader);
340
365
  gl.compileShader(blurFrag);
@@ -342,15 +367,15 @@ var createShaderProgram = (gl) => {
342
367
  const info = gl.getShaderInfoLog(blurFrag);
343
368
  throw Error(`Failed to compile blur shader: ${info}`);
344
369
  }
345
- blurVertexShader = gl.createShader(gl.VERTEX_SHADER);
370
+ const blurVertexShader = gl.createShader(gl.VERTEX_SHADER);
346
371
  if (!blurVertexShader) {
347
- throw Error("can not create blur vertex shader");
372
+ throw Error("cannot create blur vertex shader");
348
373
  }
349
- gl.shaderSource(blurVertexShader, vs);
374
+ gl.shaderSource(blurVertexShader, vertexShaderSource());
350
375
  gl.compileShader(blurVertexShader);
351
- blurProgram = gl.createProgram();
376
+ const blurProgram = gl.createProgram();
352
377
  if (!blurProgram) {
353
- throw Error("can not create blur program");
378
+ throw Error("cannot create blur program");
354
379
  }
355
380
  gl.attachShader(blurProgram, blurVertexShader);
356
381
  gl.attachShader(blurProgram, blurFrag);
@@ -359,7 +384,7 @@ var createShaderProgram = (gl) => {
359
384
  const info = gl.getProgramInfoLog(blurProgram);
360
385
  throw Error(`Failed to link blur program: ${info}`);
361
386
  }
362
- blurUniforms = {
387
+ const blurUniforms = {
363
388
  position: gl.getAttribLocation(blurProgram, "position"),
364
389
  texture: gl.getUniformLocation(blurProgram, "u_texture"),
365
390
  texelSize: gl.getUniformLocation(blurProgram, "u_texelSize"),
@@ -367,121 +392,253 @@ var createShaderProgram = (gl) => {
367
392
  radius: gl.getUniformLocation(blurProgram, "u_radius")
368
393
  };
369
394
  return {
370
- vertexShader,
371
- compositeShader,
372
- blurShader: blurFrag,
373
- compositeProgram,
374
- blurProgram,
375
- attribLocations: {
376
- position: gl.getAttribLocation(compositeProgram, "position")
377
- },
378
- uniformLocations: {
379
- mask: gl.getUniformLocation(compositeProgram, "mask"),
380
- frame: gl.getUniformLocation(compositeProgram, "frame"),
381
- background: gl.getUniformLocation(compositeProgram, "background")
382
- },
383
- blurUniforms
395
+ program: blurProgram,
396
+ shader: blurFrag,
397
+ vertexShader: blurVertexShader,
398
+ uniforms: blurUniforms
384
399
  };
385
- };
386
- function initTexture(gl, texIndex) {
387
- const texRef = gl.TEXTURE0 + texIndex;
388
- gl.activeTexture(texRef);
389
- const texture = gl.createTexture();
390
- gl.bindTexture(gl.TEXTURE_2D, texture);
391
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
392
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
393
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
394
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
395
- gl.bindTexture(gl.TEXTURE_2D, texture);
396
- return texture;
397
400
  }
398
- function createFramebuffer(gl, texture, width, height) {
399
- const framebuffer = gl.createFramebuffer();
400
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
401
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
402
- gl.bindTexture(gl.TEXTURE_2D, texture);
403
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
404
- const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
405
- if (status !== gl.FRAMEBUFFER_COMPLETE) {
406
- throw new Error("Framebuffer not complete");
407
- }
401
+ function applyBlur(gl, sourceTexture, width, height, blurRadius, blurProgram, blurUniforms, vertexBuffer, processFramebuffers, processTextures) {
402
+ gl.useProgram(blurProgram);
403
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
404
+ gl.vertexAttribPointer(blurUniforms.position, 2, gl.FLOAT, false, 0, 0);
405
+ gl.enableVertexAttribArray(blurUniforms.position);
406
+ const texelWidth = 1 / width;
407
+ const texelHeight = 1 / height;
408
+ gl.bindFramebuffer(gl.FRAMEBUFFER, processFramebuffers[0]);
409
+ gl.viewport(0, 0, width, height);
410
+ gl.activeTexture(gl.TEXTURE0);
411
+ gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
412
+ gl.uniform1i(blurUniforms.texture, 0);
413
+ gl.uniform2f(blurUniforms.texelSize, texelWidth, texelHeight);
414
+ gl.uniform2f(blurUniforms.direction, 1, 0);
415
+ gl.uniform1f(blurUniforms.radius, blurRadius);
416
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
417
+ gl.bindFramebuffer(gl.FRAMEBUFFER, processFramebuffers[1]);
418
+ gl.viewport(0, 0, width, height);
419
+ gl.activeTexture(gl.TEXTURE0);
420
+ gl.bindTexture(gl.TEXTURE_2D, processTextures[0]);
421
+ gl.uniform1i(blurUniforms.texture, 0);
422
+ gl.uniform2f(blurUniforms.direction, 0, 1);
423
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
408
424
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
409
- return framebuffer;
425
+ return processTextures[1];
410
426
  }
411
- var createVertexBuffer = (gl) => {
412
- if (!gl) {
413
- return null;
427
+
428
+ // src/webgl/shader-programs/boxBlurShader.ts
429
+ var boxBlurFragmentShader = glsl`#version 300 es
430
+ precision mediump float;
431
+
432
+ in vec2 texCoords;
433
+
434
+ uniform sampler2D u_texture;
435
+ uniform vec2 u_texelSize; // 1.0 / texture size
436
+ uniform vec2 u_direction; // (1.0, 0.0) for horizontal, (0.0, 1.0) for vertical
437
+ uniform float u_radius; // blur radius in texels
438
+
439
+ out vec4 fragColor;
440
+
441
+ void main() {
442
+ vec3 sum = vec3(0.0);
443
+ float count = 0.0;
444
+
445
+ // Limit radius to avoid excessive loop cost
446
+ const int MAX_RADIUS = 16;
447
+ int radius = int(min(float(MAX_RADIUS), u_radius));
448
+
449
+ for (int i = -MAX_RADIUS; i <= MAX_RADIUS; ++i) {
450
+ if (abs(i) > radius) continue;
451
+
452
+ vec2 offset = u_direction * u_texelSize * float(i);
453
+ sum += texture(u_texture, texCoords + offset).rgb;
454
+ count += 1.0;
414
455
  }
415
- const vertexBuffer = gl.createBuffer();
416
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
417
- gl.bufferData(
418
- gl.ARRAY_BUFFER,
419
- new Float32Array([-1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1]),
420
- gl.STATIC_DRAW
421
- );
422
- return vertexBuffer;
423
- };
456
+
457
+ fragColor = vec4(sum / count, 1.0);
458
+ }
459
+ `;
460
+ function createBoxBlurProgram(gl) {
461
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
462
+ if (!vertexShader) {
463
+ throw Error("cannot create vertex shader");
464
+ }
465
+ gl.shaderSource(vertexShader, vertexShaderSource());
466
+ gl.compileShader(vertexShader);
467
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
468
+ const info = gl.getShaderInfoLog(vertexShader);
469
+ throw Error(`Failed to compile vertex shader: ${info}`);
470
+ }
471
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
472
+ if (!fragmentShader) {
473
+ throw Error("cannot create fragment shader");
474
+ }
475
+ gl.shaderSource(fragmentShader, boxBlurFragmentShader);
476
+ gl.compileShader(fragmentShader);
477
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
478
+ const info = gl.getShaderInfoLog(fragmentShader);
479
+ throw Error(`Failed to compile box blur shader: ${info}`);
480
+ }
481
+ const program = gl.createProgram();
482
+ if (!program) {
483
+ throw Error("cannot create box blur program");
484
+ }
485
+ gl.attachShader(program, vertexShader);
486
+ gl.attachShader(program, fragmentShader);
487
+ gl.linkProgram(program);
488
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
489
+ const info = gl.getProgramInfoLog(program);
490
+ throw Error(`Failed to link box blur program: ${info}`);
491
+ }
492
+ const uniforms = {
493
+ position: gl.getAttribLocation(program, "position"),
494
+ texture: gl.getUniformLocation(program, "u_texture"),
495
+ texelSize: gl.getUniformLocation(program, "u_texelSize"),
496
+ direction: gl.getUniformLocation(program, "u_direction"),
497
+ radius: gl.getUniformLocation(program, "u_radius")
498
+ };
499
+ return {
500
+ program,
501
+ vertexShader,
502
+ fragmentShader,
503
+ uniforms
504
+ };
505
+ }
506
+
507
+ // src/webgl/shader-programs/compositeShader.ts
508
+ var compositeFragmentShader = glsl`#version 300 es
509
+ precision highp float;
510
+ in vec2 texCoords;
511
+ uniform sampler2D background;
512
+ uniform sampler2D frame;
513
+ uniform sampler2D mask;
514
+ out vec4 fragColor;
515
+
516
+ void main() {
517
+
518
+ vec4 frameTex = texture(frame, texCoords);
519
+ vec4 bgTex = texture(background, texCoords);
520
+
521
+ float maskVal = texture(mask, texCoords).r;
522
+
523
+ // Compute screen-space gradient to detect edge sharpness
524
+ float grad = length(vec2(dFdx(maskVal), dFdy(maskVal)));
525
+
526
+ float edgeSoftness = 2.0; // higher = softer
527
+
528
+ // Create a smooth edge around binary transition
529
+ float smoothAlpha = smoothstep(0.5 - grad * edgeSoftness, 0.5 + grad * edgeSoftness, maskVal);
530
+
531
+ // Optional: preserve frame alpha, or override as fully opaque
532
+ vec4 blended = mix(bgTex, vec4(frameTex.rgb, 1.0), 1.0 - smoothAlpha);
533
+
534
+ fragColor = blended;
535
+
536
+ }
537
+ `;
538
+ function createCompositeProgram(gl) {
539
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
540
+ if (!vertexShader) {
541
+ throw Error("cannot create vertex shader");
542
+ }
543
+ gl.shaderSource(vertexShader, vertexShaderSource());
544
+ gl.compileShader(vertexShader);
545
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
546
+ const info = gl.getShaderInfoLog(vertexShader);
547
+ throw Error(`Failed to compile vertex shader: ${info}`);
548
+ }
549
+ const compositeShader = gl.createShader(gl.FRAGMENT_SHADER);
550
+ if (!compositeShader) {
551
+ throw Error("cannot create fragment shader");
552
+ }
553
+ gl.shaderSource(compositeShader, compositeFragmentShader);
554
+ gl.compileShader(compositeShader);
555
+ if (!gl.getShaderParameter(compositeShader, gl.COMPILE_STATUS)) {
556
+ const info = gl.getShaderInfoLog(compositeShader);
557
+ throw Error(`Failed to compile composite shader: ${info}`);
558
+ }
559
+ const compositeProgram = gl.createProgram();
560
+ if (!compositeProgram) {
561
+ throw Error("cannot create composite program");
562
+ }
563
+ gl.attachShader(compositeProgram, vertexShader);
564
+ gl.attachShader(compositeProgram, compositeShader);
565
+ gl.linkProgram(compositeProgram);
566
+ if (!gl.getProgramParameter(compositeProgram, gl.LINK_STATUS)) {
567
+ const info = gl.getProgramInfoLog(compositeProgram);
568
+ throw Error(`Failed to link composite program: ${info}`);
569
+ }
570
+ const attribLocations = {
571
+ position: gl.getAttribLocation(compositeProgram, "position")
572
+ };
573
+ const uniformLocations = {
574
+ mask: gl.getUniformLocation(compositeProgram, "mask"),
575
+ frame: gl.getUniformLocation(compositeProgram, "frame"),
576
+ background: gl.getUniformLocation(compositeProgram, "background"),
577
+ stepWidth: gl.getUniformLocation(compositeProgram, "u_stepWidth")
578
+ };
579
+ return {
580
+ program: compositeProgram,
581
+ vertexShader,
582
+ fragmentShader: compositeShader,
583
+ attribLocations,
584
+ uniformLocations
585
+ };
586
+ }
587
+
588
+ // src/webgl/index.ts
424
589
  var setupWebGL = (canvas) => {
425
- const gl = canvas.getContext("webgl2", { premultipliedAlpha: false });
590
+ const gl = canvas.getContext("webgl2", {
591
+ antialias: true,
592
+ premultipliedAlpha: true
593
+ });
426
594
  let blurRadius = null;
427
595
  if (!gl) {
596
+ console.error("Failed to create WebGL context");
428
597
  return void 0;
429
598
  }
430
599
  gl.enable(gl.BLEND);
431
600
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
601
+ const composite = createCompositeProgram(gl);
602
+ const compositeProgram = composite.program;
603
+ const positionLocation = composite.attribLocations.position;
432
604
  const {
433
- compositeProgram,
434
- blurProgram,
435
- attribLocations: { position: positionLocation },
436
- uniformLocations: {
437
- mask: maskTextureLocation,
438
- frame: frameTextureLocation,
439
- background: bgTextureLocation
440
- },
441
- blurUniforms
442
- } = createShaderProgram(gl);
605
+ mask: maskTextureLocation,
606
+ frame: frameTextureLocation,
607
+ background: bgTextureLocation
608
+ } = composite.uniformLocations;
609
+ const blur = createBlurProgram(gl);
610
+ const blurProgram = blur.program;
611
+ const blurUniforms = blur.uniforms;
612
+ const boxBlur = createBoxBlurProgram(gl);
613
+ const boxBlurProgram = boxBlur.program;
614
+ const boxBlurUniforms = boxBlur.uniforms;
443
615
  const bgTexture = initTexture(gl, 0);
444
616
  const frameTexture = initTexture(gl, 1);
445
617
  const vertexBuffer = createVertexBuffer(gl);
446
- let processTextures = [];
447
- let processFramebuffers = [];
448
- processTextures.push(initTexture(gl, 3));
449
- processTextures.push(initTexture(gl, 4));
450
- processFramebuffers.push(createFramebuffer(gl, processTextures[0], canvas.width, canvas.height));
451
- processFramebuffers.push(createFramebuffer(gl, processTextures[1], canvas.width, canvas.height));
618
+ if (!vertexBuffer) {
619
+ throw new Error("Failed to create vertex buffer");
620
+ }
621
+ let bgBlurTextures = [];
622
+ let bgBlurFrameBuffers = [];
623
+ let maskBlurTextures = [];
624
+ let maskBlurFrameBuffers = [];
625
+ bgBlurTextures.push(initTexture(gl, 3));
626
+ bgBlurTextures.push(initTexture(gl, 4));
627
+ bgBlurFrameBuffers.push(createFramebuffer(gl, bgBlurTextures[0], canvas.width, canvas.height));
628
+ bgBlurFrameBuffers.push(createFramebuffer(gl, bgBlurTextures[1], canvas.width, canvas.height));
629
+ maskBlurTextures.push(initTexture(gl, 5));
630
+ maskBlurTextures.push(initTexture(gl, 6));
631
+ maskBlurFrameBuffers.push(
632
+ createFramebuffer(gl, maskBlurTextures[0], canvas.width, canvas.height)
633
+ );
634
+ maskBlurFrameBuffers.push(
635
+ createFramebuffer(gl, maskBlurTextures[1], canvas.width, canvas.height)
636
+ );
452
637
  gl.useProgram(compositeProgram);
453
638
  gl.uniform1i(bgTextureLocation, 0);
454
639
  gl.uniform1i(frameTextureLocation, 1);
455
640
  gl.uniform1i(maskTextureLocation, 2);
456
- let customBackgroundImage = null;
457
- function applyBlur(sourceTexture, width, height) {
458
- if (!blurRadius || !blurProgram || !blurUniforms)
459
- return bgTexture;
460
- gl.useProgram(blurProgram);
461
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
462
- gl.vertexAttribPointer(blurUniforms.position, 2, gl.FLOAT, false, 0, 0);
463
- gl.enableVertexAttribArray(blurUniforms.position);
464
- const texelWidth = 1 / width;
465
- const texelHeight = 1 / height;
466
- gl.bindFramebuffer(gl.FRAMEBUFFER, processFramebuffers[0]);
467
- gl.viewport(0, 0, width, height);
468
- gl.activeTexture(gl.TEXTURE0);
469
- gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
470
- gl.uniform1i(blurUniforms.texture, 0);
471
- gl.uniform2f(blurUniforms.texelSize, texelWidth, texelHeight);
472
- gl.uniform2f(blurUniforms.direction, 1, 0);
473
- gl.uniform1f(blurUniforms.radius, blurRadius);
474
- gl.drawArrays(gl.TRIANGLES, 0, 6);
475
- gl.bindFramebuffer(gl.FRAMEBUFFER, processFramebuffers[1]);
476
- gl.viewport(0, 0, width, height);
477
- gl.activeTexture(gl.TEXTURE0);
478
- gl.bindTexture(gl.TEXTURE_2D, processTextures[0]);
479
- gl.uniform1i(blurUniforms.texture, 0);
480
- gl.uniform2f(blurUniforms.direction, 0, 1);
481
- gl.drawArrays(gl.TRIANGLES, 0, 6);
482
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
483
- return processTextures[1];
484
- }
641
+ let customBackgroundImage = emptyImageData;
485
642
  function render(frame, mask) {
486
643
  if (frame.codedWidth === 0 || mask.width === 0) {
487
644
  return;
@@ -492,15 +649,38 @@ var setupWebGL = (canvas) => {
492
649
  gl.bindTexture(gl.TEXTURE_2D, frameTexture);
493
650
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, frame);
494
651
  let backgroundTexture = bgTexture;
495
- if (customBackgroundImage) {
652
+ if (blurRadius) {
653
+ backgroundTexture = applyBlur(
654
+ gl,
655
+ frameTexture,
656
+ width,
657
+ height,
658
+ blurRadius,
659
+ blurProgram,
660
+ blurUniforms,
661
+ vertexBuffer,
662
+ bgBlurFrameBuffers,
663
+ bgBlurTextures
664
+ );
665
+ } else {
496
666
  gl.activeTexture(gl.TEXTURE0);
497
667
  gl.bindTexture(gl.TEXTURE_2D, bgTexture);
498
668
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, customBackgroundImage);
499
669
  backgroundTexture = bgTexture;
500
- } else if (blurRadius) {
501
- backgroundTexture = applyBlur(frameTexture, width, height);
502
670
  }
503
- const maskTexture = mask.getAsWebGLTexture();
671
+ const blurredMaskTexture = applyBlur(
672
+ gl,
673
+ mask.getAsWebGLTexture(),
674
+ width,
675
+ height,
676
+ blurRadius || 1,
677
+ // Use a default blur radius if not set
678
+ boxBlurProgram,
679
+ boxBlurUniforms,
680
+ vertexBuffer,
681
+ maskBlurFrameBuffers,
682
+ maskBlurTextures
683
+ );
504
684
  gl.viewport(0, 0, width, height);
505
685
  gl.clearColor(1, 1, 1, 1);
506
686
  gl.clear(gl.COLOR_BUFFER_BIT);
@@ -515,56 +695,27 @@ var setupWebGL = (canvas) => {
515
695
  gl.bindTexture(gl.TEXTURE_2D, frameTexture);
516
696
  gl.uniform1i(frameTextureLocation, 1);
517
697
  gl.activeTexture(gl.TEXTURE2);
518
- gl.bindTexture(gl.TEXTURE_2D, maskTexture);
698
+ gl.bindTexture(gl.TEXTURE_2D, blurredMaskTexture);
519
699
  gl.uniform1i(maskTextureLocation, 2);
520
700
  gl.drawArrays(gl.TRIANGLES, 0, 6);
521
701
  mask.close();
522
702
  }
523
703
  async function setBackgroundImage(image) {
524
- customBackgroundImage = null;
704
+ customBackgroundImage = emptyImageData;
525
705
  if (image) {
526
706
  try {
527
- const canvasWidth = canvas.width;
528
- const canvasHeight = canvas.height;
529
- const imgAspect = image.width / image.height;
530
- const canvasAspect = canvasWidth / canvasHeight;
531
- let sx = 0;
532
- let sy = 0;
533
- let sWidth = image.width;
534
- let sHeight = image.height;
535
- if (imgAspect > canvasAspect) {
536
- sWidth = Math.round(image.height * canvasAspect);
537
- sx = Math.round((image.width - sWidth) / 2);
538
- } else if (imgAspect < canvasAspect) {
539
- sHeight = Math.round(image.width / canvasAspect);
540
- sy = Math.round((image.height - sHeight) / 2);
541
- }
542
- const croppedImage = await createImageBitmap(image, sx, sy, sWidth, sHeight, {
543
- resizeWidth: canvasWidth,
544
- resizeHeight: canvasHeight,
545
- resizeQuality: "medium"
546
- });
707
+ const croppedImage = await resizeImageToCover(image, canvas.width, canvas.height);
547
708
  customBackgroundImage = croppedImage;
548
- gl.activeTexture(gl.TEXTURE0);
549
- gl.bindTexture(gl.TEXTURE_2D, bgTexture);
550
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, croppedImage);
551
709
  } catch (error) {
552
- console.error("Error processing background image:", error);
553
- customBackgroundImage = image;
554
- gl.activeTexture(gl.TEXTURE0);
555
- gl.bindTexture(gl.TEXTURE_2D, bgTexture);
556
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
710
+ console.error(
711
+ "Error processing background image, falling back to black background:",
712
+ error
713
+ );
557
714
  }
558
- } else {
559
- const emptyImage = new ImageData(2, 2);
560
- emptyImage.data[0] = 0;
561
- emptyImage.data[1] = 0;
562
- emptyImage.data[2] = 0;
563
- emptyImage.data[3] = 0;
564
- gl.activeTexture(gl.TEXTURE0);
565
- gl.bindTexture(gl.TEXTURE_2D, bgTexture);
566
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, emptyImage);
567
715
  }
716
+ gl.activeTexture(gl.TEXTURE0);
717
+ gl.bindTexture(gl.TEXTURE_2D, bgTexture);
718
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, customBackgroundImage);
568
719
  }
569
720
  function setBlurRadius(radius) {
570
721
  blurRadius = radius;
@@ -573,21 +724,32 @@ var setupWebGL = (canvas) => {
573
724
  function cleanup() {
574
725
  gl.deleteProgram(compositeProgram);
575
726
  gl.deleteProgram(blurProgram);
727
+ gl.deleteProgram(boxBlurProgram);
576
728
  gl.deleteTexture(bgTexture);
577
729
  gl.deleteTexture(frameTexture);
578
- for (const texture of processTextures) {
730
+ for (const texture of bgBlurTextures) {
579
731
  gl.deleteTexture(texture);
580
732
  }
581
- for (const framebuffer of processFramebuffers) {
733
+ for (const framebuffer of bgBlurFrameBuffers) {
734
+ gl.deleteFramebuffer(framebuffer);
735
+ }
736
+ for (const texture of maskBlurTextures) {
737
+ gl.deleteTexture(texture);
738
+ }
739
+ for (const framebuffer of maskBlurFrameBuffers) {
582
740
  gl.deleteFramebuffer(framebuffer);
583
741
  }
584
742
  gl.deleteBuffer(vertexBuffer);
585
743
  if (customBackgroundImage) {
586
- customBackgroundImage.close();
587
- customBackgroundImage = null;
744
+ if (customBackgroundImage instanceof ImageBitmap) {
745
+ customBackgroundImage.close();
746
+ }
747
+ customBackgroundImage = emptyImageData;
588
748
  }
589
- processTextures = [];
590
- processFramebuffers = [];
749
+ bgBlurTextures = [];
750
+ bgBlurFrameBuffers = [];
751
+ maskBlurTextures = [];
752
+ maskBlurFrameBuffers = [];
591
753
  }
592
754
  return { render, setBackgroundImage, setBlurRadius, cleanup };
593
755
  };
@@ -797,17 +959,14 @@ var BackgroundProcessor2 = (options, name = "background-processor") => {
797
959
  onFrameProcessed,
798
960
  ...processorOpts
799
961
  } = options;
800
- const processor = new ProcessorWrapper(
801
- new BackgroundProcessor({
802
- blurRadius,
803
- imagePath,
804
- segmenterOptions,
805
- assetPaths,
806
- onFrameProcessed
807
- }),
808
- name,
809
- processorOpts
810
- );
962
+ const transformer = new BackgroundProcessor({
963
+ blurRadius,
964
+ imagePath,
965
+ segmenterOptions,
966
+ assetPaths,
967
+ onFrameProcessed
968
+ });
969
+ const processor = new ProcessorWrapper(transformer, name, processorOpts);
811
970
  return processor;
812
971
  };
813
972