Skip to content

Commit e91aedb

Browse files
authored
Merge pull request #8352 from radarhere/scale
2 parents 302b63f + 5cb736d commit e91aedb

File tree

5 files changed

+77
-42
lines changed

5 files changed

+77
-42
lines changed

Tests/test_file_icns.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def test_save_append_images(tmp_path: Path) -> None:
6363
assert_image_similar_tofile(im, temp_file, 1)
6464

6565
with Image.open(temp_file) as reread:
66-
reread.size = (16, 16, 2)
67-
reread.load()
66+
reread.size = (16, 16)
67+
reread.load(2)
6868
assert_image_equal(reread, provided_im)
6969

7070

@@ -87,14 +87,21 @@ def test_sizes() -> None:
8787
for w, h, r in im.info["sizes"]:
8888
wr = w * r
8989
hr = h * r
90-
im.size = (w, h, r)
90+
with pytest.warns(DeprecationWarning):
91+
im.size = (w, h, r)
9192
im.load()
9293
assert im.mode == "RGBA"
9394
assert im.size == (wr, hr)
9495

96+
# Test using load() with scale
97+
im.size = (w, h)
98+
im.load(scale=r)
99+
assert im.mode == "RGBA"
100+
assert im.size == (wr, hr)
101+
95102
# Check that we cannot load an incorrect size
96103
with pytest.raises(ValueError):
97-
im.size = (1, 1)
104+
im.size = (1, 2)
98105

99106

100107
def test_older_icon() -> None:
@@ -105,8 +112,8 @@ def test_older_icon() -> None:
105112
wr = w * r
106113
hr = h * r
107114
with Image.open("Tests/images/pillow2.icns") as im2:
108-
im2.size = (w, h, r)
109-
im2.load()
115+
im2.size = (w, h)
116+
im2.load(r)
110117
assert im2.mode == "RGBA"
111118
assert im2.size == (wr, hr)
112119

@@ -122,8 +129,8 @@ def test_jp2_icon() -> None:
122129
wr = w * r
123130
hr = h * r
124131
with Image.open("Tests/images/pillow3.icns") as im2:
125-
im2.size = (w, h, r)
126-
im2.load()
132+
im2.size = (w, h)
133+
im2.load(r)
127134
assert im2.mode == "RGBA"
128135
assert im2.size == (wr, hr)
129136

docs/deprecations.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ vulnerability introduced in FreeType 2.6 (:cve:`2020-15999`).
122122

123123
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
124124

125+
ICNS (width, height, scale) sizes
126+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
127+
128+
.. deprecated:: 11.0.0
129+
130+
Setting an ICNS image size to ``(width, height, scale)`` before loading has been
131+
deprecated. Instead, ``load(scale)`` can be used.
132+
125133
Image isImageType()
126134
^^^^^^^^^^^^^^^^^^^
127135

docs/handbook/image-file-formats.rst

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,12 +324,19 @@ sets the following :py:attr:`~PIL.Image.Image.info` property:
324324
**sizes**
325325
A list of supported sizes found in this icon file; these are a
326326
3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina
327-
icon and 1 for a standard icon. You *are* permitted to use this 3-tuple
328-
format for the :py:attr:`~PIL.Image.Image.size` property if you set it
329-
before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size
330-
will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you
331-
ask for ``(512, 512, 2)``, the final value of
332-
:py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``).
327+
icon and 1 for a standard icon.
328+
329+
.. _icns-loading:
330+
331+
Loading
332+
~~~~~~~
333+
334+
You can call the :py:meth:`~PIL.Image.Image.load` method with the following parameter.
335+
336+
**scale**
337+
Affects the scale of the resultant image. If the size is set to ``(512, 512)``,
338+
after loading at scale 2, the final value of :py:attr:`~PIL.Image.Image.size` will
339+
be ``(1024, 1024)``.
333340

334341
.. _icns-saving:
335342

docs/releasenotes/11.0.0.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ vulnerability introduced in FreeType 2.6 (:cve:`2020-15999`).
6161

6262
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
6363

