如何在每像素位图上正确记忆 Marshal.ReadByte

How to properly memoize Marshal.ReadByte on a 1 bit per pixel bitmap

我正在尝试改进我组装的 1bpp 图像搜索功能。

我参考了 Bob Powell 的优秀页面和此处的一些答案,我能够将这个运行良好的例程组合在一起。

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace AutoBot
{
    public partial class ActiveScreenMatch
    {
        public static bool ScreenMatch(Rectangle rect = default, string path = "")
        {
            if (rect == default && string.IsNullOrEmpty(path))
            {
                return false;
            }
            Bitmap bw;
            if (rect == default)
            {
                bw = new Bitmap(path);
                bw = bw.Clone(new Rectangle(new Point(0, 0), new Size(bw.Width, bw.Height)), PixelFormat.Format1bppIndexed);
            }
            else
            {
                bw = GetBlackWhiteAt(rect.Location, rect.Size);
            }
            /// Initialize Search image array.
            bool[][] ba1;

            using (bw)
            {
                BitmapData data = bw.LockBits(new Rectangle(0, 0, bw.Width, bw.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
                ba1 = new bool[bw.Height][];
                for (int y = 0; y <= bw.Height - 1; y++)
                {
                    ba1[y] = new bool[bw.Width];
                    for (int x = 0; x <= bw.Width - 1; x++)
                    {
                        if (GetIndexedPixel(x, y, data) > 0)
                        {
                            ba1[y][x] = true;
                        }
                    }
                }
                bw.UnlockBits(data);
            }
            int SkippedBlackLines = 0;
            foreach (bool[] bl1 in ba1)
            {
                if (bl1.Any(x => x))
                {
                    break;
                }
                else
                {
                    SkippedBlackLines++;
                }
            }
            bool[][] ba2;
            using (Bitmap SearchWindow = GetBlackWhiteAt(new Point(0, 0), new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)))
            {
                BitmapData data = SearchWindow.LockBits(new Rectangle(0, 0, SearchWindow.Width, SearchWindow.Height), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);

                ba2 = new bool[SearchWindow.Height][];
                for (int y = 0; y <= SearchWindow.Height - 1; y++)
                {
                    ba2[y] = new bool[SearchWindow.Width];
                    for (int x = 0; x <= SearchWindow.Width - 1; x++)
                    {
                        if (GetIndexedPixel(x, y, data) > 0)
                        {
                            ba2[y][x] = true;
                        }
                    }
                }
                SearchWindow.UnlockBits(data);
            }
            var po = new ParallelOptions()
            {
                MaxDegreeOfParallelism = 4
            };
            var partitions = Partitioner.Create(0, ba2.GetUpperBound(0), ba2.Length /4);
            var Result = Parallel.ForEach(partitions, po, (item, loopstate) =>
            {
                var Base = ba1.Skip(SkippedBlackLines);
                for (int i = item.Item2; i != item.Item1; i--)
                {
                    if (SubListIndex(ba2[i].AsEnumerable(), 0, Base.LastOrDefault()) != -1)
                    {
                        if (Base.Count() == 1)
                        {
                            loopstate.Break();
                            MoveTo(
                                    SubListIndex(ba2[i].AsEnumerable(), 0, Base.LastOrDefault()) + (ba1[0].Length / 2),
                                    i + (ba1.GetUpperBound(0) / 2));
                        }
                        else
                        {
                            Base = Base.Take(Base.Count() - 1);
                        }
                    }
                }
            });
            return !Result.IsCompleted;
        }

        private static int GetIndexedPixel(int x, int y, BitmapData data)
        {
            var index = (y * data.Stride) + (x >> 3);
            var mask = (byte)(0x80 >> (x & 0x7));
            byte ret = Marshal.ReadByte(data.Scan0, index);
            ret &= mask;
            return ret;
        }

        private static int SubListIndex(IEnumerable<bool> list, int start, IEnumerable<bool> sublist)
        {
            for (int listIndex = start; listIndex < list.Count() - sublist.Count() + 1; listIndex++)
            {
                int count = 0;
                while (count < sublist.Count() && sublist.ElementAt(count).Equals(list.ElementAt(listIndex + count)))
                    count++;
                if (count == sublist.Count())
                    return listIndex;
            }
            return -1;
        }

        private static Bitmap GetBlackWhiteAt(Point On, Size PickArea)
        {
            // Create a new bitmap.
            using (Bitmap bmp = PrintWindow())
                return bmp.Clone(new Rectangle(On, PickArea), PixelFormat.Format1bppIndexed);
        }

        private static void PrintScreen()
        {
            keybd_event(VKey.VK_SNAPSHOT, 0, KEYEVENTF_EXTENDEDKEY, 0);
            keybd_event(VKey.VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0);
        }

        private static Bitmap PrintWindow()
        {
            PrintScreen();
            Application.DoEvents();
            if (Clipboard.ContainsImage())
            {
                using (Image img = Clipboard.GetImage())
                {
                    if (img != null)
                    {
                        return new Bitmap(img);
                    }
                }
            }
            return PrintWindow();
        }
    }

    public static class VKey
    {
        public readonly static byte VK_BACK = 0x08;
        public readonly static byte VK_TAB = 0x09;
        public readonly static byte VK_RETURN = 0x0D;
        public readonly static byte VK_SHIFT = 0x10;
        public readonly static byte VK_CONTROL = 0x11;
        public readonly static byte VK_MENU = 0x12;
        public readonly static byte VK_PAUSE = 0x13;
        public readonly static byte VK_CAPITAL = 0x14;
        public readonly static byte VK_ESCAPE = 0x1B;
        public readonly static byte VK_SPACE = 0x20;
        public readonly static byte VK_END = 0x23;
        public readonly static byte VK_HOME = 0x24;
        public readonly static byte VK_LEFT = 0x25;
        public readonly static byte VK_UP = 0x26;
        public readonly static byte VK_RIGHT = 0x27;
        public readonly static byte VK_DOWN = 0x28;
        public readonly static byte VK_PRINT = 0x2A;
        public readonly static byte VK_SNAPSHOT = 0x2C;
        public readonly static byte VK_INSERT = 0x2D;
        public readonly static byte VK_DELETE = 0x2E;
        public readonly static byte VK_LWIN = 0x5B;
        public readonly static byte VK_RWIN = 0x5C;
        public readonly static byte VK_NUMPAD0 = 0x60;
        public readonly static byte VK_NUMPAD1 = 0x61;
        public readonly static byte VK_NUMPAD2 = 0x62;
        public readonly static byte VK_NUMPAD3 = 0x63;
        public readonly static byte VK_NUMPAD4 = 0x64;
        public readonly static byte VK_NUMPAD5 = 0x65;
        public readonly static byte VK_NUMPAD6 = 0x66;
        public readonly static byte VK_NUMPAD7 = 0x67;
        public readonly static byte VK_NUMPAD8 = 0x68;
        public readonly static byte VK_NUMPAD9 = 0x69;
        public readonly static byte VK_MULTIPLY = 0x6A;
        public readonly static byte VK_ADD = 0x6B;
        public readonly static byte VK_SEPARATOR = 0x6C;
        public readonly static byte VK_SUBTRACT = 0x6D;
        public readonly static byte VK_DECIMAL = 0x6E;
        public readonly static byte VK_DIVIDE = 0x6F;
        public readonly static byte VK_F1 = 0x70;
        public readonly static byte VK_F2 = 0x71;
        public readonly static byte VK_F3 = 0x72;
        public readonly static byte VK_F4 = 0x73;
        public readonly static byte VK_F5 = 0x74;
        public readonly static byte VK_F6 = 0x75;
        public readonly static byte VK_F7 = 0x76;
        public readonly static byte VK_F8 = 0x77;
        public readonly static byte VK_F9 = 0x78;
        public readonly static byte VK_F10 = 0x79;
        public readonly static byte VK_F11 = 0x7A;
        public readonly static byte VK_F12 = 0x7B;
        public readonly static byte VK_NUMLOCK = 0x90;
        public readonly static byte VK_SCROLL = 0x91;
        public readonly static byte VK_LSHIFT = 0xA0;
        public readonly static byte VK_RSHIFT = 0xA1;
        public readonly static byte VK_LCONTROL = 0xA2;
        public readonly static byte VK_RCONTROL = 0xA3;
        public readonly static byte VK_LMENU = 0xA4;
        public readonly static byte VK_RMENU = 0xA5;
    }
}

我想读取 x 索引在该范围内的字节(8 位)和 return,为该组计算记住字节。 :)

任何意见或想法都会很棒。

如果您使用 unsafe 关键字,您可以反过来使用 指针 按索引提取 byte (如果我理解正确的话)

The unsafe keyword denotes an unsafe context, which is required for any operation involving pointers.

byte p = *((byte*)data.Scan0 + index);

要执行此操作,必须将您的方法标记为 unsafe

unsafe int GetIndexedPixel(int x, int y, BitmapData data)

注意:您还需要设置项目构建选项以允许不安全代码