using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace anjianjingling.Vision { /// /// 找色:LockBits 锁内存逐像素扫描,支持容差。 /// public sealed class ColorFinder { /// 解析 "RRGGBB" 或 "#RRGGBB" 颜色。 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); } /// /// 在 bmp 的指定区域内找颜色,返回第一个匹配点的 bmp 内坐标;未找到返回 (-1,-1)。 /// 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); } /// LockBits 包装,按 Format24bpp/32bpp 通用读取 BGR。 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); } } } }