Skip to content

Commit 5beb446

Browse files
authored
Do not resize macOS retina screenshots by default (#9266)
1 parent a46c1c0 commit 5beb446

4 files changed

Lines changed: 36 additions & 13 deletions

File tree

Tests/test_imagegrab.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,15 @@ def test_grab(self) -> None:
2525
ImageGrab.grab(include_layered_windows=True)
2626
ImageGrab.grab(all_screens=True)
2727

28-
im = ImageGrab.grab(bbox=(10, 20, 50, 80))
29-
assert im.size == (40, 60)
28+
if sys.platform == "darwin":
29+
im = ImageGrab.grab(bbox=(10, 20, 50, 80))
30+
assert im.size in ((40, 60), (80, 120))
31+
32+
im = ImageGrab.grab(bbox=(10, 20, 50, 80), scale_down=True)
33+
assert im.size == (40, 60)
34+
else:
35+
im = ImageGrab.grab(bbox=(10, 20, 50, 80))
36+
assert im.size == (40, 60)
3037

3138
@skip_unless_feature("xcb")
3239
def test_grab_x11(self) -> None:
@@ -81,7 +88,7 @@ def test_grab_handle(self) -> None:
8188

8289
ImageGrab.grab(window=window)
8390

84-
im = ImageGrab.grab((0, 0, 10, 10), window=window)
91+
im = ImageGrab.grab((0, 0, 10, 10), window=window, scale_down=True)
8592
assert im.size == (10, 10)
8693

8794
@pytest.mark.skipif(

docs/reference/ImageGrab.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ or the clipboard to a PIL image memory.
99

1010
.. versionadded:: 1.1.3
1111

12-
.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None, window=None)
12+
.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None, window=None, scale_down=False)
1313
1414
Take a snapshot of the screen. The pixels inside the bounding box are returned as
15-
an "RGBA" on macOS, or an "RGB" image otherwise. If the bounding box is omitted,
16-
the entire screen is copied, and on macOS, it will be at 2x if on a Retina screen.
15+
"RGBA" on macOS, or "RGB" image otherwise. If the bounding box is omitted,
16+
the entire screen is copied.
17+
18+
On macOS, it will be at 2x if on a Retina screen. If this is not desired, pass
19+
``scale_down=True``.
1720

1821
On Linux, if ``xdisplay`` is ``None`` and the default X11 display does not return
1922
a snapshot of the screen, ``gnome-screenshot``, ``grim`` or ``spectacle`` will be
@@ -25,8 +28,8 @@ or the clipboard to a PIL image memory.
2528
.. versionadded:: 7.1.0 Linux support
2629

2730
:param bbox: What region to copy. Default is the entire screen.
28-
On macOS, this is not increased to 2x for Retina screens, so the full
29-
width of a Retina screen would be 1440, not 2880.
31+
On macOS, this is increased to 2x for Retina screens, so the full
32+
width of a Retina screen would be 2880, not 1440.
3033
On Windows, the top-left point may be negative if ``all_screens=True``
3134
is used.
3235
:param include_layered_windows: Includes layered windows. Windows OS only.
@@ -49,6 +52,11 @@ or the clipboard to a PIL image memory.
4952

5053
.. versionadded:: 11.2.1 Windows support
5154
.. versionadded:: 12.1.0 macOS support
55+
56+
:param scale_down: On macOS, Retina screens will provide ``bbox`` images at 2x size by default. This will prevent that, and scale down to 1x.
57+
Keyword-only argument.
58+
59+
.. versionadded:: 12.3.0
5260
:return: An image
5361

5462
.. py:function:: grabclipboard()

docs/releasenotes/12.3.0.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ TODO
4141
API additions
4242
=============
4343

44-
TODO
45-
^^^^
44+
Added ``scale_down`` argument to ``ImageGrab.grab()``
45+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4646

47-
TODO
47+
:py:meth:`~PIL.ImageGrab.grab` now accepts an optional keyword argument of
48+
``scale_down``. This affects macOS screenshots with a ``bbox`` on a Retina screen. By
49+
default, images will be captured at 2x. If ``scale_down`` is ``True``, they will be at
50+
1x.
51+
52+
Previously, macOS screenshots with a ``bbox`` were captured at 1x by default.
4853

4954
Other changes
5055
=============

src/PIL/ImageGrab.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def grab(
3636
all_screens: bool = False,
3737
xdisplay: str | None = None,
3838
window: int | ImageWin.HWND | None = None,
39+
*,
40+
scale_down: bool = False,
3941
) -> Image.Image:
4042
im: Image.Image
4143
if xdisplay is None:
@@ -74,15 +76,16 @@ def grab(
7476
# crop the image manually
7577
if retina:
7678
left, top, right, bottom = bbox
79+
scale = 1 if scale_down else 2
7780
im_cropped = im.resize(
78-
(right - left, bottom - top),
81+
((right - left) * scale, (bottom - top) * scale),
7982
box=tuple(coord * 2 for coord in bbox),
8083
)
8184
else:
8285
im_cropped = im.crop(bbox)
8386
im.close()
8487
return im_cropped
85-
else:
88+
elif scale_down:
8689
im_resized = im.resize((right - left, bottom - top))
8790
im.close()
8891
return im_resized

0 commit comments

Comments
 (0)