diff --git a/src/about.py b/src/about.py index 93f90bfe..66bd36f1 100644 --- a/src/about.py +++ b/src/about.py @@ -59,7 +59,7 @@ def retranslateUi(self, aboutAutoSplitWidget): aboutAutoSplitWidget.setWindowTitle(_translate("aboutAutoSplitWidget", "About AutoSplit", None)) self.okButton.setText(_translate("aboutAutoSplitWidget", "OK", None)) self.createdbyLabel.setText(_translate("aboutAutoSplitWidget", "
", None)) - self.versionLabel.setText(_translate("aboutAutoSplitWidget", "Version: 1.5.1", None)) + self.versionLabel.setText(_translate("aboutAutoSplitWidget", "Version: 1.5.2", None)) self.donatetextLabel.setText(_translate("aboutAutoSplitWidget", "If you enjoy using this program, please\n" " consider donating. Thank you!", None)) self.donatebuttonLabel.setText(_translate("aboutAutoSplitWidget", "", None)) diff --git a/src/capture_windows.py b/src/capture_windows.py index 7e3d928e..b0a9c84f 100644 --- a/src/capture_windows.py +++ b/src/capture_windows.py @@ -1,12 +1,18 @@ from ctypes import windll -from ctypes.wintypes import LONG, RECT +from ctypes.wintypes import LONG, RECT, HBITMAP +from typing import Dict from win32 import win32gui +import sys +from packaging import version +import platform import numpy as np import win32ui import win32con # This is an undocumented nFlag value for PrintWindow PW_RENDERFULLCONTENT = 0x00000002 +accelerated_windows: Dict[int, bool] = {} +is_windows_11 = version.parse(platform.version()) >= version.parse("10.0.22000") def capture_region(hwnd: int, rect: RECT): @@ -19,26 +25,44 @@ def capture_region(hwnd: int, rect: RECT): @return: The image of the region in the window in BGRA format """ + # Windows 11 has some jank, and we're not ready to fully investigate it + # for now let's ensure it works at the cost of performance + is_accelerated_window = is_windows_11 or accelerated_windows.get(hwnd) + + # The window type is not yet known, let's find out! + if is_accelerated_window is None: + # We need to get the image at least once to check if it's full black + image = __get_image(hwnd, rect, False) + # TODO check for first non-black pixel, no need to iterate through the whole image + is_accelerated_window = not np.count_nonzero(image) + accelerated_windows[hwnd] = is_accelerated_window + return __get_image(hwnd, rect, True) if is_accelerated_window else image + + return __get_image(hwnd, rect, is_accelerated_window) + + +def __get_image(hwnd: int, rect: RECT, print_window=False): width: LONG = rect.right - rect.left height: LONG = rect.bottom - rect.top - - windowDC = win32gui.GetWindowDC(hwnd) + windowDC: int = win32gui.GetWindowDC(hwnd) dcObject = win32ui.CreateDCFromHandle(windowDC) + + # Causes a 10-15x performance drop. But allows recording hardware accelerated windows + if (print_window): + windll.user32.PrintWindow(hwnd, dcObject.GetSafeHdc(), PW_RENDERFULLCONTENT) + compatibleDC = dcObject.CreateCompatibleDC() - bmp = win32ui.CreateBitmap() - bmp.CreateCompatibleBitmap(dcObject, width, height) - compatibleDC.SelectObject(bmp) + bitmap: HBITMAP = win32ui.CreateBitmap() + bitmap.CreateCompatibleBitmap(dcObject, width, height) + compatibleDC.SelectObject(bitmap) compatibleDC.BitBlt((0, 0), (width, height), dcObject, (rect.left, rect.top), win32con.SRCCOPY) - # Force render full content through PrintWindow. Workaround to capture hardware accelerated windows - windll.user32.PrintWindow(hwnd, dcObject.GetSafeHdc(), PW_RENDERFULLCONTENT) - - img: np._BufferType = np.frombuffer(bmp.GetBitmapBits(True), dtype='uint8') - img.shape = (height, width, 4) + image: np._BufferType = np.frombuffer(bitmap.GetBitmapBits(True), dtype='uint8') + image.shape = (height, width, 4) dcObject.DeleteDC() compatibleDC.DeleteDC() win32gui.ReleaseDC(hwnd, windowDC) - win32gui.DeleteObject(bmp.GetHandle()) + win32gui.DeleteObject(bitmap.GetHandle()) - return img + return image