using System;
using System.Drawing;
using System.Drawing.Imaging;
+using System.IO;
using System.Runtime.InteropServices;
using System.Text;
{
private const int WidthMin = 600, HeightMin = 400;
private IntPtr _hWnd;
+ private Rect _rect;
private Rectangle _rectangle;
+ private string _title;
public Bitmap CaptureGameScreen()
{
if (_hWnd == IntPtr.Zero || _rectangle.IsEmpty)
return null;
- using (var bmp = CaptureWindow(_hWnd))
+ using (var bmp = CaptureWindow(_hWnd, _rect))
return bmp.Clone(_rectangle, bmp.PixelFormat);
}
_hWnd = FindWindow(title);
if (_hWnd == IntPtr.Zero)
return null;
- using (var bmp = CaptureWindow(_hWnd))
+ var rect = new Rect();
+ GetWindowRect(_hWnd, ref rect);
+ using (var bmp = CaptureWindow(_hWnd, rect))
{
- _rectangle = DetectGameScreen(bmp);
- if (_rectangle.IsEmpty)
- return null;
+ var rectangle = DetectGameScreen(bmp);
+ if (!rectangle.IsEmpty)
+ {
+ _rect = rect;
+ _rectangle = rectangle;
+ _title = title;
+ }
+ else
+ {
+ using (var file = File.Create("debug.png"))
+ bmp.Save(file, ImageFormat.Png);
+ if (_rectangle.IsEmpty || !_rect.Equals(rect) || _title != title)
+ return null;
+ }
return bmp.Clone(_rectangle, bmp.PixelFormat);
}
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
- public static Bitmap CaptureWindow(IntPtr hWnd)
+ public static Bitmap CaptureWindow(IntPtr hWnd, Rect rect)
{
- var winDC = GetWindowDC(hWnd);
- var rect = new Rect();
- GetWindowRect(hWnd, ref rect);
- var bmp = new Bitmap(rect.Right - rect.Left, rect.Bottom - rect.Top, PixelFormat.Format32bppArgb);
- var g = Graphics.FromImage(bmp);
- var hDC = g.GetHdc();
- BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, winDC, 0, 0, SRCCOPY);
- g.ReleaseHdc(hDC);
- g.Dispose();
- ReleaseDC(hWnd, winDC);
+ var width = rect.Right - rect.Left;
+ var height = rect.Bottom - rect.Top;
+ var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
+ using (var g = Graphics.FromImage(bmp))
+ g.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
return bmp;
}
[DllImport("user32.dll")]
- private static extern IntPtr GetWindowDC(IntPtr hWnd);
-
- [DllImport("user32.dll")]
- private static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
-
- // ReSharper disable once InconsistentNaming
- private const int SRCCOPY = 0xcc0020;
-
- [DllImport("gdi32.dll")]
- private static extern int BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight,
- IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
-
- [DllImport("user32.dll")]
private static extern int GetWindowRect(IntPtr hWnd, ref Rect lpRec);
[StructLayout(LayoutKind.Sequential)]
- public struct Rect
+ public struct Rect : IEquatable<Rect>
{
public int Left;
public int Top;
public int Right;
public int Bottom;
+
+ public bool Equals(Rect other)
+ {
+ return Left == other.Left && Top == other.Top && Right == other.Right && Bottom == other.Bottom;
+ }
}
private Rectangle DetectGameScreen(Bitmap bmp)
var width = bmp.Width;
var map = new byte[height, width];
var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
- PixelFormat.Format32bppArgb);
+ PixelFormat.Format24bppRgb);
unsafe
{
var ptr = (byte*)data.Scan0;
{
for (var x = 0; x < data.Width; x++)
{
- var p = ptr + y * data.Stride + x * 4;
+ var p = ptr + y * data.Stride + x * 3;
map[y, x] = (byte)(p[0] == 255 && p[1] == 255 && p[2] == 255 ? 1 : 0);
}
}
}
bmp.UnlockBits(data);
- const int corner = 40;
- for (var y = 0; y < height; y++)
+ for (var y = 0; y < height - 1; y++)
{
- var n = 0;
- for (var x = 0; x < width; x++)
+ var rect = Rectangle.Empty;
+ if (!CheckEdge(map, 0, width, y, y, Edge.HorizontalTop))
+ continue;
+ rect.Y = y + 1;
+ for (var x = 0; x < width - 1; x++)
{
- if ((map[y, x] & 1) == 1)
- {
- if (++n >= corner)
- map[y, x - corner + 1] |= 2;
- }
- else
- {
- n = 0;
- }
+ if (!CheckEdge(map, x, x, rect.Y, height, Edge.VerticalLeft))
+ continue;
+ rect.X = x + 1;
+ rect = FindBottomAndRight(map, rect);
+ if (rect == Rectangle.Empty)
+ continue;
+ if (!CheckEdge(map, rect.X, rect.Right, y, y, Edge.HorizontalTop) ||
+ !CheckEitherEndClean(map, rect.X, rect.Right, y, y, Edge.HorizontalTop))
+ break;
+ if (!CheckEdge(map, x, x, rect.Y, rect.Bottom, Edge.VerticalLeft) ||
+ !CheckEitherEndClean(map, x, x, rect.Y, rect.Bottom, Edge.VerticalLeft))
+ continue;
+ RoundUpRectangle(map, ref rect);
+ return rect;
}
}
- for (var x = 0; x < width; x++)
+ return Rectangle.Empty;
+ }
+
+ private Rectangle FindBottomAndRight(byte[,] map, Rectangle rect)
+ {
+ var height = map.GetLength(0);
+ var width = map.GetLength(1);
+ for (var y = rect.Y; y < height - 1; y++)
{
- var n = 0;
- for (var y = 0; y < height; y++)
+ if (!CheckEdge(map, rect.X, width, y, y, Edge.HorizontalBottom))
+ continue;
+ rect.Height = y - rect.Y + 1;
+ rect.Width = 0;
+ for (var x = rect.X; x < width - 1; x++)
{
- if ((map[y, x] & 1) == 1)
+ if (!CheckEdge(map, x, x, rect.Y, rect.Bottom, Edge.VerticalRight) ||
+ !CheckEitherEndClean(map, x, x, rect.Y, rect.Bottom, Edge.VerticalRight))
+ continue;
+ rect.Width = x - rect.X + 1;
+ break;
+ }
+ if (rect.Width == 0)
+ continue;
+ if (CheckEitherEndClean(map, rect.X, rect.Right, rect.Bottom, rect.Bottom, Edge.HorizontalBottom))
+ break;
+ }
+ if (rect.Width == 0)
+ return Rectangle.Empty;
+ // check a smaller rectangle
+ for (var y = rect.Y; y < rect.Height - 1; y++)
+ {
+ if (!CheckEdge(map, rect.X, rect.Right, y, y, Edge.HorizontalBottom) ||
+ !CheckEitherEndClean(map, rect.X, rect.Right, y, y, Edge.HorizontalBottom))
+ continue;
+ rect.Height = y - rect.Y + 1;
+ }
+ return rect.Width >= WidthMin && rect.Height >= HeightMin ? rect : Rectangle.Empty;
+ }
+
+ private bool CheckEdge(byte[,] map, int left, int right, int top, int bottom, Edge edge)
+ {
+ var n = 0;
+ switch (edge)
+ {
+ case Edge.HorizontalTop:
+ for (; left < right; left++)
{
- if (++n >= corner)
- map[y - corner + 1, x] |= 4;
+ if (!(map[top, left] == 1 && map[top + 1, left] == 0))
+ continue;
+ if (++n < WidthMin / 3)
+ continue;
+ return true;
}
- else
+ return false;
+ case Edge.VerticalLeft:
+ for (; top < bottom; top++)
{
- n = 0;
+ if (!(map[top, left] == 1 && map[top, left + 1] == 0))
+ continue;
+ if (++n < HeightMin / 3)
+ continue;
+ return true;
}
- }
- }
- var rect = new Rectangle();
- var found = false;
- for (var y = 0; y < height - corner; y++)
- {
- for (var x = 0; x < height - corner; x++)
- {
- if (!(map[y, x] == 7 && map[y + 1, x + 1] == 0))
- continue;
- rect.X = x + 1;
- rect.Y = y + 1;
- for (var x1 = rect.X; x1 < width; x1++)
+ return false;
+ case Edge.HorizontalBottom:
+ for (; left < right; left++)
{
- if ((map[rect.Y, x1] & 4) == 0)
+ if (!(map[bottom, left] == 0 && map[bottom + 1, left] == 1))
continue;
- rect.Width = x1 - rect.X;
- break;
+ if (++n < WidthMin / 3)
+ continue;
+ return true;
}
- if (rect.Width < WidthMin)
- continue;
- for (var y1 = rect.Y; y1 < height; y1++)
+ return false;
+ case Edge.VerticalRight:
+ for (; top < bottom; top++)
{
- if ((map[y1, rect.X] & 2) == 0)
+ if (!(map[top, right] == 0 && map[top, right + 1] == 1))
continue;
- rect.Height = y1 - rect.Y;
- break;
+ if (++n < HeightMin / 3)
+ continue;
+ return true;
}
- if (rect.Height < HeightMin)
- continue;
- found = true;
- break;
+ return false;
+ }
+ return false;
+ }
+
+ private bool CheckEitherEndClean(byte[,] map, int left, int right, int top, int bottom, Edge edge)
+ {
+ switch (edge)
+ {
+ case Edge.HorizontalTop:
+ for (var x = left; x <= left + WidthMin / 10; x++)
+ {
+ if (map[top - 1, x] == 0)
+ goto tright;
+ }
+ return true;
+ tright:
+ for (var x = right; x >= right - WidthMin / 10; x--)
+ {
+ if (map[top - 1, x] == 0)
+ return false;
+ }
+ return true;
+ case Edge.VerticalLeft:
+ for (var y = top; y <= top + HeightMin / 10; y++)
+ {
+ if (map[y, left - 1] == 0)
+ goto lbottom;
+ }
+ return true;
+ lbottom:
+ for (var y = bottom; y >= bottom - HeightMin / 10; y--)
+ {
+ if (map[y, left - 1] == 0)
+ return false;
+ }
+ return true;
+ case Edge.HorizontalBottom:
+ for (var x = left; x <= left + WidthMin / 10; x++)
+ {
+ if (map[bottom + 1, x] == 0)
+ goto bright;
+ }
+ return true;
+ bright:
+ for (var x = right; x >= right - WidthMin / 10; x--)
+ {
+ if (map[bottom + 1, x] == 0)
+ return false;
+ }
+ return true;
+ case Edge.VerticalRight:
+ for (var y = top; y <= top + HeightMin / 10; y++)
+ {
+ if (map[y, right + 1] == 0)
+ goto rbottom;
+ }
+ return true;
+ rbottom:
+ for (var y = bottom; y >= bottom - HeightMin / 10; y--)
+ {
+ if (map[y, right + 1] == 0)
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private enum Edge
+ {
+ HorizontalTop,
+ VerticalLeft,
+ HorizontalBottom,
+ VerticalRight
+ }
+
+ public void RoundUpRectangle(byte[,] map, ref Rectangle rect)
+ {
+ var r = rect.Height % 10;
+ if (r != 0)
+ {
+ var top = 0;
+ var bottom = 0;
+ for (var x = rect.X; x < rect.Right; x++)
+ {
+ if (map[rect.Top - 1, x] == 1)
+ top++;
+ if (map[rect.Bottom + 1, x] == 1)
+ bottom++;
}
- if (found)
- break;
+ rect.Height += 10 - r;
+ if (top <= bottom) // expand unbiguous edge
+ rect.Y -= 10 - r;
+ }
+ r = rect.Width % 10;
+ if (r != 0)
+ {
+ var left = 0;
+ var right = 0;
+ for (var y = rect.Y; y < rect.Bottom; y++)
+ {
+ if (map[y, rect.Left - 1] == 1)
+ left++;
+ if (map[y, rect.Right + 1] == 1)
+ right++;
+ }
+ rect.Width += 10 - r;
+ if (right <= left) // expand unbiguous edge
+ rect.X -= 10 - r;
}
- return found ? rect : Rectangle.Empty;
}
}
}
\ No newline at end of file