64+
ICNS (width, height, scale) sizes
65+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
67+
.. deprecated:: 11.0.0
68+
69+
Setting an ICNS image size to ``(width, height, scale)`` before loading has been
70+
deprecated. Instead, ``load(scale)`` can be used.
71+
6472
Image isImageType()
6573
^^^^^^^^^^^^^^^^^^^
6674

@@ -72,6 +80,8 @@ instead.
7280
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
7381
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7482

83+
.. deprecated:: 11.0.0
84+
7585
The ``options`` parameter in :py:meth:`~PIL.ImageMath.lambda_eval()` and
7686
:py:meth:`~PIL.ImageMath.unsafe_eval()` has been deprecated. One or more
7787
keyword arguments can be used instead.
@@ -87,6 +97,8 @@ have been deprecated, and will be removed in Pillow 12 (2025-10-15).
8797
Specific WebP Feature Checks
8898
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8999

100+
.. deprecated:: 11.0.0
101+
90102
``features.check("transp_webp")``, ``features.check("webp_mux")`` and
91103
``features.check("webp_anim")`` are now deprecated. They will always return
92104
``True`` if the WebP module is installed, until they are removed in Pillow

src/PIL/IcnsImagePlugin.py

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from typing import IO
2626

2727
from . import Image, ImageFile, PngImagePlugin, features
28+
from ._deprecate import deprecate
2829

2930
enable_jpeg2k = features.check_codec("jpg_2000")
3031
if enable_jpeg2k:
@@ -275,37 +276,37 @@ def _open(self) -> None:
275276
self.best_size[1] * self.best_size[2],
276277
)
277278

278-
@property
279-
def size(self):
279+
@property # type: ignore[override]
280+
def size(self) -> tuple[int, int] | tuple[int, int, int]:
280281
return self._size
281282

282283
@size.setter
283-
def size(self, value) -> None:
284-
info_size = value
285-
if info_size not in self.info["sizes"] and len(info_size) == 2:
286-
info_size = (info_size[0], info_size[1], 1)
287-
if (
288-
info_size not in self.info["sizes"]
289-
and len(info_size) == 3
290-
and info_size[2] == 1
291-
):
292-
simple_sizes = [
293-
(size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"]
294-
]
295-
if value in simple_sizes:
296-
info_size = self.info["sizes"][simple_sizes.index(value)]
297-
if info_size not in self.info["sizes"]:
298-
msg = "This is not one of the allowed sizes of this image"
299-
raise ValueError(msg)
300-
self._size = value
301-
302-
def load(self) -> Image.core.PixelAccess | None:
303-
if len(self.size) == 3:
304-
self.best_size = self.size
305-
self.size = (
306-
self.best_size[0] * self.best_size[2],
307-
self.best_size[1] * self.best_size[2],
308-
)
284+
def size(self, value: tuple[int, int] | tuple[int, int, int]) -> None:
285+
if len(value) == 3:
286+
deprecate("Setting size to (width, height, scale)", 12, "load(scale)")
287+
if value in self.info["sizes"]:
288+
self._size = value # type: ignore[assignment]
289+
return
290+
else:
291+
# Check that a matching size exists,
292+
# or that there is a scale that would create a size that matches
293+
for size in self.info["sizes"]:
294+
simple_size = size[0] * size[2], size[1] * size[2]
295+
scale = simple_size[0] // value[0]
296+
if simple_size[1] / value[1] == scale:
297+
self._size = value
298+
return
299+
msg = "This is not one of the allowed sizes of this image"
300+
raise ValueError(msg)
301+
302+
def load(self, scale: int | None = None) -> Image.core.PixelAccess | None:
303+
if scale is not None or len(self.size) == 3:
304+
if scale is None and len(self.size) == 3:
305+
scale = self.size[2]
306+
assert scale is not None
307+
width, height = self.size[:2]
308+
self.size = width * scale, height * scale
309+
self.best_size = width, height, scale
309310

310311
px = Image.Image.load(self)
311312
if self._im is not None and self.im.size == self.size:

0 commit comments

Comments
 (0)