From 0a272ccc7f5a0fc6a60336f4e9c26e6756846a0f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 18 Oct 2025 11:13:34 +1100 Subject: [PATCH 1/2] Do not resize macOS retina screenshots by default --- Tests/test_imagegrab.py | 11 +++++++++-- docs/reference/ImageGrab.rst | 18 +++++++++++++----- src/PIL/ImageGrab.py | 4 +++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 01fa090dc3a..eb18c65d92e 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -25,8 +25,15 @@ def test_grab(self) -> None: ImageGrab.grab(include_layered_windows=True) ImageGrab.grab(all_screens=True) - im = ImageGrab.grab(bbox=(10, 20, 50, 80)) - assert im.size == (40, 60) + if sys.platform == "darwin": + im = ImageGrab.grab(bbox=(10, 20, 50, 80)) + assert im.size in ((40, 60), (80, 120)) + + im = ImageGrab.grab(bbox=(10, 20, 50, 80), scale_down=True) + assert im.size == (40, 60) + else: + im = ImageGrab.grab(bbox=(10, 20, 50, 80)) + assert im.size == (40, 60) @skip_unless_feature("xcb") def test_grab_x11(self) -> None: diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 5c3a73fada1..d8b50764b38 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -9,11 +9,14 @@ or the clipboard to a PIL image memory. .. versionadded:: 1.1.3 -.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None, window=None) +.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None, window=None, scale_down=False) Take a snapshot of the screen. The pixels inside the bounding box are returned as - an "RGBA" on macOS, or an "RGB" image otherwise. If the bounding box is omitted, - the entire screen is copied, and on macOS, it will be at 2x if on a Retina screen. + "RGBA" on macOS, or "RGB" image otherwise. If the bounding box is omitted, + the entire screen is copied. + + On macOS, it will be at 2x if on a Retina screen. If this is not desired, pass + ``scale_down=True``. On Linux, if ``xdisplay`` is ``None`` and the default X11 display does not return a snapshot of the screen, ``gnome-screenshot``, ``grim`` or ``spectacle`` will be @@ -25,8 +28,8 @@ or the clipboard to a PIL image memory. .. versionadded:: 7.1.0 Linux support :param bbox: What region to copy. Default is the entire screen. - On macOS, this is not increased to 2x for Retina screens, so the full - width of a Retina screen would be 1440, not 2880. + On macOS, this is increased to 2x for Retina screens, so the full + width of a Retina screen would be 2880, not 1440. On Windows, the top-left point may be negative if ``all_screens=True`` is used. :param include_layered_windows: Includes layered windows. Windows OS only. @@ -47,6 +50,11 @@ or the clipboard to a PIL image memory. HWND, to capture a single window. Windows only. .. versionadded:: 11.2.1 + + :param scale_down: On macOS, Retina screens will provide images at 2x size by default. This will prevent that, and scale down to 1x. + Keyword-only argument. + + .. versionadded:: 12.1.0 :return: An image .. py:function:: grabclipboard() diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 1eb4507344c..e41f92806c8 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -36,6 +36,8 @@ def grab( all_screens: bool = False, xdisplay: str | None = None, window: int | ImageWin.HWND | None = None, + *, + scale_down: bool = False, ) -> Image.Image: im: Image.Image if xdisplay is None: @@ -50,7 +52,7 @@ def grab( im = Image.open(filepath) im.load() os.unlink(filepath) - if bbox: + if bbox and scale_down: im_resized = im.resize((right - left, bottom - top)) im.close() return im_resized From 3dc1937cae141c831ef017214432f412ccb433f6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 11 Dec 2025 17:10:32 +1100 Subject: [PATCH 2/2] Added release notes --- docs/releasenotes/12.1.0.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/releasenotes/12.1.0.rst b/docs/releasenotes/12.1.0.rst index b6e1810c60a..17e2402eb6f 100644 --- a/docs/releasenotes/12.1.0.rst +++ b/docs/releasenotes/12.1.0.rst @@ -41,6 +41,16 @@ TODO API additions ============= +Added ``scale_down`` argument to ``ImageGrab.grab()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:py:meth:`~PIL.ImageGrab.grab` now accepts an optional keyword argument of +``scale_down``. This affects macOS screenshots with a ``bbox`` on a Retina screen. By +default, images will be captured at 2x. If ``scale_down`` is ``True``, they will be at +1x. + +Previously, macOS screenshots with a ``bbox`` were captured at 1x by default. + Specify window in ImageGrab on macOS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^