From 9af7575d2bd79593983431b493b63c08d41e7e20 Mon Sep 17 00:00:00 2001 From: Takaranoao Date: Thu, 3 Apr 2025 21:28:13 +0800 Subject: [PATCH] =?UTF-8?q?bitblt=20=E8=BF=98=E8=83=BD=E6=9B=B4=E5=BF=AB?= =?UTF-8?q?=20(#1395)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bitblt:GDI直接返回转换后的位图,不再由CV二次转换 * update documentation for clarity * typo --- Fischless.GameCapture/BitBlt/BitBltCapture.cs | 47 ++++++++++++++----- Fischless.GameCapture/BitBlt/BitBltSession.cs | 32 ++++++++++--- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Fischless.GameCapture/BitBlt/BitBltCapture.cs b/Fischless.GameCapture/BitBlt/BitBltCapture.cs index bf8eff15..85992d13 100644 --- a/Fischless.GameCapture/BitBlt/BitBltCapture.cs +++ b/Fischless.GameCapture/BitBlt/BitBltCapture.cs @@ -92,7 +92,7 @@ public class BitBltCapture : IGameCapture } catch (Exception e) { - Error.WriteLine("Failed to create bitblt session:{0}", e); + Error.WriteLine("[BitBlt]Failed to create session:{0}", e); return false; } finally @@ -123,27 +123,48 @@ public class BitBltCapture : IGameCapture } } + Mat? mat = null; try { _lockSlim.EnterReadLock(); - using var mat = _session?.BitBlt(); - if (mat is null || mat.Empty()) + if (_session is null) { - if (_session is not null && !_session.IsInvalid()) - { - // 有时候会出现截图失败的情况,这时候重置会话 - _needResetSession = true; - } - + // 没有成功创建会话,直接返回空 return null; } + mat = _session.GetMat(); + + if (mat is not null) // 成功执行 + { + if (!mat.Empty()) // 并且获取到了图 + { + _needResetSession = false; + return mat; + } + else // 成功执行但是没有图,可能是截图过快导致的 + { + mat.Dispose(); + mat = null; // 防止二次释放 + } + } - var bgrMat = new Mat(); - Cv2.CvtColor(mat, bgrMat, ColorConversionCodes.BGRA2BGR); - return bgrMat; + // 无论没有图还是执行失败都会到这里 + if (_session is not null && !_session.IsInvalid()) + { + // _session正确但是截图失败,如果持续出现这个问题需要重置会话 + _needResetSession = true; + } + + return null; } - catch (Exception) + catch (Exception e) { + // 理论这里不应出现异常,除非窗口不存在了或者有什么bug + // 出现异常的时候释放内存 + mat?.Dispose(); + // 如果持续出现异常则重置会话 + _needResetSession = true; + Error.WriteLine("[BitBlt]Failed to capture image {0}", e); return null; } finally diff --git a/Fischless.GameCapture/BitBlt/BitBltSession.cs b/Fischless.GameCapture/BitBlt/BitBltSession.cs index 6d102f71..ff3efee7 100644 --- a/Fischless.GameCapture/BitBlt/BitBltSession.cs +++ b/Fischless.GameCapture/BitBlt/BitBltSession.cs @@ -25,8 +25,18 @@ public class BitBltSession : IDisposable // 旧位图,析构时一起释放掉 private HGDIOBJ _oldBitmap; public int Width { get; } + public int Height { get; } + // 用于过滤alpha通道 + private static readonly Gdi32.BLENDFUNCTION BlendFunction = new() + { + BlendOp = 0, //Gdi32.BlendOperation.AC_SRC_OVER, + BlendFlags = 0, + SourceConstantAlpha = 255, + AlphaFormat = 0, + }; + private readonly object _lockObject = new object(); @@ -71,9 +81,9 @@ public class BitBltSession : IDisposable biWidth = Width, biHeight = -Height, // Top-down image biPlanes = 1, - biBitCount = 32, + biBitCount = 24, biCompression = 0, // BI_RGB - biSizeImage = 0 + biSizeImage = 0, } }; _hBitmap = Gdi32.CreateDIBSection(_hdcDest, bmi, Gdi32.DIBColorMode.DIB_RGB_COLORS, out var bits, @@ -105,17 +115,27 @@ public class BitBltSession : IDisposable } /// - /// 调用bitblt复制并返回新的mat + /// 调用GDI复制到缓冲区并返回新的mat /// - /// - public Mat? BitBlt() + /// MatType.CV_8UC3 + public Mat? GetMat() { lock (_lockObject) { - return Gdi32.BitBlt(_hdcDest, 0, 0, Width, Height, _hdcSrc, 0, 0, Gdi32.RasterOperationMode.SRCCOPY) ? Mat.FromPixelData(Height, Width, MatType.CV_8UC4, _bitsPtr) : null; + // 直接返回转换后的位图 + return Gdi32.AlphaBlend(_hdcDest, 0, 0, Width, Height, _hdcSrc, 0, 0, Width, Height, BlendFunction) + ? Mat.FromPixelData(Height, Width, MatType.CV_8UC3, _bitsPtr, (Width * 3 + 3) & ~3) + : null; + + // 原始宏 ((((biWidth * biBitCount) + 31) & ~31) >> 3) => (biWidth * biBitCount + 3) & ~3) (在总位数是8的倍数时,两者等价) + // 对齐 https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader + + // 适用于32位(RGBA) + // return Gdi32.BitBlt(_hdcDest, 0, 0, Width, Height, _hdcSrc, 0, 0, Gdi32.RasterOperationMode.SRCCOPY ) ? Mat.FromPixelData(Height, Width, MatType.CV_8UC3, _bitsPtr) : null; } } + /// /// 不是所有的失效情况都能被检测到 ///