OSDN Git Service

Detect a rectangle with vaguer edges
[buragesnap/BurageSnap.git] / BurageSnap / Capture.cs
1 // Copyright (C) 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
2 //
3 // This program is part of BurageSnap.
4 //
5 // BurageSnap is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.
17
18 using System;
19 using System.Drawing;
20 using System.Drawing.Imaging;
21 using System.Runtime.InteropServices;
22 using System.Text;
23
24 namespace BurageSnap
25 {
26     public class Capture
27     {
28         private const int WidthMin = 600, HeightMin = 400;
29         private IntPtr _hWnd;
30         private Rectangle _rectangle;
31
32         public Bitmap CaptureGameScreen()
33         {
34             if (_hWnd == IntPtr.Zero || _rectangle.IsEmpty)
35                 return null;
36             using (var bmp = CaptureWindow(_hWnd))
37                 return bmp.Clone(_rectangle, bmp.PixelFormat);
38         }
39
40         public Bitmap CaptureGameScreen(string title)
41         {
42             _hWnd = FindWindow(title);
43             if (_hWnd == IntPtr.Zero)
44                 return null;
45             using (var bmp = CaptureWindow(_hWnd))
46             {
47                 _rectangle = DetectGameScreen(bmp);
48                 if (_rectangle.IsEmpty)
49                     return null;
50                 return bmp.Clone(_rectangle, bmp.PixelFormat);
51             }
52         }
53
54         private IntPtr FindWindow(string title)
55         {
56             var found = IntPtr.Zero;
57             EnumWindows((hWnd, lParam) =>
58             {
59                 var rect = new Rect();
60                 if (GetWindowRect(hWnd, ref rect) == 0 || rect.Right - rect.Left < WidthMin ||
61                     rect.Bottom - rect.Top < HeightMin)
62                     return true;
63                 if (!GetWindowText(hWnd).Contains(title))
64                     return true;
65                 found = hWnd;
66                 return false;
67             }, IntPtr.Zero);
68             return found;
69         }
70
71         public static string GetWindowText(IntPtr hWnd)
72         {
73             var size = GetWindowTextLength(hWnd);
74             if (size == 0)
75                 return "";
76             var sb = new StringBuilder(size + 1);
77             GetWindowText(hWnd, sb, sb.Capacity);
78             return sb.ToString();
79         }
80
81         [DllImport("user32.dll", CharSet = CharSet.Unicode)]
82         private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
83
84         [DllImport("user32.dll", CharSet = CharSet.Unicode)]
85         private static extern int GetWindowTextLength(IntPtr hWnd);
86
87         public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
88
89         [DllImport("user32.dll", CharSet = CharSet.Unicode)]
90         private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
91
92         public static Bitmap CaptureWindow(IntPtr hWnd)
93         {
94             var rect = new Rect();
95             GetWindowRect(hWnd, ref rect);
96             var width = rect.Right - rect.Left;
97             var height = rect.Bottom - rect.Top;
98             var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
99             using (var g = Graphics.FromImage(bmp))
100                 g.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
101             return bmp;
102         }
103
104         [DllImport("user32.dll")]
105         private static extern int GetWindowRect(IntPtr hWnd, ref Rect lpRec);
106
107         [StructLayout(LayoutKind.Sequential)]
108         public struct Rect
109         {
110             public int Left;
111             public int Top;
112             public int Right;
113             public int Bottom;
114         }
115
116         private Rectangle DetectGameScreen(Bitmap bmp)
117         {
118             var height = bmp.Height;
119             var width = bmp.Width;
120             var map = new byte[height, width];
121             var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
122                 PixelFormat.Format24bppRgb);
123             unsafe
124             {
125                 var ptr = (byte*)data.Scan0;
126                 for (var y = 0; y < data.Height; y++)
127                 {
128                     for (var x = 0; x < data.Width; x++)
129                     {
130                         var p = ptr + y * data.Stride + x * 3;
131                         map[y, x] = (byte)(p[0] == 255 && p[1] == 255 && p[2] == 255 ? 1 : 0);
132                     }
133                 }
134             }
135             bmp.UnlockBits(data);
136             for (var y = 0; y < height - 1; y++)
137             {
138                 var rect = Rectangle.Empty;
139                 if (!CheckEdge(map, 0, width, y, y, Edge.HorizontalTop))
140                     continue;
141                 rect.Y = y + 1;
142                 for (var x = 0; x < width - 1; x++)
143                 {
144                     if (!CheckEdge(map, x, x, rect.Y, height, Edge.VerticalLeft))
145                         continue;
146                     rect.X = x + 1;
147                     rect = FindBottomAndRight(map, rect);
148                     if (rect == Rectangle.Empty)
149                         continue;
150                     if (!CheckEdge(map, rect.X, rect.Right, y, y, Edge.HorizontalTop))
151                         break;
152                     if (CheckEdge(map, x, x, rect.Y, rect.Bottom, Edge.VerticalLeft))
153                         return rect;
154                 }
155             }
156             return Rectangle.Empty;
157         }
158
159         private Rectangle FindBottomAndRight(byte[,] map, Rectangle rect)
160         {
161             var height = map.GetLength(0);
162             var width = map.GetLength(1);
163             for (var y = rect.Y; y < height - 1; y++)
164             {
165                 if (!CheckEdge(map, rect.X, width, y, y, Edge.HorizontalBottom))
166                     continue;
167                 rect.Height = y - rect.Y + 1;
168                 rect.Width = 0;
169                 for (var x = rect.X; x < width - 1; x++)
170                 {
171                     if (!CheckEdge(map, x, x, rect.Y, rect.Bottom, Edge.VerticalRight))
172                         continue;
173                     rect.Width = x - rect.X + 1;
174                     break;
175                 }
176                 if (rect.Width == 0)
177                     return Rectangle.Empty;
178                 if (!CheckEdge(map, rect.X, rect.Right, y, y, Edge.HorizontalBottom))
179                     continue;
180                 return rect.Width >= WidthMin && rect.Height >= HeightMin ? rect : Rectangle.Empty;
181             }
182             return Rectangle.Empty;
183         }
184
185         private bool CheckEdge(byte[,] map, int left, int right, int top, int bottom, Edge edge)
186         {
187             var n = 0;
188             switch (edge)
189             {
190                 case Edge.HorizontalTop:
191                     for (; left < right; left++)
192                     {
193                         if (!(map[top, left] == 1 && map[top + 1, left] == 0))
194                             continue;
195                         if (++n < WidthMin / 3)
196                             continue;
197                         return true;
198                     }
199                     return false;
200                 case Edge.VerticalLeft:
201                     for (; top < bottom; top++)
202                     {
203                         if (!(map[top, left] == 1 && map[top, left + 1] == 0))
204                             continue;
205                         if (++n < HeightMin / 3)
206                             continue;
207                         return true;
208                     }
209                     return false;
210                 case Edge.HorizontalBottom:
211                     for (; left < right; left++)
212                     {
213                         if (!(map[bottom, left] == 0 && map[bottom + 1, left] == 1))
214                             continue;
215                         if (++n < WidthMin / 3)
216                             continue;
217                         return true;
218                     }
219                     return false;
220                 case Edge.VerticalRight:
221                     for (; top < bottom; top++)
222                     {
223                         if (!(map[top, right] == 0 && map[top, right + 1] == 1))
224                             continue;
225                         if (++n < HeightMin / 3)
226                             continue;
227                         return true;
228                     }
229                     return false;
230             }
231             return false;
232         }
233
234         private enum Edge
235         {
236             HorizontalTop,
237             VerticalLeft,
238             HorizontalBottom,
239             VerticalRight
240         }
241     }
242 }