From 7da7e22ebef274934a763059b89c7250b8ce13a5 Mon Sep 17 00:00:00 2001 From: young Date: Tue, 25 Aug 2026 19:05:46 +1000 Subject: [PATCH] Clean up preview edges and blur controls --- src/components/video-editor/VideoPlayback.tsx | 59 +++---------------- src/i18n/locales/de/settings.json | 2 +- src/i18n/locales/en/settings.json | 2 +- src/i18n/locales/es/settings.json | 2 +- src/i18n/locales/fr/settings.json | 2 +- src/i18n/locales/it/settings.json | 2 +- src/i18n/locales/ko/settings.json | 2 +- src/i18n/locales/nl/settings.json | 2 +- src/i18n/locales/pt-BR/settings.json | 2 +- src/i18n/locales/ru/settings.json | 2 +- src/i18n/locales/zh-CN/settings.json | 2 +- src/i18n/locales/zh-TW/settings.json | 2 +- src/lib/exporter/frameRenderer.ts | 6 +- src/lib/exporter/modernFrameRenderer.ts | 13 +++- 14 files changed, 34 insertions(+), 66 deletions(-) diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index 5aebd7ec..de81b01f 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -171,9 +171,7 @@ import { SNAP_TO_EDGES_RATIO_AUTO, } from "./videoPlayback/cursorFollowCamera"; import { clampFocusToStage as clampFocusToStageUtil } from "./videoPlayback/focusUtils"; -import { - layoutVideoContent as layoutVideoContentUtil, -} from "./videoPlayback/layoutUtils"; +import { layoutVideoContent as layoutVideoContentUtil } from "./videoPlayback/layoutUtils"; import { updateOverlayIndicator } from "./videoPlayback/overlayUtils"; import { createVideoEventHandlers } from "./videoPlayback/videoEventHandlers"; import { getWebcamMediaTargetTimeSeconds, shouldSeekWebcamMedia } from "./videoPlayback/webcamSync"; @@ -1909,53 +1907,6 @@ const VideoPlayback = forwardRef( }); }, [pixiReady, videoReady, layoutVideoContent, cropRegion]); - useEffect(() => { - const previewFrame = previewFrameRef.current; - if (!previewFrame) { - return; - } - let frameId: number | null = null; - - const applyPreviewFrameSquircle = () => { - const width = previewFrame.offsetWidth; - const height = previewFrame.offsetHeight; - if (width <= 0 || height <= 0) { - return; - } - - const squirclePath = getSquircleSvgPath({ - x: 0, - y: 0, - width, - height, - radius: 12, - }); - previewFrame.style.clipPath = `path('${squirclePath}')`; - previewFrame.style.setProperty("-webkit-clip-path", `path('${squirclePath}')`); - }; - - applyPreviewFrameSquircle(); - - if (typeof ResizeObserver === "undefined") { - return; - } - - const observer = new ResizeObserver(() => { - if (frameId !== null) { - cancelAnimationFrame(frameId); - } - frameId = requestAnimationFrame(applyPreviewFrameSquircle); - }); - - observer.observe(previewFrame); - return () => { - if (frameId !== null) { - cancelAnimationFrame(frameId); - } - observer.disconnect(); - }; - }, []); - useEffect(() => { if (!pixiReady || !videoReady) return; const container = containerRef.current; @@ -2955,6 +2906,9 @@ const VideoPlayback = forwardRef( : resolvedWallpaperKind === "video" ? {} : { background: resolvedWallpaper || "" }; + // Overscan blurred wallpaper layers so the browser never samples transparent + // pixels beyond the preview bounds, which otherwise looks like a vignette. + const backgroundBlurOverscan = backgroundBlur > 0 ? Math.ceil(backgroundBlur * 2) : 0; const fallbackVideoClassName = pixiRendererError ? "absolute inset-0 h-full w-full object-cover" : "pointer-events-none absolute left-0 top-0 h-px w-px opacity-0"; @@ -2985,7 +2939,8 @@ const VideoPlayback = forwardRef( style={{ width: "100%", aspectRatio: formatAspectRatioForCSS(aspectRatio, nativeAspectRatio), - borderRadius: "12px", + borderRadius: 0, + clipPath: "none", }} > {/* Background layer */} @@ -3000,6 +2955,7 @@ const VideoPlayback = forwardRef( playsInline style={{ filter: backgroundBlur > 0 ? `blur(${backgroundBlur}px)` : "none", + inset: -backgroundBlurOverscan, }} /> ) : ( @@ -3008,6 +2964,7 @@ const VideoPlayback = forwardRef( style={{ ...backgroundStyle, filter: backgroundBlur > 0 ? `blur(${backgroundBlur}px)` : "none", + inset: -backgroundBlurOverscan, }} /> )} diff --git a/src/i18n/locales/de/settings.json b/src/i18n/locales/de/settings.json index 706dcc0d..2f1f57e7 100644 --- a/src/i18n/locales/de/settings.json +++ b/src/i18n/locales/de/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "Hintergrundunschärfe", + "backgroundBlur": "Unschärfe", "zoomMotionBlur": "Zoom-Bewegungsunschärfe", "temporalZoomMotionBlur": "Zeitliche Zoom-Bewegungsunschärfe", "temporalZoomMotionBlurDescription": "Steuert das Verschlussfenster und die Bildausschnitte, die vom neueren Zoom-Bewegungsunschärfe-Durchlauf verwendet werden.", diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index 60aa8622..8f0cfb7b 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "Background Blur", + "backgroundBlur": "Blur", "zoomMotionBlur": "Zoom Motion Blur", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/es/settings.json b/src/i18n/locales/es/settings.json index d3545d59..d3c3f9fd 100644 --- a/src/i18n/locales/es/settings.json +++ b/src/i18n/locales/es/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "Desenfoque de fondo", + "backgroundBlur": "Desenfoque", "zoomMotionBlur": "Desenfoque de movimiento del zoom", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index 03d17ba6..45c223b8 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Tortue" }, - "backgroundBlur": "Flou d’arrière-plan", + "backgroundBlur": "Flou", "zoomMotionBlur": "Flou de mouvement du zoom", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/it/settings.json b/src/i18n/locales/it/settings.json index 7158b0da..3410299e 100644 --- a/src/i18n/locales/it/settings.json +++ b/src/i18n/locales/it/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Tartaruga" }, - "backgroundBlur": "Sfocatura sfondo", + "backgroundBlur": "Sfocatura", "zoomMotionBlur": "Motion blur dello zoom", "temporalZoomMotionBlur": "Sfocatura zoom temporale", "temporalZoomMotionBlurDescription": "Controlla la finestra dell'otturatore e i campioni di frame usati dal nuovo passaggio di sfocatura zoom.", diff --git a/src/i18n/locales/ko/settings.json b/src/i18n/locales/ko/settings.json index e33828da..17c7d6b1 100644 --- a/src/i18n/locales/ko/settings.json +++ b/src/i18n/locales/ko/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "배경 블러", + "backgroundBlur": "블러", "zoomMotionBlur": "확대 모션 블러", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/nl/settings.json b/src/i18n/locales/nl/settings.json index 8d7269c7..11762069 100644 --- a/src/i18n/locales/nl/settings.json +++ b/src/i18n/locales/nl/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Schildpad" }, - "backgroundBlur": "Achtergrondvervaging", + "backgroundBlur": "Vervaging", "zoomMotionBlur": "Zoom-bewegingsonscherpte", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/pt-BR/settings.json b/src/i18n/locales/pt-BR/settings.json index 6cea132e..e295b27f 100644 --- a/src/i18n/locales/pt-BR/settings.json +++ b/src/i18n/locales/pt-BR/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "Desfoque de fundo", + "backgroundBlur": "Desfoque", "zoomMotionBlur": "Desfoque de movimento do zoom", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index 15197cab..dd347bf8 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Черепаха" }, - "backgroundBlur": "Размытие фона", + "backgroundBlur": "Размытие", "zoomMotionBlur": "Размытие при зуме", "temporalZoomMotionBlur": "Временное размытие зума", "temporalZoomMotionBlurDescription": "Настройка плавности зума по времени и кадрам.", diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index 424324e9..ce740260 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "背景模糊", + "backgroundBlur": "模糊", "zoomMotionBlur": "缩放运动模糊", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index 47fbbc38..263e054d 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -43,7 +43,7 @@ "amongus": "Among Us", "turtle": "Turtle" }, - "backgroundBlur": "背景模糊", + "backgroundBlur": "模糊", "zoomMotionBlur": "縮放動態模糊", "temporalZoomMotionBlur": "Temporal Zoom Blur", "temporalZoomMotionBlurDescription": "Control the shutter window and frame samples used by the newer zoom blur pass.", diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts index 018bd176..9891a518 100644 --- a/src/lib/exporter/frameRenderer.ts +++ b/src/lib/exporter/frameRenderer.ts @@ -2225,8 +2225,10 @@ export class FrameRenderer { if (this.config.backgroundBlur > 0) { ctx.save(); - ctx.filter = `blur(${this.config.backgroundBlur * 3}px)`; - ctx.drawImage(bgCanvas, 0, 0, w, h); + const blurPx = this.config.backgroundBlur * 3; + const overscan = Math.ceil(blurPx * 2); + ctx.filter = `blur(${blurPx}px)`; + ctx.drawImage(bgCanvas, -overscan, -overscan, w + overscan * 2, h + overscan * 2); ctx.restore(); } else { ctx.drawImage(bgCanvas, 0, 0, w, h); diff --git a/src/lib/exporter/modernFrameRenderer.ts b/src/lib/exporter/modernFrameRenderer.ts index 33aad0dd..325bb28f 100644 --- a/src/lib/exporter/modernFrameRenderer.ts +++ b/src/lib/exporter/modernFrameRenderer.ts @@ -1380,6 +1380,7 @@ export class FrameRenderer { this.backgroundBlurFilter.blur = this.config.backgroundBlur * 3; this.backgroundBlurFilter.quality = 4; this.backgroundBlurFilter.resolution = this.app?.renderer.resolution ?? 1; + this.backgroundBlurFilter.repeatEdgePixels = true; this.backgroundSprite.filters = [this.backgroundBlurFilter]; } } else if (this.backgroundTextureSource) { @@ -1410,8 +1411,16 @@ export class FrameRenderer { } blurredCtx.save(); - blurredCtx.filter = `blur(${this.config.backgroundBlur * 3}px)`; - blurredCtx.drawImage(sourceCanvas, 0, 0, blurredCanvas.width, blurredCanvas.height); + const blurPx = this.config.backgroundBlur * 3; + const overscan = Math.ceil(blurPx * 2); + blurredCtx.filter = `blur(${blurPx}px)`; + blurredCtx.drawImage( + sourceCanvas, + -overscan, + -overscan, + blurredCanvas.width + overscan * 2, + blurredCanvas.height + overscan * 2, + ); blurredCtx.restore(); return blurredCanvas;