Implement background blur on Safari.

Safari doesn't implement filtered drawing on HTML canvas,
it silently ignores the "filter" property of Context2D.
We implement our own blurring by scaling the background down
and then back up.
This commit is contained in:
Juliusz Chroboczek
2025-01-19 11:44:09 +01:00
parent 590af61ba8
commit 5fa945b073
+30 -10
View File
@@ -1341,13 +1341,6 @@ let filters = {
'background-blur': { 'background-blur': {
description: 'Background blur', description: 'Background blur',
predicate: async function() { predicate: async function() {
if(isSafari()) {
console.warn(
'Background blur does not work on Safari, disabled.'
);
return false;
}
let r = await fetch('/third-party/tasks-vision/vision_bundle.mjs', { let r = await fetch('/third-party/tasks-vision/vision_bundle.mjs', {
method: 'HEAD', method: 'HEAD',
}); });
@@ -1399,21 +1392,48 @@ let filters = {
// set the alpha mask, background is opaque // set the alpha mask, background is opaque
ctx.globalCompositeOperation = 'copy'; ctx.globalCompositeOperation = 'copy';
ctx.filter = 'none';
ctx.drawImage(mask, 0, 0); ctx.drawImage(mask, 0, 0);
// rather than blurring the original image, we first mask // rather than blurring the original image, we first mask
// the background then blur, this avoids a halo effect // the background then blur, this avoids a halo effect
ctx.globalCompositeOperation = 'source-in'; ctx.globalCompositeOperation = 'source-in';
ctx.filter = 'none';
ctx.drawImage(result.bitmap, 0, 0); ctx.drawImage(result.bitmap, 0, 0);
if('filter' in ctx) {
ctx.globalCompositeOperation = 'copy'; ctx.globalCompositeOperation = 'copy';
ctx.filter = `blur(${src.videoWidth / 48}px)`; ctx.filter = `blur(${src.videoWidth / 48}px)`;
ctx.drawImage(ctx.canvas, 0, 0); ctx.drawImage(ctx.canvas, 0, 0);
ctx.filter = 'none';
} else {
// Safari bug 198416, context.filter is not supported.
let scale = 24;
let swidth = src.videoWidth / scale;
let sheight = src.videoHeight / scale;
if(!('canvas' in this.userdata))
this.userdata.canvas = document.createElement('canvas');
let c2 = this.userdata.canvas;
if(c2.width != swidth)
c2.width = swidth;
if(c2.height != sheight)
c2.height = sheight;
let ctx2 = c2.getContext('2d');
// scale down the background
ctx2.globalCompositeOperation = 'copy';
ctx2.drawImage(ctx.canvas,
0, 0, src.videoWidth, src.videoHeight,
0, 0, swidth, sheight,
);
// scale back up, composite atop the original background
ctx.globalCompositeOperation = 'source-atop';
ctx.drawImage(ctx2.canvas,
0, 0,
src.videoWidth / scale,
src.videoHeight / scale,
0, 0, src.videoWidth, src.videoHeight,
);
}
// now draw the foreground // now draw the foreground
ctx.globalCompositeOperation = 'destination-atop'; ctx.globalCompositeOperation = 'destination-atop';
ctx.filter = 'none';
ctx.drawImage(result.bitmap, 0, 0); ctx.drawImage(result.bitmap, 0, 0);
ctx.globalCompositeOperation = 'source-over'; ctx.globalCompositeOperation = 'source-over';