diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj
index efae8eab..375c884b 100644
--- a/BetterGenshinImpact/BetterGenshinImpact.csproj
+++ b/BetterGenshinImpact/BetterGenshinImpact.csproj
@@ -153,6 +153,7 @@
+
diff --git a/BetterGenshinImpact/Core/Config/AllConfig.cs b/BetterGenshinImpact/Core/Config/AllConfig.cs
index 2fdfb32d..06b1639a 100644
--- a/BetterGenshinImpact/Core/Config/AllConfig.cs
+++ b/BetterGenshinImpact/Core/Config/AllConfig.cs
@@ -51,13 +51,13 @@ public partial class AllConfig : ObservableObject
[ObservableProperty]
private int _triggerInterval = 50;
- ///
- /// WGC使用位图缓存
- /// 高帧率情况下,可能会导致卡顿
- /// 云原神可能会出现黑屏
- ///
- [ObservableProperty]
- private bool _wgcUseBitmapCache = true;
+ // ///
+ // /// WGC使用位图缓存
+ // /// 高帧率情况下,可能会导致卡顿
+ // /// 云原神可能会出现黑屏
+ // ///
+ // [ObservableProperty]
+ // private bool _wgcUseBitmapCache = true;
///
/// 自动修复Win11下BitBlt截图方式不可用的问题
diff --git a/BetterGenshinImpact/Core/Recorder/DirectInputCalibration.cs b/BetterGenshinImpact/Core/Recorder/DirectInputCalibration.cs
index 91599fd9..ad3977b3 100644
--- a/BetterGenshinImpact/Core/Recorder/DirectInputCalibration.cs
+++ b/BetterGenshinImpact/Core/Recorder/DirectInputCalibration.cs
@@ -8,7 +8,7 @@
// using BetterGenshinImpact.GameTask.Common.Element.Assets;
// using BetterGenshinImpact.GameTask.Common.Map;
// using BetterGenshinImpact.GameTask.Model.Area;
-// using BetterGenshinImpact.GameTask.Model.Enum;
+//
// using BetterGenshinImpact.View.Drawable;
// using BetterGenshinImpact.ViewModel.Pages;
// using Microsoft.Extensions.Logging;
diff --git a/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs b/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs
index 9e87f34f..4d19c4a3 100644
--- a/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs
+++ b/BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs
@@ -4,10 +4,8 @@ using BetterGenshinImpact.ViewModel.Pages;
using System;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.AutoDomain;
-using BetterGenshinImpact.GameTask.AutoFight;
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.AutoWood;
-using BetterGenshinImpact.GameTask.Model.Enum;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation;
using BetterGenshinImpact.GameTask.AutoPathing.Handler;
diff --git a/BetterGenshinImpact/GameTask/AutoFight/OneKeyFightTask.cs b/BetterGenshinImpact/GameTask/AutoFight/OneKeyFightTask.cs
index a8143010..5f5dfc0a 100644
--- a/BetterGenshinImpact/GameTask/AutoFight/OneKeyFightTask.cs
+++ b/BetterGenshinImpact/GameTask/AutoFight/OneKeyFightTask.cs
@@ -1,7 +1,7 @@
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask.AutoFight.Model;
using BetterGenshinImpact.GameTask.AutoFight.Script;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service;
using Microsoft.Extensions.Logging;
@@ -128,14 +128,6 @@ public class OneKeyFightTask : Singleton
///
private Task FightTask(CancellationToken ct)
{
- // 切换截图模式
- var dispatcherCaptureMode = TaskTriggerDispatcher.Instance().GetCacheCaptureMode();
- if (dispatcherCaptureMode != DispatcherCaptureModeEnum.CacheCaptureWithTrigger)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger);
- Sleep(TaskContext.Instance().Config.TriggerInterval * 2, ct); // 等待缓存图像
- }
-
var imageRegion = CaptureToRectArea();
var combatScenes = new CombatScenes().InitializeTeam(imageRegion);
if (!combatScenes.CheckTeamInitialized())
diff --git a/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTask.cs b/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTask.cs
index 6adce465..b9866a89 100644
--- a/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTask.cs
+++ b/BetterGenshinImpact/GameTask/AutoFishing/AutoFishingTask.cs
@@ -121,7 +121,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
break;
}
- using var bitmap = TaskControl.CaptureGameBitmapNoRetry(TaskTriggerDispatcher.Instance().GameCapture);
+ using var bitmap = TaskControl.CaptureGameImageNoRetry(TaskTriggerDispatcher.Instance().GameCapture);
if (bitmap == null)
{
_logger.LogWarning("截图失败");
diff --git a/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs b/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs
index 675e2401..5d656b18 100644
--- a/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs
+++ b/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs
@@ -463,7 +463,7 @@ public class PathExecutor
}
}
- Logger.LogError("此路径存在 {action} 收集动作,队伍中没有对应元素角色:{},无法执行此路径!", action, string.Join(",", ElementalCollectAvatarConfigs.GetAvatarNameList(el)));
+ Logger.LogError("此路径存在 {El}元素采集 动作,队伍中没有对应元素角色:{Names},无法执行此路径!", el.ToChinese(), string.Join(",", ElementalCollectAvatarConfigs.GetAvatarNameList(el)));
return false;
}
else
diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs
index bf29b95a..1687b30a 100644
--- a/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs
+++ b/BetterGenshinImpact/GameTask/AutoTrackPath/AutoTrackPathTask.cs
@@ -9,7 +9,7 @@ using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Map;
using BetterGenshinImpact.GameTask.Model.Area;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using BetterGenshinImpact.Service;
@@ -87,7 +87,6 @@ public class AutoTrackPathTask
finally
{
VisionContext.Instance().DrawContent.ClearAll();
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.NormalTrigger);
Logger.LogInformation("→ {Text}", "自动路线结束");
if (hasLock)
@@ -101,8 +100,6 @@ public class AutoTrackPathTask
{
SystemControl.ActivateWindow();
Logger.LogInformation("→ {Text}", "自动路线,启动!");
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.OnlyCacheCapture);
- Sleep(TaskContext.Instance().Config.TriggerInterval * 5, _ct); // 等待缓存图像
}
public async Task DoTask()
diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/PathPointRecorder.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/PathPointRecorder.cs
index 0377058d..50cab4bd 100644
--- a/BetterGenshinImpact/GameTask/AutoTrackPath/PathPointRecorder.cs
+++ b/BetterGenshinImpact/GameTask/AutoTrackPath/PathPointRecorder.cs
@@ -4,7 +4,7 @@ using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.AutoTrackPath.Model;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Map;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Helpers.Extensions;
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service;
@@ -34,16 +34,12 @@ public class PathPointRecorder : Singleton
{
if (_recordTask == null)
{
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.OnlyCacheCapture);
-
_recordTaskCts = new CancellationTokenSource();
_recordTask = RecordTask(_recordTaskCts.Token);
_recordTask.Start();
}
else
{
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.NormalTrigger);
-
_recordTaskCts?.Cancel();
_recordTask = null;
}
diff --git a/BetterGenshinImpact/GameTask/CaptureContent.cs b/BetterGenshinImpact/GameTask/CaptureContent.cs
index c409ef03..c6da11f5 100644
--- a/BetterGenshinImpact/GameTask/CaptureContent.cs
+++ b/BetterGenshinImpact/GameTask/CaptureContent.cs
@@ -1,6 +1,7 @@
using BetterGenshinImpact.GameTask.Model.Area;
using System;
using System.Drawing;
+using OpenCvSharp;
namespace BetterGenshinImpact.GameTask;
@@ -11,7 +12,6 @@ namespace BetterGenshinImpact.GameTask;
public class CaptureContent : IDisposable
{
public static readonly int MaxFrameIndexSecond = 60;
- private Bitmap? SrcBitmap { get; }
public int FrameIndex { get; }
public double TimerInterval { get; }
@@ -19,14 +19,13 @@ public class CaptureContent : IDisposable
public ImageRegion CaptureRectArea { get; private set; }
- public CaptureContent(Bitmap srcBitmap, int frameIndex, double interval)
+ public CaptureContent(Mat image, int frameIndex, double interval)
{
- SrcBitmap = srcBitmap;
FrameIndex = frameIndex;
TimerInterval = interval;
var systemInfo = TaskContext.Instance().SystemInfo;
- var gameCaptureRegion = systemInfo.DesktopRectArea.Derive(srcBitmap, systemInfo.CaptureAreaRect.X, systemInfo.CaptureAreaRect.Y);
+ var gameCaptureRegion = systemInfo.DesktopRectArea.Derive(image, systemInfo.CaptureAreaRect.X, systemInfo.CaptureAreaRect.Y);
CaptureRectArea = gameCaptureRegion.DeriveTo1080P();
}
@@ -42,6 +41,5 @@ public class CaptureContent : IDisposable
public void Dispose()
{
CaptureRectArea.Dispose();
- SrcBitmap?.Dispose();
}
-}
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/GameTask/Common/TaskControl.cs b/BetterGenshinImpact/GameTask/Common/TaskControl.cs
index a819adc2..8ca354f2 100644
--- a/BetterGenshinImpact/GameTask/Common/TaskControl.cs
+++ b/BetterGenshinImpact/GameTask/Common/TaskControl.cs
@@ -7,6 +7,7 @@ using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.Model.Area;
using Fischless.GameCapture;
using Microsoft.Extensions.Logging;
+using OpenCvSharp;
using Vanara.PInvoke;
namespace BetterGenshinImpact.GameTask.Common;
@@ -16,8 +17,6 @@ public class TaskControl
public static ILogger Logger { get; } = App.GetLogger();
public static readonly SemaphoreSlim TaskSemaphore = new(1, 1);
-
-
public static void CheckAndSleep(int millisecondsTimeout)
@@ -177,88 +176,45 @@ public class TaskControl
}
}
- public static Bitmap CaptureGameBitmap(IGameCapture? gameCapture)
+ public static Mat CaptureGameImage(IGameCapture? gameCapture)
{
- var bitmap = gameCapture?.Capture();
- // wgc 缓冲区设置的2 所以至少截图3次
- if (gameCapture?.Mode == CaptureModes.WindowsGraphicsCapture)
- {
- for (int i = 0; i < 2; i++)
- {
- bitmap = gameCapture?.Capture();
- Sleep(50);
- }
- }
-
- if (bitmap == null)
+ var image = gameCapture?.Capture();
+ if (image == null)
{
Logger.LogWarning("截图失败!");
- // 重试5次
- for (var i = 0; i < 5; i++)
+ // 重试3次
+ for (var i = 0; i < 3; i++)
{
- bitmap = gameCapture?.Capture();
- if (bitmap != null)
+ image = gameCapture?.Capture();
+ if (image != null)
{
- return bitmap;
+ return image;
}
-
+
Sleep(30);
}
-
+
throw new Exception("尝试多次后,截图失败!");
}
else
{
- return bitmap;
+ return image;
}
}
- public static Bitmap? CaptureGameBitmapNoRetry(IGameCapture? gameCapture)
+ public static Mat? CaptureGameImageNoRetry(IGameCapture? gameCapture)
{
return gameCapture?.Capture();
}
- // private static CaptureContent CaptureToContent(IGameCapture? gameCapture)
- // {
- // var bitmap = CaptureGameBitmap(gameCapture);
- // return new CaptureContent(bitmap, 0, 0);
- // }
- //
- // public static CaptureContent CaptureToContent()
- // {
- // return CaptureToContent(TaskTriggerDispatcher.GlobalGameCapture);
- // }
-
- // public static ImageRegion CaptureToRectArea()
- // {
- // return CaptureToContent(TaskTriggerDispatcher.GlobalGameCapture).CaptureRectArea;
- // }
-
- // ///
- // /// 此方法 TaskDispatcher至少处于 DispatcherCaptureModeEnum.CacheCaptureWithTrigger 状态才能使用
- // ///
- // ///
- // [Obsolete]
- // public static CaptureContent GetContentFromDispatcher()
- // {
- // return TaskTriggerDispatcher.Instance().GetLastCaptureContent();
- // }
-
- // ///
- // /// 此方法 TaskDispatcher至少处于 DispatcherCaptureModeEnum.CacheCaptureWithTrigger 状态才能使用
- // ///
- // ///
- // public static ImageRegion GetRectAreaFromDispatcher()
- // {
- // return TaskTriggerDispatcher.Instance().GetLastCaptureContent().CaptureRectArea;
- // }
-
///
/// 自动判断当前运行上下文中截图方式,并选择合适的截图方式返回
///
///
public static ImageRegion CaptureToRectArea(bool forceNew = false)
{
- return TaskTriggerDispatcher.Instance().CaptureToRectArea(forceNew);
+ var image =CaptureGameImage(TaskTriggerDispatcher.GlobalGameCapture);
+ var content = new CaptureContent(image, 0, 0);
+ return content.CaptureRectArea;
}
}
diff --git a/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs b/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs
index 1c4607cc..1d2255cf 100644
--- a/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs
+++ b/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs
@@ -3,6 +3,7 @@ using BetterGenshinImpact.GameTask.Model.Area.Converter;
using BetterGenshinImpact.Helpers;
using Fischless.WindowsInput;
using System.Drawing;
+using OpenCvSharp;
namespace BetterGenshinImpact.GameTask.Model.Area;
@@ -67,8 +68,8 @@ public class DesktopRegion : Region
Simulation.SendInput.Mouse.MoveMouseBy((int)dx, (int)dy);
}
- public GameCaptureRegion Derive(Bitmap captureBitmap, int x, int y)
+ public GameCaptureRegion Derive(Mat captureMat, int x, int y)
{
- return new GameCaptureRegion(captureBitmap, x, y, this, new TranslationConverter(x, y));
+ return new GameCaptureRegion(captureMat, x, y, this, new TranslationConverter(x, y));
}
}
diff --git a/BetterGenshinImpact/GameTask/Model/Area/GameCaptureRegion.cs b/BetterGenshinImpact/GameTask/Model/Area/GameCaptureRegion.cs
index 4e497304..975b05df 100644
--- a/BetterGenshinImpact/GameTask/Model/Area/GameCaptureRegion.cs
+++ b/BetterGenshinImpact/GameTask/Model/Area/GameCaptureRegion.cs
@@ -11,7 +11,7 @@ namespace BetterGenshinImpact.GameTask.Model.Area;
/// 游戏捕获区域类
/// 主要用于转换到遮罩窗口的坐标
///
-public class GameCaptureRegion(Bitmap bitmap, int initX, int initY, Region? owner = null, INodeConverter? converter = null, DrawContent? drawContent = null) : ImageRegion(bitmap, initX, initY, owner, converter, drawContent)
+public class GameCaptureRegion(Mat mat, int initX, int initY, Region? owner = null, INodeConverter? converter = null, DrawContent? drawContent = null) : ImageRegion(mat, initX, initY, owner, converter, drawContent)
{
///
/// 在游戏捕获图像的坐标维度进行转换到遮罩窗口的坐标维度
diff --git a/BetterGenshinImpact/GameTask/Model/Area/ImageRegion.cs b/BetterGenshinImpact/GameTask/Model/Area/ImageRegion.cs
index ffdd0c2a..47f57d98 100644
--- a/BetterGenshinImpact/GameTask/Model/Area/ImageRegion.cs
+++ b/BetterGenshinImpact/GameTask/Model/Area/ImageRegion.cs
@@ -76,7 +76,7 @@ public class ImageRegion : Region
_srcBitmap = bitmap;
}
- public ImageRegion(Mat mat, int x, int y, Region? owner = null, INodeConverter? converter = null) : base(x, y, mat.Width, mat.Height, owner, converter)
+ public ImageRegion(Mat mat, int x, int y, Region? owner = null, INodeConverter? converter = null, DrawContent? drawContent = null) : base(x, y, mat.Width, mat.Height, owner, converter)
{
_srcMat = mat;
}
diff --git a/BetterGenshinImpact/GameTask/Model/BaseTaskParam.cs b/BetterGenshinImpact/GameTask/Model/BaseTaskParam.cs
index 979b44b9..527f1a96 100644
--- a/BetterGenshinImpact/GameTask/Model/BaseTaskParam.cs
+++ b/BetterGenshinImpact/GameTask/Model/BaseTaskParam.cs
@@ -1,4 +1,4 @@
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using System.Threading;
namespace BetterGenshinImpact.GameTask.Model;
diff --git a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs b/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs
deleted file mode 100644
index ec64160d..00000000
--- a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherCaptureModeEnum.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace BetterGenshinImpact.GameTask.Model.Enum;
-
-///
-/// 调度器捕获模式, 影响以下内容:
-/// 1. 是否缓存图像
-/// 2. 是否执行触发器
-///
-public enum DispatcherCaptureModeEnum
-{
- // 正常运行调度器
- NormalTrigger,
-
- // 正常运行调度器,但不执行触发器,仅捕获并缓存图像模式
- OnlyCacheCapture,
-
- // 正常运行调度器,捕获并缓存图像模式,并执行触发器
- CacheCaptureWithTrigger,
-
- // --------------------------------------------
- // 下面两个模式无法直接设置,只能通过调度器的 StartTimer 和 StopTimer 方法来设置
-
- // 停止运行整个调度器
- Stop,
-
- // 启动整个调度器
- Start,
-
- // --------------------------------------------
-}
diff --git a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs b/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs
deleted file mode 100644
index 2a3033a9..00000000
--- a/BetterGenshinImpact/GameTask/Model/Enum/DispatcherTimerOperationEnum.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace BetterGenshinImpact.GameTask.Model.Enum;
-
-///
-/// 存在触发器运行的情况下,优先使用触发器的缓存图像
-/// 此枚举会影响调度器使用的 DispatcherCaptureModeEnum 模式
-///
-public enum DispatcherTimerOperationEnum
-{
- // 关闭实时触发器,自己主动获取图像
- UseSelfCaptureImage,
-
- // 使用实时触发器的缓存图模式,但是不执行触发器
- UseCacheImage,
-
- // 使用实时触发器的缓存图模式,并执行触发器
- UseCacheImageWithTrigger,
-
- // 使用实时触发器的缓存图模式,并清空当前已有触发器,执行触发器
- // 清空触发器是为了让js脚本手动添加触发器
- UseCacheImageWithTriggerEmpty,
-
- // 不做任何操作
- None
-}
diff --git a/BetterGenshinImpact/GameTask/TaskRunner.cs b/BetterGenshinImpact/GameTask/TaskRunner.cs
index 6662c1d7..caf29db0 100644
--- a/BetterGenshinImpact/GameTask/TaskRunner.cs
+++ b/BetterGenshinImpact/GameTask/TaskRunner.cs
@@ -1,6 +1,6 @@
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.View;
using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.Logging;
@@ -23,7 +23,7 @@ public class TaskRunner
{
private readonly ILogger _logger = App.GetLogger();
- private readonly DispatcherTimerOperationEnum _timerOperation = DispatcherTimerOperationEnum.None;
+ // private readonly DispatcherTimerOperationEnum _timerOperation = DispatcherTimerOperationEnum.None;
private readonly string _name = string.Empty;
@@ -31,11 +31,11 @@ public class TaskRunner
{
}
- public TaskRunner(DispatcherTimerOperationEnum timerOperation)
- {
- _timerOperation = timerOperation;
- }
-
+ // public TaskRunner(DispatcherTimerOperationEnum timerOperation)
+ // {
+ // _timerOperation = timerOperation;
+ // }
+
///
/// 加锁并独立运行任务
///
@@ -134,27 +134,6 @@ public class TaskRunner
var maskWindow = MaskWindow.Instance();
SystemControl.ActivateWindow();
maskWindow.Invoke(maskWindow.Show);
- if (_timerOperation == DispatcherTimerOperationEnum.UseSelfCaptureImage)
- {
- Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待日志窗口被激活
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.Stop);
- }
- else if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImage)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.OnlyCacheCapture);
- Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像
- }
- else if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImageWithTrigger)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger);
- Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像
- }
- else if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger);
- TaskTriggerDispatcher.Instance().ClearTriggers();
- Thread.Sleep(TaskContext.Instance().Config.TriggerInterval * 5); // 等待缓存图像
- }
}
public void End()
@@ -165,27 +144,6 @@ public class TaskRunner
}
VisionContext.Instance().DrawContent.ClearAll();
- if (_timerOperation == DispatcherTimerOperationEnum.UseSelfCaptureImage)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.Start);
- }
- else if (_timerOperation is DispatcherTimerOperationEnum.UseCacheImage or DispatcherTimerOperationEnum.UseCacheImageWithTrigger or DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty)
- {
- // 还原到原来的模式
- if (TaskContext.Instance().Config.CommonConfig.ScreenshotEnabled || TaskContext.Instance().Config.MacroConfig.CombatMacroEnabled)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger);
- }
- else
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.NormalTrigger);
- }
-
- if (_timerOperation == DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty)
- {
- TaskTriggerDispatcher.Instance().SetTriggers(GameTaskManager.LoadInitialTriggers());
- }
- }
}
}
diff --git a/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs b/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs
index f7559ea6..c3afca4f 100644
--- a/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs
+++ b/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs
@@ -1,31 +1,18 @@
using BetterGenshinImpact.Core.Config;
-using BetterGenshinImpact.GameTask.AutoDomain;
-using BetterGenshinImpact.GameTask.AutoFight;
-using BetterGenshinImpact.GameTask.AutoGeniusInvokation;
-using BetterGenshinImpact.GameTask.AutoMusicGame;
-using BetterGenshinImpact.GameTask.AutoSkip;
-using BetterGenshinImpact.GameTask.AutoSkip.Model;
-using BetterGenshinImpact.GameTask.AutoTrackPath;
-using BetterGenshinImpact.GameTask.AutoWood;
using BetterGenshinImpact.GameTask.Common;
-using BetterGenshinImpact.GameTask.Model;
-using BetterGenshinImpact.GameTask.Model.Area;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.View;
using Fischless.GameCapture;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
-using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Drawing;
-using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
-using System.Threading.Tasks;
+using Fischless.GameCapture.Graphics;
using Vanara.PInvoke;
namespace BetterGenshinImpact.GameTask
@@ -49,19 +36,6 @@ namespace BetterGenshinImpact.GameTask
private DateTime _prevManualGc = DateTime.MinValue;
- ///
- /// 捕获结果队列
- ///
- private Bitmap _bitmap = new(10, 10);
-
- ///
- /// 调度器捕获模式, 影响以下内容:
- /// 1. 是否缓存图像
- /// 2. 是否执行触发器
- ///
- private DispatcherCaptureModeEnum _dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.NormalTrigger;
-
- private static readonly object _bitmapLocker = new();
private static readonly object _triggerListLocker = new();
public event EventHandler? UiTaskStopTickEvent;
@@ -138,22 +112,20 @@ namespace BetterGenshinImpact.GameTask
// 初始化触发器(一定要在任务上下文初始化完毕后使用)
_triggers = GameTaskManager.LoadInitialTriggers();
+
+ if (GraphicsCapture.IsHdrEnabled(hWnd))
+ {
+ _logger.LogError("游戏窗口在HDR模式下无法获取正常颜色的截图,请关闭HDR模式!");
+ }
// 启动截图
GameCapture.Start(hWnd,
new Dictionary()
{
- { "useBitmapCache", TaskContext.Instance().Config.WgcUseBitmapCache },
{ "autoFixWin11BitBlt", OsVersionHelper.IsWindows11_OrGreater && TaskContext.Instance().Config.AutoFixWin11BitBlt }
}
);
-
- // 捕获模式初始化配置
- if (TaskContext.Instance().Config.CommonConfig.ScreenshotEnabled || TaskContext.Instance().Config.MacroConfig.CombatMacroEnabled)
- {
- _dispatcherCacheCaptureMode = DispatcherCaptureModeEnum.CacheCaptureWithTrigger;
- }
-
+
// 启动定时器
_frameIndex = 0;
_timer.Interval = interval;
@@ -187,11 +159,6 @@ namespace BetterGenshinImpact.GameTask
}
}
- public System.Timers.Timer GetTimer()
- {
- return _timer;
- }
-
public void Dispose()
{
Stop();
@@ -284,13 +251,13 @@ namespace BetterGenshinImpact.GameTask
{
// if (!_prevGameActive)
// {
- maskWindow.Invoke(() =>
+ maskWindow.Invoke(() =>
+ {
+ if (maskWindow.IsExist())
{
- if (maskWindow.IsExist())
- {
- maskWindow.Show();
- }
- });
+ maskWindow.Show();
+ }
+ });
// }
}
@@ -305,10 +272,9 @@ namespace BetterGenshinImpact.GameTask
// 帧序号自增 1分钟后归零(MaxFrameIndexSecond)
_frameIndex = (_frameIndex + 1) % (int)(CaptureContent.MaxFrameIndexSecond * 1000d / _timer.Interval);
- if (_dispatcherCacheCaptureMode == DispatcherCaptureModeEnum.NormalTrigger
- && (_triggers == null || !_triggers.Exists(t => t.IsEnabled)))
+ if (_triggers == null || !_triggers.Exists(t => t.IsEnabled))
{
- // Debug.WriteLine("没有可用的触发器且不处于仅截屏状态, 不再进行截屏");
+ Debug.WriteLine("没有可用的触发器且不处于仅截屏状态, 不再进行截屏");
return;
}
@@ -323,11 +289,6 @@ namespace BetterGenshinImpact.GameTask
return;
}
- if (IsOnlyCacheCapture(bitmap))
- {
- return;
- }
-
// 循环执行所有触发器 有独占状态的触发器的时候只执行独占触发器
var content = new CaptureContent(bitmap, _frameIndex, _timer.Interval);
@@ -336,7 +297,6 @@ namespace BetterGenshinImpact.GameTask
var exclusiveTrigger = _triggers!.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true });
if (exclusiveTrigger != null)
{
-
exclusiveTrigger.OnCapture(content);
speedTimer.Record(exclusiveTrigger.Name);
}
@@ -350,13 +310,12 @@ namespace BetterGenshinImpact.GameTask
foreach (var trigger in runningTriggers)
{
-
trigger.OnCapture(content);
speedTimer.Record(trigger.Name);
}
}
}
-
+
speedTimer.DebugPrint();
content.Dispose();
}
@@ -413,118 +372,38 @@ namespace BetterGenshinImpact.GameTask
return rect.Width == 0 || rect.Height == 0;
}
- ///
- /// 是否仅缓存截图
- ///
- ///
- ///
- private bool IsOnlyCacheCapture(Bitmap bitmap)
- {
- lock (_bitmapLocker)
- {
- if (_dispatcherCacheCaptureMode is DispatcherCaptureModeEnum.OnlyCacheCapture or DispatcherCaptureModeEnum.CacheCaptureWithTrigger)
- {
- _bitmap = new Bitmap(bitmap);
- if (_dispatcherCacheCaptureMode == DispatcherCaptureModeEnum.OnlyCacheCapture)
- {
- return true;
- }
- }
-
- return false;
- }
- }
-
- public void SetCacheCaptureMode(DispatcherCaptureModeEnum mode)
- {
- if (mode is DispatcherCaptureModeEnum.Start)
- {
- this.StartTimer();
- }
- else if (mode is DispatcherCaptureModeEnum.Stop)
- {
- this.StopTimer();
- }
- else
- {
- _dispatcherCacheCaptureMode = mode;
- }
- }
-
- public DispatcherCaptureModeEnum GetCacheCaptureMode()
- {
- return _dispatcherCacheCaptureMode;
- }
-
- public Bitmap GetLastCaptureBitmap()
- {
- lock (_bitmapLocker)
- {
- return new Bitmap(_bitmap);
- }
- }
-
- public CaptureContent GetLastCaptureContent()
- {
- var bitmap = GetLastCaptureBitmap();
- return new CaptureContent(bitmap, _frameIndex, _timer.Interval);
- }
-
- public ImageRegion CaptureToRectArea(bool forceNew = false)
- {
- // 触发器启动的情况下优先使用触发器的截图
- if (!forceNew && _timer.Enabled && _dispatcherCacheCaptureMode is DispatcherCaptureModeEnum.OnlyCacheCapture or DispatcherCaptureModeEnum.CacheCaptureWithTrigger)
- {
- return GetLastCaptureContent().CaptureRectArea;
- }
- else
- {
- var bitmap = TaskControl.CaptureGameBitmap(GameCapture);
- var content = new CaptureContent(bitmap, 0, 0);
- return content.CaptureRectArea;
- }
- }
-
public void TakeScreenshot()
{
- if (_dispatcherCacheCaptureMode is DispatcherCaptureModeEnum.OnlyCacheCapture or DispatcherCaptureModeEnum.CacheCaptureWithTrigger)
+ try
{
- try
+ var path = Global.Absolute($@"log\screenshot\");
+ if (!Directory.Exists(path))
{
- var path = Global.Absolute($@"log\screenshot\");
- if (!Directory.Exists(path))
- {
- Directory.CreateDirectory(path);
- }
-
- var bitmap = GetLastCaptureBitmap();
- var name = $@"{DateTime.Now:yyyyMMddHHmmssffff}.png";
- var savePath = Global.Absolute($@"log\screenshot\{name}");
-
- if (TaskContext.Instance().Config.CommonConfig.ScreenshotUidCoverEnabled)
- {
- var mat = bitmap.ToMat();
- var rect = TaskContext.Instance().Config.MaskWindowConfig.UidCoverRect;
- mat.Rectangle(rect, Scalar.White, -1);
- Cv2.ImWrite(savePath, mat);
- }
- else
- {
- bitmap.Save(savePath, ImageFormat.Png);
- }
-
- _logger.LogInformation("截图已保存: {Name}", name);
+ Directory.CreateDirectory(path);
}
- catch (Exception e)
+
+ var bitmap = TaskControl.CaptureGameImage(GameCapture);
+ var name = $@"{DateTime.Now:yyyyMMddHHmmssffff}.png";
+ var savePath = Global.Absolute($@"log\screenshot\{name}");
+
+ if (TaskContext.Instance().Config.CommonConfig.ScreenshotUidCoverEnabled)
{
- _logger.LogError("截图保存失败: {Message}", e.Message);
- _logger.LogDebug("截图保存失败: {StackTrace}", e.StackTrace);
+ var rect = TaskContext.Instance().Config.MaskWindowConfig.UidCoverRect;
+ bitmap.Rectangle(rect, Scalar.White, -1);
+ Cv2.ImWrite(savePath, bitmap);
}
+ else
+ {
+ Cv2.ImWrite(savePath, bitmap);
+ }
+
+ _logger.LogInformation("截图已保存: {Name}", name);
}
- else
+ catch (Exception e)
{
- _logger.LogWarning("当前不处于截图模式,无法保存截图");
+ _logger.LogError("截图保存失败: {Message}", e.Message);
+ _logger.LogDebug("截图保存失败: {StackTrace}", e.StackTrace);
}
}
}
-}
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/Service/Notification/NotificationService.cs b/BetterGenshinImpact/Service/Notification/NotificationService.cs
index 952087eb..d5329966 100644
--- a/BetterGenshinImpact/Service/Notification/NotificationService.cs
+++ b/BetterGenshinImpact/Service/Notification/NotificationService.cs
@@ -1,3 +1,8 @@
+using BetterGenshinImpact.Service.Notification.Model;
+using BetterGenshinImpact.Service.Notifier;
+using BetterGenshinImpact.Service.Notifier.Exception;
+using BetterGenshinImpact.Service.Notifier.Interface;
+using Microsoft.Extensions.Hosting;
using System;
using System.Drawing;
using System.Net.Http;
@@ -13,6 +18,8 @@ using BetterGenshinImpact.Service.Notifier.Exception;
using BetterGenshinImpact.Service.Notifier.Interface;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using System.Text.Json;
+using OpenCvSharp.Extensions;
namespace BetterGenshinImpact.Service.Notification;
@@ -170,10 +177,10 @@ public class NotificationService : IHostedService
{
if (TaskContext.Instance().Config.NotificationConfig.IncludeScreenShot)
{
- var bitmap = TaskControl.CaptureGameBitmapNoRetry(TaskTriggerDispatcher.GlobalGameCapture);
+ var bitmap = TaskControl.CaptureGameImageNoRetry(TaskTriggerDispatcher.GlobalGameCapture);
if (bitmap != null)
{
- notificationData.Screenshot = (Bitmap)bitmap.Clone();
+ notificationData.Screenshot = bitmap.ToBitmap();
}
}
}
diff --git a/BetterGenshinImpact/Service/ScriptService.cs b/BetterGenshinImpact/Service/ScriptService.cs
index e22a65cf..3d3915b7 100644
--- a/BetterGenshinImpact/Service/ScriptService.cs
+++ b/BetterGenshinImpact/Service/ScriptService.cs
@@ -2,7 +2,7 @@
using BetterGenshinImpact.Core.Script.Group;
using BetterGenshinImpact.Core.Script.Project;
using BetterGenshinImpact.GameTask;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Service.Interface;
using BetterGenshinImpact.ViewModel.Pages;
using Microsoft.Extensions.Logging;
@@ -23,6 +23,7 @@ namespace BetterGenshinImpact.Service;
public partial class ScriptService : IScriptService
{
private readonly ILogger _logger = App.GetLogger();
+
private static bool IsCurrentHourEqual(string input)
{
// 尝试将输入字符串转换为整数
@@ -41,52 +42,51 @@ public partial class ScriptService : IScriptService
// 如果输入非数字或不合法,返回 false
return false;
}
+
public async Task RunMulti(IEnumerable projectList, string? groupName = null)
{
groupName ??= "默认";
- var hasTimer = false;
- var list = ReloadScriptProjects(projectList, ref hasTimer);
+ var list = ReloadScriptProjects(projectList);
- // 针对JS 脚本,检查是否包含定时器操作
- var jsProjects = ExtractJsProjects(list);
- if (!hasTimer && jsProjects.Count > 0)
- {
- var codeList = await ReadCodeList(jsProjects);
- hasTimer = HasTimerOperation(codeList);
- }
+ // // 针对JS 脚本,检查是否包含定时器操作
+ // var jsProjects = ExtractJsProjects(list);
+ // if (!hasTimer && jsProjects.Count > 0)
+ // {
+ // var codeList = await ReadCodeList(jsProjects);
+ // hasTimer = HasTimerOperation(codeList);
+ // }
// 没启动时候,启动截图器
await StartGameTask();
if (!string.IsNullOrEmpty(groupName))
{
- if (hasTimer)
- {
- _logger.LogInformation("配置组 {Name} 包含实时任务操作调用", groupName);
- }
+ // if (hasTimer)
+ // {
+ // _logger.LogInformation("配置组 {Name} 包含实时任务操作调用", groupName);
+ // }
_logger.LogInformation("配置组 {Name} 加载完成,共{Cnt}个脚本,开始执行", groupName, list.Count);
}
- var timerOperation = hasTimer ? DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty : DispatcherTimerOperationEnum.UseSelfCaptureImage;
-
+ // var timerOperation = hasTimer ? DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty : DispatcherTimerOperationEnum.UseSelfCaptureImage;
+
Notify.Event(NotificationEvent.GroupStart).Success($"配置组{groupName}启动");
-
- await new TaskRunner(timerOperation)
+
+ await new TaskRunner()
.RunThreadAsync(async () =>
{
var stopwatch = new Stopwatch();
foreach (var project in list)
{
-
if (project.GroupInfo is { Config.PathingConfig.Enabled: true } && IsCurrentHourEqual(project.GroupInfo.Config.PathingConfig.SkipDuring))
{
_logger.LogInformation($"{project.Name}任务已到禁止执行时段,将跳过!");
continue;
}
-
+
if (project.Status != "Enabled")
{
_logger.LogInformation("脚本 {Name} 状态为禁用,跳过执行", project.Name);
@@ -103,24 +103,21 @@ public partial class ScriptService : IScriptService
{
try
{
- if (hasTimer)
- {
- TaskTriggerDispatcher.Instance().ClearTriggers();
- }
+ TaskTriggerDispatcher.Instance().ClearTriggers();
+
_logger.LogInformation("------------------------------");
stopwatch.Reset();
stopwatch.Start();
await ExecuteProject(project);
-
+
//多次执行时及时中断
if (project.GroupInfo is { Config.PathingConfig.Enabled: true } && IsCurrentHourEqual(project.GroupInfo.Config.PathingConfig.SkipDuring))
{
_logger.LogInformation($"{project.Name}任务已到禁止执行时段,将跳过!");
break;
}
-
}
catch (NormalEndException e)
{
@@ -144,7 +141,6 @@ public partial class ScriptService : IScriptService
_logger.LogInformation("→ 脚本执行结束: {Name}, 耗时: {Minutes}分{Seconds:0.000}秒", project.Name,
elapsedTime.Hours * 60 + elapsedTime.Minutes, elapsedTime.TotalSeconds % 60);
_logger.LogInformation("------------------------------");
-
}
await Task.Delay(2000);
@@ -152,14 +148,18 @@ public partial class ScriptService : IScriptService
}
});
+ // 还原定时器
+ TaskTriggerDispatcher.Instance().SetTriggers(GameTaskManager.LoadInitialTriggers());
+
if (!string.IsNullOrEmpty(groupName))
{
_logger.LogInformation("配置组 {Name} 执行结束", groupName);
}
+
Notify.Event(NotificationEvent.GroupEnd).Success($"配置组{groupName}结束");
}
- private List ReloadScriptProjects(IEnumerable projectList, ref bool hasTimer)
+ private List ReloadScriptProjects(IEnumerable projectList)
{
var list = new List();
foreach (var project in projectList)
@@ -181,14 +181,14 @@ public partial class ScriptService : IScriptService
var newProject = ScriptGroupProject.BuildPathingProject(project.Name, project.FolderName);
CopyProjectProperties(project, newProject);
list.Add(newProject);
- hasTimer = true;
+ // hasTimer = true;
}
else if (project.Type == "Shell")
{
var newProject = ScriptGroupProject.BuildShellProject(project.Name);
CopyProjectProperties(project, newProject);
list.Add(newProject);
- hasTimer = true;
+ // hasTimer = true;
}
}
@@ -204,23 +204,22 @@ public partial class ScriptService : IScriptService
target.GroupInfo = source.GroupInfo;
}
- private List ExtractJsProjects(List list)
- {
- var jsProjects = new List();
- foreach (var project in list)
- {
- if (project is { Type: "Javascript", Project: not null })
- {
- jsProjects.Add(project.Project);
- }
- }
-
- return jsProjects;
- }
+ // private List ExtractJsProjects(List list)
+ // {
+ // var jsProjects = new List();
+ // foreach (var project in list)
+ // {
+ // if (project is { Type: "Javascript", Project: not null })
+ // {
+ // jsProjects.Add(project.Project);
+ // }
+ // }
+ //
+ // return jsProjects;
+ // }
private async Task ExecuteProject(ScriptGroupProject project)
{
-
if (project.Type == "Javascript")
{
if (project.Project == null)
@@ -241,7 +240,8 @@ public partial class ScriptService : IScriptService
_logger.LogInformation("→ 开始执行地图追踪任务: {Name}", project.Name);
await project.Run();
}
- else if (project.Type == "Shell"){
+ else if (project.Type == "Shell")
+ {
_logger.LogInformation("→ 开始执行shell: {Name}", project.Name);
await project.Run();
}
diff --git a/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs b/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs
index 366563b5..5ab0fdde 100644
--- a/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs
+++ b/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs
@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using BetterGenshinImpact.Helpers;
+using OpenCvSharp.WpfExtensions;
using Wpf.Ui.Violeta.Controls;
namespace BetterGenshinImpact.View;
@@ -49,7 +50,6 @@ public partial class CaptureTestWindow : Window
_capture.Start(hWnd,
new Dictionary()
{
- { "useBitmapCache", TaskContext.Instance().Config.WgcUseBitmapCache },
{ "autoFixWin11BitBlt", OsVersionHelper.IsWindows11 && TaskContext.Instance().Config.AutoFixWin11BitBlt }
}
);
@@ -72,7 +72,7 @@ public partial class CaptureTestWindow : Window
_captureCount++;
sw.Reset();
sw.Start();
- DisplayCaptureResultImage.Source = bitmap.ToBitmapImage();
+ DisplayCaptureResultImage.Source = bitmap.ToBitmapSource();
sw.Stop();
Debug.WriteLine("转换耗时:" + sw.ElapsedMilliseconds);
_transferTime += sw.ElapsedMilliseconds;
diff --git a/BetterGenshinImpact/View/Pages/HomePage.xaml b/BetterGenshinImpact/View/Pages/HomePage.xaml
index 90053173..d2c156a7 100644
--- a/BetterGenshinImpact/View/Pages/HomePage.xaml
+++ b/BetterGenshinImpact/View/Pages/HomePage.xaml
@@ -176,7 +176,7 @@
Margin="0,0,36,0"
Text="{Binding Config.TriggerInterval, Mode=TwoWay}" />
-
+
diff --git a/BetterGenshinImpact/ViewModel/Pages/CommonSettingsPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/CommonSettingsPageViewModel.cs
index dba9eb06..17c84354 100644
--- a/BetterGenshinImpact/ViewModel/Pages/CommonSettingsPageViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/CommonSettingsPageViewModel.cs
@@ -1,7 +1,6 @@
using System.Collections.ObjectModel;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.GameTask;
-using BetterGenshinImpact.GameTask.Model.Enum;
using BetterGenshinImpact.Service.Interface;
using BetterGenshinImpact.View.Pages;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -157,13 +156,6 @@ public partial class CommonSettingsPageViewModel : ViewModel
[RelayCommand]
public void OnSwitchTakenScreenshotEnabled()
{
- if (Config.CommonConfig.ScreenshotEnabled)
- {
- if (TaskTriggerDispatcher.Instance().GetCacheCaptureMode() == DispatcherCaptureModeEnum.NormalTrigger)
- {
- TaskTriggerDispatcher.Instance().SetCacheCaptureMode(DispatcherCaptureModeEnum.CacheCaptureWithTrigger);
- }
- }
}
[RelayCommand]
diff --git a/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs
index 52206e70..393d5ca0 100644
--- a/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs
@@ -2,7 +2,6 @@
using BetterGenshinImpact.Core.Recorder;
using BetterGenshinImpact.Core.Script;
using BetterGenshinImpact.GameTask;
-using BetterGenshinImpact.GameTask.Model.Enum;
using BetterGenshinImpact.Model;
using BetterGenshinImpact.Service.Interface;
using BetterGenshinImpact.View.Windows;
@@ -125,7 +124,7 @@ public partial class KeyMouseRecordPageViewModel : ViewModel
{
var s = await File.ReadAllTextAsync(path);
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunThreadAsync(async () => await KeyMouseMacroPlayer.PlayMacro(s, CancellationContext.Instance.Cts.Token));
}
catch (Exception e)
diff --git a/BetterGenshinImpact/ViewModel/Pages/OneDragonFlowViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/OneDragonFlowViewModel.cs
index f4c01070..c0504042 100644
--- a/BetterGenshinImpact/ViewModel/Pages/OneDragonFlowViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/OneDragonFlowViewModel.cs
@@ -15,7 +15,7 @@ using BetterGenshinImpact.Core.Script.Group;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Job;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Service;
using BetterGenshinImpact.Service.Notification;
@@ -213,7 +213,7 @@ public partial class OneDragonFlowViewModel : ViewModel
// 没启动的时候先启动
await ScriptService.StartGameTask();
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunThreadAsync(async () =>
{
Notify.Event(NotificationEvent.DragonStart).Success("一条龙启动");
diff --git a/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs
index af7e7409..f91db6a4 100644
--- a/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs
@@ -23,7 +23,7 @@ using System.Threading.Tasks;
using Windows.System;
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
-using BetterGenshinImpact.GameTask.Model.Enum;
+
using BetterGenshinImpact.Helpers;
using Wpf.Ui;
using Wpf.Ui.Controls;
@@ -183,7 +183,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
}
SwitchAutoGeniusInvokationEnabled = true;
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoGeniusInvokationTask(new GeniusInvokationTaskParam(content)));
SwitchAutoGeniusInvokationEnabled = false;
}
@@ -219,7 +219,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
public async Task OnSwitchAutoWood()
{
SwitchAutoWoodEnabled = true;
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoWoodTask(new WoodTaskParam(AutoWoodRoundNum, AutoWoodDailyMaxCount)));
SwitchAutoWoodEnabled = false;
}
@@ -241,7 +241,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
var param = new AutoFightParam(path, Config.AutoFightConfig);
SwitchAutoFightEnabled = true;
- await new TaskRunner(DispatcherTimerOperationEnum.UseCacheImageWithTrigger)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoFightTask(param));
SwitchAutoFightEnabled = false;
}
@@ -261,7 +261,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
}
SwitchAutoDomainEnabled = true;
- await new TaskRunner(DispatcherTimerOperationEnum.UseCacheImage)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoDomainTask(new AutoDomainParam(AutoDomainRoundNum, path)));
SwitchAutoDomainEnabled = false;
}
@@ -376,7 +376,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
private async Task OnSwitchAutoMusicGame()
{
SwitchAutoMusicGameEnabled = true;
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoMusicGameTask(new AutoMusicGameParam()));
SwitchAutoMusicGameEnabled = false;
}
@@ -391,7 +391,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
private async Task OnSwitchAutoAlbum()
{
SwitchAutoAlbumEnabled = true;
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoAlbumTask(new AutoMusicGameParam()));
SwitchAutoAlbumEnabled = false;
}
@@ -401,7 +401,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
{
SwitchAutoFishingEnabled = true;
var param = AutoFishingTaskParam.BuildFromConfig(TaskContext.Instance().Config.AutoFishingConfig, SaveScreenshotOnKeyTick);
- await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
+ await new TaskRunner()
.RunSoloTaskAsync(new AutoFishingTask(param));
SwitchAutoFishingEnabled = false;
}
diff --git a/Fischless.GameCapture/BitBlt/BitBltCapture.cs b/Fischless.GameCapture/BitBlt/BitBltCapture.cs
index de379344..6024ad2e 100644
--- a/Fischless.GameCapture/BitBlt/BitBltCapture.cs
+++ b/Fischless.GameCapture/BitBlt/BitBltCapture.cs
@@ -1,4 +1,6 @@
using System.Diagnostics;
+using System.Runtime.InteropServices;
+using OpenCvSharp;
using Vanara.PInvoke;
namespace Fischless.GameCapture.BitBlt;
@@ -7,6 +9,9 @@ public class BitBltCapture : IGameCapture
{
private nint _hWnd;
+ private static readonly object LockObject = new object();
+
+
public CaptureModes Mode => CaptureModes.BitBlt;
public bool IsCapturing { get; private set; }
@@ -26,36 +31,79 @@ public class BitBltCapture : IGameCapture
}
}
- public Bitmap? Capture()
+ public Mat? Capture()
{
if (_hWnd == IntPtr.Zero)
{
return null;
}
- try
+ // 加锁
+ lock (LockObject)
{
- User32.GetClientRect(_hWnd, out var windowRect);
- int x = default, y = default;
- var width = windowRect.right - windowRect.left;
- var height = windowRect.bottom - windowRect.top;
+ User32.SafeReleaseHDC hdcSrc = User32.SafeReleaseHDC.Null;
+ Gdi32.SafeHDC hdcDest = Gdi32.SafeHDC.Null;
+ Gdi32.SafeHBITMAP hBitmap = Gdi32.SafeHBITMAP.Null;
+ try
+ {
+ User32.GetClientRect(_hWnd, out var windowRect);
+ int x = 0, y = 0;
+ var width = windowRect.right - windowRect.left;
+ var height = windowRect.bottom - windowRect.top;
- Bitmap bitmap = new(width, height);
- using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
- var hdcDest = g.GetHdc();
- var hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd);
- Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY);
- g.ReleaseHdc();
- Gdi32.DeleteDC(hdcDest);
- Gdi32.DeleteDC(hdcSrc);
- return bitmap;
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
+ hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd);
+ hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
- return null!;
+ var bmi = new Gdi32.BITMAPINFO
+ {
+ bmiHeader = new Gdi32.BITMAPINFOHEADER
+ {
+ biSize = (uint)Marshal.SizeOf(),
+ biWidth = width,
+ biHeight = -height, // Top-down image
+ biPlanes = 1,
+ biBitCount = 32,
+ biCompression = 0, // BI_RGB
+ biSizeImage = 0
+ }
+ };
+
+ nint bits = 0;
+ hBitmap = Gdi32.CreateDIBSection(hdcDest, bmi, Gdi32.DIBColorMode.DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
+ var oldBitmap = Gdi32.SelectObject(hdcDest, hBitmap);
+
+ Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY);
+
+ var mat = new Mat(height, width, MatType.CV_8UC4, bits);
+ Mat bgrMat = new Mat();
+ Cv2.CvtColor(mat, bgrMat, ColorConversionCodes.BGRA2BGR);
+
+ Gdi32.SelectObject(hdcDest, oldBitmap);
+ return bgrMat;
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ return null!;
+ }
+ finally
+ {
+ if (hBitmap != Gdi32.SafeHBITMAP.Null)
+ {
+ Gdi32.DeleteObject(hBitmap);
+ }
+
+ if (hdcDest != Gdi32.SafeHDC.Null)
+ {
+ Gdi32.DeleteDC(hdcDest);
+ }
+
+ if (_hWnd != IntPtr.Zero)
+ {
+ User32.ReleaseDC(_hWnd, hdcSrc);
+ }
+ }
+ }
}
public void Stop()
@@ -63,4 +111,4 @@ public class BitBltCapture : IGameCapture
_hWnd = IntPtr.Zero;
IsCapturing = false;
}
-}
+}
\ No newline at end of file
diff --git a/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs b/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs
index b5f59828..53f2b6c8 100644
--- a/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs
+++ b/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs
@@ -3,6 +3,8 @@ using Fischless.GameCapture.Graphics.Helpers;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using System.Diagnostics;
+using OpenCvSharp;
+using OpenCvSharp.Extensions;
using Vanara.PInvoke;
using Device = SharpDX.Direct3D11.Device;
@@ -11,6 +13,7 @@ namespace Fischless.GameCapture.DwmSharedSurface
public class SharedSurfaceCapture : IGameCapture
{
private nint _hWnd;
+ private static readonly object LockObject = new object();
private Device? _d3dDevice;
public void Dispose() => Stop();
@@ -64,34 +67,45 @@ namespace Fischless.GameCapture.DwmSharedSurface
return region;
}
- public Bitmap? Capture()
+ public Mat? Capture()
{
if (_hWnd == nint.Zero)
{
return null;
}
-
- NativeMethods.DwmGetDxSharedSurface(_hWnd, out var phSurface, out _, out _, out _, out _);
- if (phSurface == nint.Zero)
+ lock (LockObject)
{
- return null;
- }
+ NativeMethods.DwmGetDxSharedSurface(_hWnd, out var phSurface, out _, out _, out _, out _);
+ if (phSurface == nint.Zero)
+ {
+ return null;
+ }
- return ToBitmap(phSurface);
+
+ if (_d3dDevice == null)
+ {
+ Debug.WriteLine("D3Device is null.");
+ return null;
+ }
+
+ using var surfaceTexture = _d3dDevice.OpenSharedResource(phSurface);
+ using var stagingTexture = CreateStagingTexture(surfaceTexture, _d3dDevice);
+ var mat = stagingTexture.CreateMat(_d3dDevice, surfaceTexture, _region);
+ if (mat == null)
+ {
+ return null;
+ }
+ var bgrMat = new Mat();
+ Cv2.CvtColor(mat, bgrMat, ColorConversionCodes.BGRA2BGR);
+ return bgrMat;
+ }
}
+
- private Bitmap? ToBitmap(nint phSurface)
+ private Texture2D CreateStagingTexture(Texture2D surfaceTexture, Device device)
{
- if (_d3dDevice == null)
- {
- Debug.WriteLine("D3Device is null.");
- return null;
- }
-
- using var surfaceTexture = _d3dDevice.OpenSharedResource(phSurface);
-
- var staging = new Texture2D(_d3dDevice, new Texture2DDescription
+ return new Texture2D(device, new Texture2DDescription
{
Width = _region == null ? surfaceTexture.Description.Width : _region.Value.Right - _region.Value.Left,
Height = _region == null ? surfaceTexture.Description.Height : _region.Value.Bottom - _region.Value.Top,
@@ -104,9 +118,6 @@ namespace Fischless.GameCapture.DwmSharedSurface
CpuAccessFlags = CpuAccessFlags.Read,
OptionFlags = ResourceOptionFlags.None
});
-
-
- return staging.CreateBitmap(_d3dDevice, surfaceTexture, _region);
}
public void Stop()
diff --git a/Fischless.GameCapture/Fischless.GameCapture.csproj b/Fischless.GameCapture/Fischless.GameCapture.csproj
index 7b895bae..1e832bb0 100644
--- a/Fischless.GameCapture/Fischless.GameCapture.csproj
+++ b/Fischless.GameCapture/Fischless.GameCapture.csproj
@@ -19,6 +19,8 @@
+
+
\ No newline at end of file
diff --git a/Fischless.GameCapture/Graphics/GraphicsCapture.cs b/Fischless.GameCapture/Graphics/GraphicsCapture.cs
index 6113635a..a36f4a16 100644
--- a/Fischless.GameCapture/Graphics/GraphicsCapture.cs
+++ b/Fischless.GameCapture/Graphics/GraphicsCapture.cs
@@ -5,6 +5,11 @@ using Vanara.PInvoke;
using Windows.Foundation.Metadata;
using Windows.Graphics.Capture;
using Windows.Graphics.DirectX;
+using Windows.Graphics.DirectX.Direct3D11;
+using OpenCvSharp;
+using SharpDX.DXGI;
+using Device = SharpDX.Direct3D11.Device;
+using MapFlags = SharpDX.Direct3D11.MapFlags;
namespace Fischless.GameCapture.Graphics;
@@ -14,15 +19,27 @@ public class GraphicsCapture : IGameCapture
private Direct3D11CaptureFramePool _captureFramePool = null!;
private GraphicsCaptureItem _captureItem = null!;
+
private GraphicsCaptureSession _captureSession = null!;
+ private IDirect3DDevice _d3dDevice = null!;
+
public CaptureModes Mode => CaptureModes.WindowsGraphicsCapture;
public bool IsCapturing { get; private set; }
private ResourceRegion? _region;
- private bool _useBitmapCache = false;
- private Bitmap? _currentBitmap;
+ // HDR相关
+ private bool _isHdrEnabled;
+ private DirectXPixelFormat _pixelFormat = DirectXPixelFormat.B8G8R8A8UIntNormalized;
+
+
+ // 最新帧的存储
+ private Mat? _latestFrame;
+ private readonly ReaderWriterLockSlim _frameAccessLock = new();
+
+ // 用于获取帧数据的临时纹理和暂存资源
+ private Texture2D? _stagingTexture;
public void Dispose() => Stop();
@@ -41,24 +58,31 @@ public class GraphicsCapture : IGameCapture
throw new InvalidOperationException("Failed to create capture item.");
}
- _captureItem.Closed += CaptureItemOnClosed;
- var device = Direct3D11Helper.CreateDevice();
+ // 创建D3D设备
+ _d3dDevice = Direct3D11Helper.CreateDevice();
- _captureFramePool = Direct3D11CaptureFramePool.Create(device, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2,
+ // 检测HDR状态
+ _isHdrEnabled = IsHdrEnabled(_hWnd);
+ _pixelFormat = _isHdrEnabled ? DirectXPixelFormat.R16G16B16A16Float : DirectXPixelFormat.B8G8R8A8UIntNormalized;
+
+ // 创建帧池
+ _captureFramePool = Direct3D11CaptureFramePool.Create(
+ _d3dDevice,
+ _pixelFormat,
+ 2,
_captureItem.Size);
- if (settings != null && settings.TryGetValue("useBitmapCache", out object? value) && (bool)value)
- {
- _useBitmapCache = true;
- _captureFramePool.FrameArrived += OnFrameArrived;
- }
+
+ _captureItem.Closed += CaptureItemOnClosed;
+ _captureFramePool.FrameArrived += OnFrameArrived;
_captureSession = _captureFramePool.CreateCaptureSession(_captureItem);
if (ApiInformation.IsPropertyPresent("Windows.Graphics.Capture.GraphicsCaptureSession", "IsCursorCaptureEnabled"))
{
_captureSession.IsCursorCaptureEnabled = false;
}
+
if (ApiInformation.IsWriteablePropertyPresent("Windows.Graphics.Capture.GraphicsCaptureSession", "IsBorderRequired"))
{
_captureSession.IsBorderRequired = false;
@@ -98,55 +122,165 @@ public class GraphicsCapture : IGameCapture
return region;
}
- private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
+ public static bool IsHdrEnabled(nint hWnd)
{
- using var frame = _captureFramePool?.TryGetNextFrame();
-
- if (frame != null)
+ try
{
- var b = frame.ToBitmap(_region);
- if (b != null)
+ var hdc = User32.GetDC(hWnd);
+ if (hdc != IntPtr.Zero)
{
- _currentBitmap = b;
+ int bitsPerPixel = Gdi32.GetDeviceCaps(hdc, Gdi32.DeviceCap.BITSPIXEL);
+ User32.ReleaseDC(hWnd, hdc);
+
+ // 如果位深大于等于32位,认为支持HDR
+ return bitsPerPixel >= 32;
}
+
+ return false;
+ }
+ catch
+ {
+ return false;
}
}
- public Bitmap? Capture()
+ private Texture2D CreateStagingTexture(Direct3D11CaptureFrame frame, Device device)
{
- if (_hWnd == IntPtr.Zero)
+ // 创建可以用于CPU读取的暂存纹理
+ var textureDesc = new Texture2DDescription
{
- return null;
+ CpuAccessFlags = CpuAccessFlags.Read,
+ BindFlags = BindFlags.None,
+ Format = _isHdrEnabled ? Format.R16G16B16A16_Float : Format.B8G8R8A8_UNorm,
+ Width = _region == null ? frame.ContentSize.Width : _region.Value.Right - _region.Value.Left,
+ Height = _region == null ? frame.ContentSize.Height : _region.Value.Bottom - _region.Value.Top,
+ OptionFlags = ResourceOptionFlags.None,
+ MipLevels = 1,
+ ArraySize = 1,
+ SampleDescription = { Count = 1, Quality = 0 },
+ Usage = ResourceUsage.Staging
+ };
+
+ return new Texture2D(device, textureDesc);
+ }
+
+ private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
+ {
+ using var frame = sender.TryGetNextFrame();
+ if (frame == null)
+ {
+ return;
}
- if (!_useBitmapCache)
+ var frameSize = _captureItem.Size;
+
+ // 检查帧大小是否变化 // 不会被访问到的代码
+ if (frameSize.Width != frame.ContentSize.Width ||
+ frameSize.Height != frame.ContentSize.Height)
{
- try
- {
- using var frame = _captureFramePool?.TryGetNextFrame();
+ frameSize = frame.ContentSize;
+ _captureFramePool.Recreate(
+ _d3dDevice,
+ _pixelFormat,
+ 2,
+ frameSize
+ );
+ _stagingTexture = null;
+ }
- if (frame == null)
- {
- return null;
- }
+ // 从捕获的帧创建一个可以被访问的纹理
+ using var surfaceTexture = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface);
+ var d3dDevice = surfaceTexture.Device;
- return frame.ToBitmap(_region);
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
+ _stagingTexture ??= CreateStagingTexture(frame, d3dDevice);
+ var stagingTexture = _stagingTexture;
- return null;
+ // 将捕获的纹理复制到暂存纹理
+ if (_region != null)
+ {
+ d3dDevice.ImmediateContext.CopySubresourceRegion(surfaceTexture, 0, _region, stagingTexture, 0);
}
else
{
- if (_currentBitmap == null)
+ d3dDevice.ImmediateContext.CopyResource(surfaceTexture, stagingTexture);
+ }
+
+
+ // 映射纹理以便CPU读取
+ var dataBox = d3dDevice.ImmediateContext.MapSubresource(
+ stagingTexture,
+ 0,
+ MapMode.Read,
+ MapFlags.None);
+
+ try
+ {
+ // 创建一个新的Mat
+ var newFrame = new Mat(stagingTexture.Description.Height, stagingTexture.Description.Width,
+ _isHdrEnabled ? MatType.MakeType(7, 4) : MatType.CV_8UC4, dataBox.DataPointer);
+
+ // 如果是HDR,进行HDR到SDR的转换
+ if (_isHdrEnabled)
+ {
+ // rgb -> bgr
+ newFrame = ConvertHdrToSdr(newFrame);
+ }
+
+ // 使用写锁更新最新帧
+ _frameAccessLock.EnterWriteLock();
+ try
+ {
+ // 释放之前的帧
+ _latestFrame?.Dispose();
+ // 克隆新帧以保持对其的引用(因为dataBox.DataPointer将被释放)
+ _latestFrame = newFrame.Clone();
+ }
+ finally
+ {
+ newFrame.Dispose();
+ _frameAccessLock.ExitWriteLock();
+ }
+ }
+ finally
+ {
+ // 取消映射纹理
+ d3dDevice.ImmediateContext.UnmapSubresource(surfaceTexture, 0);
+ }
+ }
+
+ private static Mat ConvertHdrToSdr(Mat hdrMat)
+ {
+ // 创建一个目标 8UC4 Mat
+ Mat sdkMat = new Mat(hdrMat.Size(), MatType.CV_8UC4);
+
+ // 将 32FC4 缩放到 0-255 范围并转换为 8UC4
+ // 注意:这种简单缩放可能不会保留 HDR 的所有细节
+ hdrMat.ConvertTo(sdkMat, MatType.CV_8UC4, 255.0);
+
+ // 将 HDR 的 RGB 通道转换为 BGR
+ Cv2.CvtColor(sdkMat, sdkMat, ColorConversionCodes.RGBA2BGRA);
+
+ return sdkMat;
+ }
+
+ public Mat? Capture()
+ {
+ // 使用读锁获取最新帧
+ _frameAccessLock.EnterReadLock();
+ try
+ {
+ // 如果没有可用帧则返回null
+ if (_latestFrame == null)
{
return null;
}
- return (Bitmap)_currentBitmap.Clone();
+ // 返回最新帧的副本(这里我们必须克隆,因为Mat是不线程安全的)
+ return _latestFrame.Clone();
+ }
+ finally
+ {
+ _frameAccessLock.ExitReadLock();
}
}
@@ -157,9 +291,25 @@ public class GraphicsCapture : IGameCapture
_captureSession = null!;
_captureFramePool = null!;
_captureItem = null!;
+ _stagingTexture?.Dispose();
+ _d3dDevice?.Dispose();
_hWnd = IntPtr.Zero;
IsCapturing = false;
+
+ // 释放最新帧
+ _frameAccessLock.EnterWriteLock();
+ try
+ {
+ _latestFrame?.Dispose();
+ _latestFrame = null;
+ }
+ finally
+ {
+ _frameAccessLock.ExitWriteLock();
+ }
+
+ _frameAccessLock.Dispose();
}
private void CaptureItemOnClosed(GraphicsCaptureItem sender, object args)
diff --git a/Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs b/Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs
index 0feca9c5..f3f94a4e 100644
--- a/Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs
+++ b/Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs
@@ -4,6 +4,7 @@ using SharpDX.DXGI;
using System.Diagnostics;
using System.Drawing.Imaging;
using Windows.Graphics.Capture;
+using OpenCvSharp;
namespace Fischless.GameCapture.Graphics.Helpers;
@@ -67,4 +68,40 @@ public static class Texture2DExtensions
staging.Dispose();
}
}
+
+ public static Mat? CreateMat(this Texture2D staging, SharpDX.Direct3D11.Device d3dDevice, Texture2D surfaceTexture, ResourceRegion? region = null)
+ {
+ try
+ {
+ // Copy data
+ if (region != null)
+ {
+ d3dDevice.ImmediateContext.CopySubresourceRegion(surfaceTexture, 0, region, staging, 0);
+ }
+ else
+ {
+ d3dDevice.ImmediateContext.CopyResource(surfaceTexture, staging);
+ }
+
+ // 映射纹理以便CPU读取
+ var dataBox = d3dDevice.ImmediateContext.MapSubresource(
+ staging,
+ 0,
+ MapMode.Read,
+ SharpDX.Direct3D11.MapFlags.None);
+
+ var mat = new Mat(staging.Description.Height, staging.Description.Width, MatType.CV_8UC4, dataBox.DataPointer);
+ return mat;
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Failed to copy texture to mat.");
+ Debug.WriteLine(e.StackTrace);
+ return null;
+ }
+ finally
+ {
+ staging.Dispose();
+ }
+ }
}
\ No newline at end of file
diff --git a/Fischless.GameCapture/IGameCapture.cs b/Fischless.GameCapture/IGameCapture.cs
index d6922cf2..ffb85923 100644
--- a/Fischless.GameCapture/IGameCapture.cs
+++ b/Fischless.GameCapture/IGameCapture.cs
@@ -1,4 +1,6 @@
-namespace Fischless.GameCapture;
+using OpenCvSharp;
+
+namespace Fischless.GameCapture;
public interface IGameCapture : IDisposable
{
@@ -7,7 +9,7 @@ public interface IGameCapture : IDisposable
public void Start(nint hWnd, Dictionary? settings = null);
- public Bitmap? Capture();
+ public Mat? Capture();
public void Stop();
}
diff --git a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.FishBite.cs b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.FishBite.cs
index 5d42179a..56e24ff5 100644
--- a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.FishBite.cs
+++ b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.FishBite.cs
@@ -6,6 +6,7 @@ using System.Drawing;
using BehaviourTree.Composites;
using BehaviourTree.FluentBuilder;
using Microsoft.Extensions.Time.Testing;
+using OpenCvSharp;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
@@ -20,7 +21,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void FishBite_ShouldSuccess(string screenshot1080p)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
//
@@ -39,7 +40,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void FishBite_Tree_Timeout_ShouldSuccess(string screenshot1080pCheckThrowRod, string screenshot1080pFishBite)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080pCheckThrowRod}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080pCheckThrowRod}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
DateTimeOffset dateTime = new DateTimeOffset(2025, 2, 26, 16, 13, 54, 285, TimeSpan.FromHours(8));
@@ -70,7 +71,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
//
fakeTimeProvider.SetUtcNow(dateTime.AddSeconds(16));
- bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080pFishBite}");
+ bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080pFishBite}");
imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
//
diff --git a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishBoxArea.cs b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishBoxArea.cs
index 5b8f13de..ed005f51 100644
--- a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishBoxArea.cs
+++ b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishBoxArea.cs
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using System.Drawing;
using BetterGenshinImpact.Core.Recognition.ONNX.YOLO;
using Microsoft.Extensions.Time.Testing;
+using OpenCvSharp;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
@@ -25,7 +26,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void GetFishBoxArea_ShouldSuccess(string screenshot1080p)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, drawContent: new FakeDrawContent());
var blackboard = new Blackboard(null, sleep: i => { });
@@ -47,7 +48,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void GetFishBoxArea_ShouldFail(string screenshot1080p)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, drawContent: new FakeDrawContent());
var blackboard = new Blackboard(null, sleep: i => { });
diff --git a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishpond.cs b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishpond.cs
index e77aed18..5e9d4603 100644
--- a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishpond.cs
+++ b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.GetFishpond.cs
@@ -10,6 +10,7 @@ using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Core.Config;
using Compunet.YoloV8;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
+using OpenCvSharp;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
@@ -25,7 +26,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void GetFishpondTest_VariousFishExist_ShouldSuccess(string screenshot1080p, IEnumerable fishNames)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
@@ -53,7 +54,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void GetFishpondTest_AllIgnored_ShouldBeRunning(string screenshot1080p, IEnumerable chooseBaitfailures, IEnumerable throwRodNoTargetFishfailures)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
@@ -88,7 +89,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void GetFishpondTest_FishCount_ShouldSuccess(string screenshot1080p, string fishName, int count)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
diff --git a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.ThrowRod.cs b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.ThrowRod.cs
index 0e5b3ff4..3277ee01 100644
--- a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.ThrowRod.cs
+++ b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoFishingTests/BehavioursTests.ThrowRod.cs
@@ -12,6 +12,7 @@ using System.Drawing;
using BetterGenshinImpact.Core.Config;
using Compunet.YoloV8;
using Microsoft.Extensions.Time.Testing;
+using OpenCvSharp;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
@@ -26,7 +27,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void ThrowRodTest_VariousFish_ShouldSuccess(string screenshot1080p, string selectedBaitName)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
@@ -56,7 +57,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void ThrowRodTest_VariousFish_ShouldFail(string screenshot1080p, string selectedBaitName)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
@@ -85,7 +86,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void ThrowRodTest_NoBaitFish_ShouldFail(string screenshot1080p, string selectedBaitName)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
@@ -126,7 +127,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void ThrowRodTest_Target_ShouldBeTheLeftOne()
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\202503082114541115.png");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\202503082114541115.png");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();
@@ -147,7 +148,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
Assert.Equal(blackboard.fishpond.Fishes.OrderBy(f => f.Rect.X).First(), actual);
//
- bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\202503082114560489.png");
+ bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\202503082114560489.png");
imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
//
@@ -168,7 +169,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public void ThrowRodTest_NoTarget_ShouldFail(string screenshot1080p)
{
//
- Bitmap bitmap = new Bitmap(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
+ Mat bitmap = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
var imageRegion = new GameCaptureRegion(bitmap, 0, 0, new DesktopRegion(new FakeMouseSimulator()), converter: new ScaleConverter(1d), drawContent: new FakeDrawContent());
var predictor = YoloV8Builder.CreateDefaultBuilder().UseOnnxModel(Global.Absolute(@"Assets\Model\Fish\bgi_fish.onnx")).Build();