From 42735d13216d9f10ff885741984d0d6bcf5e1990 Mon Sep 17 00:00:00 2001 From: coldays <9074700+coldays@users.noreply.github.com> Date: Fri, 15 Aug 2025 11:05:09 +0200 Subject: [PATCH 1/5] QoL: Reset zoom smaller than 1 and don't let user pan out of more than half of the video frame --- .../MediaRenderer/Renderer.SwapChain.cs | 32 +++++++++++++++++++ .../MediaFramework/MediaRenderer/Renderer.cs | 20 ++++++++++++ 2 files changed, 52 insertions(+) diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs index e5115cf4..e88b47b4 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs @@ -324,6 +324,38 @@ public void SetViewport(bool refresh = true) yZoomPixels = newHeight - (ControlHeight - SideYPixels); } + // Don't let the user pan or zoom outside of half the video's height + int newY = (int)(y - yZoomPixels * (float)zoomCenter.Y); + int bottom = newHeight + newY; + if (newY > ControlHeight / 2d) + { + y = (int)((ControlHeight / 2d) + yZoomPixels * (float)zoomCenter.Y); + panYOffset = y - SideYPixels / 2; + } + else if (bottom < ControlHeight / 2d) + { + int newBottom = (int)(ControlHeight / 2d); + int newNewY = newBottom - newHeight; + y = (int)(newNewY + yZoomPixels * (float)zoomCenter.Y); + panYOffset = y - SideYPixels / 2; + } + + // Don't let the user pan or zoom outside of half the video's width + int newX = (int)(x - xZoomPixels * (float)zoomCenter.X); + int right = newWidth + newX; + if (newX > ControlWidth / 2d) + { + x = (int)((ControlWidth / 2d) + xZoomPixels * (float)zoomCenter.X); + panXOffset = x - SideXPixels / 2; + } + else if (right < ControlWidth / 2d) + { + int newRight = (int)(ControlWidth / 2d); + int newNewX = newRight - newWidth; + x = (int)(newNewX + xZoomPixels * (float)zoomCenter.X); + panXOffset = x - SideXPixels / 2; + } + GetViewport = new(x - xZoomPixels * (float)zoomCenter.X, y - yZoomPixels * (float)zoomCenter.Y, newWidth, newHeight); ViewportChanged?.Invoke(this, new()); diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs index 6a173bdd..d815ded0 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs @@ -121,6 +121,12 @@ public void SetZoom(double zoom, bool refresh = true) { lock(lockDevice) { + if (zoom < 1) + { + ResetPanAndZoom(); + return; + } + this.zoom = zoom; if (Disposed) @@ -150,6 +156,12 @@ public void SetZoomAndCenter(double zoom, Point p, bool refresh = true) { lock(lockDevice) { + if (zoom < 1) + { + ResetPanAndZoom(); + return; + } + this.zoom = zoom; zoomCenter = p; @@ -178,6 +190,14 @@ public void SetPanAll(int panX, int panY, uint rotation, double zoom, Point p, b } } + private void ResetPanAndZoom() + { + panXOffset = panYOffset = 0; + zoom = 1; + zoomCenter = ZoomCenterPoint; + SetViewport(); + } + public int UniqueId { get; private set; } public Dictionary From 667a025ee1b26dc4a339d0cb8e093b2aae28d9c3 Mon Sep 17 00:00:00 2001 From: coldays <9074700+coldays@users.noreply.github.com> Date: Fri, 15 Aug 2025 12:00:14 +0200 Subject: [PATCH 2/5] Guard against disposed in ResetPanAndZoom --- FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs index d815ded0..10787000 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs @@ -195,6 +195,10 @@ private void ResetPanAndZoom() panXOffset = panYOffset = 0; zoom = 1; zoomCenter = ZoomCenterPoint; + + if (Disposed) + return; + SetViewport(); } From 9d50bb34857bc43f68097f53b6203f6eb2146f81 Mon Sep 17 00:00:00 2001 From: coldays <9074700+coldays@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:30:24 +0200 Subject: [PATCH 3/5] Prevent EventArgs allocation every viewport change --- FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs index e88b47b4..e0c9b010 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs @@ -357,7 +357,7 @@ public void SetViewport(bool refresh = true) } GetViewport = new(x - xZoomPixels * (float)zoomCenter.X, y - yZoomPixels * (float)zoomCenter.Y, newWidth, newHeight); - ViewportChanged?.Invoke(this, new()); + ViewportChanged?.Invoke(this, EventArgs.Empty); if (videoProcessor == VideoProcessors.D3D11) { From bb2988ce6454182bf1bda3d481b122ebf64eb19c Mon Sep 17 00:00:00 2001 From: coldays <9074700+coldays@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:15:44 +0200 Subject: [PATCH 4/5] Introduce config for RestrictPanning and ResetPanOnZoomOut --- FlyleafLib/Engine/Config.cs | 10 ++++ .../MediaRenderer/Renderer.SwapChain.cs | 59 ++++++++++--------- .../MediaFramework/MediaRenderer/Renderer.cs | 23 +++++--- 3 files changed, 56 insertions(+), 36 deletions(-) diff --git a/FlyleafLib/Engine/Config.cs b/FlyleafLib/Engine/Config.cs index fa1ac333..fe548451 100644 --- a/FlyleafLib/Engine/Config.cs +++ b/FlyleafLib/Engine/Config.cs @@ -300,6 +300,16 @@ public ThreadPriority /// public Usage Usage { get; set; } = Usage.AVS; + /// + /// Restrict panning of the video outside of half the video width or height + /// + public bool RestrictPanning { get; set; } = false; + + /// + /// Reset pan and zoom level when zooming out to less than 100% + /// + public bool ResetPanOnZoomedOut { get; set; } = false; + // Offsets public long AudioDelayOffset { get; set; } = 100 * 10000; public long AudioDelayOffset2 { get; set; } = 1000 * 10000; diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs index 5aab4d28..b960a0ee 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.SwapChain.cs @@ -325,36 +325,39 @@ public void SetViewport(bool refresh = true) yZoomPixels = newHeight - (ControlHeight - SideYPixels); } - // Don't let the user pan or zoom outside of half the video's height - int newY = (int)(y - yZoomPixels * (float)zoomCenter.Y); - int bottom = newHeight + newY; - if (newY > ControlHeight / 2d) + if (Config.Player.RestrictPanning) { - y = (int)((ControlHeight / 2d) + yZoomPixels * (float)zoomCenter.Y); - panYOffset = y - SideYPixels / 2; - } - else if (bottom < ControlHeight / 2d) - { - int newBottom = (int)(ControlHeight / 2d); - int newNewY = newBottom - newHeight; - y = (int)(newNewY + yZoomPixels * (float)zoomCenter.Y); - panYOffset = y - SideYPixels / 2; - } + // Don't let the user pan or zoom outside of half the video's height + int newY = (int)(y - yZoomPixels * (float)zoomCenter.Y); + int bottom = newHeight + newY; + if (newY > ControlHeight / 2d) + { + y = (int)((ControlHeight / 2d) + yZoomPixels * (float)zoomCenter.Y); + panYOffset = y - SideYPixels / 2; + } + else if (bottom < ControlHeight / 2d) + { + int newBottom = (int)(ControlHeight / 2d); + int newNewY = newBottom - newHeight; + y = (int)(newNewY + yZoomPixels * (float)zoomCenter.Y); + panYOffset = y - SideYPixels / 2; + } - // Don't let the user pan or zoom outside of half the video's width - int newX = (int)(x - xZoomPixels * (float)zoomCenter.X); - int right = newWidth + newX; - if (newX > ControlWidth / 2d) - { - x = (int)((ControlWidth / 2d) + xZoomPixels * (float)zoomCenter.X); - panXOffset = x - SideXPixels / 2; - } - else if (right < ControlWidth / 2d) - { - int newRight = (int)(ControlWidth / 2d); - int newNewX = newRight - newWidth; - x = (int)(newNewX + xZoomPixels * (float)zoomCenter.X); - panXOffset = x - SideXPixels / 2; + // Don't let the user pan or zoom outside of half the video's width + int newX = (int)(x - xZoomPixels * (float)zoomCenter.X); + int right = newWidth + newX; + if (newX > ControlWidth / 2d) + { + x = (int)((ControlWidth / 2d) + xZoomPixels * (float)zoomCenter.X); + panXOffset = x - SideXPixels / 2; + } + else if (right < ControlWidth / 2d) + { + int newRight = (int)(ControlWidth / 2d); + int newNewX = newRight - newWidth; + x = (int)(newNewX + xZoomPixels * (float)zoomCenter.X); + panXOffset = x - SideXPixels / 2; + } } GetViewport = new(x - xZoomPixels * (float)zoomCenter.X, y - yZoomPixels * (float)zoomCenter.Y, newWidth, newHeight); diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs index 19c348e3..41ae7f10 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.cs @@ -122,10 +122,13 @@ public void SetZoom(double zoom, bool refresh = true) { lock(lockDevice) { - if (zoom < 1) + if (Config.Player.ResetPanOnZoomedOut) { - ResetPanAndZoom(); - return; + if (zoom < 1) + { + ResetPanAndZoom(refresh); + return; + } } this.zoom = zoom; @@ -157,10 +160,13 @@ public void SetZoomAndCenter(double zoom, Point p, bool refresh = true) { lock(lockDevice) { - if (zoom < 1) + if (Config.Player.ResetPanOnZoomedOut) { - ResetPanAndZoom(); - return; + if (zoom < 1) + { + ResetPanAndZoom(refresh); + return; + } } this.zoom = zoom; @@ -191,7 +197,7 @@ public void SetPanAll(int panX, int panY, uint rotation, double zoom, Point p, b } } - private void ResetPanAndZoom() + private void ResetPanAndZoom(bool refresh = true) { panXOffset = panYOffset = 0; zoom = 1; @@ -200,7 +206,8 @@ private void ResetPanAndZoom() if (Disposed) return; - SetViewport(); + if (refresh) + SetViewport(); } public int UniqueId { get; private set; } From 19085a3ccf715735aec26f2a092a642d58951333 Mon Sep 17 00:00:00 2001 From: coldays Date: Fri, 16 Jan 2026 13:40:52 +0100 Subject: [PATCH 5/5] Fix after merge from master --- .../MediaRenderer/Renderer.VP.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VP.cs b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VP.cs index fa4fba4b..2cfdaaf6 100644 --- a/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VP.cs +++ b/FlyleafLib/MediaFramework/MediaRenderer/Renderer.VP.cs @@ -317,6 +317,41 @@ void SetViewport(int width, int height) yZoomPixels = newHeight - (height - sideYPixels); } + if (ucfg.RestrictPanning) + { + // Don't let the user pan or zoom outside of half the video's height + int newY = (int)(y - yZoomPixels * ucfg.zoomCenter.Y); + int bottom = newHeight + newY; + if (newY > height / 2d) + { + y = (int)((height / 2d) + yZoomPixels * ucfg.zoomCenter.Y); + ucfg.panYOffset = (y - SideYPixels / 2) / (double)height; + } + else if (bottom < height / 2d) + { + int newBottom = (int)(height / 2d); + int newNewY = newBottom - newHeight; + y = (int)(newNewY + yZoomPixels * ucfg.zoomCenter.Y); + ucfg.panYOffset = (y - SideYPixels / 2) / (double)height; + } + + // Don't let the user pan or zoom outside of half the video's width + int newX = (int)(x - xZoomPixels * ucfg.zoomCenter.X); + int right = newWidth + newX; + if (newX > width / 2d) + { + x = (int)((width / 2d) + xZoomPixels * ucfg.zoomCenter.X); + ucfg.panXOffset = (x - SideXPixels / 2) / (double)width; + } + else if (right < width / 2d) + { + int newRight = (int)(width / 2d); + int newNewX = newRight - newWidth; + x = (int)(newNewX + xZoomPixels * (float)ucfg.zoomCenter.X); + ucfg.panXOffset = (x - SideXPixels / 2) / (double)width; + } + } + Viewport = new((int)(x - xZoomPixels * (float)ucfg.zoomCenter.X), (int)(y - yZoomPixels * (float)ucfg.zoomCenter.Y), newWidth, newHeight); ViewportChanged?.Invoke(this, new()); } @@ -503,6 +538,16 @@ public WPoint public int ZoomOffset { get => zoomOffset; set { Set(ref zoomOffset, value); } } int zoomOffset = 10; + /// + /// Restrict panning of the video outside of half the video width or height + /// + public bool RestrictPanning { get; set; } = true; + + /// + /// Reset pan and zoom level when zooming out to less than 100% + /// + public bool ResetPanOnZoomedOut { get; set; } = true; + public void ResetViewport() => SetViewport(0, 0, 0, 100, new(0.5, 0.5), CropRect.Empty, AspectRatio.Keep, false, false); @@ -551,6 +596,12 @@ internal void SetZoomWithCenterPoint(Point p, double zoom) */ zoom = SnapToInt(zoom); + if (ResetPanOnZoomedOut && zoom <= 1.0) + { + zoom = 1.0; + panXOffset = 0; + panYOffset = 0; + } Viewport view = vp.Viewport; if (!(p.X >= view.X && p.X < view.X + view.Width && p.Y >= view.Y && p.Y < view.Y + view.Height)) // Point out of view