Files
2026-10-24 13:02:19 +08:00

86 lines
3.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace anjianjingling.Vision
{
/// <summary>
/// 找色:LockBits 锁内存逐像素扫描,支持容差。
/// </summary>
public sealed class ColorFinder
{
/// <summary>解析 "RRGGBB" 或 "#RRGGBB" 颜色。</summary>
public static Color ParseHex(string hex)
{
hex = hex.Trim().TrimStart('#');
if (hex.Length != 6) throw new ArgumentException($"颜色格式非法:{hex}(应为 RRGGBB)");
int r = Convert.ToInt32(hex[0..2], 16);
int g = Convert.ToInt32(hex[2..4], 16);
int b = Convert.ToInt32(hex[4..6], 16);
return Color.FromArgb(r, g, b);
}
/// <summary>
/// 在 bmp 的指定区域内找颜色,返回第一个匹配点的 bmp 内坐标;未找到返回 (-1,-1)。
/// </summary>
public Point FindColor(Bitmap bmp, int x1, int y1, int x2, int y2, Color target, int tolerance = 0)
{
// 裁剪到合法范围
x1 = Math.Clamp(x1, 0, bmp.Width - 1);
x2 = Math.Clamp(x2, 0, bmp.Width - 1);
y1 = Math.Clamp(y1, 0, bmp.Height - 1);
y2 = Math.Clamp(y2, 0, bmp.Height - 1);
if (x1 > x2 || y1 > y2) return new Point(-1, -1);
var rect = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
using var fast = new LockedBitmap(bmp, rect);
for (int y = 0; y < rect.Height; y++)
{
for (int x = 0; x < rect.Width; x++)
{
var c = fast.GetPixel(x, y);
if (Math.Abs(c.R - target.R) <= tolerance &&
Math.Abs(c.G - target.G) <= tolerance &&
Math.Abs(c.B - target.B) <= tolerance)
{
return new Point(rect.X + x, rect.Y + y);
}
}
}
return new Point(-1, -1);
}
/// <summary>LockBits 包装,按 Format24bpp/32bpp 通用读取 BGR。</summary>
private sealed class LockedBitmap : IDisposable
{
private readonly BitmapData _data;
private readonly Bitmap _bmp;
private readonly int _stride;
private readonly byte[] _buf;
private readonly int _bytesPerPixel;
public LockedBitmap(Bitmap bmp, Rectangle rect)
{
_bmp = bmp;
_bytesPerPixel = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
if (_bytesPerPixel < 3) _bytesPerPixel = 3;
_data = bmp.LockBits(rect, ImageLockMode.ReadOnly,
_bytesPerPixel == 3 ? PixelFormat.Format24bppRgb : PixelFormat.Format32bppArgb);
_stride = Math.Abs(_data.Stride);
_buf = new byte[_stride * rect.Height];
Marshal.Copy(_data.Scan0, _buf, 0, _buf.Length);
}
public Color GetPixel(int x, int y)
{
int i = y * _stride + x * _bytesPerPixel;
return Color.FromArgb(_buf[i + 2], _buf[i + 1], _buf[i]);
}
public void Dispose()
{
_bmp.UnlockBits(_data);
}
}
}
}