OSDN Git Service

Capture the region detected previously on failure to detect
[buragesnap/BurageSnap.git] / BurageSnap / Capture.cs
index 28cf091..fde00b6 100644 (file)
@@ -18,6 +18,7 @@
 using System;
 using System.Drawing;
 using System.Drawing.Imaging;
+using System.IO;
 using System.Runtime.InteropServices;
 using System.Text;
 
@@ -27,13 +28,15 @@ namespace BurageSnap
     {
         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);
         }
 
@@ -42,11 +45,24 @@ namespace BurageSnap
             _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);
             }
         }
@@ -89,44 +105,31 @@ namespace BurageSnap
         [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)
@@ -135,7 +138,7 @@ namespace BurageSnap
             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;
@@ -143,80 +146,230 @@ namespace BurageSnap
                 {
                     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