-
Notifications
You must be signed in to change notification settings - Fork 1
/
screen_capture_source_windows.py
118 lines (100 loc) · 3.97 KB
/
screen_capture_source_windows.py
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
114
115
116
117
118
import cv2
import numpy as np
import pygetwindow as gw
import numpy as np
import win32gui
import win32ui
import win32con
import win32api
from sc_logging import logger
from ctypes import windll
class ScreenCaptureWindows:
@staticmethod
def list_windows():
# list the windows on the screen
windows = gw.getAllTitles()
return [(w, w) for w in windows if w != ""]
def __init__(self, window_name):
self.capture_whole_screen = (
window_name is None
or window_name == ""
or (type(window_name) == int and window_name < 0)
)
self.hwnd = 0
if self.capture_whole_screen:
# Get the device context (DC) for the entire screen
self.hwnd = win32gui.GetDesktopWindow()
else:
# Find the window
self.hwnd = win32gui.FindWindow(None, window_name)
if self.hwnd == 0:
logger.error("No window found")
return
# Get the window device context (DC)
self.hwndDC = win32gui.GetWindowDC(self.hwnd)
if self.hwndDC == 0:
logger.error("Error getting window device context for window")
return
# Create a memory device context
self.mfcDC = win32ui.CreateDCFromHandle(self.hwndDC)
if self.mfcDC is None:
logger.error("Error creating memory device context for window")
return
self.saveDC = self.mfcDC.CreateCompatibleDC()
if self.saveDC is None:
logger.error(f"Error creating compatible memory device context for window")
return
def isOpened(self):
return self.hwnd != 0
def release(self):
try:
self.saveDC.DeleteDC()
self.mfcDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, self.hwndDC)
except:
logger.exception("Error releasing screen capture resources")
def read(self):
if self.hwnd == 0:
return False, None
# check window handle is valid
if not win32gui.IsWindow(self.hwnd):
logger.error(f"Window handle {self.hwnd} is invalid")
return False, None
if not self.capture_whole_screen:
# Get window dimensions
left, top, right, bot = win32gui.GetWindowRect(self.hwnd)
width = right - left
height = bot - top
else:
# Get the device context (DC) for the entire screen
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
# Create a bitmap object
saveBitMap = win32ui.CreateBitmap()
if saveBitMap is None:
logger.error(f"Error creating bitmap for window")
return False, None
saveBitMap.CreateCompatibleBitmap(self.mfcDC, width, height)
self.saveDC.SelectObject(saveBitMap)
if self.capture_whole_screen:
# use bitblt to copy the window image to the bitmap
self.saveDC.BitBlt(
(0, 0), (width, height), self.mfcDC, (left, top), win32con.SRCCOPY
)
else:
# use print window to copy the window image to the bitmap
result = windll.user32.PrintWindow(self.hwnd, self.saveDC.GetSafeHdc(), 3)
if not result:
logger.error(f"Unable to acquire screenshot! Result: {result}")
return False, None
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
img = np.frombuffer(bmpstr, dtype=np.uint8).reshape(
(bmpinfo["bmHeight"], bmpinfo["bmWidth"], 4)
)
win32gui.DeleteObject(saveBitMap.GetHandle())
image = np.ascontiguousarray(img) # Ensure array is contiguous
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
return True, image