OSDN Git Service

Refactor FormMain into some classes
[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 winDC = GetWindowDC(hWnd);
95             var rect = new Rect();
96             GetWindowRect(hWnd, ref rect);
97             var bmp = new Bitmap(rect.Right - rect.Left, rect.Bottom - rect.Top, PixelFormat.Format32bppArgb);
98             var g = Graphics.FromImage(bmp);
99             var hDC = g.GetHdc();
100             BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, winDC, 0, 0, SRCCOPY);
101             g.ReleaseHdc(hDC);
102             g.Dispose();
103             ReleaseDC(hWnd, winDC);
104             return bmp;
105         }
106
107         [DllImport("user32.dll")]
108         private static extern IntPtr GetWindowDC(IntPtr hWnd);
109
110         [DllImport("user32.dll")]
111         private static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
112
113         // ReSharper disable once InconsistentNaming
114         private const int SRCCOPY = 0xcc0020;
115
116         [DllImport("gdi32.dll")]
117         private static extern int BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight,
118             IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
119
120         [DllImport("user32.dll")]
121         private static extern int GetWindowRect(IntPtr hWnd, ref Rect lpRec);
122
123         [StructLayout(LayoutKind.Sequential)]
124         public struct Rect
125         {
126             public int Left;
127             public int Top;
128             public int Right;
129             public int Bottom;
130         }
131
132         private Rectangle DetectGameScreen(Bitmap bmp)
133         {
134             var height = bmp.Height;
135             var width = bmp.Width;
136             var map = new byte[height, width];
137             var data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
138                 PixelFormat.Format32bppArgb);
139             unsafe
140             {
141                 var ptr = (byte*)data.Scan0;
142                 for (var y = 0; y < data.Height; y++)
143                 {
144                     for (var x = 0; x < data.Width; x++)
145                     {
146                         var p = ptr + y * data.Stride + x * 4;
147                         map[y, x] = (byte)(p[0] == 255 && p[1] == 255 && p[2] == 255 ? 1 : 0);
148                     }
149                 }
150             }
151             bmp.UnlockBits(data);
152             const int corner = 40;
153             for (var y = 0; y < height; y++)
154             {
155                 var n = 0;
156                 for (var x = 0; x < width; x++)
157                 {
158                     if ((map[y, x] & 1) == 1)
159                     {
160                         if (++n >= corner)
161                             map[y, x - corner + 1] |= 2;
162                     }
163                     else
164                     {
165                         n = 0;
166                     }
167                 }
168             }
169             for (var x = 0; x < width; x++)
170             {
171                 var n = 0;
172                 for (var y = 0; y < height; y++)
173                 {
174                     if ((map[y, x] & 1) == 1)
175                     {
176                         if (++n >= corner)
177                             map[y - corner + 1, x] |= 4;
178                     }
179                     else
180                     {
181                         n = 0;
182                     }
183                 }
184             }
185             var rect = new Rectangle();
186             var found = false;
187             for (var y = 0; y < height - corner; y++)
188             {
189                 for (var x = 0; x < height - corner; x++)
190                 {
191                     if (!(map[y, x] == 7 && map[y + 1, x + 1] == 0))
192                         continue;
193                     rect.X = x + 1;
194                     rect.Y = y + 1;
195                     for (var x1 = rect.X; x1 < width; x1++)
196                     {
197                         if ((map[rect.Y, x1] & 4) == 0)
198                             continue;
199                         rect.Width = x1 - rect.X;
200                         break;
201                     }
202                     if (rect.Width < WidthMin)
203                         continue;
204                     for (var y1 = rect.Y; y1 < height; y1++)
205                     {
206                         if ((map[y1, rect.X] & 2) == 0)
207                             continue;
208                         rect.Height = y1 - rect.Y;
209                         break;
210                     }
211                     if (rect.Height < HeightMin)
212                         continue;
213                     found = true;
214                     break;
215                 }
216                 if (found)
217                     break;
218             }
219             return found ? rect : Rectangle.Empty;
220         }
221     }
222 }