Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate ICNS (width, height, scale) sizes in favour of load(scale) #8352

Merged
merged 4 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions Tests/test_file_icns.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def test_save_append_images(tmp_path: Path) -> None:
assert_image_similar_tofile(im, temp_file, 1)

with Image.open(temp_file) as reread:
reread.size = (16, 16, 2)
reread.load()
reread.size = (16, 16)
reread.load(2)
assert_image_equal(reread, provided_im)


Expand All @@ -87,14 +87,21 @@ def test_sizes() -> None:
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
im.size = (w, h, r)
with pytest.warns(DeprecationWarning):
im.size = (w, h, r)
im.load()
assert im.mode == "RGBA"
assert im.size == (wr, hr)

# Test using load() with scale
im.size = (w, h)
im.load(scale=r)
assert im.mode == "RGBA"
assert im.size == (wr, hr)

# Check that we cannot load an incorrect size
with pytest.raises(ValueError):
im.size = (1, 1)
im.size = (1, 2)


def test_older_icon() -> None:
Expand All @@ -105,8 +112,8 @@ def test_older_icon() -> None:
wr = w * r
hr = h * r
with Image.open("Tests/images/pillow2.icns") as im2:
im2.size = (w, h, r)
im2.load()
im2.size = (w, h)
im2.load(r)
assert im2.mode == "RGBA"
assert im2.size == (wr, hr)

Expand All @@ -122,8 +129,8 @@ def test_jp2_icon() -> None:
wr = w * r
hr = h * r
with Image.open("Tests/images/pillow3.icns") as im2:
im2.size = (w, h, r)
im2.load()
im2.size = (w, h)
im2.load(r)
assert im2.mode == "RGBA"
assert im2.size == (wr, hr)

Expand Down
8 changes: 8 additions & 0 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ vulnerability introduced in FreeType 2.6 (:cve:`2020-15999`).

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

ICNS (width, height, scale) sizes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. deprecated:: 11.0.0

Setting an ICNS image size to ``(width, height, scale)`` before loading has been
deprecated. Instead, ``load(scale)`` can be used.

Image isImageType()
^^^^^^^^^^^^^^^^^^^

Expand Down
19 changes: 13 additions & 6 deletions docs/handbook/image-file-formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,19 @@ sets the following :py:attr:`~PIL.Image.Image.info` property:
**sizes**
A list of supported sizes found in this icon file; these are a
3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina
icon and 1 for a standard icon. You *are* permitted to use this 3-tuple
format for the :py:attr:`~PIL.Image.Image.size` property if you set it
before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size
will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you
ask for ``(512, 512, 2)``, the final value of
:py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``).
icon and 1 for a standard icon.

.. _icns-loading:

Loading
~~~~~~~

You can call the :py:meth:`~PIL.Image.Image.load` method with the following parameter.

**scale**
Affects the scale of the resultant image. If the size is set to ``(512, 512)``,
after loading at scale 2, the final value of :py:attr:`~PIL.Image.Image.size` will
be ``(1024, 1024)``.

.. _icns-saving:

Expand Down
12 changes: 12 additions & 0 deletions docs/releasenotes/11.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ vulnerability introduced in FreeType 2.6 (:cve:`2020-15999`).

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

ICNS (width, height, scale) sizes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. deprecated:: 11.0.0

Setting an ICNS image size to ``(width, height, scale)`` before loading has been
deprecated. Instead, ``load(scale)`` can be used.

Image isImageType()
^^^^^^^^^^^^^^^^^^^

Expand All @@ -72,6 +80,8 @@ instead.
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. deprecated:: 11.0.0

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

.. deprecated:: 11.0.0

``features.check("transp_webp")``, ``features.check("webp_mux")`` and
``features.check("webp_anim")`` are now deprecated. They will always return
``True`` if the WebP module is installed, until they are removed in Pillow
Expand Down
57 changes: 29 additions & 28 deletions src/PIL/IcnsImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from typing import IO

from . import Image, ImageFile, PngImagePlugin, features
from ._deprecate import deprecate

enable_jpeg2k = features.check_codec("jpg_2000")
if enable_jpeg2k:
Expand Down Expand Up @@ -275,37 +276,37 @@ def _open(self) -> None:
self.best_size[1] * self.best_size[2],
)

@property
def size(self):
@property # type: ignore[override]
def size(self) -> tuple[int, int] | tuple[int, int, int]:
return self._size

@size.setter
def size(self, value) -> None:
info_size = value
if info_size not in self.info["sizes"] and len(info_size) == 2:
info_size = (info_size[0], info_size[1], 1)
if (
info_size not in self.info["sizes"]
and len(info_size) == 3
and info_size[2] == 1
):
simple_sizes = [
(size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"]
]
if value in simple_sizes:
info_size = self.info["sizes"][simple_sizes.index(value)]
if info_size not in self.info["sizes"]:
msg = "This is not one of the allowed sizes of this image"
raise ValueError(msg)
self._size = value

def load(self) -> Image.core.PixelAccess | None:
if len(self.size) == 3:
self.best_size = self.size
self.size = (
self.best_size[0] * self.best_size[2],
self.best_size[1] * self.best_size[2],
)
def size(self, value: tuple[int, int] | tuple[int, int, int]) -> None:
if len(value) == 3:
deprecate("Setting size to (width, height, scale)", 12, "load(scale)")
if value in self.info["sizes"]:
self._size = value # type: ignore[assignment]
return
else:
# Check that a matching size exists,
# or that there is a scale that would create a size that matches
for size in self.info["sizes"]:
simple_size = size[0] * size[2], size[1] * size[2]
scale = simple_size[0] // value[0]
if simple_size[1] / value[1] == scale:
self._size = value
return
msg = "This is not one of the allowed sizes of this image"
raise ValueError(msg)

def load(self, scale: int | None = None) -> Image.core.PixelAccess | None:
if scale is not None or len(self.size) == 3:
if scale is None and len(self.size) == 3:
scale = self.size[2]
assert scale is not None
width, height = self.size[:2]
self.size = width * scale, height * scale
self.best_size = width, height, scale

px = Image.Image.load(self)
if self._im is not None and self.im.size == self.size:
Expand Down
Loading