86 lines
3.3 KiB
C#
86 lines
3.3 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
}
|