如何在每像素位图上正确记忆 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,为该组计算记住字节。 :)
任何意见或想法都会很棒。
我正在尝试改进我组装的 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,为该组计算记住字节。 :)
任何意见或想法都会很棒。