using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using XFEWorkflow.Abstractions;
namespace XFEWorkflow.Plugins.Vision;
public sealed class ScreenFrame(Bitmap bitmap, ScreenRegion region, string coordinateSpace) : IDisposable
{
public Bitmap Bitmap { get; } = bitmap;
public ScreenRegion Region { get; } = region;
public string CoordinateSpace { get; } = coordinateSpace;
public void Dispose() => Bitmap.Dispose();
}
public static class ScreenCapture
{
public static ScreenRegion DesktopBounds
{
get
{
using var dpi = new PhysicalPixelScope();
return new ScreenRegion(GetSystemMetrics(76), GetSystemMetrics(77), GetSystemMetrics(78), GetSystemMetrics(79)).Validate();
}
}
public static ScreenFrame Capture(ScreenRegion? requested = null)
{
using var dpi = new PhysicalPixelScope();
var bounds = DesktopBounds;
var region = requested ?? bounds;
region.RequireInside(bounds);
var bitmap = new Bitmap(region.Width, region.Height, PixelFormat.Format24bppRgb);
try
{
using var graphics = Graphics.FromImage(bitmap);
var desktop = GetDC(0);
if (desktop == 0) throw new IOException("无法读取当前桌面。");
try
{
var destination = graphics.GetHdc();
try
{
if (!BitBlt(destination, 0, 0, region.Width, region.Height, desktop, region.X, region.Y, 0x00CC0020u | 0x40000000u))
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), "屏幕区域截图失败。");
}
finally { graphics.ReleaseHdc(destination); }
}
finally { ReleaseDC(0, desktop); }
return new(bitmap, region, "screen");
}
catch { bitmap.Dispose(); throw; }
}
public static ScreenFrame Load(string path, ScreenRegion? requested = null)
{
if (!File.Exists(path)) throw new FileNotFoundException("找不到图像文件", path);
using var original = Image.FromFile(Path.GetFullPath(path));
var bounds = new ScreenRegion(0, 0, original.Width, original.Height).Validate();
var region = requested ?? bounds;
region.RequireInside(bounds);
var bitmap = new Bitmap(region.Width, region.Height, PixelFormat.Format24bppRgb);
try
{
using var graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawImage(original, new Rectangle(0, 0, bitmap.Width, bitmap.Height), new Rectangle(region.X, region.Y, region.Width, region.Height), GraphicsUnit.Pixel);
return new(bitmap, region, "image");
}
catch { bitmap.Dispose(); throw; }
}
public static Color Pixel(int x, int y)
{
using var frame = Capture(new(x, y, 1, 1));
return frame.Bitmap.GetPixel(0, 0);
}
// A Runner has no WinUI window and may inherit a different DPI context.
// Keep the override synchronous and restore it on the same thread.
private sealed class PhysicalPixelScope : IDisposable
{
private readonly nint previous = SetThreadDpiAwarenessContext(-4);
public void Dispose() { if (previous != 0) SetThreadDpiAwarenessContext(previous); }
}
[DllImport("user32.dll")] private static extern int GetSystemMetrics(int index);
[DllImport("user32.dll")] private static extern nint SetThreadDpiAwarenessContext(nint context);
[DllImport("user32.dll")] private static extern nint GetDC(nint window);
[DllImport("user32.dll")] private static extern int ReleaseDC(nint window, nint dc);
[DllImport("gdi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(nint destination, int x, int y, int width, int height, nint source, int sourceX, int sourceY, uint operation);
}
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using XFEWorkflow.Abstractions;
namespace XFEWorkflow.Plugins.Vision;
public sealed class ScreenFrame(Bitmap bitmap, ScreenRegion region, string coordinateSpace) : IDisposable
{
public Bitmap Bitmap { get; } = bitmap;
public ScreenRegion Region { get; } = region;
public string CoordinateSpace { get; } = coordinateSpace;
public void Dispose() => Bitmap.Dispose();
}
public static class ScreenCapture
{
public static ScreenRegion DesktopBounds
{
get
{
using var dpi = new PhysicalPixelScope();
return new ScreenRegion(GetSystemMetrics(76), GetSystemMetrics(77), GetSystemMetrics(78), GetSystemMetrics(79)).Validate();
}
}
public static ScreenFrame Capture(ScreenRegion? requested = null)
{
using var dpi = new PhysicalPixelScope();
var bounds = DesktopBounds;
var region = requested ?? bounds;
region.RequireInside(bounds);
var bitmap = new Bitmap(region.Width, region.Height, PixelFormat.Format24bppRgb);
try
{
using var graphics = Graphics.FromImage(bitmap);
var desktop = GetDC(0);
if (desktop == 0) throw new IOException("无法读取当前桌面。");
try
{
var destination = graphics.GetHdc();
try
{
if (!BitBlt(destination, 0, 0, region.Width, region.Height, desktop, region.X, region.Y, 0x00CC0020u | 0x40000000u))
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), "屏幕区域截图失败。");
}
finally { graphics.ReleaseHdc(destination); }
}
finally { ReleaseDC(0, desktop); }
return new(bitmap, region, "screen");
}
catch { bitmap.Dispose(); throw; }
}
public static ScreenFrame Load(string path, ScreenRegion? requested = null)
{
if (!File.Exists(path)) throw new FileNotFoundException("找不到图像文件", path);
using var original = Image.FromFile(Path.GetFullPath(path));
var bounds = new ScreenRegion(0, 0, original.Width, original.Height).Validate();
var region = requested ?? bounds;
region.RequireInside(bounds);
var bitmap = new Bitmap(region.Width, region.Height, PixelFormat.Format24bppRgb);
try
{
using var graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawImage(original, new Rectangle(0, 0, bitmap.Width, bitmap.Height), new Rectangle(region.X, region.Y, region.Width, region.Height), GraphicsUnit.Pixel);
return new(bitmap, region, "image");
}
catch { bitmap.Dispose(); throw; }
}
public static Color Pixel(int x, int y)
{
using var frame = Capture(new(x, y, 1, 1));
return frame.Bitmap.GetPixel(0, 0);
}
// A Runner has no WinUI window and may inherit a different DPI context.
// Keep the override synchronous and restore it on the same thread.
private sealed class PhysicalPixelScope : IDisposable
{
private readonly nint previous = SetThreadDpiAwarenessContext(-4);
public void Dispose() { if (previous != 0) SetThreadDpiAwarenessContext(previous); }
}
[DllImport("user32.dll")] private static extern int GetSystemMetrics(int index);
[DllImport("user32.dll")] private static extern nint SetThreadDpiAwarenessContext(nint context);
[DllImport("user32.dll")] private static extern nint GetDC(nint window);
[DllImport("user32.dll")] private static extern int ReleaseDC(nint window, nint dc);
[DllImport("gdi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(nint destination, int x, int y, int width, int height, nint source, int sourceX, int sourceY, uint operation);
}