summaryrefslogtreecommitdiff
path: root/Specific/grabber.py
blob: badec7cff707183678d06c435544c01d8b70fa8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from ctypes import Structure, c_int, POINTER, WINFUNCTYPE, windll, WinError, sizeof
from ctypes.wintypes import BOOL, HWND, RECT, HDC, HBITMAP, HGDIOBJ, DWORD, LONG, WORD, UINT, LPVOID
import numpy as np

SRCCOPY = 0x00CC0020
DIB_RGB_COLORS = 0
BI_RGB = 0


class BITMAPINFOHEADER(Structure):
	_fields_ = [('biSize', DWORD),
							('biWidth', LONG),
							('biHeight', LONG),
							('biPlanes', WORD),
							('biBitCount', WORD),
							('biCompression', DWORD),
							('biSizeImage', DWORD),
							('biXPelsPerMeter', LONG),
							('biYPelsPerMeter', LONG),
							('biClrUsed', DWORD),
							('biClrImportant', DWORD)]


def err_on_zero_or_null_check(result, func, args):
	if not result:
		raise WinError()
	return args


def quick_win_define(name, output, *args, **kwargs):
	dllname, fname = name.split('.')
	params = kwargs.get('params', None)
	if params:
		params = tuple([(x, ) for x in params])
	func = (WINFUNCTYPE(output, *args))((fname, getattr(windll, dllname)), params)
	err = kwargs.get('err', err_on_zero_or_null_check)
	if err:
		func.errcheck = err
	return func


GetClientRect = quick_win_define('user32.GetClientRect', BOOL, HWND, POINTER(RECT), params=(1, 2))
GetDC = quick_win_define('user32.GetDC', HDC, HWND)
CreateCompatibleDC = quick_win_define('gdi32.CreateCompatibleDC', HDC, HDC)
CreateCompatibleBitmap = quick_win_define('gdi32.CreateCompatibleBitmap', HBITMAP, HDC, c_int, c_int)
ReleaseDC = quick_win_define('user32.ReleaseDC', c_int, HWND, HDC)
DeleteDC = quick_win_define('gdi32.DeleteDC', BOOL, HDC)
DeleteObject = quick_win_define('gdi32.DeleteObject', BOOL, HGDIOBJ)
SelectObject = quick_win_define('gdi32.SelectObject', HGDIOBJ, HDC, HGDIOBJ)
BitBlt = quick_win_define('gdi32.BitBlt', BOOL, HDC, c_int, c_int, c_int, c_int, HDC, c_int, c_int, DWORD)
GetDIBits = quick_win_define('gdi32.GetDIBits', c_int, HDC, HBITMAP, UINT, UINT, LPVOID, POINTER(BITMAPINFOHEADER), UINT)
GetDesktopWindow = quick_win_define('user32.GetDesktopWindow', HWND)


class Grabber(object):
	def __init__(self, window=None, with_alpha=False, bbox=None):
		window = window or GetDesktopWindow()
		self.window = window
		rect = GetClientRect(window)
		self.width = rect.right - rect.left
		self.height = rect.bottom - rect.top
		if bbox:
			bbox = [bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]]
			if not bbox[2] or not bbox[3]:
				bbox[2] = self.width - bbox[0]
				bbox[3] = self.height - bbox[1]
			self.x, self.y, self.width, self.height = bbox
		else:
			self.x = 0
			self.y = 0
		self.windowDC = GetDC(window)
		self.memoryDC = CreateCompatibleDC(self.windowDC)
		self.bitmap = CreateCompatibleBitmap(self.windowDC, self.width, self.height)
		self.bitmapInfo = BITMAPINFOHEADER()
		self.bitmapInfo.biSize = sizeof(BITMAPINFOHEADER)
		self.bitmapInfo.biPlanes = 1
		self.bitmapInfo.biBitCount = 32 if with_alpha else 24
		self.bitmapInfo.biWidth = self.width
		self.bitmapInfo.biHeight = -self.height
		self.bitmapInfo.biCompression = BI_RGB
		self.bitmapInfo.biSizeImage = 0
		self.channels = 4 if with_alpha else 3
		self.closed = False


	def __del__(self):
		try:
			self.close()
		except:
			pass


	def close(self):
		if self.closed:
			return
		ReleaseDC(self.window, self.windowDC)
		DeleteDC(self.memoryDC)
		DeleteObject(self.bitmap)
		self.closed = True


	def grab(self, output=None):
		if self.closed:
			raise ValueError('Grabber already closed')
		if output is None:
			output = np.empty((self.height, self.width, self.channels), dtype='uint8')
		else:
			if output.shape != (self.height, self.width, self.channels):
				raise ValueError('Invalid output dimentions')
		SelectObject(self.memoryDC, self.bitmap)
		BitBlt(self.memoryDC, 0, 0, self.width, self.height, self.windowDC, self.x, self.y, SRCCOPY)
		GetDIBits(self.memoryDC, self.bitmap, 0, self.height, output.ctypes.data, self.bitmapInfo, DIB_RGB_COLORS)
		return output