Merge branch 'main' into ios-build

This commit is contained in:
Andrew Murray 2025-06-25 11:00:08 +10:00 committed by GitHub
commit ec5e5d0791
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 60 additions and 25 deletions

View File

@ -60,7 +60,7 @@ jobs:
platform: macos
os: macos-13
cibw_arch: x86_64
build: "cp3{12,13}*"
build: "cp3{12,13,14}*"
macosx_deployment_target: "10.13"
- name: "macOS 10.15 x86_64"
platform: macos

View File

@ -100,11 +100,11 @@ class TestFilePng:
assert im.format == "PNG"
assert im.get_format_mimetype() == "image/png"
for mode in ["1", "L", "P", "RGB", "I", "I;16", "I;16B"]:
for mode in ["1", "L", "P", "RGB", "I;16", "I;16B"]:
im = hopper(mode)
im.save(test_file)
with Image.open(test_file) as reloaded:
if mode in ("I", "I;16B"):
if mode == "I;16B":
reloaded = reloaded.convert(mode)
assert_image_equal(reloaded, im)
@ -801,6 +801,16 @@ class TestFilePng:
with Image.open("Tests/images/truncated_end_chunk.png") as im:
assert_image_equal_tofile(im, "Tests/images/hopper.png")
def test_deprecation(self, tmp_path: Path) -> None:
test_file = tmp_path / "out.png"
im = hopper("I")
with pytest.warns(DeprecationWarning):
im.save(test_file)
with Image.open(test_file) as reloaded:
assert_image_equal(im, reloaded.convert("I"))
@pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
@skip_unless_feature("zlib")

View File

@ -193,6 +193,20 @@ Image.Image.get_child_images()
method uses an image's file pointer, and so child images could only be retrieved from
an :py:class:`PIL.ImageFile.ImageFile` instance.
Saving I mode images as PNG
^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. deprecated:: 11.3.0
In order to fit the 32 bits of I mode images into PNG, when PNG images can only contain
at most 16 bits for a channel, Pillow has been clipping the values. Rather than quietly
changing the data, this is now deprecated. Instead, the image can be converted to
another mode before saving::
from PIL import Image
im = Image.new("I", (1, 1))
im.convert("I;16").save("out.png")
Removed features
----------------

View File

@ -23,10 +23,17 @@ TODO
Deprecations
============
TODO
^^^^
Saving I mode images as PNG
^^^^^^^^^^^^^^^^^^^^^^^^^^^
TODO
In order to fit the 32 bits of I mode images into PNG, when PNG images can only contain
at most 16 bits for a channel, Pillow has been clipping the values. Rather than quietly
changing the data, this is now deprecated. Instead, the image can be converted to
another mode before saving::
from PIL import Image
im = Image.new("I", (1, 1))
im.convert("I;16").save("out.png")
API changes
===========

View File

@ -16,7 +16,6 @@ import subprocess
import sys
import warnings
from collections.abc import Iterator
from typing import Any
from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext
@ -148,7 +147,7 @@ class RequiredDependencyException(Exception):
PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version
def _dbg(s: str, tp: Any = None) -> None:
def _dbg(s: str, tp: str | tuple[str, ...] | None = None) -> None:
if DEBUG:
if tp:
print(s % tp)
@ -522,11 +521,11 @@ class pil_build_ext(build_ext):
if root is None and pkg_config:
if isinstance(lib_name, str):
_dbg(f"Looking for `{lib_name}` using pkg-config.")
_dbg("Looking for `%s` using pkg-config.", lib_name)
root = pkg_config(lib_name)
else:
for lib_name2 in lib_name:
_dbg(f"Looking for `{lib_name2}` using pkg-config.")
_dbg("Looking for `%s` using pkg-config.", lib_name2)
root = pkg_config(lib_name2)
if root:
break
@ -757,7 +756,7 @@ class pil_build_ext(build_ext):
best_path = os.path.join(directory, name)
_dbg(
"Best openjpeg version %s so far in %s",
(best_version, best_path),
(str(best_version), best_path),
)
if best_version and _find_library_file(self, "openjp2"):

View File

@ -248,6 +248,9 @@ class ImageCmsProfile:
low-level profile object
"""
self.filename = None
self.product_name = None # profile.product_name
self.product_info = None # profile.product_info
if isinstance(profile, str):
if sys.platform == "win32":
@ -256,23 +259,18 @@ class ImageCmsProfile:
profile_bytes_path.decode("ascii")
except UnicodeDecodeError:
with open(profile, "rb") as f:
self._set(core.profile_frombytes(f.read()))
self.profile = core.profile_frombytes(f.read())
return
self._set(core.profile_open(profile), profile)
self.filename = profile
self.profile = core.profile_open(profile)
elif hasattr(profile, "read"):
self._set(core.profile_frombytes(profile.read()))
self.profile = core.profile_frombytes(profile.read())
elif isinstance(profile, core.CmsProfile):
self._set(profile)
self.profile = profile
else:
msg = "Invalid type for Profile" # type: ignore[unreachable]
raise TypeError(msg)
def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None:
self.profile = profile
self.filename = filename
self.product_name = None # profile.product_name
self.product_info = None # profile.product_info
def tobytes(self) -> bytes:
"""
Returns the profile in a format suitable for embedding in

View File

@ -48,6 +48,7 @@ from ._binary import i32be as i32
from ._binary import o8
from ._binary import o16be as o16
from ._binary import o32be as o32
from ._deprecate import deprecate
from ._util import DeferredError
TYPE_CHECKING = False
@ -1368,6 +1369,8 @@ def _save(
except KeyError as e:
msg = f"cannot write mode {mode} as PNG"
raise OSError(msg) from e
if outmode == "I":
deprecate("Saving I mode images as PNG", 13, stacklevel=4)
#
# write minimal PNG file

View File

@ -12,6 +12,7 @@ def deprecate(
*,
action: str | None = None,
plural: bool = False,
stacklevel: int = 3,
) -> None:
"""
Deprecations helper.
@ -67,5 +68,5 @@ def deprecate(
warnings.warn(
f"{deprecated} {is_} deprecated and will be removed in {removed}{action}",
DeprecationWarning,
stacklevel=3,
stacklevel=stacklevel,
)

View File

@ -1032,7 +1032,10 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
TRACE(("Encode Error, row %d\n", state->y));
state->errcode = IMAGING_CODEC_BROKEN;
if (!clientstate->fp) {
if (clientstate->fp) {
TIFFCleanup(tiff);
clientstate->tiff = NULL;
} else {
free(clientstate->data);
}
return -1;

View File

@ -385,8 +385,8 @@ DEPS: dict[str, dict[str, Any]] = {
"bins": [r"*.dll"],
},
"libavif": {
"url": f"https://github.com/AOMediaCodec/libavif/archive/v{V['LIBAVIF']}.zip",
"filename": f"libavif-{V['LIBAVIF']}.zip",
"url": f"https://github.com/AOMediaCodec/libavif/archive/v{V['LIBAVIF']}.tar.gz",
"filename": f"libavif-{V['LIBAVIF']}.tar.gz",
"license": "LICENSE",
"build": [
"rustup update",