From e4a10051b600eb1211e944225570be72013c72f5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Apr 2024 19:00:19 +1100 Subject: [PATCH 001/300] Corrected test --- Tests/test_imagecms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index fbd78032e59..bf629fa79cc 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -526,9 +526,9 @@ def test_profile_typesafety() -> None: def test_transform_typesafety() -> None: # core transform should not be directly instantiable with pytest.raises(TypeError): - ImageCms.core.CmsProfile() + ImageCms.core.CmsTransform() with pytest.raises(TypeError): - ImageCms.core.CmsProfile(0) + ImageCms.core.CmsTransform(0) def assert_aux_channel_preserved( From 2f28ebbb6d0bab2a878051f9f7dc48f2e7a88ec1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 1 Apr 2024 15:35:42 +0300 Subject: [PATCH 002/300] 10.4.0.dev0 version bump --- src/PIL/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/_version.py b/src/PIL/_version.py index dbed769edc7..12d7412eaa1 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,4 +1,4 @@ # Master version for Pillow from __future__ import annotations -__version__ = "10.3.0" +__version__ = "10.4.0.dev0" From 8e96748aeab2e081b1ca6a50ee5ac0c25cc90513 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 1 Apr 2024 09:35:56 -0500 Subject: [PATCH 003/300] correct property names in method documentation --- src/PIL/ImageCms.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 3a45572a128..162db628c8a 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -704,12 +704,12 @@ def applyTransform( """ (pyCMS) Applies a transform to a given image. - If ``im.mode != transform.inMode``, a :exc:`PyCMSError` is raised. + If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised. - If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a + If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a :exc:`PyCMSError` is raised. - If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not + If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not supported by pyCMSdll or the profiles you used for the transform, a :exc:`PyCMSError` is raised. @@ -723,10 +723,10 @@ def applyTransform( If you want to modify im in-place instead of receiving a new image as the return value, set ``inPlace`` to ``True``. This can only be done if - ``transform.inMode`` and ``transform.outMode`` are the same, because we can't - change the mode in-place (the buffer sizes for some modes are + ``transform.input_mode`` and ``transform.output_mode`` are the same, because we + can't change the mode in-place (the buffer sizes for some modes are different). The default behavior is to return a new :py:class:`~PIL.Image.Image` - object of the same dimensions in mode ``transform.outMode``. + object of the same dimensions in mode ``transform.output_mode``. :param im: An :py:class:`~PIL.Image.Image` object, and im.mode must be the same as the ``inMode`` supported by the transform. From 16ce3da0a4befe5c905da205962c18c2b1fe0ed9 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 1 Apr 2024 09:45:52 -0500 Subject: [PATCH 004/300] remove unused mode properties from CmsTransformObject/PIL.ImageCms.core.CmsTransform --- src/_imagingcms.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 84b8a7e71f9..b0ef2469c0a 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -181,8 +181,7 @@ cms_profile_dealloc(CmsProfileObject *self) { /* a transform represents the mapping between two profiles */ typedef struct { - PyObject_HEAD char mode_in[8]; - char mode_out[8]; + PyObject_HEAD cmsHTRANSFORM transform; } CmsTransformObject; @@ -191,7 +190,7 @@ static PyTypeObject CmsTransform_Type; #define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type) static PyObject * -cms_transform_new(cmsHTRANSFORM transform, char *mode_in, char *mode_out) { +cms_transform_new(cmsHTRANSFORM transform) { CmsTransformObject *self; self = PyObject_New(CmsTransformObject, &CmsTransform_Type); @@ -201,9 +200,6 @@ cms_transform_new(cmsHTRANSFORM transform, char *mode_in, char *mode_out) { self->transform = transform; - strncpy(self->mode_in, mode_in, 8); - strncpy(self->mode_out, mode_out, 8); - return (PyObject *)self; } @@ -476,7 +472,7 @@ buildTransform(PyObject *self, PyObject *args) { return NULL; } - return cms_transform_new(transform, sInMode, sOutMode); + return cms_transform_new(transform); } static PyObject * @@ -523,7 +519,7 @@ buildProofTransform(PyObject *self, PyObject *args) { return NULL; } - return cms_transform_new(transform, sInMode, sOutMode); + return cms_transform_new(transform); } static PyObject * @@ -1456,21 +1452,6 @@ static struct PyMethodDef cms_transform_methods[] = { {"apply", (PyCFunction)cms_transform_apply, 1}, {NULL, NULL} /* sentinel */ }; -static PyObject * -cms_transform_getattr_inputMode(CmsTransformObject *self, void *closure) { - return PyUnicode_FromString(self->mode_in); -} - -static PyObject * -cms_transform_getattr_outputMode(CmsTransformObject *self, void *closure) { - return PyUnicode_FromString(self->mode_out); -} - -static struct PyGetSetDef cms_transform_getsetters[] = { - {"inputMode", (getter)cms_transform_getattr_inputMode}, - {"outputMode", (getter)cms_transform_getattr_outputMode}, - {NULL}}; - static PyTypeObject CmsTransform_Type = { PyVarObject_HEAD_INIT(NULL, 0) "PIL.ImageCms.core.CmsTransform", /*tp_name*/ sizeof(CmsTransformObject), /*tp_basicsize*/ @@ -1501,7 +1482,7 @@ static PyTypeObject CmsTransform_Type = { 0, /*tp_iternext*/ cms_transform_methods, /*tp_methods*/ 0, /*tp_members*/ - cms_transform_getsetters, /*tp_getset*/ + 0, /*tp_getset*/ }; static int From 7a9b57ce0896e2f95126cba22bbca184d1a677f0 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 1 Apr 2024 10:16:23 -0500 Subject: [PATCH 005/300] remove mode properties from CmsTransform interface --- src/PIL/_imagingcms.pyi | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/PIL/_imagingcms.pyi b/src/PIL/_imagingcms.pyi index 036521b0e5e..f704047be3f 100644 --- a/src/PIL/_imagingcms.pyi +++ b/src/PIL/_imagingcms.pyi @@ -108,10 +108,6 @@ class CmsProfile: def is_intent_supported(self, intent: int, direction: int, /) -> int: ... class CmsTransform: - @property - def inputMode(self) -> str: ... - @property - def outputMode(self) -> str: ... def apply(self, id_in: int, id_out: int) -> int: ... def profile_open(profile: str, /) -> CmsProfile: ... From 793bb7fb6faf6029f995e6f53b6ecd03f07ff8c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:24:09 +0000 Subject: [PATCH 006/300] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.2.0 → v0.3.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.0...v0.3.4) - [github.com/psf/black-pre-commit-mirror: 24.1.1 → 24.3.0](https://github.com/psf/black-pre-commit-mirror/compare/24.1.1...24.3.0) - [github.com/PyCQA/bandit: 1.7.7 → 1.7.8](https://github.com/PyCQA/bandit/compare/1.7.7...1.7.8) - [github.com/Lucas-C/pre-commit-hooks: v1.5.4 → v1.5.5](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.5.4...v1.5.5) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c52fdcb552d..7e3fd3e1841 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,24 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.3.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.1.1 + rev: 24.3.0 hooks: - id: black - repo: https://github.com/PyCQA/bandit - rev: 1.7.7 + rev: 1.7.8 hooks: - id: bandit args: [--severity-level=high] files: ^src/ - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.5.4 + rev: v1.5.5 hooks: - id: remove-tabs exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$) From 88ddcde8d858b48a5361a2f00f3c989ab32c7f67 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:24:28 +0000 Subject: [PATCH 007/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/bench_cffi_access.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index c4ab3bdccb3..1d66b6636b1 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -32,9 +32,7 @@ def timer(func, label, *args) -> None: break endtime = time.time() print( - "{}: completed {} iterations in {:.4f}s, {:.6f}s per iteration".format( - label, x + 1, endtime - starttime, (endtime - starttime) / (x + 1.0) - ) + f"{label}: completed {x + 1} iterations in {endtime - starttime:.4f}s, {(endtime - starttime) / (x + 1.0):.6f}s per iteration" ) From 97c6e46f34c53ad9780e32ce8b1978ad01bc2d5d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:49:19 +0300 Subject: [PATCH 008/300] Fix lint --- Tests/bench_cffi_access.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 1d66b6636b1..d2a08c07bc4 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -32,7 +32,8 @@ def timer(func, label, *args) -> None: break endtime = time.time() print( - f"{label}: completed {x + 1} iterations in {endtime - starttime:.4f}s, {(endtime - starttime) / (x + 1.0):.6f}s per iteration" + f"{label}: completed {x + 1} iterations in {endtime - starttime:.4f}s, " + f"{(endtime - starttime) / (x + 1.0):.6f}s per iteration" ) From 55b8f9126bd5b0dd11c6cbe76b6b91c0d7630f5b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:49:47 +0300 Subject: [PATCH 009/300] Move Ruff's fix=true up from pre-commit to pyproject --- .pre-commit-config.yaml | 2 +- pyproject.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e3fd3e1841..c7156d93722 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: rev: v0.3.4 hooks: - id: ruff - args: [--fix, --exit-non-zero-on-fix] + args: [--exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.3.0 diff --git a/pyproject.toml b/pyproject.toml index 740b0ebeac6..3ce082fb980 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,9 @@ config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable" test-command = "cd {project} && .github/workflows/wheels-test.sh" test-extras = "tests" +[tool.ruff] +fix = true + [tool.ruff.lint] select = [ "C4", # flake8-comprehensions From 78670404862642a37d9895af56ba2003165f6871 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:55:19 +0300 Subject: [PATCH 010/300] Check GHA, RTD and Renovate config with pre-commit --- .pre-commit-config.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c7156d93722..51625eb4c89 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,6 +42,13 @@ repos: - id: trailing-whitespace exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/ + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.1 + hooks: + - id: check-github-workflows + - id: check-readthedocs + - id: check-renovate + - repo: https://github.com/sphinx-contrib/sphinx-lint rev: v0.9.1 hooks: @@ -62,5 +69,10 @@ repos: hooks: - id: tox-ini-fmt + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes + ci: autoupdate_schedule: monthly From b37279a29cf29a66f86a99de6ce46d6226999c75 Mon Sep 17 00:00:00 2001 From: jbjd Date: Mon, 1 Apr 2024 20:09:02 -0500 Subject: [PATCH 011/300] fix parameter name in ImageMath docs --- docs/reference/ImageMath.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst index 703b2f5b943..2535db7113c 100644 --- a/docs/reference/ImageMath.rst +++ b/docs/reference/ImageMath.rst @@ -31,7 +31,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module b=im2 ) -.. py:function:: lambda_eval(expression, environment) +.. py:function:: lambda_eval(expression, options) Returns the result of an image function. @@ -44,7 +44,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module :return: An image, an integer value, a floating point value, or a pixel tuple, depending on the expression. -.. py:function:: unsafe_eval(expression, environment) +.. py:function:: unsafe_eval(expression, options) Evaluates an image expression. From 75454646f4bd3eb21e4585702286e8e8841cdc89 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Apr 2024 15:34:46 +1100 Subject: [PATCH 012/300] Moved code onto single line --- src/_imagingcms.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index b0ef2469c0a..fd487d9da8a 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -181,8 +181,7 @@ cms_profile_dealloc(CmsProfileObject *self) { /* a transform represents the mapping between two profiles */ typedef struct { - PyObject_HEAD - cmsHTRANSFORM transform; + PyObject_HEAD cmsHTRANSFORM transform; } CmsTransformObject; static PyTypeObject CmsTransform_Type; From 38e81126508974b506d6d61914f5efd303958760 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Apr 2024 17:58:49 +1100 Subject: [PATCH 013/300] Updated macOS tested Pillow versions --- docs/installation/platform-support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/platform-support.rst b/docs/installation/platform-support.rst index 59fc312ab0b..bc60eeed445 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -81,7 +81,7 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+============================+==================+==============+ -| macOS 14 Sonoma | 3.8, 3.9, 3.10, 3.11, 3.12 | 10.2.0 |arm | +| macOS 14 Sonoma | 3.8, 3.9, 3.10, 3.11, 3.12 | 10.3.0 |arm | +----------------------------------+----------------------------+------------------+--------------+ | macOS 13 Ventura | 3.8, 3.9, 3.10, 3.11 | 10.0.1 |arm | | +----------------------------+------------------+ | From 4915f19b1370c17036cb3f4c3cbe12bbf23bc43c Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Tue, 2 Apr 2024 17:45:06 +0200 Subject: [PATCH 014/300] fromarray: add type hints --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index baef0aa112e..0dfccd9b030 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3069,7 +3069,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): return frombytes(mode, size, data, decoder_name, args) -def fromarray(obj, mode=None): +def fromarray(obj: "numpy.typing.ArrayLike", mode: Optional[str] = None) -> Image: """ Creates an image memory from an object exporting the array interface (using the buffer protocol):: From 8e47a6f2c82b696f6b96bb8fff350a937ea195b9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:46:25 +0000 Subject: [PATCH 015/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0dfccd9b030..a2723756fef 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3069,7 +3069,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): return frombytes(mode, size, data, decoder_name, args) -def fromarray(obj: "numpy.typing.ArrayLike", mode: Optional[str] = None) -> Image: +def fromarray(obj: numpy.typing.ArrayLike, mode: Optional[str] = None) -> Image: """ Creates an image memory from an object exporting the array interface (using the buffer protocol):: From 37ed8c337de3617bb0b17b721c0cef08e502dc3b Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Tue, 2 Apr 2024 17:53:11 +0200 Subject: [PATCH 016/300] Try type comment --- src/PIL/Image.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index a2723756fef..c932f1932ac 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any +from typing import IO, Optional, TYPE_CHECKING, Any # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -3069,7 +3069,10 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): return frombytes(mode, size, data, decoder_name, args) -def fromarray(obj: numpy.typing.ArrayLike, mode: Optional[str] = None) -> Image: +def fromarray( + obj, # type: numpy.typing.ArrayLike + mode: Optional[str] = None +) -> Image: """ Creates an image memory from an object exporting the array interface (using the buffer protocol):: From 8a63980e393e0ffc9fc9ae4337d97b5bbfb95524 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:53:39 +0000 Subject: [PATCH 017/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c932f1932ac..a4043c620d0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, Optional, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Optional # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -3071,7 +3071,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): def fromarray( obj, # type: numpy.typing.ArrayLike - mode: Optional[str] = None + mode: Optional[str] = None, ) -> Image: """ Creates an image memory from an object exporting the array interface From 5d19151cd31951754dc284db7434c4e6e7fbc240 Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Tue, 2 Apr 2024 17:56:52 +0200 Subject: [PATCH 018/300] Python 3.10+ --- src/PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index a4043c620d0..245fadd6b3f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, Optional +from typing import IO, TYPE_CHECKING, Any # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -3071,7 +3071,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): def fromarray( obj, # type: numpy.typing.ArrayLike - mode: Optional[str] = None, + mode: str | None = None, ) -> Image: """ Creates an image memory from an object exporting the array interface From 8c57cd56a5107fe8957f8a753dbe15f3da33e5d9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 3 Apr 2024 12:00:02 +1100 Subject: [PATCH 019/300] QoiImagePlugin uses PyDecoder --- docs/handbook/image-file-formats.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index f3f0499f052..c171705c287 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1488,7 +1488,7 @@ QOI .. versionadded:: 9.5.0 -Pillow identifies and reads images in Quite OK Image format. +Pillow reads images in Quite OK Image format, using a Python decoder. SUN ^^^ From a6793bba5e4a3c0e102359545507828966960cc1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 3 Apr 2024 12:19:45 +1100 Subject: [PATCH 020/300] Updated pattern for skipping builds based on file changes --- .appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6f35a0190d4..57a8fa5a06d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,9 +1,9 @@ skip_commits: files: - - ".github/**" + - ".github/**/*" - ".gitmodules" - - "docs/**" - - "wheels/**" + - "docs/**/*" + - "wheels/**/*" version: '{build}' clone_folder: c:\pillow From e85a84baa7ebee1b458cabbeb23b8d497c735175 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 3 Apr 2024 20:00:40 +1100 Subject: [PATCH 021/300] Added SupportsArrayInterface --- Tests/test_image_array.py | 10 ++++++++++ docs/reference/Image.rst | 2 ++ src/PIL/Image.py | 22 +++++++++++++++------- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index cf85ee4fa1c..342bd8654e1 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -91,6 +91,16 @@ def test(mode: str) -> tuple[str, tuple[int, int], bool]: Image.fromarray(wrapped) +def test_fromarray_strides_without_tobytes() -> None: + class Wrapper: + def __init__(self, arr_params: dict[str, Any]) -> None: + self.__array_interface__ = arr_params + + with pytest.raises(ValueError): + wrapped = Wrapper({"shape": (1, 1), "strides": (1, 1)}) + Image.fromarray(wrapped, "L") + + def test_fromarray_palette() -> None: # Arrange i = im.convert("L") diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 4281b182ce7..0d9b4d93d77 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -78,6 +78,8 @@ Constructing images ^^^^^^^^^^^^^^^^^^^ .. autofunction:: new +.. autoclass:: SupportsArrayInterface + :show-inheritance: .. autofunction:: fromarray .. autofunction:: frombytes .. autofunction:: frombuffer diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 245fadd6b3f..7e68ee3cb40 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Protocol # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -3013,7 +3013,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args) -> Image: return im -def frombuffer(mode, size, data, decoder_name="raw", *args): +def frombuffer(mode, size, data, decoder_name="raw", *args) -> Image: """ Creates an image memory referencing pixel data in a byte buffer. @@ -3069,10 +3069,15 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): return frombytes(mode, size, data, decoder_name, args) -def fromarray( - obj, # type: numpy.typing.ArrayLike - mode: str | None = None, -) -> Image: +class SupportsArrayInterface(Protocol): + """ + An object that has an ``__array_interface__`` dictionary. + """ + + __array_interface__: dict[str, Any] + + +def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image: """ Creates an image memory from an object exporting the array interface (using the buffer protocol):: @@ -3151,8 +3156,11 @@ def fromarray( if strides is not None: if hasattr(obj, "tobytes"): obj = obj.tobytes() - else: + elif hasattr(obj, "tostring"): obj = obj.tostring() + else: + msg = "'strides' requires either tobytes() or tostring()" + raise ValueError(msg) return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) From 0cc5de4e0949b8e5ef810465cde7332bde825c72 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Apr 2024 12:17:02 +1100 Subject: [PATCH 022/300] Link to https://pypi.org/project/qoi --- docs/handbook/image-file-formats.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index c171705c287..bff056b75d7 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1488,7 +1488,9 @@ QOI .. versionadded:: 9.5.0 -Pillow reads images in Quite OK Image format, using a Python decoder. +Pillow reads images in Quite OK Image format, using a Python decoder. If you wish to +write code specifically for this format, https://pypi.org/project/qoi is an alternative +library that uses C to decode the image, and interfaces with NumPy. SUN ^^^ From 8c14a394c1d3ca9c06f356ebfbf2acaa634da25e Mon Sep 17 00:00:00 2001 From: Nulano Date: Thu, 4 Apr 2024 19:54:07 +0200 Subject: [PATCH 023/300] add type hints for Image.open and Image.init --- src/PIL/Image.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index baef0aa112e..09ddc770f28 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any +from typing import IO, TYPE_CHECKING, Any, Literal, cast # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -55,7 +55,7 @@ _plugins, ) from ._binary import i32le, o32be, o32le -from ._typing import TypeGuard +from ._typing import StrOrBytesPath, TypeGuard from ._util import DeferredError, is_path ElementTree: ModuleType | None @@ -357,7 +357,7 @@ def preinit() -> None: _initialized = 1 -def init(): +def init() -> bool: """ Explicitly initializes the Python Imaging Library. This function loads all available file format drivers. @@ -368,7 +368,7 @@ def init(): global _initialized if _initialized >= 2: - return 0 + return False parent_name = __name__.rpartition(".")[0] for plugin in _plugins: @@ -380,7 +380,8 @@ def init(): if OPEN or SAVE: _initialized = 2 - return 1 + return True + return False # -------------------------------------------------------------------- @@ -3222,7 +3223,11 @@ def _decompression_bomb_check(size: tuple[int, int]) -> None: ) -def open(fp, mode="r", formats=None) -> Image: +def open( + fp: StrOrBytesPath | IO[bytes], + mode: Literal["r"] = "r", + formats: list[str] | tuple[str, ...] | None = None, +) -> ImageFile.ImageFile: """ Opens and identifies the given image file. @@ -3253,10 +3258,10 @@ def open(fp, mode="r", formats=None) -> Image: """ if mode != "r": - msg = f"bad mode {repr(mode)}" + msg = f"bad mode {repr(mode)}" # type: ignore[unreachable] raise ValueError(msg) elif isinstance(fp, io.StringIO): - msg = ( + msg = ( # type: ignore[unreachable] "StringIO cannot be used to open an image. " "Binary data must be used instead." ) @@ -3265,7 +3270,7 @@ def open(fp, mode="r", formats=None) -> Image: if formats is None: formats = ID elif not isinstance(formats, (list, tuple)): - msg = "formats must be a list or tuple" + msg = "formats must be a list or tuple" # type: ignore[unreachable] raise TypeError(msg) exclusive_fp = False @@ -3276,6 +3281,8 @@ def open(fp, mode="r", formats=None) -> Image: if filename: fp = builtins.open(filename, "rb") exclusive_fp = True + else: + fp = cast(IO[bytes], fp) try: fp.seek(0) @@ -3287,9 +3294,14 @@ def open(fp, mode="r", formats=None) -> Image: preinit() - accept_warnings = [] + accept_warnings: list[str | bytes] = [] - def _open_core(fp, filename, prefix, formats): + def _open_core( + fp: IO[bytes], + filename: str | bytes, + prefix: bytes, + formats: list[str] | tuple[str, ...], + ) -> ImageFile.ImageFile | None: for i in formats: i = i.upper() if i not in OPEN: @@ -3298,7 +3310,7 @@ def _open_core(fp, filename, prefix, formats): factory, accept = OPEN[i] result = not accept or accept(prefix) if type(result) in [str, bytes]: - accept_warnings.append(result) + accept_warnings.append(result) # type: ignore[arg-type] elif result: fp.seek(0) im = factory(fp, filename) @@ -3318,7 +3330,7 @@ def _open_core(fp, filename, prefix, formats): im = _open_core(fp, filename, prefix, formats) if im is None and formats is ID: - checked_formats = formats.copy() + checked_formats = ID.copy() if init(): im = _open_core( fp, @@ -3334,7 +3346,7 @@ def _open_core(fp, filename, prefix, formats): if exclusive_fp: fp.close() for message in accept_warnings: - warnings.warn(message) + warnings.warn(message) # type: ignore[arg-type] msg = "cannot identify image file %r" % (filename if filename else fp) raise UnidentifiedImageError(msg) From 2a2588d5df13c329d442b613960bc0ba7e606543 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 5 Apr 2024 05:09:11 +1100 Subject: [PATCH 024/300] Use extlink Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- docs/handbook/image-file-formats.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index bff056b75d7..fbef08bcecc 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1489,7 +1489,7 @@ QOI .. versionadded:: 9.5.0 Pillow reads images in Quite OK Image format, using a Python decoder. If you wish to -write code specifically for this format, https://pypi.org/project/qoi is an alternative +write code specifically for this format, :pypi:`qoi` is an alternative library that uses C to decode the image, and interfaces with NumPy. SUN From 0702f704fa147ea583eb61ca01280c9e59665264 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 5 Apr 2024 05:16:41 +1100 Subject: [PATCH 025/300] Remove commas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondrej Baranovič --- docs/handbook/image-file-formats.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index fbef08bcecc..06716255bff 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1488,9 +1488,9 @@ QOI .. versionadded:: 9.5.0 -Pillow reads images in Quite OK Image format, using a Python decoder. If you wish to +Pillow reads images in Quite OK Image format using a Python decoder. If you wish to write code specifically for this format, :pypi:`qoi` is an alternative -library that uses C to decode the image, and interfaces with NumPy. +library that uses C to decode the image and interfaces with NumPy. SUN ^^^ From 059b8e91717c04e2827a937c1c5bfd7d5d947548 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 5 Apr 2024 06:52:36 +1100 Subject: [PATCH 026/300] Updated line formatting --- docs/handbook/image-file-formats.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 06716255bff..1ec97214900 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1489,8 +1489,8 @@ QOI .. versionadded:: 9.5.0 Pillow reads images in Quite OK Image format using a Python decoder. If you wish to -write code specifically for this format, :pypi:`qoi` is an alternative -library that uses C to decode the image and interfaces with NumPy. +write code specifically for this format, :pypi:`qoi` is an alternative library that +uses C to decode the image and interfaces with NumPy. SUN ^^^ From 819e1b9dd2bfeb6455524053b6d76b65d77bd66c Mon Sep 17 00:00:00 2001 From: Nulano Date: Thu, 4 Apr 2024 23:38:50 +0200 Subject: [PATCH 027/300] add type hints for Image.save --- src/PIL/Image.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 09ddc770f28..8bae2974c11 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2374,7 +2374,9 @@ def transform(x, y, matrix): (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor ) - def save(self, fp, format=None, **params) -> None: + def save( + self, fp: StrOrBytesPath | IO[bytes], format: str | None = None, **params: Any + ) -> None: """ Saves this image under the given filename. If no format is specified, the format to use is determined from the filename @@ -2455,6 +2457,8 @@ def save(self, fp, format=None, **params) -> None: fp = builtins.open(filename, "r+b") else: fp = builtins.open(filename, "w+b") + else: + fp = cast(IO[bytes], fp) try: save_handler(self, fp, filename) From c61a4810289e6283e2d20cc5d4941f4c57b19a64 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Apr 2024 13:40:39 +1100 Subject: [PATCH 028/300] Support reading CMYK JPEG2000 images --- Tests/test_file_jpeg2k.py | 10 ++++++++++ src/PIL/Jpeg2KImagePlugin.py | 4 ++++ src/libImaging/Jpeg2KDecode.c | 1 + 3 files changed, 15 insertions(+) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 81f75cc72cf..a7cae563adb 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -289,6 +289,16 @@ def test_rgba(ext: str) -> None: assert im.mode == "RGBA" +@pytest.mark.skipif( + not os.path.exists(EXTRA_DIR), reason="Extra image files not installed" +) +@skip_unless_feature_version("jpg_2000", "2.5.1") +def test_cmyk() -> None: + with Image.open(f"{EXTRA_DIR}/issue205.jp2") as im: + assert im.mode == "CMYK" + assert im.getpixel((0, 0)) == (185, 134, 0, 0) + + @pytest.mark.parametrize("ext", (".j2k", ".jp2")) def test_16bit_monochrome_has_correct_mode(ext: str) -> None: with Image.open("Tests/images/16bit.cropped" + ext) as im: diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index be000c351b6..51b29bca2c9 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -176,6 +176,10 @@ def _parse_jp2_header(fp): mode = "RGB" elif nc == 4: mode = "RGBA" + elif tbox == b"colr" and nc == 4: + meth, _, _, enumcs = header.read_fields(">BBBI") + if meth == 1 and enumcs == 12: + mode = "CMYK" elif tbox == b"pclr" and mode in ("L", "LA"): ne, npc = header.read_fields(">HB") bitdepths = header.read_fields(">" + ("B" * npc)) diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c index 13f363422da..78a09bb8333 100644 --- a/src/libImaging/Jpeg2KDecode.c +++ b/src/libImaging/Jpeg2KDecode.c @@ -632,6 +632,7 @@ static const struct j2k_decode_unpacker j2k_unpackers[] = { {"RGBA", OPJ_CLRSPC_SYCC, 3, 1, j2ku_sycc_rgb}, {"RGBA", OPJ_CLRSPC_SRGB, 4, 1, j2ku_srgba_rgba}, {"RGBA", OPJ_CLRSPC_SYCC, 4, 1, j2ku_sycca_rgba}, + {"CMYK", OPJ_CLRSPC_CMYK, 4, 1, j2ku_srgba_rgba}, }; /* -------------------------------------------------------------------- */ From 1c2a323a90fabf8cedb4229c8864aac8d6a24ed5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Apr 2024 15:18:43 +1100 Subject: [PATCH 029/300] Corrected variable name --- src/PIL/ImageCms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 162db628c8a..4af1b79e2e3 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -728,8 +728,8 @@ def applyTransform( different). The default behavior is to return a new :py:class:`~PIL.Image.Image` object of the same dimensions in mode ``transform.output_mode``. - :param im: An :py:class:`~PIL.Image.Image` object, and im.mode must be the same - as the ``inMode`` supported by the transform. + :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same + as the ``input_mode`` supported by the transform. :param transform: A valid CmsTransform class object :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the From 7eee479ce5f52e0aec8e08f9380ec528bda84c72 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Apr 2024 14:28:33 +1100 Subject: [PATCH 030/300] Corrected indentation --- src/_imagingcms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index fd487d9da8a..f18d55a5718 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -390,7 +390,7 @@ _buildTransform( Py_END_ALLOW_THREADS - if (!hTransform) { + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build transform"); } @@ -424,7 +424,7 @@ _buildProofTransform( Py_END_ALLOW_THREADS - if (!hTransform) { + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build proof transform"); } From 2245df0ac30976960781f291bc45a2ad56cee064 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Apr 2024 20:59:06 +1100 Subject: [PATCH 031/300] Only preserve IPTC_NAA_CHUNK tag if type is BYTE or UNDEFINED --- Tests/test_file_tiff.py | 13 +++++++++++++ src/PIL/TiffImagePlugin.py | 30 ++++++++++++++++++------------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 8821fb46a84..0bc1e2d0e6b 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -621,6 +621,19 @@ def test_roundtrip_tiff_uint16(self, tmp_path: Path) -> None: assert_image_equal_tofile(im, tmpfile) + def test_iptc(self, tmp_path: Path) -> None: + # Do not preserve IPTC_NAA_CHUNK by default if type is LONG + outfile = str(tmp_path / "temp.tif") + im = hopper() + ifd = TiffImagePlugin.ImageFileDirectory_v2() + ifd[33723] = 1 + ifd.tagtype[33723] = 4 + im.tag_v2 = ifd + im.save(outfile) + + with Image.open(outfile) as im: + assert 33723 not in im.tag_v2 + def test_rowsperstrip(self, tmp_path: Path) -> None: outfile = str(tmp_path / "temp.tif") im = hopper() diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 8bfcd29075e..c3683efb35e 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1653,6 +1653,16 @@ def _save(im, fp, filename): except Exception: pass # might not be an IFD. Might not have populated type + legacy_ifd = {} + if hasattr(im, "tag"): + legacy_ifd = im.tag.to_v2() + + supplied_tags = {**legacy_ifd, **getattr(im, "tag_v2", {})} + if SAMPLEFORMAT in supplied_tags: + # SAMPLEFORMAT is determined by the image format and should not be copied + # from legacy_ifd. + del supplied_tags[SAMPLEFORMAT] + # additions written by Greg Couch, gregc@cgl.ucsf.edu # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com if hasattr(im, "tag_v2"): @@ -1666,8 +1676,14 @@ def _save(im, fp, filename): XMP, ): if key in im.tag_v2: - ifd[key] = im.tag_v2[key] - ifd.tagtype[key] = im.tag_v2.tagtype[key] + if key == IPTC_NAA_CHUNK and im.tag_v2.tagtype[key] not in ( + TiffTags.BYTE, + TiffTags.UNDEFINED, + ): + del supplied_tags[key] + else: + ifd[key] = im.tag_v2[key] + ifd.tagtype[key] = im.tag_v2.tagtype[key] # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech @@ -1807,16 +1823,6 @@ def _save(im, fp, filename): # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. - legacy_ifd = {} - if hasattr(im, "tag"): - legacy_ifd = im.tag.to_v2() - - # SAMPLEFORMAT is determined by the image format and should not be copied - # from legacy_ifd. - supplied_tags = {**getattr(im, "tag_v2", {}), **legacy_ifd} - if SAMPLEFORMAT in supplied_tags: - del supplied_tags[SAMPLEFORMAT] - for tag, value in itertools.chain(ifd.items(), supplied_tags.items()): # Libtiff can only process certain core items without adding # them to the custom dictionary. From 6a255de24fadfb2daf52f1fa86849611078127ab Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 6 Apr 2024 11:14:38 -0500 Subject: [PATCH 032/300] Rename test_roundtrip() to test_hopper() This test isn't actually roundtripping anything. --- Tests/test_image_getdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_getdata.py b/Tests/test_image_getdata.py index ac27400be94..9181b24095b 100644 --- a/Tests/test_image_getdata.py +++ b/Tests/test_image_getdata.py @@ -14,7 +14,7 @@ def test_sanity() -> None: assert data[0] == (20, 20, 70) -def test_roundtrip() -> None: +def test_hopper() -> None: def getdata(mode: str) -> tuple[float | tuple[int, ...], int, int]: im = hopper(mode).resize((32, 30), Image.Resampling.NEAREST) data = im.getdata() From 48b270590c61abf77547dc17b1ee5303ef6f4283 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Apr 2024 13:58:53 +1100 Subject: [PATCH 033/300] accept returns bool or str --- src/PIL/BlpImagePlugin.py | 2 +- src/PIL/BmpImagePlugin.py | 2 +- src/PIL/BufrStubImagePlugin.py | 2 +- src/PIL/CurImagePlugin.py | 2 +- src/PIL/DcxImagePlugin.py | 2 +- src/PIL/DdsImagePlugin.py | 2 +- src/PIL/EpsImagePlugin.py | 2 +- src/PIL/FliImagePlugin.py | 2 +- src/PIL/FpxImagePlugin.py | 2 +- src/PIL/FtexImagePlugin.py | 2 +- src/PIL/GbrImagePlugin.py | 2 +- src/PIL/GifImagePlugin.py | 2 +- src/PIL/GribStubImagePlugin.py | 2 +- src/PIL/Hdf5StubImagePlugin.py | 2 +- src/PIL/IcnsImagePlugin.py | 2 +- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/Image.py | 12 ++++++------ src/PIL/Jpeg2KImagePlugin.py | 2 +- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MicImagePlugin.py | 2 +- src/PIL/PngImagePlugin.py | 2 +- src/PIL/PsdImagePlugin.py | 2 +- src/PIL/QoiImagePlugin.py | 2 +- src/PIL/TiffImagePlugin.py | 2 +- src/PIL/WebPImagePlugin.py | 3 ++- src/PIL/WmfImagePlugin.py | 2 +- src/PIL/XpmImagePlugin.py | 2 +- 27 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 95e8077811d..8d351ce9100 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -241,7 +241,7 @@ class BLPFormatError(NotImplementedError): pass -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] in (b"BLP1", b"BLP2") diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 9947f439b07..6643ac39bc5 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -48,7 +48,7 @@ } -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:2] == b"BM" diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 60f3ec25b38..1cbd50d1997 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -29,7 +29,7 @@ def register_handler(handler): # Image adapter -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index 5fb2b0193ca..b8790e20963 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -25,7 +25,7 @@ # -------------------------------------------------------------------- -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"\0\0\2\0" diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index f7344df44a7..b24c16329d3 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -29,7 +29,7 @@ MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return len(prefix) >= 4 and i32(prefix) == MAGIC diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 93c8e341d44..3032e4aec52 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -562,7 +562,7 @@ def _save(im, fp, filename): ) -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"DDS " diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 523ffcbf7ee..ec6705742ab 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -195,7 +195,7 @@ def readline(self): return b"".join(s).decode("latin-1") -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"%!PS" or (len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5) diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index f9e4c731c88..7a233d01584 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -27,7 +27,7 @@ # decoder -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return ( len(prefix) >= 6 and i16(prefix, 4) in [0xAF11, 0xAF12] diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 75680a94e02..cfaf862399d 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -41,7 +41,7 @@ # -------------------------------------------------------------------- -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:8] == olefile.MAGIC diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index b4488e6ee9c..a746959a3a8 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -107,7 +107,7 @@ def load_seek(self, pos): pass -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == MAGIC diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index 6722fa2b144..62197e36c98 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -29,7 +29,7 @@ from ._binary import i32be as i32 -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 6b415d2384a..93be7fefb86 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -60,7 +60,7 @@ class LoadingStrategy(IntEnum): # Identify/read GIF files -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:6] in [b"GIF87a", b"GIF89a"] diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index f8106800c42..a80fe0a234a 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -29,7 +29,7 @@ def register_handler(handler): # Image adapter -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"GRIB" and prefix[7] == 1 diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index 65409e269fc..f50e6bf1625 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -29,7 +29,7 @@ def register_handler(handler): # Image adapter -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:8] == b"\x89HDF\r\n\x1a\n" diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index d877b4ecba6..c2c9508633d 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -374,7 +374,7 @@ def _save(im, fp, filename): fp.flush() -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == MAGIC diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index d66fbc28797..82b190eb8f4 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -114,7 +114,7 @@ def _save(im, fp, filename): fp.seek(current) -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == _MAGIC diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8bae2974c11..3ae90106087 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -223,7 +223,7 @@ class Quantize(IntEnum): str, tuple[ Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], - Callable[[bytes], bool] | None, + Callable[[bytes], bool | str] | None, ], ] = {} MIME: dict[str, str] = {} @@ -3298,7 +3298,7 @@ def open( preinit() - accept_warnings: list[str | bytes] = [] + accept_warnings: list[str] = [] def _open_core( fp: IO[bytes], @@ -3313,8 +3313,8 @@ def _open_core( try: factory, accept = OPEN[i] result = not accept or accept(prefix) - if type(result) in [str, bytes]: - accept_warnings.append(result) # type: ignore[arg-type] + if isinstance(result, str): + accept_warnings.append(result) elif result: fp.seek(0) im = factory(fp, filename) @@ -3350,7 +3350,7 @@ def _open_core( if exclusive_fp: fp.close() for message in accept_warnings: - warnings.warn(message) # type: ignore[arg-type] + warnings.warn(message) msg = "cannot identify image file %r" % (filename if filename else fp) raise UnidentifiedImageError(msg) @@ -3464,7 +3464,7 @@ def merge(mode, bands): def register_open( id, factory: Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], - accept: Callable[[bytes], bool] | None = None, + accept: Callable[[bytes], bool | str] | None = None, ) -> None: """ Register an image file plugin. This function should not be used diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index be000c351b6..f28de5c4da0 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -313,7 +313,7 @@ def load(self): return ImageFile.ImageFile.load(self) -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return ( prefix[:4] == b"\xff\x4f\xff\x51" or prefix[:12] == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 81b8749a332..e3c0083e944 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -344,7 +344,7 @@ def DQT(self, marker): } -def _accept(prefix): +def _accept(prefix: bytes) -> bool: # Magic number was taken from https://en.wikipedia.org/wiki/JPEG return prefix[:3] == b"\xFF\xD8\xFF" diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index f4529d9ae74..96de386a851 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -25,7 +25,7 @@ # -------------------------------------------------------------------- -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:8] == olefile.MAGIC diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index d922bacfb9c..8b81e54ea77 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -689,7 +689,7 @@ def chunk_fdAT(self, pos, length): # PNG reader -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:8] == _MAGIC diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index d29bcf9970c..b15918313be 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -44,7 +44,7 @@ # read PSD images -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"8BPS" diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index f8aa720c134..2875b8d752b 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -13,7 +13,7 @@ from ._binary import i32be as i32 -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] == b"qoif" diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 8bfcd29075e..10ac9ea3a2a 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -277,7 +277,7 @@ ] -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:4] in PREFIXES diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index c07abcaf928..9c8d53336ef 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -23,7 +23,7 @@ } -def _accept(prefix): +def _accept(prefix: bytes) -> bool | str: is_riff_file_format = prefix[:4] == b"RIFF" is_webp_file = prefix[8:12] == b"WEBP" is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER @@ -34,6 +34,7 @@ def _accept(prefix): "image file could not be identified because WEBP support not installed" ) return True + return False class WebPImageFile(ImageFile.ImageFile): diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index b5b8c69b171..7f045ec7da4 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -65,7 +65,7 @@ def load(self, im): # Read WMF file -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return ( prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00" ) diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index 3125f8d52c6..a638547af61 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -24,7 +24,7 @@ xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') -def _accept(prefix): +def _accept(prefix: bytes) -> bool: return prefix[:9] == b"/* XPM */" From 1635e7a5714243cfe762b1dfaf3709bdf8a25118 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 6 Apr 2024 15:18:16 -0500 Subject: [PATCH 034/300] Update Tests/test_image_getdata.py Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_image_getdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_getdata.py b/Tests/test_image_getdata.py index 9181b24095b..dd3d70b3450 100644 --- a/Tests/test_image_getdata.py +++ b/Tests/test_image_getdata.py @@ -14,7 +14,7 @@ def test_sanity() -> None: assert data[0] == (20, 20, 70) -def test_hopper() -> None: +def test_mode() -> None: def getdata(mode: str) -> tuple[float | tuple[int, ...], int, int]: im = hopper(mode).resize((32, 30), Image.Resampling.NEAREST) data = im.getdata() From 05d231460600f485dd954cca88f73967b4fdd790 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 6 Apr 2024 15:52:17 -0500 Subject: [PATCH 035/300] Make ModeDescriptor a NamedTuple --- src/PIL/ImageMode.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 0b31f608174..b0c84655323 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -16,24 +16,17 @@ import sys from functools import lru_cache +from typing import NamedTuple -class ModeDescriptor: +class ModeDescriptor(NamedTuple): """Wrapper for mode strings.""" - def __init__( - self, - mode: str, - bands: tuple[str, ...], - basemode: str, - basetype: str, - typestr: str, - ) -> None: - self.mode = mode - self.bands = bands - self.basemode = basemode - self.basetype = basetype - self.typestr = typestr + mode: str + bands: tuple[str, ...] + basemode: str + basetype: str + typestr: str def __str__(self) -> str: return self.mode From a25a1aef059911d29765406c37b1a92797bac3e3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Apr 2024 08:38:43 +1000 Subject: [PATCH 036/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9fae613a5ae..e39031aea4a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ Changelog (Pillow) ================== +10.4.0 (unreleased) +------------------- + +- Support reading CMYK JPEG2000 images #7947 + [radarhere] + 10.3.0 (2024-04-01) ------------------- From bcb2db6a871189160ce619ad581411e30d9b0b63 Mon Sep 17 00:00:00 2001 From: Nulano Date: Sun, 7 Apr 2024 00:44:53 +0200 Subject: [PATCH 037/300] ImageStat: use functools.cached_property and add type hints --- docs/reference/ImageStat.rst | 67 ++--------------------- src/PIL/ImageStat.py | 102 +++++++++++++++++++++++------------ 2 files changed, 71 insertions(+), 98 deletions(-) diff --git a/docs/reference/ImageStat.rst b/docs/reference/ImageStat.rst index f61d123131a..f694663828a 100644 --- a/docs/reference/ImageStat.rst +++ b/docs/reference/ImageStat.rst @@ -7,67 +7,6 @@ The :py:mod:`~PIL.ImageStat` module calculates global statistics for an image, or for a region of an image. -.. py:class:: Stat(image_or_list, mask=None) - - Calculate statistics for the given image. If a mask is included, - only the regions covered by that mask are included in the - statistics. You can also pass in a previously calculated histogram. - - :param image: A PIL image, or a precalculated histogram. - - .. note:: - - For a PIL image, calculations rely on the - :py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are - grouped into 256 bins, even if the image has more than 8 bits per - channel. So ``I`` and ``F`` mode images have a maximum ``mean``, - ``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum - of more than 255. - - :param mask: An optional mask. - - .. py:attribute:: extrema - - Min/max values for each band in the image. - - .. note:: - - This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and - simply returns the low and high bins used. This is correct for - images with 8 bits per channel, but fails for other modes such as - ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to - return per-band extrema for the image. This is more correct and - efficient because, for non-8-bit modes, the histogram method uses - :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. - - .. py:attribute:: count - - Total number of pixels for each band in the image. - - .. py:attribute:: sum - - Sum of all pixels for each band in the image. - - .. py:attribute:: sum2 - - Squared sum of all pixels for each band in the image. - - .. py:attribute:: mean - - Average (arithmetic mean) pixel level for each band in the image. - - .. py:attribute:: median - - Median pixel level for each band in the image. - - .. py:attribute:: rms - - RMS (root-mean-square) for each band in the image. - - .. py:attribute:: var - - Variance for each band in the image. - - .. py:attribute:: stddev - - Standard deviation for each band in the image. +.. autoclass:: Stat + :members: + :special-members: __init__ diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index 13864e59cfc..07ea76e6faf 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -23,35 +23,61 @@ from __future__ import annotations import math +from functools import cached_property + +from . import Image class Stat: - def __init__(self, image_or_list, mask=None): - try: + def __init__( + self, image_or_list: Image.Image | list[int], mask: Image.Image | None = None + ) -> None: + """ + Calculate statistics for the given image. If a mask is included, + only the regions covered by that mask are included in the + statistics. You can also pass in a previously calculated histogram. + + :param image: A PIL image, or a precalculated histogram. + + .. note:: + + For a PIL image, calculations rely on the + :py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are + grouped into 256 bins, even if the image has more than 8 bits per + channel. So ``I`` and ``F`` mode images have a maximum ``mean``, + ``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum + of more than 255. + + :param mask: An optional mask. + """ + if isinstance(image_or_list, Image.Image): if mask: self.h = image_or_list.histogram(mask) else: self.h = image_or_list.histogram() - except AttributeError: - self.h = image_or_list # assume it to be a histogram list + else: + self.h = image_or_list if not isinstance(self.h, list): - msg = "first argument must be image or list" + msg = "first argument must be image or list" # type: ignore[unreachable] raise TypeError(msg) self.bands = list(range(len(self.h) // 256)) - def __getattr__(self, id): - """Calculate missing attribute""" - if id[:4] == "_get": - raise AttributeError(id) - # calculate missing attribute - v = getattr(self, "_get" + id)() - setattr(self, id, v) - return v - - def _getextrema(self): - """Get min/max values for each band in the image""" - - def minmax(histogram): + @cached_property + def extrema(self) -> list[tuple[int, int]]: + """ + Min/max values for each band in the image. + + .. note:: + This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and + simply returns the low and high bins used. This is correct for + images with 8 bits per channel, but fails for other modes such as + ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to + return per-band extrema for the image. This is more correct and + efficient because, for non-8-bit modes, the histogram method uses + :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. + """ + + def minmax(histogram: list[int]) -> tuple[int, int]: res_min, res_max = 255, 0 for i in range(256): if histogram[i]: @@ -65,12 +91,14 @@ def minmax(histogram): return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)] - def _getcount(self): - """Get total number of pixels in each layer""" + @cached_property + def count(self) -> list[int]: + """Total number of pixels for each band in the image.""" return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)] - def _getsum(self): - """Get sum of all pixels in each layer""" + @cached_property + def sum(self) -> list[float]: + """Sum of all pixels for each band in the image.""" v = [] for i in range(0, len(self.h), 256): @@ -80,8 +108,9 @@ def _getsum(self): v.append(layer_sum) return v - def _getsum2(self): - """Get squared sum of all pixels in each layer""" + @cached_property + def sum2(self) -> list[float]: + """Squared sum of all pixels for each band in the image.""" v = [] for i in range(0, len(self.h), 256): @@ -91,12 +120,14 @@ def _getsum2(self): v.append(sum2) return v - def _getmean(self): - """Get average pixel level for each layer""" + @cached_property + def mean(self) -> list[float]: + """Average (arithmetic mean) pixel level for each band in the image.""" return [self.sum[i] / self.count[i] for i in self.bands] - def _getmedian(self): - """Get median pixel level for each layer""" + @cached_property + def median(self) -> list[int]: + """Median pixel level for each band in the image.""" v = [] for i in self.bands: @@ -110,19 +141,22 @@ def _getmedian(self): v.append(j) return v - def _getrms(self): - """Get RMS for each layer""" + @cached_property + def rms(self) -> list[float]: + """RMS (root-mean-square) for each band in the image.""" return [math.sqrt(self.sum2[i] / self.count[i]) for i in self.bands] - def _getvar(self): - """Get variance for each layer""" + @cached_property + def var(self) -> list[float]: + """Variance for each band in the image.""" return [ (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i] for i in self.bands ] - def _getstddev(self): - """Get standard deviation for each layer""" + @cached_property + def stddev(self) -> list[float]: + """Standard deviation for each band in the image.""" return [math.sqrt(self.var[i]) for i in self.bands] From 76fb002dd4d7bac29407c532dc119ed8ba07dd10 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 7 Apr 2024 16:14:41 +1000 Subject: [PATCH 038/300] Removed outdated comment --- src/PIL/ImageMode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 0b31f608174..f6524f44bb4 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -42,7 +42,6 @@ def __str__(self) -> str: @lru_cache def getmode(mode: str) -> ModeDescriptor: """Gets a mode descriptor for the given mode.""" - # initialize mode cache endian = "<" if sys.byteorder == "little" else ">" modes = { From 2f3281dcda446c0a5b4121ee8e17feb312192c1e Mon Sep 17 00:00:00 2001 From: Jonah Jeleniewski Date: Mon, 8 Apr 2024 21:14:19 +1000 Subject: [PATCH 039/300] Add support for bitmaps with header size 52 Size 52 is the undocumented `BITMAPV2INFOHEADER`. It adds the RGB bit masks. The format is known to be supported by: - Adobe Photoshop - Popular web browsers --- Tests/images/bmp/q/rgb32h52.bmp | Bin 0 -> 32578 bytes Tests/test_bmp_reference.py | 1 + src/PIL/BmpImagePlugin.py | 15 +++++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 Tests/images/bmp/q/rgb32h52.bmp diff --git a/Tests/images/bmp/q/rgb32h52.bmp b/Tests/images/bmp/q/rgb32h52.bmp new file mode 100644 index 0000000000000000000000000000000000000000..db6e4538ef84f48baf706603bd919223d372444b GIT binary patch literal 32578 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 B3Bv#Y literal 0 HcmV?d00001 diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 0ad49613553..5398664b88f 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -44,6 +44,7 @@ def test_questionable() -> None: "pal8os2sp.bmp", "pal8rletrns.bmp", "rgb32bf-xbgr.bmp", + "rgb32h52.bmp", ] for f in get_files("q"): try: diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 6643ac39bc5..dd8c06f4d49 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -53,7 +53,7 @@ def _accept(prefix: bytes) -> bool: def _dib_accept(prefix): - return i32(prefix) in [12, 40, 64, 108, 124] + return i32(prefix) in [12, 40, 52, 64, 108, 124] # ============================================================================= @@ -95,7 +95,7 @@ def _bitmap(self, header=0, offset=0): # --------------------------------------------- Windows Bitmap v2 to v5 # v3, OS/2 v2, v4, v5 - elif file_info["header_size"] in (40, 64, 108, 124): + elif file_info["header_size"] in (40, 52, 64, 108, 124): file_info["y_flip"] = header_data[7] == 0xFF file_info["direction"] = 1 if file_info["y_flip"] else -1 file_info["width"] = i32(header_data, 0) @@ -117,10 +117,13 @@ def _bitmap(self, header=0, offset=0): file_info["palette_padding"] = 4 self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) if file_info["compression"] == self.BITFIELDS: - if len(header_data) >= 52: - for idx, mask in enumerate( - ["r_mask", "g_mask", "b_mask", "a_mask"] - ): + if len(header_data) >= 48: + masks = ["r_mask", "g_mask", "b_mask"] + if len(header_data) >= 52: + masks.append("a_mask") + else: + file_info["a_mask"] = 0x0 + for idx, mask in enumerate(masks): file_info[mask] = i32(header_data, 36 + idx * 4) else: # 40 byte headers only have the three components in the From a1a2202ebec15c6ba4c77e0eaf94bb698ab81530 Mon Sep 17 00:00:00 2001 From: Jonah Jeleniewski Date: Mon, 8 Apr 2024 21:16:39 +1000 Subject: [PATCH 040/300] Add support for bitmaps with header size 56 Size 56 is the undocumented `BITMAPV3INFOHEADER`. It adds the alpha bit mask. The format is known to be supported by: - Windows (MS paint, etc.) - Adobe Photoshop - Popular web browsers --- Tests/images/bmp/q/rgba32h56.bmp | Bin 0 -> 32582 bytes Tests/test_bmp_reference.py | 1 + src/PIL/BmpImagePlugin.py | 4 ++-- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 Tests/images/bmp/q/rgba32h56.bmp diff --git a/Tests/images/bmp/q/rgba32h56.bmp b/Tests/images/bmp/q/rgba32h56.bmp new file mode 100644 index 0000000000000000000000000000000000000000..343baa3300f331aaeabeb40d8fcd2f232393e815 GIT binary patch literal 32582 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 None: "pal8rletrns.bmp", "rgb32bf-xbgr.bmp", "rgb32h52.bmp", + "rgba32h56.bmp", ] for f in get_files("q"): try: diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index dd8c06f4d49..82a9c47dbd5 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -53,7 +53,7 @@ def _accept(prefix: bytes) -> bool: def _dib_accept(prefix): - return i32(prefix) in [12, 40, 52, 64, 108, 124] + return i32(prefix) in [12, 40, 52, 56, 64, 108, 124] # ============================================================================= @@ -95,7 +95,7 @@ def _bitmap(self, header=0, offset=0): # --------------------------------------------- Windows Bitmap v2 to v5 # v3, OS/2 v2, v4, v5 - elif file_info["header_size"] in (40, 52, 64, 108, 124): + elif file_info["header_size"] in (40, 52, 56, 64, 108, 124): file_info["y_flip"] = header_data[7] == 0xFF file_info["direction"] = 1 if file_info["y_flip"] else -1 file_info["width"] = i32(header_data, 0) From 9e5b9fb18fdb2fe957346424432163cdb5fe3b00 Mon Sep 17 00:00:00 2001 From: Jonah Jeleniewski Date: Mon, 8 Apr 2024 21:33:24 +1000 Subject: [PATCH 041/300] Improve comments around bitmap info headers --- src/PIL/BmpImagePlugin.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 82a9c47dbd5..ddc6b3fccf9 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -83,8 +83,9 @@ def _bitmap(self, header=0, offset=0): # read the rest of the bmp header, without its size header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4) - # -------------------------------------------------- IBM OS/2 Bitmap v1 + # ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1 # ----- This format has different offsets because of width/height types + # 12: BITMAPCOREHEADER/OS21XBITMAPHEADER if file_info["header_size"] == 12: file_info["width"] = i16(header_data, 0) file_info["height"] = i16(header_data, 2) @@ -93,8 +94,13 @@ def _bitmap(self, header=0, offset=0): file_info["compression"] = self.RAW file_info["palette_padding"] = 3 - # --------------------------------------------- Windows Bitmap v2 to v5 - # v3, OS/2 v2, v4, v5 + # --------------------------------------------- Windows Bitmap v3 to v5 + # 40: BITMAPINFOHEADER + # 52: BITMAPV2HEADER + # 56: BITMAPV3HEADER + # 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER + # 108: BITMAPV4HEADER + # 124: BITMAPV5HEADER elif file_info["header_size"] in (40, 52, 56, 64, 108, 124): file_info["y_flip"] = header_data[7] == 0xFF file_info["direction"] = 1 if file_info["y_flip"] else -1 From 98ae91a65deb6ab0790c042ca1f0ebc478f88df0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Apr 2024 23:58:03 +1000 Subject: [PATCH 042/300] Added BGXR and BGAR unpackers --- src/PIL/BmpImagePlugin.py | 4 ++++ src/libImaging/Unpack.c | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index ddc6b3fccf9..fa337f6ebfc 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -184,9 +184,11 @@ def _bitmap(self, header=0, offset=0): 32: [ (0xFF0000, 0xFF00, 0xFF, 0x0), (0xFF000000, 0xFF0000, 0xFF00, 0x0), + (0xFF000000, 0xFF00, 0xFF, 0x0), (0xFF000000, 0xFF0000, 0xFF00, 0xFF), (0xFF, 0xFF00, 0xFF0000, 0xFF000000), (0xFF0000, 0xFF00, 0xFF, 0xFF000000), + (0xFF000000, 0xFF00, 0xFF, 0xFF0000), (0x0, 0x0, 0x0, 0x0), ], 24: [(0xFF0000, 0xFF00, 0xFF)], @@ -195,9 +197,11 @@ def _bitmap(self, header=0, offset=0): MASK_MODES = { (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", + (32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR", (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR", (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", + (32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 6c7d52f58d1..a84dc0a6fd2 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -790,6 +790,17 @@ ImagingUnpackBGRX(UINT8 *_out, const UINT8 *in, int pixels) { } } +static void +ImagingUnpackBGXR(UINT8 *_out, const UINT8 *in, int pixels) { + int i; + for (i = 0; i < pixels; i++) { + UINT32 iv = MAKE_UINT32(in[3], in[1], in[0], 255); + memcpy(_out, &iv, sizeof(iv)); + in += 4; + _out += 4; + } +} + static void ImagingUnpackXRGB(UINT8 *_out, const UINT8 *in, int pixels) { int i; @@ -1090,6 +1101,17 @@ unpackBGRA16B(UINT8 *_out, const UINT8 *in, int pixels) { } } +static void +unpackBGAR(UINT8 *_out, const UINT8 *in, int pixels) { + int i; + for (i = 0; i < pixels; i++) { + UINT32 iv = MAKE_UINT32(in[3], in[1], in[0], in[2]); + memcpy(_out, &iv, sizeof(iv)); + in += 4; + _out += 4; + } +} + /* Unpack to "CMYK" image */ static void @@ -1584,6 +1606,7 @@ static struct { {"RGB", "RGBA;L", 32, unpackRGBAL}, {"RGB", "RGBA;15", 16, ImagingUnpackRGBA15}, {"RGB", "BGRX", 32, ImagingUnpackBGRX}, + {"RGB", "BGXR", 32, ImagingUnpackBGXR}, {"RGB", "XRGB", 32, ImagingUnpackXRGB}, {"RGB", "XBGR", 32, ImagingUnpackXBGR}, {"RGB", "YCC;P", 24, ImagingUnpackYCC}, @@ -1624,6 +1647,7 @@ static struct { {"RGBA", "BGRA", 32, unpackBGRA}, {"RGBA", "BGRA;16L", 64, unpackBGRA16L}, {"RGBA", "BGRA;16B", 64, unpackBGRA16B}, + {"RGBA", "BGAR", 32, unpackBGAR}, {"RGBA", "ARGB", 32, unpackARGB}, {"RGBA", "ABGR", 32, unpackABGR}, {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA}, From 40504bb49007974658d19e32aa62cc93b9160fd8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:15:51 +0300 Subject: [PATCH 043/300] No more eggs --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index ad0a1adab50..5547b2e3caf 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,6 @@ release-test: python3 selftest.py python3 -m pytest Tests python3 -m pip install . - -rm dist/*.egg -rmdir dist python3 -m pytest -qq python3 -m check_manifest From bf463c25dfa75ef715470e65206eedde087323d2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:48:08 +0300 Subject: [PATCH 044/300] Inline the 'Source and Binary Distributions' step --- RELEASING.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index b013d8288ff..9e6ec5dd4c1 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -20,8 +20,10 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th. git tag 5.2.0 git push --tags ``` -* [ ] Create and upload all [source and binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#source-and-binary-distributions) -* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) +* [ ] Check the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml) + has passed, including the "Upload release to PyPI" job. This will have been triggered + by the new tag. +* [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases). * [ ] In compliance with [PEP 440](https://peps.python.org/pep-0440/), increment and append `.dev0` to version identifier in `src/PIL/_version.py` and then: ```bash @@ -50,7 +52,9 @@ Released as needed for security, installation or critical bug fixes. ```bash make sdist ``` -* [ ] Create and upload all [source and binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#source-and-binary-distributions) +* [ ] Check the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml) + has passed, including the "Upload release to PyPI" job. This will have been triggered + by the new tag. * [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) and then: ```bash git push @@ -72,18 +76,14 @@ Released as needed privately to individual vendors for critical security-related git tag 2.5.3 git push origin --tags ``` -* [ ] Create and upload all [source and binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#source-and-binary-distributions) +* [ ] Check the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml) + has passed, including the "Upload release to PyPI" job. This will have been triggered + by the new tag. * [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) and then: ```bash git push origin 2.5.x ``` -## Source and Binary Distributions - -* [ ] Check the [GitHub Actions "Wheels" workflow](https://github.com/python-pillow/Pillow/actions/workflows/wheels.yml) - has passed, including the "Upload release to PyPI" job. This will have been triggered - by the new tag. - ## Publicize Release * [ ] Announce release availability via [Mastodon](https://fosstodon.org/@pillow) e.g. https://fosstodon.org/@pillow/110639450470725321 From 3a92d4af0162d80bc13ae15efbc2e47483870d3b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 9 Apr 2024 07:34:52 +1000 Subject: [PATCH 045/300] Replace ImageMath.eval with ImageMath.lambda_eval --- selftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/selftest.py b/selftest.py index ed5252c4464..661abcddb74 100755 --- a/selftest.py +++ b/selftest.py @@ -139,7 +139,9 @@ def testimage() -> None: In 1.1.6, you can use the ImageMath module to do image calculations. - >>> im = ImageMath.eval("float(im + 20)", im=im.convert("L")) + >>> im = ImageMath.lambda_eval( \ + lambda args: args["float"](args["im"] + 20), im=im.convert("L") \ + ) >>> im.mode, im.size ('F', (128, 128)) From 4b4cdbd40c865cd932329f1ace42f0d42ce1cb71 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 10 Apr 2024 19:01:34 +1000 Subject: [PATCH 046/300] Added image to supported list --- Tests/test_bmp_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index a5a4283e3bb..7f848792131 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -44,6 +44,7 @@ def test_questionable() -> None: "pal8os2sp.bmp", "pal8rletrns.bmp", "rgb32bf-xbgr.bmp", + "rgba32.bmp", "rgb32h52.bmp", "rgba32h56.bmp", ] From e2a57263c72cd0e55822b78aaf1faa246a8d6895 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 10 Apr 2024 19:17:20 +1000 Subject: [PATCH 047/300] Reduced duplicate code --- src/PIL/BmpImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index fa337f6ebfc..9ce0fed88fe 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -123,8 +123,8 @@ def _bitmap(self, header=0, offset=0): file_info["palette_padding"] = 4 self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) if file_info["compression"] == self.BITFIELDS: + masks = ["r_mask", "g_mask", "b_mask"] if len(header_data) >= 48: - masks = ["r_mask", "g_mask", "b_mask"] if len(header_data) >= 52: masks.append("a_mask") else: @@ -141,7 +141,7 @@ def _bitmap(self, header=0, offset=0): # location, but it is listed as a reserved component, # and it is not generally an alpha channel file_info["a_mask"] = 0x0 - for mask in ["r_mask", "g_mask", "b_mask"]: + for mask in masks: file_info[mask] = i32(read(4)) file_info["rgb_mask"] = ( file_info["r_mask"], From 94fe670c0bc9ad88c8d7d55491cb1c55f7ed14dc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 10 Apr 2024 21:50:17 +1000 Subject: [PATCH 048/300] Test DIB header size --- Tests/test_file_bmp.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index 1eaff0c7df0..c7c9b24e763 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -5,7 +5,7 @@ import pytest -from PIL import BmpImagePlugin, Image +from PIL import BmpImagePlugin, Image, _binary from .helper import ( assert_image_equal, @@ -128,6 +128,29 @@ def test_load_dib() -> None: assert_image_equal_tofile(im, "Tests/images/clipboard_target.png") +@pytest.mark.parametrize( + "header_size, path", + ( + (12, "g/pal8os2.bmp"), + (40, "g/pal1.bmp"), + (52, "q/rgb32h52.bmp"), + (56, "q/rgba32h56.bmp"), + (64, "q/pal8os2v2.bmp"), + (108, "g/pal8v4.bmp"), + (124, "g/pal8v5.bmp"), + ), +) +def test_dib_header_size(header_size, path): + image_path = "Tests/images/bmp/" + path + with open(image_path, "rb") as fp: + data = fp.read()[14:] + assert _binary.i32le(data) == header_size + + dib = io.BytesIO(data) + with Image.open(dib) as im: + im.load() + + def test_save_dib(tmp_path: Path) -> None: outfile = str(tmp_path / "temp.dib") From 34b3cb519c3235366c3881f4e8a59ef3bf2c22b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 10 Apr 2024 23:37:38 +1000 Subject: [PATCH 049/300] Converted example images to WebP --- docs/deprecations.rst | 2 +- docs/example/anchors.png | Bin 9726 -> 0 bytes docs/example/anchors.py | 2 +- docs/example/anchors.webp | Bin 0 -> 6350 bytes docs/example/image_thumbnail.png | Bin 19241 -> 0 bytes docs/example/image_thumbnail.webp | Bin 0 -> 2560 bytes docs/example/imageops_contain.png | Bin 19241 -> 0 bytes docs/example/imageops_contain.webp | Bin 0 -> 2560 bytes docs/example/imageops_cover.png | Bin 38843 -> 0 bytes docs/example/imageops_cover.webp | Bin 0 -> 4164 bytes docs/example/imageops_fit.png | Bin 28146 -> 0 bytes docs/example/imageops_fit.webp | Bin 0 -> 3234 bytes docs/example/imageops_pad.png | Bin 19499 -> 0 bytes docs/example/imageops_pad.webp | Bin 0 -> 2566 bytes docs/example/size_vs_bbox.png | Bin 12934 -> 0 bytes docs/example/size_vs_bbox.webp | Bin 0 -> 4948 bytes docs/handbook/text-anchors.rst | 2 +- docs/handbook/tutorial.rst | 32 ++++++++++++++--------------- docs/reference/ImageOps.rst | 32 ++++++++++++++--------------- docs/releasenotes/9.2.0.rst | 2 +- 20 files changed, 36 insertions(+), 36 deletions(-) delete mode 100644 docs/example/anchors.png create mode 100644 docs/example/anchors.webp delete mode 100644 docs/example/image_thumbnail.png create mode 100644 docs/example/image_thumbnail.webp delete mode 100644 docs/example/imageops_contain.png create mode 100644 docs/example/imageops_contain.webp delete mode 100644 docs/example/imageops_cover.png create mode 100644 docs/example/imageops_cover.webp delete mode 100644 docs/example/imageops_fit.png create mode 100644 docs/example/imageops_fit.webp delete mode 100644 docs/example/imageops_pad.png create mode 100644 docs/example/imageops_pad.webp delete mode 100644 docs/example/size_vs_bbox.png create mode 100644 docs/example/size_vs_bbox.webp diff --git a/docs/deprecations.rst b/docs/deprecations.rst index c3d1ba4f028..f530d5741c9 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -264,7 +264,7 @@ Previously, the ``size`` methods returned a ``height`` that included the vertica offset of the text, while the new ``bbox`` methods distinguish this as a ``top`` offset. -.. image:: ./example/size_vs_bbox.png +.. image:: ./example/size_vs_bbox.webp :alt: In bbox methods, top measures the vertical distance above the text, while bottom measures that plus the vertical distance of the text itself. In size methods, height also measures the vertical distance above the text plus the vertical distance of the text itself. :align: center diff --git a/docs/example/anchors.png b/docs/example/anchors.png deleted file mode 100644 index 40476b0922b4494b8d154a81bf3788798b078d75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9726 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 Awg3PC diff --git a/docs/example/anchors.py b/docs/example/anchors.py index b5d76b4fe40..2ee11103f1a 100644 --- a/docs/example/anchors.py +++ b/docs/example/anchors.py @@ -26,5 +26,5 @@ def test(anchor: str) -> Image.Image: d.line(((x * 200, y * 100), (x * 200, (y + 1) * 100)), "black", 3) if y != 0: d.line(((x * 200, y * 100), ((x + 1) * 200, y * 100)), "black", 3) - im.save("docs/example/anchors.png") + im.save("docs/example/anchors.webp") im.show() diff --git a/docs/example/anchors.webp b/docs/example/anchors.webp new file mode 100644 index 0000000000000000000000000000000000000000..216b6c235a26b7515e3b756e38bc26b2c16a9c56 GIT binary patch literal 6350 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 OdA)BXvok02N7 zy&fl?X676c+A+nU`yD8d|GL9V6|F=ciCb;byvpZ6*D9?EY}(dg3>Y>USW+&y+R|S> z&NTfWgLdbr5|&Hd-}g!qT#BVg1`+LKhq2;*?s)GAWrWA`27^T8Ff!!Z+C>&Xysw({ zF!g*6Eq8s=yd3E zMMHAHSq2QIys=Qip(o>3u=RDyE!U_n7xHBMHqd+`#aP)X;D$3{KF+z?(T0hkIAcH< zTzfw9W1W4}%%_0U9z~nvk;tj`|D)F^n0e?n<>42`ZPu$820^lU8qYs_h(6o?HHGiiFL= zKI{2M3B;;FwO}l|cO9KSh)AfdFwWM&W$JKYbVn)**e%SVNeuzS4GU+$Bu(-xWd6O@ zbm2^H>bl?$6#fd}17y4amWEnzNUtpkck(<$NE_pgjW2@*L`JImcE;__ot~EuOavIE zTwE+jG=Bci%qZ%|875jF<2+@uiu4(5{VG0C$8sd;{+ znvnEKQn@8G0EUx*0;S_(FK`2i#zn+QL?LL9P@ z?O7h<;9&=H_G=VU{OrV%?l01Fgz#p(Jzx6<4fw0uH zhsq?&T&^3Z>VPt`cm}W8tkA_Vw7ttR#@kXbloPiVc2e{3H{ceZ7$3T>9Y|5wd-5(j z=VLNM>frnKhGHhZvJ-gPo{>nyR_8hwyNMt;y(HCFRheMEPBkb%YP_9wD!MJUP2QT0 zs)98MJnQ%rhssR{OGJ=O(IVANpoc@|W{1RTIv6`JOl5kMCjPvbjo*Y2We3pV@j!8^ zb}LbEGV1aXD+jRfM3g^|5o-q>rwoA2w{}d)04PS-fIO0)QCU@{hfu}gVZJrtbI}D1 z-BUPiiWVZ3rNHD&4tWy1REWkp*}6b2u3&>k9y@bYu`gD%pT_e_CNEBk!TrJ zgH*Y^VR{u)SXD~Yr^Ab6If+7A9pX9G5?9w9Sc;%sYY=b8hSU=kW@S8emNyGkHofss z6$*6CXSrJ+g)nLoD3!PnRB(tq%yV2V9CKD#15is55zh7$m;*;@52)SnG##-RFVN9-zT^}* zJ8{Cpjlkd8)DZ_7ke}DsJU`5t9B)CDfQY}^&%#Aazy|^9?;=+4N!5b9BYB08{$P^m z0l$P;#@=Y-ho3@Fvo+jwRWV@QC2;$6VW0WAiC2#eD>WCes0v|P zpdUC78%M&4RcaiiSdOa(6&GK?W^8%f-s1a!k*QmBli2|=k&b~%kj^=}V_acjVzG?V zy;1EQkKQF+@RW}fT{e}(-SPNv6q#LxUFk7w`n;WmFIAWZYu1h=iqO^Ej~R% zJW6v!L_yfqZcemRSH#hC7JVR6o0+Yjv9&!Sk%q0#bT4-jKyi9Ws;;Xt!F-%*P=M8W zI_gz)TWp)XH62w1Y7%+Z@F@?JnhutUClHk+4gYCM(1&*Z_}$a>4W_ir%0m}DVB#mC;D8EL Q)Yh+1e63w>000000CfdR{r~^~ literal 0 HcmV?d00001 diff --git a/docs/example/image_thumbnail.png b/docs/example/image_thumbnail.png deleted file mode 100644 index 293b05794659734d56726ce7c21310f16773e0b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19241 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 QfB*mh07*qoM6N<$f=X7t3;+NC diff --git a/docs/example/image_thumbnail.webp b/docs/example/image_thumbnail.webp new file mode 100644 index 0000000000000000000000000000000000000000..9780f2852fe434eaaf8e3daf43fff9cca3799c29 GIT binary patch literal 2560 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 QfB*mh07*qoM6N<$f=X7t3;+NC diff --git a/docs/example/imageops_contain.webp b/docs/example/imageops_contain.webp new file mode 100644 index 0000000000000000000000000000000000000000..9780f2852fe434eaaf8e3daf43fff9cca3799c29 GIT binary patch literal 2560 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 Ta2hb800000NkvXXu0mjfV0nW_ diff --git a/docs/example/imageops_cover.webp b/docs/example/imageops_cover.webp new file mode 100644 index 0000000000000000000000000000000000000000..a0b6c10bf2ffc5c6686eba10c7065780313b1c3e GIT binary patch literal 4164 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 O3znKIDw7Iy0000#)g(Cp literal 0 HcmV?d00001 diff --git a/docs/example/imageops_fit.png b/docs/example/imageops_fit.png deleted file mode 100644 index 13a3d5e3febdf3cc465f2bcc45dacfb817a5e7f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28146 zcmV)=K!m@EP)dNklL zcd%t?avz9g=4Xz%_W4)dSNrbi?wP@O0}N*19Z3+NS&&PHxR5B3id9gt6t&U@BUU1U zyAhN`Vu{s?T9P0M28n?m%nUHV0OPxRdfIn&dHejk{+Q2X>W_2ZtLp9

RqO9bLC> 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�$GZJxvZH8z;sgJBI|(>jxnf|IB1r+TVx5C-3ZHTfffyhnix3S_^vk zvD10lj|f`5ITZ0X+b?2iN4OE9?-A4U0BLdLA=b4piZ8?vee{1oKda9t6-NqkW&(uQ z#^RCq0I~XJc0y^_Bckx~QNkzi#8_;c6yMM9Xu`vxQ2P}7{cH@fm%LzVKrrNGzE+m2 zWynK7%?nUzX=v$}UQ*YfO#2oE1(}d;EMG2rB@P@MoHbU9Nth$?U(MWGsbjUMmoLkK zNdFO&J4htLtg@kL#wCl1JphAZ} zWFSOB@djj7!fTAGNchbgv>&!s{jCGvqG=vnwl->L4M(iS@!+tbfN<@3I1ZbMk`gjc zMvA+SWGOHWpwheztTBmJCeR z|Ja%1{ZEw?s%Gm*c>oydG-_zrbl8CKHGB%R=zVq39bQtowgK5exld&&io`NW4XBK7 zA54UIq>F#6!)WW>9PwsL&tQe`exmx^V&N^xUP76sPR3a|nK{OKBRCCF)@26aYat9f zMqEcg971kjDK}eQ<>+834khp2yBf9*%1*(;e83cDlPC>zk`{~AZukz0JuE^a2Q7s5 zK&C)2n1+r3IP^cdXp5ofMgakABV{&-J1K7|ovLI9TPO~Ic}CF$Ns#1*7S-}N@V!dJ zYqK>fDd|to5Md}UkdH*hM3_uVS4WpA7-e9Y&L95+)}f({2i+Ve0iru~+Xa@~{^gsH zxO_<0-PY~5jpWHgVQ8BYzs}k~!*n!HU#xx-LuuTG3X2Tmm-97ZX)_{eaKz|hwhGq1 zn0L>gKQ(8=B2{gFc6D>Tw}x4A6fcaDh`-<`TXSz3vu=pcGVyKG<9_|StMU20_ zIB-n`s}Osc8YUv2dcb6vy&UY(|Vg0J~uoAyTuT2B>*%C>;@$K0Wu|R^kCS zP2APZ_xP`Jld>bt;}Y38FSfiSIh}zgL`rQc&z=V)Gxckg4@8vN9f9-TkgzKd9iQ&y z_Zc$h#lGE|$60~Axwr4?9Y)PkLi}y!7_UkN6yQ7|o&_@0Ux-<2XGp~-G!g)2+?Q1E zxWvaeOzgK_Q!2)uWt+IZCD(@Fntv@2qFJf@I(l28=@Aq4syY=K%)>iEuUMlk6QVr4 z<5^)-rzzL{`AxZt!op!GAWsY9M}-j0Z4FJ^^TWq`vFA0K$DZ7Wn)inHH-{^A4$nj^ zDxdRcV@M@&(Rm!Z!&pnn!MJAdsMs@;|462ZKIKsld9|5acC=OFXKs1pS#dH8C{qT98&8O%(FQ%(N8JQKrS)~kYeTy; zXA1g-29B0QMf*L~JV`e%c5!Gr(>v30P~e#AlUX=e3lJQoMGU~M`LY|wlu7zF>CrUk zLRb92;xu*X>;TdEFAscL+UdH=Il2F%GxPXe;@$&}wht981Vyg-Qu_ijBBmS^wa5#@ z7Y;nj<6xft-fNN4vjU$(Il=Lt^RWpszJS6Ns#;PYCCjfl1|I#%$I zGNqKKG#dPvzP(^_x|9VCjRXW_ z<_X6uwJsST2^q%_>sD_0@bE&J1p21hrsLbPw~pEzZRk0<&vR_pA54F~#DK zT~GTRtM|u4W&%+g0fs>*--F!Fi#$*y#P4y|W3mlD&Hv|H@pO!M9wO_y9O`|LAxsEG z9eOAaO~uA1=s3TtEh8OPToCd5;AV6C(aANht(JSY*Kg%9C;#lMq~v|JSD8)~p3d0R zp6+NETs{ zqiEQu>ZGMdO27=wv@J8o=LR}4_2~sMHE(* zv~O;_vtRX=_4gIl_GL@mYT#Wj&BJ8z%`4%~fSt`5r$vgZ9@@3q`j%9Qv%Zb>bUH~S z3{4uTchY(h`CaaXt^Up7o~`jp^k-qPL35$ z;J-Ph@|!R`epQZdEGqGMSU2J6SFu+2{obqVX~s~h)xKWO(BMBC*Pf~d9T5li4|QT0 zcnH!ouAJ77fq~ZhzbrVTx4Dd(T`Ow?Zu&G60=3r%;_g`%%IZFp322>tx^`Nu-Ds`0 z-i?|s?*>B6;STgWAXYXbom#s(F0SOsO!|C{+<863)~MpFjg3t$nMsxiqSNEOWf5VEwe7kmYE zBfau8-hU0)nUwyXh4?+@c@bjIxx&*pWhC5+xY=Lt?Z^GS8hECaVf({|uU5Fa`Mgha zB98G(9-h`Ur%IL9{CnyW*z4vjp9srXxC_TKn`Zi5D0o(#auTX^=&oU7Al&ROr= z^t575+zUMZ0i}c6sv!HtGob|4rR8zyjGYEwF+>P9kSp0+n={5tkhMfng0BA0N=fcn|@zd2M(1wiSHSu!mu21QM_6Qm%pH_|ADHfAJSMA}6;-C8R`!;{^ zYdWx+8e2>yOeVB6Lykt|_r7qnefMERhena$oMU8y|78qc-4E5kyVpEDIxVLyOg5Ca zw3=z8FJ6g19N%|6PduO3l^uPtJkZX@h*qjfd(q!;%SYxH<6tdS4+cLi;Lj$ULnX|O#Yw!UNPu2U- z%S%6zH`WhghZpks9M?M|UocJ25_RumR;eV`TP;&GxsBue46W{PX zA#u_UnT`#$@WoNFY3JGBOpH$~BK=bPZFcIgZdwY1e<99p4&&F~pQr9sFQZk~4K1}x zRQMw`lnt|!Ci7q2d%0v%vA2&VGdfhsFnqU9p0=?a96wke$x~%2#oOX<`_GH{#`)&` z#j+z)bkW<~J7)@`g*T2)cHtg{y`e@fj#2#yvC89I^<@H=DW!Y^3c-EfSd-YQG|}ez zxT|3JB$7h^!EZtTDDZljTg5_IIM@jJSw892!nDm(1Xs{^Ms1cH1_nL!5IycZcJ51n z*7}M+;<}7La|%EiGr@xe-RyW#`=%ii?fc;8m2=NU*$+L&S^A%KiYfR8rbEIkfA5UX zU?w}y1Y9kpoBT-@UO`;W{hf35@U<^mZrwZOS(~edx>_zGlyk-S%9{^q8FTBO6IT4KGI_Lxq3}4bJB~hSXGV^h%rF1N>`xCZ8iWCYOmzTPUtTqUty=HF_>}E?C!FN@LsZ#Oj5o(;!p+@u5cV-Oe-3*Xspd|dwIUgckI?8D}-CsjK z(`!yys0oHXUkl^z9H}Xk(u+7|bv?rzTgl*u`nwK+Wx>xx;=E1)Buivyl=tjlUkefw@Le+{z(LeJl-*WJ&& zp4Y20+2oys=khC&&qs#*3V5XiY$~x#VQM(rdT+S2rHIkhUZYbU*J&=PWmPR@r*Tnm z1`&SAhkOh*!6I+L>n#z*fe<$Y5fR5ii1=zR2=Vfw!>M>awk{9FO`IK< z|FPg>8G1SPBXwsoq$gSB}c5V%8moERWD zs3MasWdpw|Y$d&BF)%d)z~%EcPL)u*lbz1t$6YB<_ijOj!&W z*EHpZ(W{YfJ36E`rU6&|94G17Z2CQD_zHaSFB91sd^b1pY2BS`pR)co;j^Hw%Mcr-eqMgOF(sq?WDsF}YMri%gI zney}Wz5K}|AK=#@AS{Xm2xM+%*R%4bk-=i&1I$2IXP-`;s`CDZZ#tUgST9c%$4Jv@ zcS(M=IX8ce<33f)L1%hh$WAW@-qpp*FD(VDld1g&xhrzPqQ#MvA?~nGSEC0I7WP8E z(ry?pylo7+Pk8!%QdWU~kS>~S^vFO#9ja(VzOhNTE_KNJdu0rXT#xo?8dU1Ck z{3v7`IgS+^ektxcRW(2iC4~|G%wtuAsI_}YhHzTR*iOXP+;rSLMac@Bc*RDepLOav zSKwO&e6&MNJ56wo7ZM0$_Du`l^ea~J=}~q%KX)o>Wxfjv^znR4&_5_~JIv8MoZW^o zIEYCh-P1NRvcEpI)LWEZlmBP>wm-k;?oXjyv>F)VK%YRzBS`q6lAo;g===S3Wl;q4 zm9ukJwRwFik`~Kx>e%H_M5<5OiR?1F{$OMo^U+lUiu#bX{;-@@R3Gxl(`%3NS#ZS&Z5xWjq4igN8qUpdTVUZr{K8 z8po=hb}xFk8kFH*_W_rM_H`h_M~drlg`;s9F;#|`sMGQ?JAB2OM>%j$EAr$?uKoeA8d!e3Lf?G z=9fMej9ihh(9dxN7S;+cQAsp9S$XR=ygOT)wdF`J7%H9i&8X0}Py>S1fIuc$45>5H zfFa+ShMSH@r;~uy8yOwt6`cIUH7yxN!0XqF-ME@{8`9FrM2moGd9A{XIQ7K2h5F<- zKRDXm&x>Z4ukI8Z3XlVHYsUQpLR2DNb|>OCzxV5Lb{q-IMvgoDDkTCR>n21^owTYP z7~xZUA8C2j1q8pX*Oh zF%r*2nwr@O0f)F94I~c3G2UrT>2nLtJIJ3$q%%5DWojIoYVrT-+n5xN1Ab?&{{1GT z(=H^7jwWlxFEtp7u2(;E>3iE!^8QLO3l9DKxG+mibS6g-`?}5m5>s3}K0Sz0*wfla ztb1VQC`J+9DigIL)miI?PVAy!O3bz-6V>op&9f+P_HThKVAm8F6Ciel(tns9%z zUxiLv!?qf2t$q@v<fux~)x!M>oibfC^ zhjmUb*VscQFOQYCR@-h;qmU|~n!CayNSrWr9-PE8zN45IPd;}~U8+iNsoXx}Y+|HW zF(u1xNkm3Unr+V-8ntn>vG`6zmj{5G6?ApUUX?TK?(&v7Dc|1HJFlj;eV1Q2a>~~3 zs2gVK|Iu-@FZJL5!TT9tM$8UpLA~2gw#nC438)Yvi&a?C$LFJo?2{k?JF9X$SoZ@ICRlc`&d~~pqgGY zSN1AV7CA3uY(WW=yK{n1c&dGB+K0IvTnP*Vam~rHs5$`nLa?xr$-D96o`8h--$LqpKiHF;W zA@g=E+TN*RYjVn|aG&kU+m4I_jbi+3G=)9@a0&*to3pm<=BWx7aUfXhDkwQfM$t6$ zFYjAlyP&L{Glm|XXo7kNt}KOC?#9@Q-yMCN9XFQ#UN|A<@oV9aC?bi+YL zI@`5y$SoDC!SR)0@JQ`sl`ro4qvpE_5Bv4R+JPA?1m!Tr?3z9K$j`6lR20 z7qs?T3jL>s+>|W9qWE$wBG`#2d+TO@TcWE|hsS07pIEgehsBTDX=FLf-qMo2@%QoN z`LVsNr|$VX>}e^*9x7b$ zr!ZwBKkaNAs9VLTkz-gxPX(L%tUbTYaZBoT3Qu=W~Jz`Pi%D)%HMCH++ngPr$8 zmGQF?Szb*sm3264y(*tMsPHNkw@UY7JhIZrT+7^Dm}4HY;Si~n9)Bl8jSj!(88Q{p%IvbL zhM0m`Cmr?%#fHhFOD!C93JR)ukL8fB25At+18eaK=_D^*kYa$hTFd$`qhaC|zdsz) z4LPqCJ0zMG-`RZS)>g&Y4F(_q^d>Zp9G1S7-{K& zuAOE$Kb!~2)$ISOr8>Qq@9}v@cy$5>mT=dBJ5I^!-dY=aiZ7s=TAsx=hxi zBr!_6&bew}7u@%$2YIk&Z{^!UHkp(!Ntxb$+Tbgl1#qjFAzy9Ph61qy7}$BE@p@-^ zZGC>;7I}%V`>ytv}!< zG?OegWhx$Eed%saN$-4$uR()X(<~Kc%S_Abc9{MunA-<34e=4mZEN>sNq?*OyI|h2s=RTVREIRf;i$mwo6Pv+&r~rA zCpY16QLq-1%7`DkwHQ^Y6f8W>ek%^;%8)mI*mciEOHXg}rsK(hk=v3)4-^6jAyfY> z`nNgtZ)i+AQm_0kPk4uV?eeJ^Yrz^07*ifO@Sm<}V2#^G>NAmzC-C4SqJYJu<2NzQ!SJ z#sj3Qj-ofWujEwC7We#qefF;4PN_lH>V3xxT64m>>i-nsZc|s=e5zS&%r9tyMnzIp z#KpOrxAuy26-A2eQ|t~>&kn8+qu5DkK03F|EBnzKDPSb){E=do(KT`j^x0av>|I({ z?C7lu+L-*uex{^-qxm}!)o+%t`glx^d7D zT8kb2Mf0S=1Kny?sUq1rNjIef+ME!N;_R2TO{=~KG;Lw;s%1qRSXV`gl z`e57T^I8=nVcGivcS54E9}&49CpLRz@Fp<+6u zsG^z^P_LRviOz0A{F5+!3hn^$gkYTRm2vsAzxQ(qxJigTc}LNlN;X0s_8BBhTWnvF z>l%n}0>7lV1wv>2`Y%UP8j`A6F+%{}=s+uNVxX>L1-?@ia~tcc9}AsnbOTVZ-fLK-hP3Xl+^fi^2ZoW zg8L1vY%i-Lmm+>AJoL-GX@7bp(Y*6ByJ5g;tmYkCs4kpSfcfVvpdtSvCH%lWiN>+H zoZbCpKs6*=GMOx7lv&qJ=_wAe>^aeSKt>Zp#g*-hoKPT{4#fwtEm)=%^p)@Xmm4*= z&pY2o4tuix{$nEQ&eK+3&);M)$D(Rthd$I?ARj4h>Hk&mUF*9bUr-N3V9pEsZPbB4 zOu>LcLG^k->9_S>*J;NOaKldrQ1eA=G_<%s`5Cinloa#6fB&ANO?V?M;qVH`c0~(U z(0Kw2B6}`ic@piuYPIbIRWq=&BI|a_R$%SOQnXz>q%||mO3$6o2Efn;S?#lgX@#D| znA<~M2IDE<*uCtg$Rt^296)q!tOWgyN4&UNiuqmj<_GXf>U4z&qq2H{6ifNI2#Ge; z9ZtU~|3k`QdeU~HAw@4GvefmSzA@FcvqS8e_pKD zurk62Mqvo0&+Y%i-1>^VHcDvb{-}34G12YPo3fE?Z@y|(oCy0a#h+vyfK_L}s>om~ z$vBsZuDiL1`5hA<-^H11Oqb3_Wj_L=lXjM9P#!A^103zh2msZJk4QoQ*jgb-wNWo# z$&d2Z#aOie#O2m#ARrn%Yezrvu+q@^E(g&W;O}VmwDsH1S+u+iw?fzCoY;1#q!vGK zg>sN2B05f6xjJj$w6DJ<{r!$fMutj&-g|V8vRAYYU=AR}y9}0I_u?YH&D&hZ1+Ne- z?0rZ4C;AV4%Q-~3EGevHFW2pMhPolpR)4!{sLa6aidjc!to>1U<1#}VLDO`jj;?wK zpsG$@=1ZR|U{yayZ1pJHcdux;!tdDsyI9-xtN2n+OL|Nco47D-DnL(h?va6~tdD^H9^`pqg)UIB?}uHjJ3gKiarI>D=gVm|nST$LFwe zk8v8%?nxy;>NR}JB>7!daXTZGOdAZWG)pZE7b`gv4*6*XPKoQM!e5eJR4)9*3tt^eB(`+b%SB7zTb; z$sOQkmfMpP6Z$wRRh5^v|wu~xXuw- zO^Aj*+3PeS>EM4CNb*y94e1EEeA&t$&q{%^&|364Q2tQ%I9J>`_Lg5JRa%_r{KIfY z9(CM_rxYv~p5CL?W1rFuP&Bi}e`^Xm#kjlZb}!~?|9JP*)Opp|OFOKFqs@`!^bz_B+aYMTG*l>x7;X;g> zS~4V4fh8q?yy39V4POh!V>T7~hN9t(IMcuL~ z0D0_w_QE*4d(jP6dXNSI1zt%gtr_5y9_w@|ozKPR;9nK}Q{Y3_KS^)RoW0ykxHEfv zS*KF99g0yf6;jhGMPFd=7%l_!*rb+4bRARE2#B73U-kV;GXuHW!DEG`=c-wQL)FIf zrSVDMDz0DuDq`8@>i9dh5tm<+e^#;Rb+p`I`~36Y!_+_ntK-SzXjm{>zbDQJWMV5Q z4Gx8F(?&~jLP|+{)QPRdr!XQbDt;8*rm2cPojzQKZmt9;6v9{w^7>#fGsmx`Sj3@a zeWeS~DkWBNK!G_WFuRhQO6*`su%M)9K(n`=rA?@SmJ&)~QKI9+!5M{jDUhhr8BO^1sQxfw#8lGUrIz zMpFN#x3l{C|8;ur_sa!4X)SjkPHHHfsaAcBExltN*L=L zoNqX*UIiUI&UQUjWZ>y1q8V%Lqge*a>UVUSUeyzDKI*S z_A9MQSUJZSv%)4L-tqGN#`8HD^+U3ReUpwX#(-~6I;$nxSo6ZMdqe7VbciBvxL0NJ ziKy48$m&h<^x5(&%#_mGUu5DU9v>7T>1yh4bjyf<^tI}+RaISXbJ)=*(Q+IJtREmu z1K_SvkL|f#>5WKEdt)Mcv+vQg`Z&<>DV}gJ0s+>WvBYK#C1lnA$Qtq2h@UkHcNY|R zX-OjyWIh1bj<=M=qeu3EzG^i_V94s)(*hVRv|?$^@Dc&c1%j+GkYKuMazIDze!ph% zM3$&^VKfnisP{vK#C1?T6R9R8RW@qI0quKO3qqkI#PL=7$lp}$KX#*)H1}lBjg0|a zaBU)%vLD-)EGi#;U)6s_;$b9>gv80`F`D=*%#DVqSZB+s;T6&Bi#XY;<3%?U`;)4Z zm+uC1zR3=}{vC8-8+7-n%WrRKv_?h^sGbsNnU2y(5tUZa@wfug3XA#e_iiLe#0k2i ztkU%b3Z_s(NHAVNnnC1h0VL7AAWJ`3OFx)cTRpu0!=DBttCC5}LgF5}$-87fjZVdfR4tr9XnO5j#4igF! zn>ZY<9zv`DO(7m4@{v+=nR+>v=&Sf@i@N`Xg|68#ER-kxlN^mtc}qRvIV~~voR~}d z&NgDXYt9j9>6zE)xpSSAUo)IEJX~SO>~-?glY1YrFfI*{O-%15c{*72-d!(xNi?XW z2!}Oh;mE?_QeTtMzxP70L`!T*`hICAgRm26(fg#PV=$~FlYOx}6YKQ_sEf#w4FBu! z-k&DJzxoslOr&Sa^OL^NmogLcI@xlJ3Rf;GV#`|BHe$PqK!>wH&2`JcsCu$ zvBEW=t1S~H|1vAs8y`TQCk`o8=f(+#_5G7ET>MDcp2-eHnhaTdJghkRT;3$=>AUk3 zhKVWnwzxgnSTDW8X;I8|*&Mb(tOtaWlBLo-?9T?jWrZt=v(lnDZtg(U)zw46oUrsn zYKXYdr`8#hdmAS$^8FO%2+& z4SW|}j)UrslA{T@PPb)KC|@@|?Ng!{GNR_gjAre#Ml1W*OW_TOP~L)Dr>mzc%CPQz zA|<5y+#@fe?z`su;}&;WhbURy4DK2p-G@=%ypg)0Dsgo=kIP4;o$25pwgXfM8?06D za7_AK3u}!w)?Tq*hgcAmZmT}~zh zUKaM;Pj8W!HbZc%7}uXKgPzY7+iqf>?@Z}Da>!`z=LV5OExi;LHZblqKL+wIGp`xx zzaUDotmH}`DQ@Dl(9086!1$7IAl5uuov%hsZ1(oAsauavE-N!iZFX>Utn}#eHi!F} zla^=WD}!l@7b`L0pAS;b_Ix#;TN9h#M5F|+pI@DGIxAuVu4DWJElW$e+AlSVXB(oJ z=%J6@RnOa1j|){fZ#{h7K1{Y099l8MPjGgYK1p2WuipRs6!j-_)&FlvaZc@wf#qGD z_s0Ocg>OlC2Q87KWNyh$?Y13`luzN>^~6nB`HK!!96v(0BPC_h8L(k^IqJ?Gw~-xf z2n^HkM56t>tO|Up%EWO&)8z+5X4k4WgKlsUY^>@Z(sA=$@WI{>+DZ%B5(+vH!rfs@ zQLLK9h4{M)M#Dmog{=G6zFk*_U5`S!f2Rf|o@TREn#y6gbb!Rp#*3GN8p4@%!Yh7I z?1OJxtiL>aT+sTjBJ|%00)I^8`qi}XI>@UBBcbVfV&S3(UN2}wVR=2D;-qflhC@pq zFE?z0O*Fr^ei$_NnH602LH8t_d6$hz{SYnn_saCX`{e03h<$6hd?@PYTQdI5%2WDM z3MsQ~mE!yNU6=1o?-ynPw9)j&G`Imw8bwb&Tir8P+10m^zYz}{<|QbQ(TbG)%Qq&% zEw$X%3I#)vQn67{{3tGRYk&W6C^^qx+F$_dPS*qQ{W_!BU4^RQ8d!PruT z8y)}a5rJcA%)}Exi9Uyd`{T~~GJy;2_my*%2TUTAT!umTQA06YU6*4X6TUm|Z+Zvh zPw}iQ83-dIBl{v)6@GCXeDoEJ2IOV^Fd&XcVyoSwOI07s+_#-Z`+X0`A=GLxDzQFK zpPfyNzARgD>rj2628jYfnHt^iraWBQPuhelK{c2H@R9KSaIB8Mv^e!9sv3>nWq^G| z!A17sY0yp=q{hGMz_kWJr(9S2Dx~6SWiaV+KKC(QHAqb zf4KgYy1W4dVk_VBNI`&{?U-!$Kgh9Q>N473NMhCL0i}a2AAjZ(j*#h6du=EV@HQ_p z&OEyO$1iHsO3?G8#3|Zp(CJS}HYPUUNw2-fk+RF(On=Gi9pia0vf0GHz00M z(x!VxXKv2iWmbHxyUhlq%O9LlRLE%k7YPK4S;}9`%=1+_t_H+}1+!vkk*Gm_mt#o| z!9&!b@cLDu2qw*>lZVZqs)`fbIlSWmrT^7(9{x~23>^PDj}g>3iUUboa9k6IL+AQEsuh*{IR@|`6q_YpTobBjdJtf(}X zz89X#P=>MaJnXAsotsa357SShKeCm}OO(}rs(R8L&$gezZ#n#!#@-fnr_fv=7@|uT z3JGQbH||f@G@cx-H@Np_MHS$L&0R}rF(;#4DJjV%N+1;8&hK_P9{_zHRRe(t4*x*B zn==NCOz7qdFEak^>=clZDKgXAWC7q9BAl4kF1mw2&6SKDHy=##o(Eb0)L21n?G3LcCky%XQWB##pi77`8IA} z4O%HIYx((CUC;VfO-O6$L_uNmi`k0TEgkj%92$$CyY73f$g)k8>;#16z$hTZ2w`)k zsUE})BTKMIyDs;fQRidDb5Y2_Pzs_$3nFP zgk5w_+a`Ce{^(HWYUixDBpQnBg(<2vZG5e{7qIs*J1wg|4DwN_mzf`#78du>O3jZ5 zjNwq$Adu)fm7(FAem>5cPi3z=mkD{TEZ9i$iTD9D3(n1?1SG&hJT+?2rr`{#cGc{b zyQ{?YSBk3iHBR0Tnh6>kA%N8eQQQ4{<@zey@1Oh%YJ75dMg-X~sugq`kp8}uMsqK0 z7<1ED-@3^LUktS5&wQ>%wlhk)PXAE_4$w!GVT7tzGWo+EjNmHF{G>S+_k+sOwC z-jPw~ycKEN*I^G%kZ5fMA*Ul8F&mWNZ`tNXFA*@%Zd=Wy{At}JN#bzA)LM?oW*dpY z3RWPiasK?KCYJ?;UiAC65XRe%T(^5f4jDQtyY;lejRB)lGqzxp!lsY)Ln~E_3yZf; zIux4xw?_2~X4SrASe(60d770fCdjsK^>VW#B`e5ga%t&+p?Ki-(*5VV*Ug(#%J*BI zo-P@`Rbse8;t8xKgPzKqWEfe7$ZfW81jm%LiTE7}x_;5b7zaDZE;dnVkQCmmdV&NU0e!5uby(1sK^@);BIR@x)zhYhL22 zFY~qk?mhW+iGF}`Ed6eY%dAtsk_G`MNU;)OUrM81xrFYT$@g!E9-fWlivIVr&hgsp zNMeLr3ty9XeOMTO<0V07z%@_qiwI$-Lest3+2^@1A}e60V!QBO6nBhet)CvTJMHN^ zNL&Tp63GLK&tWx|7~!!E4exJU;N8l69c{ljDF!YI!MU=_nXJ(C8cIo`p>kz6C`d~d zHaQnMM&c2LlIx-|NQzB7O}j#jw90iROOr~*!_}2Qy^`q}8NLXhr+NB;)^-u7m4ion zkil4*nJK4j7I0oHjo{KDsoc0e?WDrfde}+|Y47c8cpXK<7ydE8K zTMe?lai|}cE=i$zqrH7bWhEGLeixi;c)hqgbiZl8sal}pA^P)> zK@79VRDHFC20<^|Z|(g01Eo1CR@jJyWnyP%T;2j@x))Dc2)Y?uLLpV(^vL!R5SB52 z#|YH(034g(vfGhjb;?gmc6Md^|BY~Ped6Tg^zxZa!9-*Ut@9g07ZNV})>n7om0j1p zA1}LhdbvnaylyO$E4(;Vy!f;5H<701<^jibi@qL{&$s42%Nh1(Ywpi5TiJ-H%oXMH z8`w9o!1BalOb8f;A3QFAY_jq03iR;ni8&q)s`$H>)*twH`!n&~w|C64UYt{F9eH^l z$LN~T20_2VMf^Txh6?zLgAF*S8UanudOjFv&<>#&qFNp#yDi{VHqpe=f6kBojadV%IiHiQl7?ar@ zy1eU8i^tiO;F;iD^Ob7)aR9o2R$_>-hoO46Lx4;#)3Ly<&sSrFZf+c$iy3|$bIS?nq2NS^K!;Z=)&Q)cBxS`UO@-dPC77u}K1LG)YdrFRMk$;7p@Y%b*A(76CoYQmVp> z+1;rlR=(D&gdd(qtuyqgyo;6xGalZupEReveLcJ4Jhb+)rI?1JXicks$0C-!9Njg& zL$6Ga`Tvb;b5=_&ob`m_n$#Cx7jL7>z;!%)aV)qLZVV3s3!p-27`5N^H|_3F!NHmME%6`5gj{4+^;AFj5NrfX? z(C`EC_rR)kyt8BPLA5=P6P322HxPS)dVjke(UoqpaB6d;Rzm2p`EvpJZET@*x}cYK zci|DQtp;!N5`J34NC}Zc8(ZGLp44RBok|6 z_w~hnR@o$d1;g=qSlGu~DwM>Nco1AFjawOpxr**LuN~;gyo8l0^pKK@l>y_i2zEXI z%jGj_fwO!Y?Rdx&be7Ms!iR&dX02gUPN{4~r8|tu*`EHJ;8L2|82dCe z4mor=)&X%X%W6Rvm8TDmZ50cqkU(N$P!5O~nUm!${?4w$Luf?bedAyYIg+3yg?1%qE{k!8fu%X&WVy=6$3mjZer;X%&)}m#dXiroVNdo;yo|!6T0x zc8+tQ7H3Vy+0u6tWLAm4I=T3Od??H^t+e7*M0>q(uAs{~5FF`o+VLp{dkzsCp84gdnVC&*bXV&)f9ipcxJ; zbQEMw2+S*)kGnHgZ9wFS#a6A2gL95|lm>&Df_a}kdp4-KpD8grz82&xc#8*WqJ4=4h?DPZ-(9^z$DnnX_{1?F%SKt5u diff --git a/docs/example/imageops_pad.webp b/docs/example/imageops_pad.webp new file mode 100644 index 0000000000000000000000000000000000000000..0ab63ef42e02a5408d79beba6ea393c4d5fe47c0 GIT binary patch literal 2566 zcmV+h3i<|BMM6+kP&gp+2><}_EdZSXDr5kb06uLpl}4l^B%vx5+bEC? ziDCfJ^$Pz0JO96@p!|sO^wR&E<61D*$p2CPtJy}~Uo*e2KdxTxx^!FARqKC<@bAfg zt$n@zwa+#qUn%?L`tBkA6lw+RZ}nULU<2!i{67tyQ+y+?AwUn#9*DXS`n%XerD4+< zQ|GBD@`~w4cb-`8tjKJTIG>#v(h#tC0HU`tHuSEr5um=c?B99snuwqhCR(fzwAd0y z96j;-Wv!>kNeVL$C!_%uyb1UiXy zj^FbHjQsST>1KDqz^{_-MOLzAlY_i66m zKcK_?1|Rwf001=n(Gq-R#)X%}qx8Eigd-x&&G0vJlJWRO9?Ow;M-Zs~Uf0@?454U* zN|HXmM3zUg$O?9TdB2saWp0&jraQ2 zyM9YgPIT@Y^=S)AETbZC*=K+5cEM!!S3K5%Bpc9{k+W6(i5MpM{67Pa7ykaQd&TsD zD_Rmi9ZJo zPP)~Ucg)k3_f0+0RT#Eyg4WxjnN$iPzOouG&+cF4H3PRr_0jYxeog*WX*(OhI33CN zQgp(h*CICFcfve#+^qhF8$}TfIYn4Ui~p<6#)sKoGg|893UV;D$yx9d{+3=d#gfg< z**#~URZSapb8Ddej+@i3=p4db--wyhiP<1Ng+>NHyQBOZYeKz$Y*7tPa>vLK5!kV_ zpXbTb>@>2+lO*k+zDANfvHid`qoQ>)`^r#+@UnfZZ@gIU4Cw4dYFne!+!i`H<{)Qr z`-s%zp*B2moab{)4V5PC`CRPX*_?y^e|`eJ9cVE8*0c$laytL1h-fz}QztIs$|9I7 z4<&l;OL8Y6WgElZc00>W!y-2onzYX{2e!|}8zp6njQx;ISs zl=VGg)rz)$2GE7FD$MY0@X}tNtzENFxcg*oe99#p=#%I-y0ebp2G)Tg=u|<<6B)sc zyvEJ9S6uDV{j-7o6m7tsSj42?7w{9Y-F)&yfNFChlRS4uhp$zpp zNRgn^YXz>9*dy9d7fO~gObFD451h8zcCJLv=aV+epP@7q{EJD0pmGz0wgC+@YiCbj zwxF+)u37X7H&a;kn9J*g(7qkLFVxLgNgD)NmE?gF>qLElP*d)aP<;!6W+uDG`pqxT z+|e4wQO3pe%f^FSA9ln>zM87ejd1Re`eQRb{1XXoSG+bjqAXqxlLMqKGmxzN#J(AS%$ z`H4eb)Q+`og&|+8H7JQ~sV10(QEWQGYJR3`HciMsU6&$Z#~omu#YRJyRT3sUgo>sJ z#f8o7`jcB3^;_Pr7sv&M@JdMeAU4ePi1Q$AIhCQ2$|o^=*Psu@~|+Bgoq3 z0O(5S7rM72!E@>JCs|z^Mgr9br9R~WMhR%te?zH>jYJySivKXdZc;U8C=(P0dDG8r z0p4cdv0ytUwwQygUW@Q&B`ba#Z^2))rh(Oj=0sYKXq`A!xCAmde z&%Tf0=P(iiaA~RJ@aQ*fg#xDUTy|K6tvHNgUsJZvFKhrveSM&M-*^KHJ3Gt*%Y>$V zhL4ZqLsI4}Y$O=J5f;WUCVv7ZI_e$cq^92s6+3AErIJZteo6Zr+Sk^bn|*lGmq?aj zYshZ=iVk)q5guk5`_)8=7o!;Omcx~&(OPz4*{o-P3jkt$9_Vwm9vEu5`k_;t{H=qP zb>Mb-rgDhKF~NfgD$<4isav)!BIC1d=btt+i`q z4eIg(WC%3=z_Zf|Hy8gk+P+5J1p})EmQ%-DQ||BzCvmb+Np&xEO&q9`3LIYY|Gtk6X}gz$1@`h#_IZxf+QSH zPuHqPPE@El=s?46&UXO>PFfD9L`-YzaB`iakyKLs&nk%c=3?b9e^?*07J%lYp;eDF zaZu;qkSNax=dC8gjkF%4FZA&Q8kY2+7tm%m5j9VkKGRKZfC05Ljs--Rm=@+E%u5d! cs5@GC(gS8efiIaw&ZnBm!e}UV@)loZ1 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 diff --git a/docs/example/size_vs_bbox.webp b/docs/example/size_vs_bbox.webp new file mode 100644 index 0000000000000000000000000000000000000000..391162d2d4ba02f3ebf4d866a7fdf458c5612faf GIT binary patch literal 4948 zcmV-a6RYe}Nk&FY6952LMM6+kP&gn!6952^TmYQ`Dl7rW06vjGn@XjlsUf9QyOAIg z2~FRPg7dQcvsd0N=g;rCK)YHmu4CvwIREW^N&cMn68%!?N#+ms!}ibI_x-Q#U%IdB zpX|MYf2e<2^@abJ>{0!v{x7g6?60S1?a#uW_MiM7!QZ!E|NA^TQTmbnf3_d)f6_dK z@hj=9_wrZR9;H7c`z87>?f!%Juks7{-%wv&c9r)$01wf>yZ4=HiS1Y9|LOl(_yFLw z^WR`^&wh*=F?z@LU*J!rx1m{5A1#Y)w5Aw0ZXZ7!xhY|0xJtcayQzM>1$7bQ%uL6N z+J#LlNrMs%G#0g62u_wt882^~!sBAA3>M6T#KN<5HryDv*Fyg)lYyc;`Q~PYJj;?S z4w+*^(@D^xBfcpiRMB#kWmOclbj{OtGiQsO`T0?}#s4mA@V6IAgsUfxKm*)4@HIH1 zj_LyvX70;UmKTSVSXrD}J3XSgK-2F1)hH>(xKiU1ghxl5Tw^w=Q*BKz?t1dm%LQNU z1i|Pp*|F)x%WOcGkw^_q7C@YJ=U1=a6`!Vh=r$x`M=?+2hn0S}0#GF_kPDKbWQr=_ z?e*Wp#S-=~1dJvVm|D)+*5k1Q58^>B+v~r6(wBrM!wua~-NH%+EsRhJzs7nUEaPXe zL#y@5qOkJ>EdCnKan*diXAbwCGOCs}M6F#F$|A5ySm&T!+E_Au&d&EcCnXfK$9l#U z)V`zr`^WRI@``K3u1i>05S}etQtkK^1Uh9(qMO_S8$Ea2cwm%q>yz!115IDO1OOC> z-8b`^R!?*+!}q#$6D9{QoNd|r$LmV+PHaURi=d9Vp2-hn%Q;RgoD;R(IPrZC>Er{v z<^W9`Q}IwoGt%P1=+Mh8mS8LejXxgDeoy1{z+y0h(d}FX-kw|W`4!K~DvDs`o!`ms zg0d{mj{iR2@J^BOo4mffoz6!V;wX3aw0RHDm ziV8UK)R`ERW0b0XwpQ$7>2*UyB~v1_k;Jqu%Z`uX$_lFNvVW0`k^BwT z>YdyZ>(BTKEAxR#!;hco3=XC{onMc`TK0|tE&&?~EUvUlJv7v2X~et8EqF7mtP-LD z`t{3a4B6$_u?NsX3r>zbqQel3mNnQW@x0_+5U`q_iG*w5gTECU4it;Yo|kVlx5 zpWb3vSE+90Et>|J|3 zN-eO~2Z~JR3@?T1P%oh1XN4(DEm_B@fYt9EM0f(3w@ z)7S#4m6-=acn>JQk~Vu1BuC(ZO2LXN%K1atw6;K8p1-CN3~ zqIUJKPni2~wpE_cS6m_KsXU155Ll$<4I8>>*1t@(EIZfr3?EAGlL9@6oEKcPo2kZ| zEH#o@-^>9SajL9;(m}*-7#0Zkbit_*;}?uSkT?op#u$st>{IbJ5LNjQf+@EPe{W>q z2A+jfE-x@s(YjUI%3tfwgEm!4k67cL{n$ce%m^cc4<-UUPxQFoYYQHAc0obI)x4lOnf3C zcJ6%@lorrqWPqP;U7>N>7z$RP7QkpcaQpD8iQgiRMPg&uWn8h!%_Bcd;ptiM@%~KI z0!Y2l82+6SwkI}hjI-7TrQ8S~{;d2|Imxr;k>s3|)TJ)Y4>RaQZ){{>wjZxD1S3&i zaPw|i#;B#XN8Oj5%a|zK7S#biU!B)5UA@y;v+Vgu6A_bt&;G` z!&~~5>ExiA0Yw6gd%+3j!7J_$TGhaTlPA`!@L z#M!+)Tc|OtIW%FyBZHC zLKV_W434$*eQH3}!PCS?q!TO{~LWT zss3vr*L+Uh1?EDH*gC!uPw}J}AGC^+0K%n!wMENFa#o=>x84U)Z;4Gt@-%l`(C=O| zC@-H<%c8k;!_#)Q{jk6UAv7se%ZFEpsP0E@iPqx)+bgtT)FaHZJ>pzQA)jho5p0eT zq!qhN8?rzg^%ML#v5YAXl$H+bfNkb1PBL@gz_Gcw+Sj>_mSLYWa}gu-v6Q-<S;g931RLFt?K;l}Sv$;cRR@A~e!Lr=H{uYbe1aLYDkxUb(a29jIv_q_B;Kg?T35 zDMBi*@zhuFU&X@B~GayH(d-1x|_yU#bV zU^e)&+IjXz=vS9Ve<_@UPNn0Pg!<4;RLwzI&|kZ!eX9qc()>bQ(zqC;-qa11*vlaDC{`$ZsA}BDTTxh_hmlX zPn#F}u}0Y+&*-yG)IS#29m-otTc~V|JS|$g=P{4Axk&|FkW&DTBtk)HT(J3Hvk)iB zzf~jqScb6!iHmGKux8dj5}5zDu~@XQULrmz#`Bh1SE8=)jlVI`CUBLuf~l}BJ3+Yk zb{yIp_h9ZM7P|zDDmd7mS@`_&Zep*z0%EUV@H7~ycf{yt-ssLSpYmEmT!N15Y2NLw zOgUm(UL5XV@9Qi|(|dDIgg2hRT%G8zhKnIe^ZKWKq7di6>KaptdU}45V$~ioGk?Iw z%DCV$tb{ie>X!yaFWOq3Kc6&a&y!4)V6qT^;t6dH5M#b1CDZqH{Io@O%jnyAU4OyK zY4Vs0vtHRT6vo?eIM1yBOqh@V^+>_@r-_=f5R2M(2-q$U0CETJaad@cBX*eb#}fqx9ZRuwd6a-A<7Iy=2yt zhbK^#)>NIUDt1V+ZbpM261%O{P@R+fu*9Z$3~F)xL4Ml@prC3&JO!0eH3U1@?n#|| zv%|k-w5W4*5|R||wwUrd+k}=&4u}b9HwdeW>sCgszghWC#rt6 zgAVs@qlQ*AO=so?ADR80O`FKCN`eQ_3;Q-wOy^`yZI& zzfW9)e-Q_Xx93Ykodv!E{!=2BUo`WW0Cy=dv%ool-6- zYP8u9I5qoZI}6_>jgY{hM^lIXDcKSmud}))e(%F)OU{FM^Lb%@*J}l|Ut>Uk2M$*_ z-kGp(vp`n^u<_ZN$|KW1x6SlPWk{#H5uO>2G+_>0#nszYmNb&L+HQO+F}I$#2v^I(EhTV#4gxf^c@ZFMx&$& z*%E-~s1upY$0dMl;a@HUiV7~I%XBn#-_JvYlKthkw&M65vR8^LFV(H^E+Y!*@+Tdh z8S{i*N2YYULYjAB{51;Fg+Fj}b9$|VKOqf&gK{!XJC2xc5=@f9Z$q*^4(<-l^@T0l z|0nJzQ2%l_KUf6XTFz!rJlKE>3SI9RZSK>c4V*_< zGwRgude%?)O_g>aDX04xWG(g;b4`vRIY`qd7PgBtA{ZKP5~F7TM?Xh9Tyq1r3ht@2 z(MTE(`0eb=0@shFxzVX{ziuA>a2~qBPY;@^98;(UA%T9O4sWo(#Tu&nU@Ke7z(RZo z77Obx*b9)tn{(T6WS3aTZuj+!Ujb@^u{*}wDkgy=Z9fkcO)?vf^t_jY1AY+x;4wDU z=S-)*mZ_9rcS^P#?F)|&EK4&qclAg)C|2*RX<+yrx!1ZKC`R9dLg!D3fE^;{!xJ9% z(eAS-Vsuyv-1{<=3Ujk-BbX6~?SsIk_L?Y!a{++7Tt-Mc{*J1fc3O|XuAY!l*lTt# zT*@WdGiQ%fU_2#b`vASrwbo7Tne{I+>tI-!LbmGzD(8fKo>Npk8^k6b4m!!eT-ceg z+qYky(HE&sybMd#-p$0c`h4J|S(C@`^ABa?BXZwZyeX=PM*?1cM0G||JLu2KEI~R| z#YR@o60%!Z2ZTFu{%;C}GuI`u#m`A+AnIA8VQe?yU#9VUW^rwUuIdj)_~pBVumb4r z#JD67hz(&Erw_uDNNdk$jy!t@atom#;#eOBc`odL!r#u6=zP=O=3Ye{WkJW8cLAyA SC^AeeSg-&9000000000w&A&VV literal 0 HcmV?d00001 diff --git a/docs/handbook/text-anchors.rst b/docs/handbook/text-anchors.rst index 3a9572ab221..48de4bc9531 100644 --- a/docs/handbook/text-anchors.rst +++ b/docs/handbook/text-anchors.rst @@ -132,7 +132,7 @@ of the two lines. .. comment: Image generated with ../example/anchors.py -.. image:: ../example/anchors.png +.. image:: ../example/anchors.webp :alt: Text anchor examples :align: center diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst index 523e2ad7494..6cb1e26392c 100644 --- a/docs/handbook/tutorial.rst +++ b/docs/handbook/tutorial.rst @@ -278,26 +278,26 @@ choose to resize relative to a given size. from PIL import Image, ImageOps size = (100, 150) - with Image.open("Tests/images/hopper.png") as im: - ImageOps.contain(im, size).save("imageops_contain.png") - ImageOps.cover(im, size).save("imageops_cover.png") - ImageOps.fit(im, size).save("imageops_fit.png") - ImageOps.pad(im, size, color="#f00").save("imageops_pad.png") + with Image.open("Tests/images/hopper.webp") as im: + ImageOps.contain(im, size).save("imageops_contain.webp") + ImageOps.cover(im, size).save("imageops_cover.webp") + ImageOps.fit(im, size).save("imageops_fit.webp") + ImageOps.pad(im, size, color="#f00").save("imageops_pad.webp") # thumbnail() can also be used, # but will modify the image object in place im.thumbnail(size) - im.save("imageops_thumbnail.png") - -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ -| | :py:meth:`~PIL.Image.Image.thumbnail` | :py:meth:`~PIL.ImageOps.contain` | :py:meth:`~PIL.ImageOps.cover` | :py:meth:`~PIL.ImageOps.fit` | :py:meth:`~PIL.ImageOps.pad` | -+================+===========================================+============================================+==========================================+========================================+========================================+ -|Given size | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ -|Resulting image | .. image:: ../example/image_thumbnail.png | .. image:: ../example/imageops_contain.png | .. image:: ../example/imageops_cover.png | .. image:: ../example/imageops_fit.png | .. image:: ../example/imageops_pad.png | -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ -|Resulting size | ``100×100`` | ``100×100`` | ``150×150`` | ``100×150`` | ``100×150`` | -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ + im.save("image_thumbnail.webp") + ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ +| | :py:meth:`~PIL.Image.Image.thumbnail` | :py:meth:`~PIL.ImageOps.contain` | :py:meth:`~PIL.ImageOps.cover` | :py:meth:`~PIL.ImageOps.fit` | :py:meth:`~PIL.ImageOps.pad` | ++================+============================================+=============================================+===========================================+=========================================+=========================================+ +|Given size | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ +|Resulting image | .. image:: ../example/image_thumbnail.webp | .. image:: ../example/imageops_contain.webp | .. image:: ../example/imageops_cover.webp | .. image:: ../example/imageops_fit.webp | .. image:: ../example/imageops_pad.webp | ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ +|Resulting size | ``100×100`` | ``100×100`` | ``150×150`` | ``100×150`` | ``100×150`` | ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ .. _color-transforms: diff --git a/docs/reference/ImageOps.rst b/docs/reference/ImageOps.rst index 051fdcfc987..fcaa3c8f675 100644 --- a/docs/reference/ImageOps.rst +++ b/docs/reference/ImageOps.rst @@ -36,26 +36,26 @@ Resize relative to a given size from PIL import Image, ImageOps size = (100, 150) - with Image.open("Tests/images/hopper.png") as im: - ImageOps.contain(im, size).save("imageops_contain.png") - ImageOps.cover(im, size).save("imageops_cover.png") - ImageOps.fit(im, size).save("imageops_fit.png") - ImageOps.pad(im, size, color="#f00").save("imageops_pad.png") + with Image.open("Tests/images/hopper.webp") as im: + ImageOps.contain(im, size).save("imageops_contain.webp") + ImageOps.cover(im, size).save("imageops_cover.webp") + ImageOps.fit(im, size).save("imageops_fit.webp") + ImageOps.pad(im, size, color="#f00").save("imageops_pad.webp") # thumbnail() can also be used, # but will modify the image object in place im.thumbnail(size) - im.save("imageops_thumbnail.png") - -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ -| | :py:meth:`~PIL.Image.Image.thumbnail` | :py:meth:`~PIL.ImageOps.contain` | :py:meth:`~PIL.ImageOps.cover` | :py:meth:`~PIL.ImageOps.fit` | :py:meth:`~PIL.ImageOps.pad` | -+================+===========================================+============================================+==========================================+========================================+========================================+ -|Given size | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ -|Resulting image | .. image:: ../example/image_thumbnail.png | .. image:: ../example/imageops_contain.png | .. image:: ../example/imageops_cover.png | .. image:: ../example/imageops_fit.png | .. image:: ../example/imageops_pad.png | -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ -|Resulting size | ``100×100`` | ``100×100`` | ``150×150`` | ``100×150`` | ``100×150`` | -+----------------+-------------------------------------------+--------------------------------------------+------------------------------------------+----------------------------------------+----------------------------------------+ + im.save("image_thumbnail.webp") + ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ +| | :py:meth:`~PIL.Image.Image.thumbnail` | :py:meth:`~PIL.ImageOps.contain` | :py:meth:`~PIL.ImageOps.cover` | :py:meth:`~PIL.ImageOps.fit` | :py:meth:`~PIL.ImageOps.pad` | ++================+============================================+=============================================+===========================================+=========================================+=========================================+ +|Given size | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ``(100, 150)`` | ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ +|Resulting image | .. image:: ../example/image_thumbnail.webp | .. image:: ../example/imageops_contain.webp | .. image:: ../example/imageops_cover.webp | .. image:: ../example/imageops_fit.webp | .. image:: ../example/imageops_pad.webp | ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ +|Resulting size | ``100×100`` | ``100×100`` | ``150×150`` | ``100×150`` | ``100×150`` | ++----------------+--------------------------------------------+---------------------------------------------+-------------------------------------------+-----------------------------------------+-----------------------------------------+ .. autofunction:: contain .. autofunction:: cover diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index fe29f2e4f05..6e064734391 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -98,7 +98,7 @@ Previously, the ``size`` methods returned a ``height`` that included the vertica offset of the text, while the new ``bbox`` methods distinguish this as a ``top`` offset. -.. image:: ../example/size_vs_bbox.png +.. image:: ../example/size_vs_bbox.webp :alt: In bbox methods, top measures the vertical distance above the text, while bottom measures that plus the vertical distance of the text itself. In size methods, height also measures the vertical distance above the text plus the vertical distance of the text itself. :align: center From 82b7b8a9ee1c474f1f78132d4de61e80c2f434a8 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 11 Apr 2024 00:29:31 -0500 Subject: [PATCH 050/300] Fix some comments --- src/libImaging/Pack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libImaging/Pack.c b/src/libImaging/Pack.c index d47344245cc..f3b71421595 100644 --- a/src/libImaging/Pack.c +++ b/src/libImaging/Pack.c @@ -339,7 +339,7 @@ ImagingPackXBGR(UINT8 *out, const UINT8 *in, int pixels) { void ImagingPackBGRA(UINT8 *out, const UINT8 *in, int pixels) { int i; - /* BGRX, reversed bytes with right padding */ + /* BGRA, reversed bytes with right alpha */ for (i = 0; i < pixels; i++) { out[0] = in[B]; out[1] = in[G]; @@ -353,7 +353,7 @@ ImagingPackBGRA(UINT8 *out, const UINT8 *in, int pixels) { void ImagingPackABGR(UINT8 *out, const UINT8 *in, int pixels) { int i; - /* XBGR, reversed bytes with left padding */ + /* ABGR, reversed bytes with left alpha */ for (i = 0; i < pixels; i++) { out[0] = in[A]; out[1] = in[B]; From c1f6abbd3746d8332b6f9aae69f1fd262a246257 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 11 Apr 2024 14:07:07 -0500 Subject: [PATCH 051/300] Fix test error message grammar --- Tests/test_image_resample.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index dbe19380831..9b3bdf3306f 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -284,7 +284,7 @@ def run_levels_case(self, i: Image.Image) -> None: used_colors = {px[x, y][0] for x in range(i.size[0])} assert 256 == len(used_colors), ( "All colors should be present in resized image. " - f"Only {len(used_colors)} on {y} line." + f"Only {len(used_colors)} on line {y}." ) @pytest.mark.xfail(reason="Current implementation isn't precise enough") From 7b9a276c7f2dcba699bb2d9c7530f70bb499fa00 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Apr 2024 13:47:52 +1000 Subject: [PATCH 052/300] Updated libwebp to 1.4.0 --- .github/workflows/wheels-dependencies.sh | 2 +- depends/install_webp.sh | 2 +- winbuild/build_prepare.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 0d45d5a209d..0cf5c58ab8b 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -33,7 +33,7 @@ if [[ -n "$IS_MACOS" ]] || [[ "$MB_ML_VER" != 2014 ]]; then else ZLIB_VERSION=1.2.8 fi -LIBWEBP_VERSION=1.3.2 +LIBWEBP_VERSION=1.4.0 BZIP2_VERSION=1.0.8 LIBXCB_VERSION=1.16.1 BROTLI_VERSION=1.1.0 diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 6f867ab3788..c47fb35f125 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,7 +1,7 @@ #!/bin/bash # install webp -archive=libwebp-1.3.2 +archive=libwebp-1.4.0 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0d6da77549f..7ff645fc9ef 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -117,7 +117,7 @@ def cmd_msbuild( "JPEGTURBO": "3.0.2", "LCMS2": "2.16", "LIBPNG": "1.6.43", - "LIBWEBP": "1.3.2", + "LIBWEBP": "1.4.0", "OPENJPEG": "2.5.2", "TIFF": "4.6.0", "XZ": "5.4.5", From 77e2c38aea9d88adb3e3f6a9dd8bc56f85b2a075 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Apr 2024 15:54:42 +1000 Subject: [PATCH 053/300] dist directory is no longer created --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 5547b2e3caf..477d92609d1 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,6 @@ release-test: python3 selftest.py python3 -m pytest Tests python3 -m pip install . - -rmdir dist python3 -m pytest -qq python3 -m check_manifest python3 -m pyroma . From e58cccfc2322ccba038af77d48a269681867a277 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Apr 2024 16:28:29 +1000 Subject: [PATCH 054/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e39031aea4a..196f8ed2020 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Add support for reading BITMAPV2INFOHEADER and BITMAPV3INFOHEADER #7956 + [Cirras, radarhere] + - Support reading CMYK JPEG2000 images #7947 [radarhere] From 71029803e74c2148567a1a2dda7f8e0e3c49d27c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Apr 2024 21:57:29 +1000 Subject: [PATCH 055/300] Corrected check for libtiff feature --- Tests/test_tiff_ifdrational.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index f6adae3e6e7..7cbc1a2660a 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -3,10 +3,12 @@ from fractions import Fraction from pathlib import Path -from PIL import Image, TiffImagePlugin, features +import pytest + +from PIL import Image, TiffImagePlugin from PIL.TiffImagePlugin import IFDRational -from .helper import hopper +from .helper import hopper, skip_unless_feature def _test_equal(num, denom, target) -> None: @@ -52,18 +54,17 @@ def test_nonetype() -> None: assert xres and yres -def test_ifd_rational_save(tmp_path: Path) -> None: - methods = [True] - if features.check("libtiff"): - methods.append(False) - - for libtiff in methods: - TiffImagePlugin.WRITE_LIBTIFF = libtiff +@pytest.mark.parametrize( + "libtiff", (pytest.param(True, marks=skip_unless_feature("libtiff")), False) +) +def test_ifd_rational_save(tmp_path: Path, libtiff: bool) -> None: + im = hopper() + out = str(tmp_path / "temp.tiff") + res = IFDRational(301, 1) - im = hopper() - out = str(tmp_path / "temp.tiff") - res = IFDRational(301, 1) - im.save(out, dpi=(res, res), compression="raw") + TiffImagePlugin.WRITE_LIBTIFF = libtiff + im.save(out, dpi=(res, res), compression="raw") + TiffImagePlugin.WRITE_LIBTIFF = False - with Image.open(out) as reloaded: - assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) + with Image.open(out) as reloaded: + assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) From 3e1df0afeb13041b132939b28d53860093e5f3d6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Apr 2024 22:28:28 +1000 Subject: [PATCH 056/300] Removed CentOS 7 --- .github/workflows/test-docker.yml | 1 - docs/installation/platform-support.rst | 2 -- 2 files changed, 3 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index f40286fe434..4b8041f9daf 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -43,7 +43,6 @@ jobs: amazon-2-amd64, amazon-2023-amd64, arch, - centos-7-amd64, centos-stream-8-amd64, centos-stream-9-amd64, debian-11-bullseye-amd64, diff --git a/docs/installation/platform-support.rst b/docs/installation/platform-support.rst index bc60eeed445..5b52105d219 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -25,8 +25,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Arch | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| CentOS 7 | 3.9 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | CentOS Stream 9 | 3.9 | x86-64 | From 25b46523246a7908959615ccab547f3b9345dd62 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Apr 2024 22:45:14 +1000 Subject: [PATCH 057/300] Removed CentOS Stream 8 --- .github/workflows/test-docker.yml | 1 - docs/installation/platform-support.rst | 2 -- 2 files changed, 3 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 4b8041f9daf..70426d7b509 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -43,7 +43,6 @@ jobs: amazon-2-amd64, amazon-2023-amd64, arch, - centos-stream-8-amd64, centos-stream-9-amd64, debian-11-bullseye-amd64, debian-12-bookworm-x86, diff --git a/docs/installation/platform-support.rst b/docs/installation/platform-support.rst index 5b52105d219..af205a4e8cc 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -25,8 +25,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Arch | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| CentOS Stream 8 | 3.9 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | CentOS Stream 9 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | Debian 11 Bullseye | 3.9 | x86-64 | From d431c97ba3b19d40d4090763ca25499536287141 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 15 Apr 2024 19:28:52 +1000 Subject: [PATCH 058/300] Deprecate BGR;15, BGR;16 and BGR;24 --- Tests/helper.py | 7 ++++++- Tests/test_image.py | 30 +++++++++++++++++++++++++----- Tests/test_image_access.py | 6 +++++- Tests/test_image_putdata.py | 3 ++- Tests/test_lib_pack.py | 13 ++++++++----- docs/deprecations.rst | 7 +++++++ docs/handbook/concepts.rst | 3 --- src/PIL/Image.py | 7 +++++++ src/PIL/ImageMode.py | 4 ++++ 9 files changed, 64 insertions(+), 16 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index c1399e89bf8..213d994270d 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -273,7 +273,12 @@ def _cached_hopper(mode: str) -> Image.Image: im = hopper("L") else: im = hopper() - return im.convert(mode) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + im = im.convert(mode) + else: + im = im.convert(mode) + return im def djpeg_available() -> bool: diff --git a/Tests/test_image.py b/Tests/test_image.py index 941ec40d9bc..ed80be503fb 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -66,7 +66,11 @@ class TestImage: @pytest.mark.parametrize("mode", image_mode_names) def test_image_modes_success(self, mode: str) -> None: - Image.new(mode, (1, 1)) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + Image.new(mode, (1, 1)) + else: + Image.new(mode, (1, 1)) @pytest.mark.parametrize("mode", ("", "bad", "very very long")) def test_image_modes_fail(self, mode: str) -> None: @@ -1050,7 +1054,11 @@ def test_roundtrip_bytes_constructor(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() - reloaded = Image.frombytes(mode, im.size, source_bytes) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + reloaded = Image.frombytes(mode, im.size, source_bytes) + else: + reloaded = Image.frombytes(mode, im.size, source_bytes) assert reloaded.tobytes() == source_bytes @pytest.mark.parametrize("mode", image_mode_names) @@ -1058,17 +1066,29 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() - reloaded = Image.new(mode, im.size) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + reloaded = Image.new(mode, im.size) + else: + reloaded = Image.new(mode, im.size) reloaded.frombytes(source_bytes) assert reloaded.tobytes() == source_bytes @pytest.mark.parametrize(("mode", "pixelsize"), image_modes) def test_getdata_putdata(self, mode: str, pixelsize: int) -> None: - im = Image.new(mode, (2, 2)) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + im = Image.new(mode, (2, 2)) + else: + im = Image.new(mode, (2, 2)) source_bytes = bytes(range(im.width * im.height * pixelsize)) im.frombytes(source_bytes) - reloaded = Image.new(mode, im.size) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + reloaded = Image.new(mode, im.size) + else: + reloaded = Image.new(mode, im.size) reloaded.putdata(im.getdata()) assert_image_equal(im, reloaded) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 8c42da57a37..8bb90710aa3 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -229,7 +229,11 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: ), ) def test_basic(self, mode: str) -> None: - self.check(mode) + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + self.check(mode) + else: + self.check(mode) def test_list(self) -> None: im = hopper() diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 73145faac15..dad26ef144c 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -81,7 +81,8 @@ def test_mode_F() -> None: @pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24")) def test_mode_BGR(mode: str) -> None: data = [(16, 32, 49), (32, 32, 98)] - im = Image.new(mode, (1, 2)) + with pytest.warns(DeprecationWarning): + im = Image.new(mode, (1, 2)) im.putdata(data) assert list(im.getdata()) == data diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 6a0e704b89a..f80c5b78c9c 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -359,11 +359,14 @@ def test_RGB(self) -> None: ) def test_BGR(self) -> None: - self.assert_unpack("BGR;15", "BGR;15", 3, (8, 131, 0), (24, 0, 8), (41, 131, 8)) - self.assert_unpack( - "BGR;16", "BGR;16", 3, (8, 64, 0), (24, 129, 0), (41, 194, 0) - ) - self.assert_unpack("BGR;24", "BGR;24", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) + with pytest.warns(DeprecationWarning): + self.assert_unpack( + "BGR;15", "BGR;15", 3, (8, 131, 0), (24, 0, 8), (41, 131, 8) + ) + self.assert_unpack( + "BGR;16", "BGR;16", 3, (8, 64, 0), (24, 129, 0), (41, 194, 0) + ) + self.assert_unpack("BGR;24", "BGR;24", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) def test_RGBA(self) -> None: self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index c3d1ba4f028..da4e9e597f5 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -100,6 +100,13 @@ ImageMath eval() ``ImageMath.eval()`` has been deprecated. Use :py:meth:`~PIL.ImageMath.lambda_eval` or :py:meth:`~PIL.ImageMath.unsafe_eval` instead. +BGR;15, BGR 16 and BGR;24 +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 10.4.0 + +The experimental BGR;15, BGR;16 and BGR;24 modes have been deprecated. + Removed features ---------------- diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index e0975a12132..5094dbf3f27 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -59,9 +59,6 @@ Pillow also provides limited support for a few additional modes, including: * ``I;16L`` (16-bit little endian unsigned integer pixels) * ``I;16B`` (16-bit big endian unsigned integer pixels) * ``I;16N`` (16-bit native endian unsigned integer pixels) - * ``BGR;15`` (15-bit reversed true colour) - * ``BGR;16`` (16-bit reversed true colour) - * ``BGR;24`` (24-bit reversed true colour) Premultiplied alpha is where the values for each other channel have been multiplied by the alpha. For example, an RGBA pixel of ``(10, 20, 30, 127)`` diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3ae90106087..26be427798a 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -55,6 +55,7 @@ _plugins, ) from ._binary import i32le, o32be, o32le +from ._deprecate import deprecate from ._typing import StrOrBytesPath, TypeGuard from ._util import DeferredError, is_path @@ -939,6 +940,9 @@ def convert( :returns: An :py:class:`~PIL.Image.Image` object. """ + if mode in ("BGR;15", "BGR;16", "BGR;24"): + deprecate(mode, 12) + self.load() has_transparency = "transparency" in self.info @@ -2956,6 +2960,9 @@ def new( :returns: An :py:class:`~PIL.Image.Image` object. """ + if mode in ("BGR;15", "BGR;16", "BGR;24"): + deprecate(mode, 12) + _check_size(size) if color is None: diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 5e05c5f43ed..7bd2afcf2fa 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -18,6 +18,8 @@ from functools import lru_cache from typing import NamedTuple +from ._deprecate import deprecate + class ModeDescriptor(NamedTuple): """Wrapper for mode strings.""" @@ -63,6 +65,8 @@ def getmode(mode: str) -> ModeDescriptor: "PA": ("RGB", "L", ("P", "A"), "|u1"), } if mode in modes: + if mode in ("BGR;15", "BGR;16", "BGR;24"): + deprecate(mode, 12) base_mode, base_type, bands, type_str = modes[mode] return ModeDescriptor(mode, bands, base_mode, base_type, type_str) From 66d32a7dffe1b859bbcc33a43a6a8ea4d1de7c4d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 16 Apr 2024 07:03:56 +1000 Subject: [PATCH 059/300] Updated installation links --- README.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 823ea76d0c5..b4c6d2987ff 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ The core image library is designed for fast access to data stored in a few basic ## More Information - [Documentation](https://pillow.readthedocs.io/) - - [Installation](https://pillow.readthedocs.io/en/latest/installation.html) + - [Installation](https://pillow.readthedocs.io/en/latest/installation/basic-installation.html) - [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html) - [Contribute](https://github.com/python-pillow/Pillow/blob/main/.github/CONTRIBUTING.md) - [Issues](https://github.com/python-pillow/Pillow/issues) diff --git a/setup.py b/setup.py index ac401dde754..7d8e1c1ee21 100644 --- a/setup.py +++ b/setup.py @@ -1018,7 +1018,7 @@ def debug_build(): a required dependency when compiling Pillow from source. Please see the install instructions at: - https://pillow.readthedocs.io/en/latest/installation.html + https://pillow.readthedocs.io/en/latest/installation/basic-installation.html """ sys.stderr.write(msg) From 1af66df732f1842494d4eb8470f81c4c1e94b5b9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 16 Apr 2024 07:13:40 +1000 Subject: [PATCH 060/300] Updated xcb-proto to 1.17.0 --- .github/workflows/wheels-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 0d45d5a209d..2d5e174ce82 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -70,7 +70,7 @@ function build { fi build_new_zlib - build_simple xcb-proto 1.16.0 https://xorg.freedesktop.org/archive/individual/proto + build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto if [ -n "$IS_MACOS" ]; then build_simple xorgproto 2024.1 https://www.x.org/pub/individual/proto build_simple libXau 1.0.11 https://www.x.org/pub/individual/lib From 712aa994f27aba19209e33be382f1a8c85ade82f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 16 Apr 2024 07:14:04 +1000 Subject: [PATCH 061/300] Updated libxcb to 1.17.0 --- .github/workflows/wheels-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 2d5e174ce82..e140665fe7d 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -35,7 +35,7 @@ else fi LIBWEBP_VERSION=1.3.2 BZIP2_VERSION=1.0.8 -LIBXCB_VERSION=1.16.1 +LIBXCB_VERSION=1.17.0 BROTLI_VERSION=1.1.0 if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then From c655dc0c6b6257ffecb7fa8464ac51d19347e651 Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Tue, 16 Apr 2024 17:53:48 +0200 Subject: [PATCH 062/300] Use a property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondrej Baranovič --- src/PIL/Image.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index b64133cbc36..c65cf38500a 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3079,7 +3079,9 @@ class SupportsArrayInterface(Protocol): An object that has an ``__array_interface__`` dictionary. """ - __array_interface__: dict[str, Any] + @property + def __array_interface__(self) -> dict[str, Any]: + raise NotImplementedError() def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image: From 8b6253861766dbb0ac0aa896be1f1845e1e9a00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Tue, 16 Apr 2024 18:27:48 +0200 Subject: [PATCH 063/300] ImageStat: simplify call to Image.histogram(mask) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- src/PIL/ImageStat.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index 07ea76e6faf..2cb841eef34 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -51,10 +51,7 @@ def __init__( :param mask: An optional mask. """ if isinstance(image_or_list, Image.Image): - if mask: - self.h = image_or_list.histogram(mask) - else: - self.h = image_or_list.histogram() + self.h = image_or_list.histogram(mask) else: self.h = image_or_list if not isinstance(self.h, list): From 2e73bed053a14e28ac4e8dd4f93db02a58630140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Tue, 16 Apr 2024 18:34:48 +0200 Subject: [PATCH 064/300] ImageStat: simplify if block Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageStat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index 2cb841eef34..8bc504526f0 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -52,9 +52,9 @@ def __init__( """ if isinstance(image_or_list, Image.Image): self.h = image_or_list.histogram(mask) - else: + elif isinstance(image_or_list, list): self.h = image_or_list - if not isinstance(self.h, list): + else: msg = "first argument must be image or list" # type: ignore[unreachable] raise TypeError(msg) self.bands = list(range(len(self.h) // 256)) From cd179541b11277caed1168f0a2d7f0870eed6545 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Apr 2024 11:47:35 +1000 Subject: [PATCH 065/300] Removed nitpick_ignore by updating Sphinx to 7.3 --- docs/conf.py | 9 ++------- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 483535f9650..8b879d1e4b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = "2.4" +needs_sphinx = "7.3" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -121,12 +121,7 @@ # generating warnings in “nitpicky mode”. Note that type should include the domain name # if present. Example entries would be ('py:func', 'int') or # ('envvar', 'LD_LIBRARY_PATH'). -nitpick_ignore = [ - # Sphinx does not understand typing.Literal[-1] - # Will be fixed in a future version. - # https://github.com/sphinx-doc/sphinx/pull/11904 - ("py:obj", "typing.Literal[-1, 1]"), -] +# nitpick_ignore = [] # -- Options for HTML output ---------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 3ce082fb980..328088e0533 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dynamic = [ docs = [ "furo", "olefile", - "sphinx>=2.4", + "sphinx>=7.3", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", From 03835ce6f52cd51d232469025a66fe197f6efed8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Apr 2024 14:51:12 +1000 Subject: [PATCH 066/300] Corrected UnixViewer command --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 4e505f2eea5..f60b1e11e18 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -199,7 +199,7 @@ def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: def get_command(self, file: str, **options: Any) -> str: command = self.get_command_ex(file, **options)[0] - return f"({command} {quote(file)}" + return f"{command} {quote(file)}" class XDGViewer(UnixViewer): From a64f4cf68587b4adb375bb70933fcf3e3283b749 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:22:22 +0300 Subject: [PATCH 067/300] Remove sphinx-removed-in, now Sphinx 7.3.0 adds versionremoved --- docs/Makefile | 2 +- docs/conf.py | 1 - pyproject.toml | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 3b4deb9bf9d..6495e5866ff 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -46,7 +46,7 @@ clean: -rm -rf $(BUILDDIR)/* install-sphinx: - $(PYTHON) -m pip install --quiet furo olefile sphinx sphinx-copybutton sphinx-inline-tabs sphinx-removed-in sphinxext-opengraph + $(PYTHON) -m pip install --quiet furo olefile sphinx sphinx-copybutton sphinx-inline-tabs sphinxext-opengraph .PHONY: html html: diff --git a/docs/conf.py b/docs/conf.py index 8b879d1e4b5..392cf317e5f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,6 @@ "sphinx.ext.viewcode", "sphinx_copybutton", "sphinx_inline_tabs", - "sphinx_removed_in", "sphinxext.opengraph", ] diff --git a/pyproject.toml b/pyproject.toml index 328088e0533..20e87ad327d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,6 @@ docs = [ "sphinx>=7.3", "sphinx-copybutton", "sphinx-inline-tabs", - "sphinx-removed-in", "sphinxext-opengraph", ] fpx = [ From 2c0b2dceba4f72694221f8a1acb2efb16e761047 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Apr 2024 08:33:37 +1000 Subject: [PATCH 068/300] Updated nasm to 2.16.03 --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 57a8fa5a06d..dfa548548b3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -32,10 +32,10 @@ install: - curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip - 7z x pillow-test-images.zip -oc:\ - xcopy /S /Y c:\test-images-main\* c:\pillow\tests\images -- curl -fsSL -o nasm-win64.zip https://raw.githubusercontent.com/python-pillow/pillow-depends/main/nasm-2.16.01-win64.zip +- curl -fsSL -o nasm-win64.zip https://raw.githubusercontent.com/python-pillow/pillow-depends/main/nasm-2.16.03-win64.zip - 7z x nasm-win64.zip -oc:\ - choco install ghostscript --version=10.3.0 -- path c:\nasm-2.16.01;C:\Program Files\gs\gs10.00.0\bin;%PATH% +- path c:\nasm-2.16.03;C:\Program Files\gs\gs10.00.0\bin;%PATH% - cd c:\pillow\winbuild\ - ps: | c:\python38\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\ From 28f436c94d21c445230c50bd5d16e898e12febd5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Apr 2024 17:57:40 +1000 Subject: [PATCH 069/300] Use monkeypatch to set READ_LIBTIFF and WRITE_LIBTIFF --- Tests/test_file_libtiff.py | 100 +++++++++++++++------------------ Tests/test_imagesequence.py | 5 +- Tests/test_tiff_ifdrational.py | 7 ++- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 6c32b5ad427..4db0bfed581 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -185,7 +185,9 @@ def test_write_metadata(self, legacy_api: bool, tmp_path: Path) -> None: assert field in reloaded, f"{field} not in metadata" @pytest.mark.valgrind_known_error(reason="Known invalid metadata") - def test_additional_metadata(self, tmp_path: Path) -> None: + def test_additional_metadata( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: # these should not crash. Seriously dummy data, most of it doesn't make # any sense, so we're running up against limits where we're asking # libtiff to do stupid things. @@ -236,12 +238,10 @@ def test_additional_metadata(self, tmp_path: Path) -> None: del new_ifd[338] out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out, tiffinfo=new_ifd) - TiffImagePlugin.WRITE_LIBTIFF = False - def test_custom_metadata(self, tmp_path: Path) -> None: class Tc(NamedTuple): value: Any @@ -343,24 +343,24 @@ def test_subifd(self, tmp_path: Path) -> None: # Should not segfault im.save(outfile) - def test_xmlpacket_tag(self, tmp_path: Path) -> None: - TiffImagePlugin.WRITE_LIBTIFF = True + def test_xmlpacket_tag( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) out = str(tmp_path / "temp.tif") hopper().save(out, tiffinfo={700: b"xmlpacket tag"}) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: if 700 in reloaded.tag_v2: assert reloaded.tag_v2[700] == b"xmlpacket tag" - def test_int_dpi(self, tmp_path: Path) -> None: + def test_int_dpi(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: # issue #1765 im = hopper("RGB") out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out, dpi=(72, 72)) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: assert reloaded.info["dpi"] == (72.0, 72.0) @@ -422,13 +422,13 @@ def test_g4_string_info(self, tmp_path: Path) -> None: assert "temp.tif" == reread.tag_v2[269] assert "temp.tif" == reread.tag[269][0] - def test_12bit_rawmode(self) -> None: + def test_12bit_rawmode(self, monkeypatch: pytest.MonkeyPatch) -> None: """Are we generating the same interpretation of the image as Imagemagick is?""" - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/12bit.cropped.tif") as im: im.load() - TiffImagePlugin.READ_LIBTIFF = False + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", False) # to make the target -- # convert 12bit.cropped.tif -depth 16 tmp.tif # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif @@ -514,12 +514,13 @@ def test_cmyk_save(self, tmp_path: Path) -> None: assert_image_equal_tofile(im, out) @pytest.mark.parametrize("im", (hopper("P"), Image.new("P", (1, 1), "#000"))) - def test_palette_save(self, im: Image.Image, tmp_path: Path) -> None: + def test_palette_save( + self, im: Image.Image, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: # colormap/palette tag @@ -548,9 +549,9 @@ def test_fp_leak(self) -> None: with pytest.raises(OSError): os.close(fn) - def test_multipage(self) -> None: + def test_multipage(self, monkeypatch: pytest.MonkeyPatch) -> None: # issue #862 - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/multipage.tiff") as im: # file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue @@ -569,11 +570,9 @@ def test_multipage(self) -> None: assert im.size == (20, 20) assert im.convert("RGB").getpixel((0, 0)) == (0, 0, 255) - TiffImagePlugin.READ_LIBTIFF = False - - def test_multipage_nframes(self) -> None: + def test_multipage_nframes(self, monkeypatch: pytest.MonkeyPatch) -> None: # issue #862 - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/multipage.tiff") as im: frames = im.n_frames assert frames == 3 @@ -582,10 +581,8 @@ def test_multipage_nframes(self) -> None: # Should not raise ValueError: I/O operation on closed file im.load() - TiffImagePlugin.READ_LIBTIFF = False - - def test_multipage_seek_backwards(self) -> None: - TiffImagePlugin.READ_LIBTIFF = True + def test_multipage_seek_backwards(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/multipage.tiff") as im: im.seek(1) im.load() @@ -593,24 +590,21 @@ def test_multipage_seek_backwards(self) -> None: im.seek(0) assert im.convert("RGB").getpixel((0, 0)) == (0, 128, 0) - TiffImagePlugin.READ_LIBTIFF = False - - def test__next(self) -> None: - TiffImagePlugin.READ_LIBTIFF = True + def test__next(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/hopper.tif") as im: assert not im.tag.next im.load() assert not im.tag.next - def test_4bit(self) -> None: + def test_4bit(self, monkeypatch: pytest.MonkeyPatch) -> None: # Arrange test_file = "Tests/images/hopper_gray_4bpp.tif" original = hopper("L") # Act - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open(test_file) as im: - TiffImagePlugin.READ_LIBTIFF = False # Assert assert im.size == (128, 128) @@ -650,12 +644,12 @@ def test_gray_semibyte_per_pixel(self) -> None: assert im2.mode == "L" assert_image_equal(im, im2) - def test_save_bytesio(self) -> None: + def test_save_bytesio(self, monkeypatch: pytest.MonkeyPatch) -> None: # PR 1011 # Test TIFF saving to io.BytesIO() object. - TiffImagePlugin.WRITE_LIBTIFF = True - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) # Generate test image pilim = hopper() @@ -672,9 +666,6 @@ def save_bytesio(compression: str | None = None) -> None: save_bytesio("packbits") save_bytesio("tiff_lzw") - TiffImagePlugin.WRITE_LIBTIFF = False - TiffImagePlugin.READ_LIBTIFF = False - def test_save_ycbcr(self, tmp_path: Path) -> None: im = hopper("YCbCr") outfile = str(tmp_path / "temp.tif") @@ -694,15 +685,16 @@ def test_exif_ifd(self, tmp_path: Path) -> None: if Image.core.libtiff_support_custom_tags: assert reloaded.tag_v2[34665] == 125456 - def test_crashing_metadata(self, tmp_path: Path) -> None: + def test_crashing_metadata( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: # issue 1597 with Image.open("Tests/images/rdf.tif") as im: out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) # this shouldn't crash im.save(out, format="TIFF") - TiffImagePlugin.WRITE_LIBTIFF = False def test_page_number_x_0(self, tmp_path: Path) -> None: # Issue 973 @@ -733,20 +725,19 @@ def test_fd_duplication(self, tmp_path: Path) -> None: # Should not raise PermissionError. os.remove(tmpfile) - def test_read_icc(self) -> None: + def test_read_icc(self, monkeypatch: pytest.MonkeyPatch) -> None: with Image.open("Tests/images/hopper.iccprofile.tif") as img: icc = img.info.get("icc_profile") assert icc is not None - TiffImagePlugin.READ_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/hopper.iccprofile.tif") as img: icc_libtiff = img.info.get("icc_profile") assert icc_libtiff is not None - TiffImagePlugin.READ_LIBTIFF = False assert icc == icc_libtiff - def test_write_icc(self, tmp_path: Path) -> None: + def test_write_icc(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: def check_write(libtiff: bool) -> None: - TiffImagePlugin.WRITE_LIBTIFF = libtiff + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) with Image.open("Tests/images/hopper.iccprofile.tif") as img: icc_profile = img.info["icc_profile"] @@ -756,10 +747,9 @@ def check_write(libtiff: bool) -> None: with Image.open(out) as reloaded: assert icc_profile == reloaded.info["icc_profile"] - libtiffs = [] + libtiffs = [False] if Image.core.libtiff_support_custom_tags: libtiffs.append(True) - libtiffs.append(False) for libtiff in libtiffs: check_write(libtiff) @@ -840,12 +830,13 @@ def test_sampleformat(self) -> None: assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB") - def test_sampleformat_write(self, tmp_path: Path) -> None: + def test_sampleformat_write( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path + ) -> None: im = Image.new("F", (1, 1)) out = str(tmp_path / "temp.tif") - TiffImagePlugin.WRITE_LIBTIFF = True + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", True) im.save(out) - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: assert reloaded.mode == "F" @@ -1091,15 +1082,14 @@ def test_sampleformat_not_corrupted(self) -> None: with Image.open(out) as im: im.load() - def test_realloc_overflow(self) -> None: - TiffImagePlugin.READ_LIBTIFF = True + def test_realloc_overflow(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) with Image.open("Tests/images/tiff_overflow_rows_per_strip.tif") as im: with pytest.raises(OSError) as e: im.load() # Assert that the error code is IMAGING_CODEC_MEMORY assert str(e.value) == "-9" - TiffImagePlugin.READ_LIBTIFF = False @pytest.mark.parametrize("compression", ("tiff_adobe_deflate", "jpeg")) def test_save_multistrip(self, compression: str, tmp_path: Path) -> None: diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 7f3a3d14181..18070377fa7 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -60,10 +60,9 @@ def test_tiff() -> None: @skip_unless_feature("libtiff") -def test_libtiff() -> None: - TiffImagePlugin.READ_LIBTIFF = True +def test_libtiff(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) _test_multipage_tiff() - TiffImagePlugin.READ_LIBTIFF = False def test_consecutive() -> None: diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index 7cbc1a2660a..ae80b98b84d 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -57,14 +57,15 @@ def test_nonetype() -> None: @pytest.mark.parametrize( "libtiff", (pytest.param(True, marks=skip_unless_feature("libtiff")), False) ) -def test_ifd_rational_save(tmp_path: Path, libtiff: bool) -> None: +def test_ifd_rational_save( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, libtiff: bool +) -> None: im = hopper() out = str(tmp_path / "temp.tiff") res = IFDRational(301, 1) - TiffImagePlugin.WRITE_LIBTIFF = libtiff + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) im.save(out, dpi=(res, res), compression="raw") - TiffImagePlugin.WRITE_LIBTIFF = False with Image.open(out) as reloaded: assert float(IFDRational(301, 1)) == float(reloaded.tag_v2[282]) From 533f78e0a255c77357e2694419d6b0f5ea6bcd05 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Apr 2024 07:47:14 +1000 Subject: [PATCH 070/300] Parametrize test --- Tests/test_file_libtiff.py | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 4db0bfed581..71f1b6f1def 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -735,24 +735,31 @@ def test_read_icc(self, monkeypatch: pytest.MonkeyPatch) -> None: assert icc_libtiff is not None assert icc == icc_libtiff - def test_write_icc(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: - def check_write(libtiff: bool) -> None: - monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) - - with Image.open("Tests/images/hopper.iccprofile.tif") as img: - icc_profile = img.info["icc_profile"] - - out = str(tmp_path / "temp.tif") - img.save(out, icc_profile=icc_profile) - with Image.open(out) as reloaded: - assert icc_profile == reloaded.info["icc_profile"] + @pytest.mark.parametrize( + "libtiff", + ( + pytest.param( + True, + marks=pytest.mark.skipif( + not Image.core.libtiff_support_custom_tags, + reason="Custom tags not supported by older libtiff", + ), + ), + False, + ), + ) + def test_write_icc( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path, libtiff: bool + ) -> None: + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) - libtiffs = [False] - if Image.core.libtiff_support_custom_tags: - libtiffs.append(True) + with Image.open("Tests/images/hopper.iccprofile.tif") as img: + icc_profile = img.info["icc_profile"] - for libtiff in libtiffs: - check_write(libtiff) + out = str(tmp_path / "temp.tif") + img.save(out, icc_profile=icc_profile) + with Image.open(out) as reloaded: + assert icc_profile == reloaded.info["icc_profile"] def test_multipage_compression(self) -> None: with Image.open("Tests/images/compression.tif") as im: From 11ac0c1703cb9f46f9de9ac692f008255222d7b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Apr 2024 17:15:10 +1000 Subject: [PATCH 071/300] Combine tests through parametrization --- Tests/test_imagesequence.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 18070377fa7..9b37435eb61 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -47,7 +47,11 @@ def test_iterator_min_frame() -> None: assert i[index] == next(i) -def _test_multipage_tiff() -> None: +@pytest.mark.parametrize( + "libtiff", (pytest.param(True, marks=skip_unless_feature("libtiff")), False) +) +def test_multipage_tiff(monkeypatch: pytest.MonkeyPatch, libtiff: bool) -> None: + monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", libtiff) with Image.open("Tests/images/multipage.tiff") as im: for index, frame in enumerate(ImageSequence.Iterator(im)): frame.load() @@ -55,16 +59,6 @@ def _test_multipage_tiff() -> None: frame.convert("RGB") -def test_tiff() -> None: - _test_multipage_tiff() - - -@skip_unless_feature("libtiff") -def test_libtiff(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setattr(TiffImagePlugin, "READ_LIBTIFF", True) - _test_multipage_tiff() - - def test_consecutive() -> None: with Image.open("Tests/images/multipage.tiff") as im: first_frame = None From 139245a3db00cf4cc6de4ed726eba4561dcd5cec Mon Sep 17 00:00:00 2001 From: Yay295 Date: Wed, 27 Mar 2024 10:29:19 -0500 Subject: [PATCH 072/300] use namedtuple for image mode info --- Tests/test_image.py | 67 ++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 941ec40d9bc..779785c5345 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -8,7 +8,7 @@ import tempfile import warnings from pathlib import Path -from typing import IO +from typing import IO, NamedTuple import pytest @@ -33,34 +33,39 @@ skip_unless_feature, ) -# name, pixel size + +class ImageModeInfo(NamedTuple): + name: str + pixel_size: int + + image_modes = ( - ("1", 1), - ("L", 1), - ("LA", 4), - ("La", 4), - ("P", 1), - ("PA", 4), - ("F", 4), - ("I", 4), - ("I;16", 2), - ("I;16L", 2), - ("I;16B", 2), - ("I;16N", 2), - ("RGB", 4), - ("RGBA", 4), - ("RGBa", 4), - ("RGBX", 4), - ("BGR;15", 2), - ("BGR;16", 2), - ("BGR;24", 3), - ("CMYK", 4), - ("YCbCr", 4), - ("HSV", 4), - ("LAB", 4), + ImageModeInfo("1", 1), + ImageModeInfo("L", 1), + ImageModeInfo("LA", 4), + ImageModeInfo("La", 4), + ImageModeInfo("P", 1), + ImageModeInfo("PA", 4), + ImageModeInfo("F", 4), + ImageModeInfo("I", 4), + ImageModeInfo("I;16", 2), + ImageModeInfo("I;16L", 2), + ImageModeInfo("I;16B", 2), + ImageModeInfo("I;16N", 2), + ImageModeInfo("RGB", 4), + ImageModeInfo("RGBA", 4), + ImageModeInfo("RGBa", 4), + ImageModeInfo("RGBX", 4), + ImageModeInfo("BGR;15", 2), + ImageModeInfo("BGR;16", 2), + ImageModeInfo("BGR;24", 3), + ImageModeInfo("CMYK", 4), + ImageModeInfo("YCbCr", 4), + ImageModeInfo("HSV", 4), + ImageModeInfo("LAB", 4), ) -image_mode_names = [name for name, _ in image_modes] +image_mode_names = [mode.name for mode in image_modes] class TestImage: @@ -1062,13 +1067,13 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: reloaded.frombytes(source_bytes) assert reloaded.tobytes() == source_bytes - @pytest.mark.parametrize(("mode", "pixelsize"), image_modes) - def test_getdata_putdata(self, mode: str, pixelsize: int) -> None: - im = Image.new(mode, (2, 2)) - source_bytes = bytes(range(im.width * im.height * pixelsize)) + @pytest.mark.parametrize("mode", image_modes) + def test_getdata_putdata(self, mode: ImageModeInfo) -> None: + im = Image.new(mode.name, (2, 2)) + source_bytes = bytes(range(im.width * im.height * mode.pixel_size)) im.frombytes(source_bytes) - reloaded = Image.new(mode, im.size) + reloaded = Image.new(mode.name, im.size) reloaded.putdata(im.getdata()) assert_image_equal(im, reloaded) From 5a4b771fb00389a43dbf32a4c13d73c9b39e3f5d Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 2 Jan 2023 16:55:31 -0600 Subject: [PATCH 073/300] move image mode info variables to helper.py --- Tests/helper.py | 36 +++++++++++++++++++++++++++++++++++- Tests/test_image.py | 39 ++++----------------------------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index c1399e89bf8..32ea99fdd9e 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -13,7 +13,7 @@ import tempfile from functools import lru_cache from io import BytesIO -from typing import Any, Callable, Sequence +from typing import Any, Callable, NamedTuple, Sequence import pytest from packaging.version import parse as parse_version @@ -29,6 +29,40 @@ uploader = "github_actions" +class ImageModeInfo(NamedTuple): + name: str + pixel_size: int + + +image_modes = ( + ImageModeInfo("1", 1), + ImageModeInfo("L", 1), + ImageModeInfo("LA", 4), + ImageModeInfo("La", 4), + ImageModeInfo("P", 1), + ImageModeInfo("PA", 4), + ImageModeInfo("F", 4), + ImageModeInfo("I", 4), + ImageModeInfo("I;16", 2), + ImageModeInfo("I;16L", 2), + ImageModeInfo("I;16B", 2), + ImageModeInfo("I;16N", 2), + ImageModeInfo("RGB", 4), + ImageModeInfo("RGBA", 4), + ImageModeInfo("RGBa", 4), + ImageModeInfo("RGBX", 4), + ImageModeInfo("BGR;15", 2), + ImageModeInfo("BGR;16", 2), + ImageModeInfo("BGR;24", 3), + ImageModeInfo("CMYK", 4), + ImageModeInfo("YCbCr", 4), + ImageModeInfo("HSV", 4), + ImageModeInfo("LAB", 4), +) + +image_mode_names = [mode.name for mode in image_modes] + + def upload(a: Image.Image, b: Image.Image) -> str | None: if uploader == "show": # local img.show for errors. diff --git a/Tests/test_image.py b/Tests/test_image.py index 779785c5345..5654307f1c4 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -8,7 +8,7 @@ import tempfile import warnings from pathlib import Path -from typing import IO, NamedTuple +from typing import IO import pytest @@ -23,51 +23,20 @@ ) from .helper import ( + ImageModeInfo, assert_image_equal, assert_image_equal_tofile, assert_image_similar_tofile, assert_not_all_same, hopper, + image_mode_names, + image_modes, is_win32, mark_if_feature_version, skip_unless_feature, ) -class ImageModeInfo(NamedTuple): - name: str - pixel_size: int - - -image_modes = ( - ImageModeInfo("1", 1), - ImageModeInfo("L", 1), - ImageModeInfo("LA", 4), - ImageModeInfo("La", 4), - ImageModeInfo("P", 1), - ImageModeInfo("PA", 4), - ImageModeInfo("F", 4), - ImageModeInfo("I", 4), - ImageModeInfo("I;16", 2), - ImageModeInfo("I;16L", 2), - ImageModeInfo("I;16B", 2), - ImageModeInfo("I;16N", 2), - ImageModeInfo("RGB", 4), - ImageModeInfo("RGBA", 4), - ImageModeInfo("RGBa", 4), - ImageModeInfo("RGBX", 4), - ImageModeInfo("BGR;15", 2), - ImageModeInfo("BGR;16", 2), - ImageModeInfo("BGR;24", 3), - ImageModeInfo("CMYK", 4), - ImageModeInfo("YCbCr", 4), - ImageModeInfo("HSV", 4), - ImageModeInfo("LAB", 4), -) - -image_mode_names = [mode.name for mode in image_modes] - - class TestImage: @pytest.mark.parametrize("mode", image_mode_names) def test_image_modes_success(self, mode: str) -> None: From 0fed6a5fbcbe40aff7540693a438d18d825c839e Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sun, 31 Mar 2024 23:29:01 -0500 Subject: [PATCH 074/300] use common image mode list for TestImageGetPixel tests --- Tests/test_image_access.py | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 8c42da57a37..96afc87a475 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -10,7 +10,7 @@ from PIL import Image -from .helper import assert_image_equal, hopper, is_win32 +from .helper import assert_image_equal, hopper, image_mode_names, is_win32 # CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2 # https://github.com/eliben/pycparser/pull/198#issuecomment-317001670 @@ -138,8 +138,8 @@ def color(mode: str) -> int | tuple[int, ...]: if bands == 1: return 1 if mode in ("BGR;15", "BGR;16"): - # These modes have less than 8 bits per band - # So (1, 2, 3) cannot be roundtripped + # These modes have less than 8 bits per band, + # so (1, 2, 3) cannot be roundtripped. return (16, 32, 49) return tuple(range(1, bands + 1)) @@ -168,16 +168,15 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: f"expected {expected_color} got {actual_color}" ) - # Check 0 + # check 0x0 image with None initial color im = Image.new(mode, (0, 0), None) assert im.load() is not None - error = ValueError if self._need_cffi_access else IndexError with pytest.raises(error): im.putpixel((0, 0), expected_color) with pytest.raises(error): im.getpixel((0, 0)) - # Check 0 negative index + # check negative index with pytest.raises(error): im.putpixel((-1, -1), expected_color) with pytest.raises(error): @@ -198,36 +197,15 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: f"expected {expected_color} got {actual_color}" ) - # Check 0 + # check 0x0 image with initial color im = Image.new(mode, (0, 0), expected_color) with pytest.raises(error): im.getpixel((0, 0)) - # Check 0 negative index + # check negative index with pytest.raises(error): im.getpixel((-1, -1)) - @pytest.mark.parametrize( - "mode", - ( - "1", - "L", - "LA", - "I", - "I;16", - "I;16B", - "F", - "P", - "PA", - "BGR;15", - "BGR;16", - "BGR;24", - "RGB", - "RGBA", - "RGBX", - "CMYK", - "YCbCr", - ), - ) + @pytest.mark.parametrize("mode", image_mode_names) def test_basic(self, mode: str) -> None: self.check(mode) From da7198c98740e9607dac0bf97ae073cf21510634 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 30 Mar 2024 14:32:45 -0500 Subject: [PATCH 075/300] fix ImagingAccess for I;16N on big-endian --- src/libImaging/Access.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libImaging/Access.c b/src/libImaging/Access.c index 091c84e18fa..04618df0965 100644 --- a/src/libImaging/Access.c +++ b/src/libImaging/Access.c @@ -81,12 +81,6 @@ get_pixel_16B(Imaging im, int x, int y, void *color) { #endif } -static void -get_pixel_16(Imaging im, int x, int y, void *color) { - UINT8 *in = (UINT8 *)&im->image[y][x + x]; - memcpy(color, in, sizeof(UINT16)); -} - static void get_pixel_BGR15(Imaging im, int x, int y, void *color) { UINT8 *in = (UINT8 *)&im->image8[y][x * 2]; @@ -207,7 +201,11 @@ ImagingAccessInit() { ADD("I;16", get_pixel_16L, put_pixel_16L); ADD("I;16L", get_pixel_16L, put_pixel_16L); ADD("I;16B", get_pixel_16B, put_pixel_16B); - ADD("I;16N", get_pixel_16, put_pixel_16L); +#ifdef WORDS_BIGENDIAN + ADD("I;16N", get_pixel_16B, put_pixel_16B); +#else + ADD("I;16N", get_pixel_16L, put_pixel_16L); +#endif ADD("I;32L", get_pixel_32L, put_pixel_32L); ADD("I;32B", get_pixel_32B, put_pixel_32B); ADD("F", get_pixel_32, put_pixel_32); From 5dabc6cf14c78d397d2b31d81d6a7fe9e10dbd3b Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 30 Mar 2024 15:10:04 -0500 Subject: [PATCH 076/300] fix I;16N lib pack test --- Tests/test_lib_pack.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 6a0e704b89a..f34ff7d0230 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -216,7 +216,10 @@ def test_I(self) -> None: ) def test_I16(self) -> None: - self.assert_pack("I;16N", "I;16N", 2, 0x0201, 0x0403, 0x0605) + if sys.byteorder == "little": + self.assert_pack("I;16N", "I;16N", 2, 0x0201, 0x0403, 0x0605) + else: + self.assert_pack("I;16N", "I;16N", 2, 0x0102, 0x0304, 0x0506) def test_F_float(self) -> None: self.assert_pack("F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) From fe79ae5653e42e5b5c42ff5428eeef36e4bbe7a6 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 6 Apr 2024 10:48:38 -0500 Subject: [PATCH 077/300] get pixel size by counting bytes in 1x1 image --- Tests/helper.py | 57 ++++++++++++++++++++------------------------- Tests/test_image.py | 19 +++++++++------ 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 32ea99fdd9e..f0bb8af00ba 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -13,7 +13,7 @@ import tempfile from functools import lru_cache from io import BytesIO -from typing import Any, Callable, NamedTuple, Sequence +from typing import Any, Callable, Sequence import pytest from packaging.version import parse as parse_version @@ -29,39 +29,32 @@ uploader = "github_actions" -class ImageModeInfo(NamedTuple): - name: str - pixel_size: int - - -image_modes = ( - ImageModeInfo("1", 1), - ImageModeInfo("L", 1), - ImageModeInfo("LA", 4), - ImageModeInfo("La", 4), - ImageModeInfo("P", 1), - ImageModeInfo("PA", 4), - ImageModeInfo("F", 4), - ImageModeInfo("I", 4), - ImageModeInfo("I;16", 2), - ImageModeInfo("I;16L", 2), - ImageModeInfo("I;16B", 2), - ImageModeInfo("I;16N", 2), - ImageModeInfo("RGB", 4), - ImageModeInfo("RGBA", 4), - ImageModeInfo("RGBa", 4), - ImageModeInfo("RGBX", 4), - ImageModeInfo("BGR;15", 2), - ImageModeInfo("BGR;16", 2), - ImageModeInfo("BGR;24", 3), - ImageModeInfo("CMYK", 4), - ImageModeInfo("YCbCr", 4), - ImageModeInfo("HSV", 4), - ImageModeInfo("LAB", 4), +image_mode_names = ( + "1", + "L", + "LA", + "La", + "P", + "PA", + "F", + "I", + "I;16", + "I;16L", + "I;16B", + "I;16N", + "RGB", + "RGBA", + "RGBa", + "RGBX", + "BGR;15", + "BGR;16", + "BGR;24", + "CMYK", + "YCbCr", + "HSV", + "LAB", ) -image_mode_names = [mode.name for mode in image_modes] - def upload(a: Image.Image, b: Image.Image) -> str | None: if uploader == "show": diff --git a/Tests/test_image.py b/Tests/test_image.py index 5654307f1c4..8b20de0a908 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -23,14 +23,12 @@ ) from .helper import ( - ImageModeInfo, assert_image_equal, assert_image_equal_tofile, assert_image_similar_tofile, assert_not_all_same, hopper, image_mode_names, - image_modes, is_win32, mark_if_feature_version, skip_unless_feature, @@ -1036,13 +1034,20 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: reloaded.frombytes(source_bytes) assert reloaded.tobytes() == source_bytes - @pytest.mark.parametrize("mode", image_modes) - def test_getdata_putdata(self, mode: ImageModeInfo) -> None: - im = Image.new(mode.name, (2, 2)) - source_bytes = bytes(range(im.width * im.height * mode.pixel_size)) + @pytest.mark.parametrize("mode", image_mode_names) + def test_getdata_putdata(self, mode: str) -> None: + # create an image with 1 pixel to get its pixel size + im = Image.new(mode, (1, 1)) + pixel_size = len(im.tobytes()) + + # create a new image with incrementing byte values + im = Image.new(mode, (2, 2)) + source_bytes = bytes(range(im.width * im.height * pixel_size)) im.frombytes(source_bytes) - reloaded = Image.new(mode.name, im.size) + # copy the data from the previous image to a new image + # and check that they are the same + reloaded = Image.new(mode, im.size) reloaded.putdata(im.getdata()) assert_image_equal(im, reloaded) From 5573ec74901945c58e700b2becb34ac800607bbe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Apr 2024 22:54:47 +1000 Subject: [PATCH 078/300] use hopper() for test_getdata_putdata() --- Tests/test_image.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 8b20de0a908..090b80f87ce 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1036,17 +1036,7 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: @pytest.mark.parametrize("mode", image_mode_names) def test_getdata_putdata(self, mode: str) -> None: - # create an image with 1 pixel to get its pixel size - im = Image.new(mode, (1, 1)) - pixel_size = len(im.tobytes()) - - # create a new image with incrementing byte values - im = Image.new(mode, (2, 2)) - source_bytes = bytes(range(im.width * im.height * pixel_size)) - im.frombytes(source_bytes) - - # copy the data from the previous image to a new image - # and check that they are the same + im = hopper(mode) reloaded = Image.new(mode, im.size) reloaded.putdata(im.getdata()) assert_image_equal(im, reloaded) From 5c960d6abc25aa94044a0831743e061383d3f486 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Apr 2024 23:01:51 +1000 Subject: [PATCH 079/300] rename "image_mode_names" to "modes" --- Tests/helper.py | 2 +- Tests/test_image.py | 10 +++++----- Tests/test_image_access.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index f0bb8af00ba..5d206f6449a 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -29,7 +29,7 @@ uploader = "github_actions" -image_mode_names = ( +modes = ( "1", "L", "LA", diff --git a/Tests/test_image.py b/Tests/test_image.py index 090b80f87ce..9985f23469b 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -28,15 +28,15 @@ assert_image_similar_tofile, assert_not_all_same, hopper, - image_mode_names, is_win32, mark_if_feature_version, + modes, skip_unless_feature, ) class TestImage: - @pytest.mark.parametrize("mode", image_mode_names) + @pytest.mark.parametrize("mode", modes) def test_image_modes_success(self, mode: str) -> None: Image.new(mode, (1, 1)) @@ -1017,7 +1017,7 @@ def test_close_graceful(self, caplog: pytest.LogCaptureFixture) -> None: class TestImageBytes: - @pytest.mark.parametrize("mode", image_mode_names) + @pytest.mark.parametrize("mode", modes) def test_roundtrip_bytes_constructor(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() @@ -1025,7 +1025,7 @@ def test_roundtrip_bytes_constructor(self, mode: str) -> None: reloaded = Image.frombytes(mode, im.size, source_bytes) assert reloaded.tobytes() == source_bytes - @pytest.mark.parametrize("mode", image_mode_names) + @pytest.mark.parametrize("mode", modes) def test_roundtrip_bytes_method(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() @@ -1034,7 +1034,7 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: reloaded.frombytes(source_bytes) assert reloaded.tobytes() == source_bytes - @pytest.mark.parametrize("mode", image_mode_names) + @pytest.mark.parametrize("mode", modes) def test_getdata_putdata(self, mode: str) -> None: im = hopper(mode) reloaded = Image.new(mode, im.size) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 96afc87a475..50afb2a2368 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -10,7 +10,7 @@ from PIL import Image -from .helper import assert_image_equal, hopper, image_mode_names, is_win32 +from .helper import assert_image_equal, hopper, is_win32, modes # CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2 # https://github.com/eliben/pycparser/pull/198#issuecomment-317001670 @@ -205,7 +205,7 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: with pytest.raises(error): im.getpixel((-1, -1)) - @pytest.mark.parametrize("mode", image_mode_names) + @pytest.mark.parametrize("mode", modes) def test_basic(self, mode: str) -> None: self.check(mode) From 98510570e6a54398c7975f1128b3de91fd8aad1f Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sat, 20 Apr 2024 09:19:20 -0500 Subject: [PATCH 080/300] ignore BGR;15/16 test failure on big-endian --- Tests/test_image.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index 9985f23469b..4a056df4083 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -28,6 +28,7 @@ assert_image_similar_tofile, assert_not_all_same, hopper, + is_big_endian, is_win32, mark_if_feature_version, modes, @@ -1036,6 +1037,8 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: @pytest.mark.parametrize("mode", modes) def test_getdata_putdata(self, mode: str) -> None: + if is_big_endian and mode in ("BGR;15", "BGR;16"): + pytest.xfail(f"Known failure of {mode} on big-endian") im = hopper(mode) reloaded = Image.new(mode, im.size) reloaded.putdata(im.getdata()) From 98a0515494bc4cb43f2ede5fd80af82ee8f4fc59 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 08:05:59 +1000 Subject: [PATCH 081/300] Read images as RGB, rather than RGBX --- src/PIL/ImImagePlugin.py | 2 +- src/PIL/TiffImagePlugin.py | 16 ++++++++-------- src/libImaging/Unpack.c | 4 ++++ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 4613e40b60f..6463572f5c2 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -78,7 +78,7 @@ "LA image": ("LA", "LA;L"), "PA image": ("LA", "PA;L"), "RGBA image": ("RGBA", "RGBA;L"), - "RGBX image": ("RGBX", "RGBX;L"), + "RGBX image": ("RGB", "RGBX;L"), "CMYK image": ("CMYK", "CMYK;L"), "YCC image": ("YCbCr", "YCbCr;L"), } diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 10ac9ea3a2a..e3714059088 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -200,12 +200,12 @@ (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"), + (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), + (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), + (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), + (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), @@ -224,8 +224,8 @@ (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"), (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"), (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16B"), + (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16L"), + (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16B"), (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"), (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"), (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"), diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index a84dc0a6fd2..f15769c4359 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1599,10 +1599,14 @@ static struct { {"RGB", "BGR;15", 16, ImagingUnpackBGR15}, {"RGB", "RGB;16", 16, ImagingUnpackRGB16}, {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGB", "RGBX;16L", 64, unpackRGBA16L}, + {"RGB", "RGBX;16B", 64, unpackRGBA16B}, {"RGB", "RGB;4B", 16, ImagingUnpackRGB4B}, {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ {"RGB", "RGBX", 32, copy4}, {"RGB", "RGBX;L", 32, unpackRGBAL}, + {"RGB", "RGBXX", 40, copy4skip1}, + {"RGB", "RGBXXX", 48, copy4skip2}, {"RGB", "RGBA;L", 32, unpackRGBAL}, {"RGB", "RGBA;15", 16, ImagingUnpackRGBA15}, {"RGB", "BGRX", 32, ImagingUnpackBGRX}, From bb2411dd01197d0a393dbcfdccfdd487a8c6d7be Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 08:11:45 +1000 Subject: [PATCH 082/300] Support reading P mode TIFF images with padding --- src/PIL/TiffImagePlugin.py | 1 + src/libImaging/Unpack.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 10ac9ea3a2a..106f09c2dde 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -244,6 +244,7 @@ (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), (II, 3, (1,), 1, (8,), ()): ("P", "P"), (MM, 3, (1,), 1, (8,), ()): ("P", "P"), + (II, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index a84dc0a6fd2..e351aa2f19b 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1582,6 +1582,7 @@ static struct { {"P", "P", 8, copy1}, {"P", "P;R", 8, unpackLR}, {"P", "L", 8, copy1}, + {"P", "PX", 16, unpackL16B}, /* palette w. alpha */ {"PA", "PA", 16, unpackLA}, From d5c1ff4b43b0b17d2379939cf941b06e3b0c3464 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 16 Apr 2024 22:22:25 +1000 Subject: [PATCH 083/300] Removed type hint ignores --- src/PIL/ImageCms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 4af1b79e2e3..5f5c5df54e7 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -838,8 +838,8 @@ def getProfileName(profile: _CmsProfileCompatible) -> str: if not (model or manufacturer): return (profile.profile.profile_description or "") + "\n" - if not manufacturer or len(model) > 30: # type: ignore[arg-type] - return model + "\n" # type: ignore[operator] + if not manufacturer or (model and len(model) > 30): + return f"{model}\n" return f"{model} - {manufacturer}\n" except (AttributeError, OSError, TypeError, ValueError) as v: From 4171435db45822f6ebf696e36a7edfacdb1e6756 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 15 Apr 2024 18:25:03 +1000 Subject: [PATCH 084/300] Added more modes --- src/PIL/Image.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c65cf38500a..8efaf8b784f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -248,7 +248,28 @@ def _conv_type_shape(im): return shape, m.typestr -MODES = ["1", "CMYK", "F", "HSV", "I", "L", "LAB", "P", "RGB", "RGBA", "RGBX", "YCbCr"] +MODES = [ + "1", + "CMYK", + "F", + "HSV", + "I", + "I;16", + "I;16B", + "I;16L", + "I;16N", + "L", + "LA", + "La", + "LAB", + "P", + "PA", + "RGB", + "RGBA", + "RGBa", + "RGBX", + "YCbCr", +] # raw modes that may be memory mapped. NOTE: if you change this, you # may have to modify the stride calculation in map.c too! From 745eb23a87b40707af9474ad732fc6d13b94628f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 15 Apr 2024 22:22:40 +1000 Subject: [PATCH 085/300] Use LAB hopper file if conversion is not supported --- Tests/helper.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/helper.py b/Tests/helper.py index c1399e89bf8..680825d4be7 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -273,7 +273,14 @@ def _cached_hopper(mode: str) -> Image.Image: im = hopper("L") else: im = hopper() - return im.convert(mode) + try: + im = im.convert(mode) + except ImportError: + if mode == "LAB": + im = Image.open("Tests/images/hopper.Lab.tif") + else: + raise + return im def djpeg_available() -> bool: From f690b7f6915ed0cfee0b1b4246741d17aa2e70b1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 13:39:35 +1000 Subject: [PATCH 086/300] Added MPEG accept function --- Tests/test_file_mpeg.py | 39 ++++++++++++++++++++++++++++++++++++++ src/PIL/MpegImagePlugin.py | 6 +++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 Tests/test_file_mpeg.py diff --git a/Tests/test_file_mpeg.py b/Tests/test_file_mpeg.py new file mode 100644 index 00000000000..468aef8a950 --- /dev/null +++ b/Tests/test_file_mpeg.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from io import BytesIO + +import pytest + +from PIL import Image, MpegImagePlugin + + +def test_identify() -> None: + # Arrange + b = BytesIO(b"\x00\x00\x01\xb3\x01\x00\x01") + + # Act + with Image.open(b) as im: + # Assert + assert im.format == "MPEG" + + assert im.mode == "RGB" + assert im.size == (16, 1) + + +def test_invalid_file() -> None: + # Arrange + invalid_file = "Tests/images/flower.jpg" + + # Act / Assert + with pytest.raises(SyntaxError): + MpegImagePlugin.MpegImageFile(invalid_file) + + +def test_load() -> None: + # Arrange + b = BytesIO(b"\x00\x00\x01\xb3\x01\x00\x01") + + with Image.open(b) as im: + # Act / Assert: cannot load + with pytest.raises(OSError): + im.load() diff --git a/src/PIL/MpegImagePlugin.py b/src/PIL/MpegImagePlugin.py index 1565612f869..ad4d3e93768 100644 --- a/src/PIL/MpegImagePlugin.py +++ b/src/PIL/MpegImagePlugin.py @@ -53,6 +53,10 @@ def read(self, bits: int) -> int: return v +def _accept(prefix: bytes) -> bool: + return prefix[:4] == b"\x00\x00\x01\xb3" + + ## # Image plugin for MPEG streams. This plugin can identify a stream, # but it cannot read it. @@ -77,7 +81,7 @@ def _open(self) -> None: # -------------------------------------------------------------------- # Registry stuff -Image.register_open(MpegImageFile.format, MpegImageFile) +Image.register_open(MpegImageFile.format, MpegImageFile, _accept) Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"]) From 023d017da00f8adb8de86719509145c405369ac8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 18:26:20 +1000 Subject: [PATCH 087/300] Deprecate libtiff < 4 --- docs/deprecations.rst | 8 ++++++++ src/PIL/TiffImagePlugin.py | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index c3d1ba4f028..91bc150b342 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -100,6 +100,14 @@ ImageMath eval() ``ImageMath.eval()`` has been deprecated. Use :py:meth:`~PIL.ImageMath.lambda_eval` or :py:meth:`~PIL.ImageMath.unsafe_eval` instead. +Support for libtiff earlier than 4 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 10.4.0 + +Support for libtiff earlier than 4 has been deprecated. Upgrade to a newer version of +libtiff instead. + Removed features ---------------- diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 10ac9ea3a2a..13bcf5d8606 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -56,6 +56,7 @@ from ._binary import i16be as i16 from ._binary import i32be as i32 from ._binary import o8 +from ._deprecate import deprecate from .TiffTags import TYPES logger = logging.getLogger(__name__) @@ -276,6 +277,9 @@ b"II\x2B\x00", # BigTIFF with little-endian byte order ] +if not getattr(Image.core, "libtiff_support_custom_tags", True): + deprecate("Support for libtiff earlier than 4", 12) + def _accept(prefix: bytes) -> bool: return prefix[:4] in PREFIXES From c7bb152ed94002a83d648d2a1b9703c2bf27e8e6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 18:30:00 +1000 Subject: [PATCH 088/300] support_custom_tags attribute is not present if libtiff is not supported --- Tests/test_file_libtiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 71f1b6f1def..e1867cffb3a 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -741,7 +741,7 @@ def test_read_icc(self, monkeypatch: pytest.MonkeyPatch) -> None: pytest.param( True, marks=pytest.mark.skipif( - not Image.core.libtiff_support_custom_tags, + not getattr(Image.core, "libtiff_support_custom_tags", False), reason="Custom tags not supported by older libtiff", ), ), From 0df8796e1910619741a36bb1a2d334bdb941e440 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 18:45:41 +1000 Subject: [PATCH 089/300] Parametrized test --- Tests/test_file_libtiff.py | 103 ++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index e1867cffb3a..11883ad24aa 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -242,7 +242,24 @@ def test_additional_metadata( im.save(out, tiffinfo=new_ifd) - def test_custom_metadata(self, tmp_path: Path) -> None: + @pytest.mark.parametrize( + "libtiff", + ( + pytest.param( + True, + marks=pytest.mark.skipif( + not getattr(Image.core, "libtiff_support_custom_tags", False), + reason="Custom tags not supported by older libtiff", + ), + ), + False, + ), + ) + def test_custom_metadata( + self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path, libtiff: bool + ) -> None: + monkeypatch.setattr(TiffImagePlugin, "WRITE_LIBTIFF", libtiff) + class Tc(NamedTuple): value: Any type: int @@ -281,53 +298,43 @@ class Tc(NamedTuple): ) } - libtiffs = [False] - if Image.core.libtiff_support_custom_tags: - libtiffs.append(True) - - for libtiff in libtiffs: - TiffImagePlugin.WRITE_LIBTIFF = libtiff - - def check_tags( - tiffinfo: TiffImagePlugin.ImageFileDirectory_v2 | dict[int, str] - ) -> None: - im = hopper() - - out = str(tmp_path / "temp.tif") - im.save(out, tiffinfo=tiffinfo) - - with Image.open(out) as reloaded: - for tag, value in tiffinfo.items(): - reloaded_value = reloaded.tag_v2[tag] - if ( - isinstance(reloaded_value, TiffImagePlugin.IFDRational) - and libtiff - ): - # libtiff does not support real RATIONALS - assert ( - round(abs(float(reloaded_value) - float(value)), 7) == 0 - ) - continue - - assert reloaded_value == value - - # Test with types - ifd = TiffImagePlugin.ImageFileDirectory_v2() - for tag, tagdata in custom.items(): - ifd[tag] = tagdata.value - ifd.tagtype[tag] = tagdata.type - check_tags(ifd) - - # Test without types. This only works for some types, int for example are - # always encoded as LONG and not SIGNED_LONG. - check_tags( - { - tag: tagdata.value - for tag, tagdata in custom.items() - if tagdata.supported_by_default - } - ) - TiffImagePlugin.WRITE_LIBTIFF = False + def check_tags( + tiffinfo: TiffImagePlugin.ImageFileDirectory_v2 | dict[int, str] + ) -> None: + im = hopper() + + out = str(tmp_path / "temp.tif") + im.save(out, tiffinfo=tiffinfo) + + with Image.open(out) as reloaded: + for tag, value in tiffinfo.items(): + reloaded_value = reloaded.tag_v2[tag] + if ( + isinstance(reloaded_value, TiffImagePlugin.IFDRational) + and libtiff + ): + # libtiff does not support real RATIONALS + assert round(abs(float(reloaded_value) - float(value)), 7) == 0 + continue + + assert reloaded_value == value + + # Test with types + ifd = TiffImagePlugin.ImageFileDirectory_v2() + for tag, tagdata in custom.items(): + ifd[tag] = tagdata.value + ifd.tagtype[tag] = tagdata.type + check_tags(ifd) + + # Test without types. This only works for some types, int for example are + # always encoded as LONG and not SIGNED_LONG. + check_tags( + { + tag: tagdata.value + for tag, tagdata in custom.items() + if tagdata.supported_by_default + } + ) def test_osubfiletype(self, tmp_path: Path) -> None: outfile = str(tmp_path / "temp.tif") From e144e418791eb205765b74da793487a038675984 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:14:23 +1000 Subject: [PATCH 090/300] Updated wording Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- docs/deprecations.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 91bc150b342..7882ec5ee2b 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -100,13 +100,13 @@ ImageMath eval() ``ImageMath.eval()`` has been deprecated. Use :py:meth:`~PIL.ImageMath.lambda_eval` or :py:meth:`~PIL.ImageMath.unsafe_eval` instead. -Support for libtiff earlier than 4 +Support for LibTIFF earlier than 4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. deprecated:: 10.4.0 -Support for libtiff earlier than 4 has been deprecated. Upgrade to a newer version of -libtiff instead. +Support for LibTIFF earlier than version 4 has been deprecated. +Upgrade to a newer version of LibTIFF instead. Removed features ---------------- From 2e1d2b2029eefe7255c1c11f81a53f862a7861e1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 19:15:38 +1000 Subject: [PATCH 091/300] Updated deprecation message --- src/PIL/TiffImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 13bcf5d8606..82ac47647fa 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -278,7 +278,7 @@ ] if not getattr(Image.core, "libtiff_support_custom_tags", True): - deprecate("Support for libtiff earlier than 4", 12) + deprecate("Support for LibTIFF earlier than version 4", 12) def _accept(prefix: bytes) -> bool: From 5a0a288dd048097d2ffa62fb2d5a24cf5727bbb9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 22 Apr 2024 19:16:55 +1000 Subject: [PATCH 092/300] Added release notes --- docs/releasenotes/10.4.0.rst | 54 ++++++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 55 insertions(+) create mode 100644 docs/releasenotes/10.4.0.rst diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst new file mode 100644 index 00000000000..0c2926732e4 --- /dev/null +++ b/docs/releasenotes/10.4.0.rst @@ -0,0 +1,54 @@ +10.4.0 +------ + +Security +======== + +TODO +^^^^ + +TODO + +:cve:`YYYY-XXXXX`: TODO +^^^^^^^^^^^^^^^^^^^^^^^ + +TODO + +Backwards Incompatible Changes +============================== + +TODO +^^^^ + +Deprecations +============ + +Support for LibTIFF earlier than 4 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Support for LibTIFF earlier than version 4 has been deprecated. +Upgrade to a newer version of LibTIFF instead. + +API Changes +=========== + +TODO +^^^^ + +TODO + +API Additions +============= + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 089d44b9075..6ee5fb6c8f4 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 10.4.0 10.3.0 10.2.0 10.1.0 From d4a4b59ee39dcf2e8dddafebfecccaaf709ec8bb Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:43:48 +0300 Subject: [PATCH 093/300] Sphinx extension to add dates to release notes Co-authored-by: Jason R. Coombs --- docs/conf.py | 1 + docs/dater.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 docs/dater.py diff --git a/docs/conf.py b/docs/conf.py index 392cf317e5f..f12b30e65fb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,6 +28,7 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + "dater", "sphinx.ext.autodoc", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", diff --git a/docs/dater.py b/docs/dater.py new file mode 100644 index 00000000000..d9e583547bd --- /dev/null +++ b/docs/dater.py @@ -0,0 +1,52 @@ +""" +Sphinx extension to add timestamps to release notes based on Git versions. + +Based on https://github.com/jaraco/rst.linker, with thanks to Jason R. Coombs. +""" + +from __future__ import annotations + +import datetime as dt +import os +import re +import subprocess +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from sphinx.application import Sphinx + +DOC_NAME_REGEX = re.compile(r"releasenotes/\d+\.\d+\.\d+") +VERSION_TITLE_REGEX = re.compile(r"^(\d+\.\d+\.\d+)\n-+\n") + + +def get_date_for(git_version: str) -> dt.datetime | None: + cmd = ["git", "log", "-1", "--format=%ai", git_version] + try: + with open(os.devnull, "w", encoding="utf-8") as devnull: + out = subprocess.check_output( + cmd, stderr=devnull, text=True, encoding="utf-8" + ) + ts = out.strip() + return dt.datetime.fromisoformat(ts) + except subprocess.CalledProcessError: + return None + + +def add_date(app: Sphinx, doc_name: str, source: list[str]) -> None: + if DOC_NAME_REGEX.match(doc_name) and (m := VERSION_TITLE_REGEX.match(source[0])): + old_title = m.group(1) + + if tag_datetime := get_date_for(old_title): + new_title = f"{old_title} ({tag_datetime:%Y-%m-%d})" + else: + new_title = f"{old_title} (unreleased)" + + new_underline = "-" * len(new_title) + + result = source[0].replace(m.group(0), f"{new_title}\n{new_underline}\n", 1) + source[0] = result + + +def setup(app: Sphinx) -> dict[str, bool]: + app.connect("source-read", add_date) + return {"parallel_read_safe": True} From 35003343386f3d2d40f179e0c18f96e0b58ce102 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:58:44 +0300 Subject: [PATCH 094/300] Fetch tags on Read the Docs --- .readthedocs.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0c8f935d58c..b83ba05b128 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -6,6 +6,10 @@ build: os: ubuntu-22.04 tools: python: "3" + jobs: + post_checkout: + - git remote add upstream https://github.com/python-pillow/Pillow.git # For forks + - git fetch upstream --tags python: install: From 7f6ad116d1213948b318423cfd58990ec1e85c07 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Apr 2024 08:02:42 +1000 Subject: [PATCH 095/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 196f8ed2020..85dc0b43c24 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,18 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Support reading P mode TIFF images with padding #7996 + [radarhere] + +- Deprecate support for libtiff < 4 #7998 + [radarhere, hugovk] + +- Corrected ImageShow UnixViewer command #7987 + [radarhere] + +- Use functools.cached_property in ImageStat #7952 + [nulano, hugovk, radarhere] + - Add support for reading BITMAPV2INFOHEADER and BITMAPV3INFOHEADER #7956 [Cirras, radarhere] From 4a4eb0f3eeb76f59c9c890bf74c91a910e98a3eb Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 23 Apr 2024 01:08:42 -0500 Subject: [PATCH 096/300] remove semicolon after function definition --- src/_imaging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_imaging.c b/src/_imaging.c index 520e5079346..9b521f552cc 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -3737,7 +3737,7 @@ _getattr_unsafe_ptrs(ImagingObject *self, void *closure) { self->image->image32, "image", self->image->image); -}; +} static struct PyGetSetDef getsetters[] = { {"mode", (getter)_getattr_mode}, From b9307f08d14e499e75edf574ff15c9b5c1f62a7d Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 23 Apr 2024 12:02:25 -0500 Subject: [PATCH 097/300] remove unused variable --- src/display.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/display.c b/src/display.c index ef2ff3754f1..6b66ddafb87 100644 --- a/src/display.c +++ b/src/display.c @@ -427,7 +427,6 @@ PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) { PyObject * PyImaging_GrabClipboardWin32(PyObject *self, PyObject *args) { - int clip; HANDLE handle = NULL; int size; void *data; From eee53ba6647b75bbecc56e98a6a99d7c2360d1ca Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 23 Apr 2024 13:06:22 -0500 Subject: [PATCH 098/300] extract band count check --- src/libImaging/Matrix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c index 182eb62a7e6..ec7f4d93e06 100644 --- a/src/libImaging/Matrix.c +++ b/src/libImaging/Matrix.c @@ -24,11 +24,11 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { ImagingSectionCookie cookie; /* Assume there's enough data in the buffer */ - if (!im) { + if (!im || im->bands != 3) { return (Imaging)ImagingError_ModeError(); } - if (strcmp(mode, "L") == 0 && im->bands == 3) { + if (strcmp(mode, "L") == 0) { imOut = ImagingNewDirty("L", im->xsize, im->ysize); if (!imOut) { return NULL; @@ -47,7 +47,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) { } ImagingSectionLeave(&cookie); - } else if (strlen(mode) == 3 && im->bands == 3) { + } else if (strlen(mode) == 3) { imOut = ImagingNewDirty(mode, im->xsize, im->ysize); if (!imOut) { return NULL; From 46b85e6ab4eb1a5f1781acca2fb085826b328c3b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Apr 2024 11:02:56 +1000 Subject: [PATCH 099/300] Simplified code --- src/libImaging/Convert.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 7e60a960c1b..64840d08c94 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -254,9 +254,8 @@ static void rgb2i16l(UINT8 *out_, const UINT8 *in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) { - UINT8 v = CLIP16(L24(in) >> 16); - *out_++ = v; - *out_++ = v >> 8; + *out_++ = L24(in) >> 16; + *out_++ = 0; } } @@ -264,9 +263,8 @@ static void rgb2i16b(UINT8 *out_, const UINT8 *in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) { - UINT8 v = CLIP16(L24(in) >> 16); - *out_++ = v >> 8; - *out_++ = v; + *out_++ = 0; + *out_++ = L24(in) >> 16; } } From 03627d92a739d31269de0af61714b45db04069de Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:23:44 +0300 Subject: [PATCH 100/300] GitHub Actions: Python 3.8 and 3.9 are on macos-13 but not macos-14 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 643273e58b3..4573fde906d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,9 +57,9 @@ jobs: - python-version: "3.10" PYTHONOPTIMIZE: 2 # M1 only available for 3.10+ - - os: "macos-latest" + - os: "macos-13" python-version: "3.9" - - os: "macos-latest" + - os: "macos-13" python-version: "3.8" exclude: - os: "macos-14" From 76c17a10f0563f2343c7dada04af217093d3ef67 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:24:23 +0300 Subject: [PATCH 101/300] GitHub Actions: macos-13 is Intel but macos-latest will be M1 --- .github/workflows/wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 36bb5405026..b2fbd314083 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -97,7 +97,7 @@ jobs: matrix: include: - name: "macOS x86_64" - os: macos-latest + os: macos-13 cibw_arch: x86_64 macosx_deployment_target: "10.10" - name: "macOS arm64" From ccf1efb3efb371818455289b8c04aa21f18d4c4d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Apr 2024 23:06:06 +1000 Subject: [PATCH 102/300] Use subprocess.DEVNULL --- docs/dater.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/dater.py b/docs/dater.py index d9e583547bd..04855956beb 100644 --- a/docs/dater.py +++ b/docs/dater.py @@ -7,7 +7,6 @@ from __future__ import annotations import datetime as dt -import os import re import subprocess from typing import TYPE_CHECKING @@ -22,11 +21,10 @@ def get_date_for(git_version: str) -> dt.datetime | None: cmd = ["git", "log", "-1", "--format=%ai", git_version] try: - with open(os.devnull, "w", encoding="utf-8") as devnull: - out = subprocess.check_output( - cmd, stderr=devnull, text=True, encoding="utf-8" - ) - ts = out.strip() + out = subprocess.check_output( + cmd, stderr=subprocess.DEVNULL, text=True, encoding="utf-8" + ) + ts = out.strip() return dt.datetime.fromisoformat(ts) except subprocess.CalledProcessError: return None From 4af831e70c41441dbe0cdebf9e2cbd46ae71eb92 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Apr 2024 23:45:25 +1000 Subject: [PATCH 103/300] Accept '.zlib-ng' suffix to zlib version --- Tests/test_features.py | 2 ++ Tests/test_file_png.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/test_features.py b/Tests/test_features.py index 3a528a7c8c6..2d402ca9114 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -37,6 +37,8 @@ def test(name: str, function: Callable[[str], bool]) -> None: else: assert function(name) == version if name != "PIL": + if name == "zlib" and version is not None: + version = version.replace(".zlib-ng", "") assert version is None or re.search(r"\d+(\.\d+)*$", version) for module in features.modules: diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 30fb14c44c4..19462dcb5a4 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -85,7 +85,9 @@ def get_chunks(self, filename: str) -> list[bytes]: def test_sanity(self, tmp_path: Path) -> None: # internal version number - assert re.search(r"\d+(\.\d+){1,3}$", features.version_codec("zlib")) + assert re.search( + r"\d+(\.\d+){1,3}(\.zlib\-ng)?$", features.version_codec("zlib") + ) test_file = str(tmp_path / "temp.png") From 03bcf03567ae934feaa737ee9128c19136cb7803 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 25 Apr 2024 08:41:15 +1000 Subject: [PATCH 104/300] Removed Fedora 38 and added Fedora 40 --- .github/workflows/test-docker.yml | 2 +- docs/installation/platform-support.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 70426d7b509..8f4a4d09009 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -47,8 +47,8 @@ jobs: debian-11-bullseye-amd64, debian-12-bookworm-x86, debian-12-bookworm-amd64, - fedora-38-amd64, fedora-39-amd64, + fedora-40-amd64, gentoo, ubuntu-20.04-focal-amd64, ubuntu-22.04-jammy-amd64, diff --git a/docs/installation/platform-support.rst b/docs/installation/platform-support.rst index af205a4e8cc..c08a53a4329 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -31,10 +31,10 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Debian 12 Bookworm | 3.11 | x86, x86-64 | +----------------------------------+----------------------------+---------------------+ -| Fedora 38 | 3.11 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | Fedora 39 | 3.12 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| Fedora 40 | 3.12 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | macOS 12 Monterey | 3.8, 3.9 | x86-64 | From 02db41119018f313df060c254a22f44a95057e15 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 25 Apr 2024 09:14:48 +1000 Subject: [PATCH 105/300] Added release notes --- docs/releasenotes/10.4.0.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 0c2926732e4..3150bf4e02f 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -23,6 +23,11 @@ TODO Deprecations ============ +BGR;15, BGR 16 and BGR;24 +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The experimental BGR;15, BGR;16 and BGR;24 modes have been deprecated. + Support for LibTIFF earlier than 4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 35ffbdc9cde567ae629ae01ddce3116a35f3483d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 01:38:43 +0000 Subject: [PATCH 106/300] Update dependency mypy to v1.10.0 --- .ci/requirements-mypy.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/requirements-mypy.txt b/.ci/requirements-mypy.txt index 6b0535fc1cf..a0dcb92d22d 100644 --- a/.ci/requirements-mypy.txt +++ b/.ci/requirements-mypy.txt @@ -1 +1 @@ -mypy==1.9.0 +mypy==1.10.0 From 5faebadd56ab3ec66139d123fcee54d3ff89ad30 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 25 Apr 2024 11:06:04 +1000 Subject: [PATCH 107/300] BGR;16 does not fail on big-endian --- Tests/test_image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 4a056df4083..8e7e40c05da 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1037,8 +1037,8 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: @pytest.mark.parametrize("mode", modes) def test_getdata_putdata(self, mode: str) -> None: - if is_big_endian and mode in ("BGR;15", "BGR;16"): - pytest.xfail(f"Known failure of {mode} on big-endian") + if is_big_endian and mode == "BGR;15": + pytest.xfail("Known failure of BGR;15 on big-endian") im = hopper(mode) reloaded = Image.new(mode, im.size) reloaded.putdata(im.getdata()) From bc35bf0c9e2b3c0a5702a21daa02d5982932f8d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 25 Apr 2024 13:10:45 +1000 Subject: [PATCH 108/300] Use split instead of datetime --- docs/dater.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/dater.py b/docs/dater.py index 04855956beb..f9fb0c1da6e 100644 --- a/docs/dater.py +++ b/docs/dater.py @@ -6,7 +6,6 @@ from __future__ import annotations -import datetime as dt import re import subprocess from typing import TYPE_CHECKING @@ -18,24 +17,23 @@ VERSION_TITLE_REGEX = re.compile(r"^(\d+\.\d+\.\d+)\n-+\n") -def get_date_for(git_version: str) -> dt.datetime | None: +def get_date_for(git_version: str) -> str | None: cmd = ["git", "log", "-1", "--format=%ai", git_version] try: out = subprocess.check_output( cmd, stderr=subprocess.DEVNULL, text=True, encoding="utf-8" ) - ts = out.strip() - return dt.datetime.fromisoformat(ts) except subprocess.CalledProcessError: return None + return out.split()[0] def add_date(app: Sphinx, doc_name: str, source: list[str]) -> None: if DOC_NAME_REGEX.match(doc_name) and (m := VERSION_TITLE_REGEX.match(source[0])): old_title = m.group(1) - if tag_datetime := get_date_for(old_title): - new_title = f"{old_title} ({tag_datetime:%Y-%m-%d})" + if tag_date := get_date_for(old_title): + new_title = f"{old_title} ({tag_date})" else: new_title = f"{old_title} (unreleased)" From bbd5a87e6046bd0c0eb9ad105102a0d019277eb5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 25 Apr 2024 16:16:33 +1000 Subject: [PATCH 109/300] Combined conditions --- src/_imagingcms.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index f18d55a5718..63d78f84daa 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -213,34 +213,37 @@ cms_transform_dealloc(CmsTransformObject *self) { static cmsUInt32Number findLCMStype(char *PILmode) { - if (strcmp(PILmode, "RGB") == 0) { + if ( + strcmp(PILmode, "RGB") == 0 || + strcmp(PILmode, "RGBA") == 0 || + strcmp(PILmode, "RGBX") == 0 + ) { return TYPE_RGBA_8; - } else if (strcmp(PILmode, "RGBA") == 0) { - return TYPE_RGBA_8; - } else if (strcmp(PILmode, "RGBX") == 0) { - return TYPE_RGBA_8; - } else if (strcmp(PILmode, "RGBA;16B") == 0) { + } + if (strcmp(PILmode, "RGBA;16B") == 0) { return TYPE_RGBA_16; - } else if (strcmp(PILmode, "CMYK") == 0) { + } + if (strcmp(PILmode, "CMYK") == 0) { return TYPE_CMYK_8; - } else if (strcmp(PILmode, "L") == 0) { - return TYPE_GRAY_8; - } else if (strcmp(PILmode, "L;16") == 0) { + } + if (strcmp(PILmode, "L;16") == 0) { return TYPE_GRAY_16; - } else if (strcmp(PILmode, "L;16B") == 0) { + } + if (strcmp(PILmode, "L;16B") == 0) { return TYPE_GRAY_16_SE; - } else if (strcmp(PILmode, "YCCA") == 0) { - return TYPE_YCbCr_8; - } else if (strcmp(PILmode, "YCC") == 0) { + } + if ( + strcmp(PILmode, "YCCA") == 0 || + strcmp(PILmode, "YCC") == 0 + ) { return TYPE_YCbCr_8; - } else if (strcmp(PILmode, "LAB") == 0) { + } + if (strcmp(PILmode, "LAB") == 0) { // LabX equivalent like ALab, but not reversed -- no #define in lcms2 return (COLORSPACE_SH(PT_LabV2) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1)); } - else { - /* take a wild guess... */ - return TYPE_GRAY_8; - } + /* presume "L" by default */ + return TYPE_GRAY_8; } #define Cms_Min(a, b) ((a) < (b) ? (a) : (b)) From 1b1c825f7ba5ffe6b62fbffdad4d094f13e254e8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:39:51 +0300 Subject: [PATCH 110/300] Add ClangFormat to pre-commit --- .pre-commit-config.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51625eb4c89..f81f03da16e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.4.1 hooks: - id: ruff args: [--exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.3.0 + rev: 24.4.1 hooks: - id: black @@ -23,13 +23,20 @@ repos: - id: remove-tabs exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$) + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v18.1.4 + hooks: + - id: clang-format + types: [c] + exclude: ^src/thirdparty/ + - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable @@ -43,7 +50,7 @@ repos: exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/ - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.1 + rev: 0.28.2 hooks: - id: check-github-workflows - id: check-readthedocs @@ -55,7 +62,7 @@ repos: - id: sphinx-lint - repo: https://github.com/tox-dev/pyproject-fmt - rev: 1.7.0 + rev: 1.8.0 hooks: - id: pyproject-fmt From 617e7295a80f2067f4be2861c43fc9e2345455e5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 12:51:20 +0000 Subject: [PATCH 111/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/Tk/tkImaging.c | 19 +- src/_imaging.c | 54 +++--- src/_imagingcms.c | 16 +- src/_imagingft.c | 127 +++++++++----- src/encode.c | 10 +- src/libImaging/Access.c | 10 +- src/libImaging/BcnDecode.c | 24 +-- src/libImaging/BoxBlur.c | 14 +- src/libImaging/Convert.c | 26 +-- src/libImaging/Draw.c | 66 +++++-- src/libImaging/Filter.c | 9 +- src/libImaging/FliDecode.c | 5 +- src/libImaging/Geometry.c | 16 +- src/libImaging/GetBBox.c | 10 +- src/libImaging/Gif.h | 8 +- src/libImaging/GifEncode.c | 316 ++++++++++++++++++---------------- src/libImaging/Imaging.h | 20 ++- src/libImaging/ImagingUtils.h | 2 +- src/libImaging/Jpeg2KDecode.c | 10 +- src/libImaging/Jpeg2KEncode.c | 6 +- src/libImaging/JpegEncode.c | 12 +- src/libImaging/Paste.c | 9 +- src/libImaging/Point.c | 2 +- src/libImaging/Quant.c | 63 +++---- src/libImaging/QuantOctree.c | 4 +- src/libImaging/Reduce.c | 2 +- src/libImaging/Resample.c | 2 +- src/libImaging/SgiRleDecode.c | 9 +- src/libImaging/Storage.c | 5 +- src/libImaging/TiffDecode.c | 136 +++++++++------ src/libImaging/TiffDecode.h | 2 +- src/libImaging/Unpack.c | 49 +++--- src/path.c | 36 ++-- 33 files changed, 607 insertions(+), 492 deletions(-) diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c index bd3cafe9596..ef1c00a94e8 100644 --- a/src/Tk/tkImaging.c +++ b/src/Tk/tkImaging.c @@ -128,14 +128,7 @@ PyImagingPhotoPut( block.pixelPtr = (unsigned char *)im->block; TK_PHOTO_PUT_BLOCK( - interp, - photo, - &block, - 0, - 0, - block.width, - block.height, - TK_PHOTO_COMPOSITE_SET); + interp, photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET); return TCL_OK; } @@ -287,7 +280,7 @@ load_tkinter_funcs(void) { * Return 0 for success, non-zero for failure. */ - HMODULE* hMods = NULL; + HMODULE *hMods = NULL; HANDLE hProcess; DWORD cbNeeded; unsigned int i; @@ -313,7 +306,7 @@ load_tkinter_funcs(void) { #endif return 1; } - if (!(hMods = (HMODULE*) malloc(cbNeeded))) { + if (!(hMods = (HMODULE *)malloc(cbNeeded))) { PyErr_NoMemory(); return 1; } @@ -345,7 +338,7 @@ load_tkinter_funcs(void) { } else if (found_tk == 0) { PyErr_SetString(PyExc_RuntimeError, "Could not find Tk routines"); } - return (int) ((found_tcl != 1) || (found_tk != 1)); + return (int)((found_tcl != 1) || (found_tk != 1)); } #else /* not Windows */ @@ -400,8 +393,8 @@ _func_loader(void *lib) { return 1; } return ( - (TK_PHOTO_PUT_BLOCK = - (Tk_PhotoPutBlock_t)_dfunc(lib, "Tk_PhotoPutBlock")) == NULL); + (TK_PHOTO_PUT_BLOCK = (Tk_PhotoPutBlock_t)_dfunc(lib, "Tk_PhotoPutBlock")) == + NULL); } int diff --git a/src/_imaging.c b/src/_imaging.c index 9b521f552cc..c565c21bb15 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -110,7 +110,7 @@ #define B16(p, i) ((((int)p[(i)]) << 8) + p[(i) + 1]) #define L16(p, i) ((((int)p[(i) + 1]) << 8) + p[(i)]) -#define S16(v) ((v) < 32768 ? (v) : ((v)-65536)) +#define S16(v) ((v) < 32768 ? (v) : ((v) - 65536)) /* -------------------------------------------------------------------- */ /* OBJECT ADMINISTRATION */ @@ -533,7 +533,9 @@ getink(PyObject *color, Imaging im, char *ink) { /* unsigned integer, single layer */ if (rIsInt != 1) { if (tupleSize != 1) { - PyErr_SetString(PyExc_TypeError, "color must be int or single-element tuple"); + PyErr_SetString( + PyExc_TypeError, + "color must be int or single-element tuple"); return NULL; } else if (!PyArg_ParseTuple(color, "L", &r)) { return NULL; @@ -552,7 +554,9 @@ getink(PyObject *color, Imaging im, char *ink) { a = 255; if (im->bands == 2) { if (tupleSize != 1 && tupleSize != 2) { - PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or two elements"); + PyErr_SetString( + PyExc_TypeError, + "color must be int, or tuple of one or two elements"); return NULL; } else if (!PyArg_ParseTuple(color, "L|i", &r, &a)) { return NULL; @@ -560,7 +564,10 @@ getink(PyObject *color, Imaging im, char *ink) { g = b = r; } else { if (tupleSize != 3 && tupleSize != 4) { - PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one, three or four elements"); + PyErr_SetString( + PyExc_TypeError, + "color must be int, or tuple of one, three or four " + "elements"); return NULL; } else if (!PyArg_ParseTuple(color, "Lii|i", &r, &g, &b, &a)) { return NULL; @@ -599,7 +606,9 @@ getink(PyObject *color, Imaging im, char *ink) { g = (UINT8)(r >> 8); r = (UINT8)r; } else if (tupleSize != 3) { - PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or three elements"); + PyErr_SetString( + PyExc_TypeError, + "color must be int, or tuple of one or three elements"); return NULL; } else if (!PyArg_ParseTuple(color, "iiL", &b, &g, &r)) { return NULL; @@ -1537,14 +1546,14 @@ _putdata(ImagingObject *self, PyObject *args) { return NULL; } -#define set_value_to_item(seq, i) \ -op = PySequence_Fast_GET_ITEM(seq, i); \ -if (PySequence_Check(op)) { \ - PyErr_SetString(PyExc_TypeError, "sequence must be flattened"); \ - return NULL; \ -} else { \ - value = PyFloat_AsDouble(op); \ -} +#define set_value_to_item(seq, i) \ + op = PySequence_Fast_GET_ITEM(seq, i); \ + if (PySequence_Check(op)) { \ + PyErr_SetString(PyExc_TypeError, "sequence must be flattened"); \ + return NULL; \ + } else { \ + value = PyFloat_AsDouble(op); \ + } if (image->image8) { if (PyBytes_Check(data)) { unsigned char *p; @@ -1596,8 +1605,10 @@ if (PySequence_Check(op)) { \ value = value * scale + offset; } if (image->type == IMAGING_TYPE_SPECIAL) { - image->image8[y][x * 2 + (bigendian ? 1 : 0)] = CLIP8((int)value % 256); - image->image8[y][x * 2 + (bigendian ? 0 : 1)] = CLIP8((int)value >> 8); + image->image8[y][x * 2 + (bigendian ? 1 : 0)] = + CLIP8((int)value % 256); + image->image8[y][x * 2 + (bigendian ? 0 : 1)] = + CLIP8((int)value >> 8); } else { image->image8[y][x] = (UINT8)CLIP8(value); } @@ -1639,8 +1650,7 @@ if (PySequence_Check(op)) { \ for (i = x = y = 0; i < n; i++) { double value; set_value_to_item(seq, i); - IMAGING_PIXEL_INT32(image, x, y) = - (INT32)(value * scale + offset); + IMAGING_PIXEL_INT32(image, x, y) = (INT32)(value * scale + offset); if (++x >= (int)image->xsize) { x = 0, y++; } @@ -2785,8 +2795,8 @@ _font_getmask(ImagingFontObject *self, PyObject *args) { glyph = &self->glyphs[text[i]]; if (i == 0 || text[i] != text[i - 1]) { ImagingDelete(bitmap); - bitmap = - ImagingCrop(self->bitmap, glyph->sx0, glyph->sy0, glyph->sx1, glyph->sy1); + bitmap = ImagingCrop( + self->bitmap, glyph->sx0, glyph->sy0, glyph->sx1, glyph->sy1); if (!bitmap) { goto failed; } @@ -3315,7 +3325,8 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) { free(xy); - if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, width, self->blend) < 0) { + if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, width, self->blend) < + 0) { free(ixy); return NULL; } @@ -4411,7 +4422,8 @@ setup_module(PyObject *m) { PyModule_AddObject(m, "HAVE_XCB", have_xcb); PyObject *pillow_version = PyUnicode_FromString(version); - PyDict_SetItemString(d, "PILLOW_VERSION", pillow_version ? pillow_version : Py_None); + PyDict_SetItemString( + d, "PILLOW_VERSION", pillow_version ? pillow_version : Py_None); Py_XDECREF(pillow_version); return 0; diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 63d78f84daa..ba8c810057b 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -213,11 +213,8 @@ cms_transform_dealloc(CmsTransformObject *self) { static cmsUInt32Number findLCMStype(char *PILmode) { - if ( - strcmp(PILmode, "RGB") == 0 || - strcmp(PILmode, "RGBA") == 0 || - strcmp(PILmode, "RGBX") == 0 - ) { + if (strcmp(PILmode, "RGB") == 0 || strcmp(PILmode, "RGBA") == 0 || + strcmp(PILmode, "RGBX") == 0) { return TYPE_RGBA_8; } if (strcmp(PILmode, "RGBA;16B") == 0) { @@ -232,10 +229,7 @@ findLCMStype(char *PILmode) { if (strcmp(PILmode, "L;16B") == 0) { return TYPE_GRAY_16_SE; } - if ( - strcmp(PILmode, "YCCA") == 0 || - strcmp(PILmode, "YCC") == 0 - ) { + if (strcmp(PILmode, "YCCA") == 0 || strcmp(PILmode, "YCC") == 0) { return TYPE_YCbCr_8; } if (strcmp(PILmode, "LAB") == 0) { @@ -393,7 +387,7 @@ _buildTransform( Py_END_ALLOW_THREADS - if (!hTransform) { + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build transform"); } @@ -427,7 +421,7 @@ _buildProofTransform( Py_END_ALLOW_THREADS - if (!hTransform) { + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build proof transform"); } diff --git a/src/_imagingft.c b/src/_imagingft.c index 6e24fcf95ed..e83ddfec122 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -47,17 +47,17 @@ ; #ifdef HAVE_RAQM -# ifdef HAVE_RAQM_SYSTEM -# include -# else -# include "thirdparty/raqm/raqm.h" -# ifdef HAVE_FRIBIDI_SYSTEM -# include -# else -# include "thirdparty/fribidi-shim/fribidi.h" -# include -# endif -# endif +#ifdef HAVE_RAQM_SYSTEM +#include +#else +#include "thirdparty/raqm/raqm.h" +#ifdef HAVE_FRIBIDI_SYSTEM +#include +#else +#include "thirdparty/fribidi-shim/fribidi.h" +#include +#endif +#endif #endif static int have_raqm = 0; @@ -490,8 +490,7 @@ text_layout( size_t count; #ifdef HAVE_RAQM if (have_raqm && self->layout_engine == LAYOUT_RAQM) { - count = text_layout_raqm( - string, self, dir, features, lang, glyph_info); + count = text_layout_raqm(string, self, dir, features, lang, glyph_info); } else #endif { @@ -550,7 +549,17 @@ font_getlength(FontObject *self, PyObject *args) { } static int -bounding_box_and_anchors(FT_Face face, const char *anchor, int horizontal_dir, GlyphInfo *glyph_info, size_t count, int load_flags, int *width, int *height, int *x_offset, int *y_offset) { +bounding_box_and_anchors( + FT_Face face, + const char *anchor, + int horizontal_dir, + GlyphInfo *glyph_info, + size_t count, + int load_flags, + int *width, + int *height, + int *x_offset, + int *y_offset) { int position; /* pen position along primary axis, in 26.6 precision */ int advanced; /* pen position along primary axis, in pixels */ int px, py; /* position of current glyph, in pixels */ @@ -558,8 +567,8 @@ bounding_box_and_anchors(FT_Face face, const char *anchor, int horizontal_dir, G int x_anchor, y_anchor; /* offset of point drawn at (0, 0), in pixels */ int error; FT_Glyph glyph; - FT_BBox bbox; /* glyph bounding box */ - size_t i; /* glyph_info index */ + FT_BBox bbox; /* glyph bounding box */ + size_t i; /* glyph_info index */ /* * text bounds are given by: * - bounding boxes of individual glyphs @@ -654,8 +663,7 @@ bounding_box_and_anchors(FT_Face face, const char *anchor, int horizontal_dir, G break; case 'm': // middle (ascender + descender) / 2 y_anchor = PIXEL( - (face->size->metrics.ascender + - face->size->metrics.descender) / + (face->size->metrics.ascender + face->size->metrics.descender) / 2); break; case 's': // horizontal baseline @@ -719,7 +727,7 @@ bounding_box_and_anchors(FT_Face face, const char *anchor, int horizontal_dir, G static PyObject * font_getsize(FontObject *self, PyObject *args) { int width, height, x_offset, y_offset; - int load_flags; /* FreeType load_flags parameter */ + int load_flags; /* FreeType load_flags parameter */ int error; GlyphInfo *glyph_info = NULL; /* computed text layout */ size_t count; /* glyph_info length */ @@ -758,7 +766,17 @@ font_getsize(FontObject *self, PyObject *args) { load_flags |= FT_LOAD_COLOR; } - error = bounding_box_and_anchors(self->face, anchor, horizontal_dir, glyph_info, count, load_flags, &width, &height, &x_offset, &y_offset); + error = bounding_box_and_anchors( + self->face, + anchor, + horizontal_dir, + glyph_info, + count, + load_flags, + &width, + &height, + &x_offset, + &y_offset); if (glyph_info) { PyMem_Free(glyph_info); glyph_info = NULL; @@ -767,12 +785,7 @@ font_getsize(FontObject *self, PyObject *args) { return NULL; } - return Py_BuildValue( - "(ii)(ii)", - width, - height, - x_offset, - y_offset); + return Py_BuildValue("(ii)(ii)", width, height, x_offset, y_offset); } static PyObject * @@ -869,7 +882,17 @@ font_render(FontObject *self, PyObject *args) { horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1; - error = bounding_box_and_anchors(self->face, anchor, horizontal_dir, glyph_info, count, load_flags, &width, &height, &x_offset, &y_offset); + error = bounding_box_and_anchors( + self->face, + anchor, + horizontal_dir, + glyph_info, + count, + load_flags, + &width, + &height, + &x_offset, + &y_offset); if (error) { PyMem_Del(glyph_info); return NULL; @@ -1066,17 +1089,26 @@ font_render(FontObject *self, PyObject *args) { /* paste only if source has data */ if (src_alpha > 0) { /* unpremultiply BGRa */ - int src_red = CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha); - int src_green = CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha); - int src_blue = CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha); + int src_red = + CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha); + int src_green = + CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha); + int src_blue = + CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha); /* blend required if target has data */ if (target[k * 4 + 3] > 0) { /* blend RGBA colors */ - target[k * 4 + 0] = BLEND(src_alpha, target[k * 4 + 0], src_red, tmp); - target[k * 4 + 1] = BLEND(src_alpha, target[k * 4 + 1], src_green, tmp); - target[k * 4 + 2] = BLEND(src_alpha, target[k * 4 + 2], src_blue, tmp); - target[k * 4 + 3] = CLIP8(src_alpha + MULDIV255(target[k * 4 + 3], (255 - src_alpha), tmp)); + target[k * 4 + 0] = + BLEND(src_alpha, target[k * 4 + 0], src_red, tmp); + target[k * 4 + 1] = + BLEND(src_alpha, target[k * 4 + 1], src_green, tmp); + target[k * 4 + 2] = + BLEND(src_alpha, target[k * 4 + 2], src_blue, tmp); + target[k * 4 + 3] = CLIP8( + src_alpha + + MULDIV255( + target[k * 4 + 3], (255 - src_alpha), tmp)); } else { /* paste unpremultiplied RGBA values */ target[k * 4 + 0] = src_red; @@ -1093,10 +1125,16 @@ font_render(FontObject *self, PyObject *args) { unsigned int src_alpha = source[k] * convert_scale; if (src_alpha > 0) { if (target[k * 4 + 3] > 0) { - target[k * 4 + 0] = BLEND(src_alpha, target[k * 4 + 0], ink[0], tmp); - target[k * 4 + 1] = BLEND(src_alpha, target[k * 4 + 1], ink[1], tmp); - target[k * 4 + 2] = BLEND(src_alpha, target[k * 4 + 2], ink[2], tmp); - target[k * 4 + 3] = CLIP8(src_alpha + MULDIV255(target[k * 4 + 3], (255 - src_alpha), tmp)); + target[k * 4 + 0] = BLEND( + src_alpha, target[k * 4 + 0], ink[0], tmp); + target[k * 4 + 1] = BLEND( + src_alpha, target[k * 4 + 1], ink[1], tmp); + target[k * 4 + 2] = BLEND( + src_alpha, target[k * 4 + 2], ink[2], tmp); + target[k * 4 + 3] = CLIP8( + src_alpha + + MULDIV255( + target[k * 4 + 3], (255 - src_alpha), tmp)); } else { target[k * 4 + 0] = ink[0]; target[k * 4 + 1] = ink[1]; @@ -1109,7 +1147,13 @@ font_render(FontObject *self, PyObject *args) { for (k = x0; k < x1; k++) { unsigned int src_alpha = source[k] * convert_scale; if (src_alpha > 0) { - target[k] = target[k] > 0 ? CLIP8(src_alpha + MULDIV255(target[k], (255 - src_alpha), tmp)) : src_alpha; + target[k] = + target[k] > 0 + ? CLIP8( + src_alpha + + MULDIV255( + target[k], (255 - src_alpha), tmp)) + : src_alpha; } } } @@ -1249,7 +1293,8 @@ font_getvaraxes(FontObject *self) { if (name.name_id == axis.strid) { axis_name = Py_BuildValue("y#", name.string, name.string_len); - PyDict_SetItemString(list_axis, "name", axis_name ? axis_name : Py_None); + PyDict_SetItemString( + list_axis, "name", axis_name ? axis_name : Py_None); Py_XDECREF(axis_name); break; } @@ -1299,7 +1344,7 @@ font_setvaraxes(FontObject *self, PyObject *args) { } num_coords = PyObject_Length(axes); - coords = (FT_Fixed*)malloc(num_coords * sizeof(FT_Fixed)); + coords = (FT_Fixed *)malloc(num_coords * sizeof(FT_Fixed)); if (coords == NULL) { return PyErr_NoMemory(); } diff --git a/src/encode.c b/src/encode.c index c7dd510150e..442b5d04f88 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1163,8 +1163,10 @@ PyImaging_JpegEncoderNew(PyObject *self, PyObject *args) { ((JPEGENCODERSTATE *)encoder->state.context)->streamtype = streamtype; ((JPEGENCODERSTATE *)encoder->state.context)->xdpi = xdpi; ((JPEGENCODERSTATE *)encoder->state.context)->ydpi = ydpi; - ((JPEGENCODERSTATE *)encoder->state.context)->restart_marker_blocks = restart_marker_blocks; - ((JPEGENCODERSTATE *)encoder->state.context)->restart_marker_rows = restart_marker_rows; + ((JPEGENCODERSTATE *)encoder->state.context)->restart_marker_blocks = + restart_marker_blocks; + ((JPEGENCODERSTATE *)encoder->state.context)->restart_marker_rows = + restart_marker_rows; ((JPEGENCODERSTATE *)encoder->state.context)->comment = comment; ((JPEGENCODERSTATE *)encoder->state.context)->comment_size = comment_size; ((JPEGENCODERSTATE *)encoder->state.context)->extra = extra; @@ -1333,9 +1335,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) { if (comment && comment_size > 0) { /* Size is stored as as an uint16, subtract 4 bytes for the header */ if (comment_size >= 65532) { - PyErr_SetString( - PyExc_ValueError, - "JPEG 2000 comment is too long"); + PyErr_SetString(PyExc_ValueError, "JPEG 2000 comment is too long"); Py_DECREF(encoder); return NULL; } diff --git a/src/libImaging/Access.c b/src/libImaging/Access.c index 091c84e18fa..97d21034ac3 100644 --- a/src/libImaging/Access.c +++ b/src/libImaging/Access.c @@ -191,11 +191,11 @@ put_pixel_32(Imaging im, int x, int y, const void *color) { void ImagingAccessInit() { -#define ADD(mode_, get_pixel_, put_pixel_) \ - { \ - ImagingAccess access = add_item(mode_); \ - access->get_pixel = get_pixel_; \ - access->put_pixel = put_pixel_; \ +#define ADD(mode_, get_pixel_, put_pixel_) \ + { \ + ImagingAccess access = add_item(mode_); \ + access->get_pixel = get_pixel_; \ + access->put_pixel = put_pixel_; \ } /* populate access table */ diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 5e4296eeba1..72f478d8d60 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -83,7 +83,6 @@ decode_bc1_color(rgba *dst, const UINT8 *src, int separate_alpha) { g1 = p[1].g; b1 = p[1].b; - /* NOTE: BC2 and BC3 reuse BC1 color blocks but always act like c0 > c1 */ if (col.c0 > col.c1 || separate_alpha) { p[2].r = (2 * r0 + 1 * r1) / 3; @@ -354,8 +353,7 @@ decode_bc7_block(rgba *col, const UINT8 *src) { } return; } - while (!(mode & (1 << bit++))) - ; + while (!(mode & (1 << bit++))); mode = bit - 1; info = &bc7_modes[mode]; /* color selection bits: {subset}{endpoint} */ @@ -546,7 +544,7 @@ static const UINT8 bc6_bit_packings[][75] = { 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 10, 112, 113, 114, 115, 64, 65, 66, 67, 26, 176, 160, 161, 162, 163, 80, 81, 82, 83, 42, 177, 128, 129, 130, 131, - 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, + 96, 97, 98, 99, 100, 178, 144, 145, 146, 147, 148, 179}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, @@ -684,7 +682,7 @@ bc6_clamp(float value) { } else if (value > 1.0f) { return 255; } else { - return (UINT8) (value * 255.0f); + return (UINT8)(value * 255.0f); } } @@ -826,7 +824,13 @@ put_block(Imaging im, ImagingCodecState state, const char *col, int sz, int C) { static int decode_bcn( - Imaging im, ImagingCodecState state, const UINT8 *src, int bytes, int N, int C, char *pixel_format) { + Imaging im, + ImagingCodecState state, + const UINT8 *src, + int bytes, + int N, + int C, + char *pixel_format) { int ymax = state->ysize + state->yoff; const UINT8 *ptr = src; switch (N) { @@ -849,8 +853,7 @@ decode_bcn( DECODE_LOOP(2, 16, rgba); DECODE_LOOP(3, 16, rgba); DECODE_LOOP(4, 8, lum); - case 5: - { + case 5: { int sign = strcmp(pixel_format, "BC5S") == 0 ? 1 : 0; while (bytes >= 16) { rgba col[16]; @@ -865,8 +868,7 @@ decode_bcn( } break; } - case 6: - { + case 6: { int sign = strcmp(pixel_format, "BC6HS") == 0 ? 1 : 0; while (bytes >= 16) { rgba col[16]; @@ -880,7 +882,7 @@ decode_bcn( } break; } - DECODE_LOOP(7, 16, rgba); + DECODE_LOOP(7, 16, rgba); #undef DECODE_LOOP } return (int)(ptr - src); diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index adf425d0dbd..4ea9c77178a 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -313,12 +313,12 @@ _gaussian_blur_radius(float radius, int passes) { } Imaging -ImagingGaussianBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int passes) { +ImagingGaussianBlur( + Imaging imOut, Imaging imIn, float xradius, float yradius, int passes) { return ImagingBoxBlur( - imOut, - imIn, - _gaussian_blur_radius(xradius, passes), - _gaussian_blur_radius(yradius, passes), - passes - ); + imOut, + imIn, + _gaussian_blur_radius(xradius, passes), + _gaussian_blur_radius(yradius, passes), + passes); } diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 64840d08c94..fcb5f7ad95e 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -518,8 +518,8 @@ rgba2rgb_(UINT8 *out, const UINT8 *in, int xsize) { /* * Conversion of RGB + single transparent color either to - * RGBA or LA, where any pixel matching the color will have the alpha channel set to 0, or - * RGBa or La, where any pixel matching the color will have all channels set to 0 + * RGBA or LA, where any pixel matching the color will have the alpha channel set to 0, + * or RGBa or La, where any pixel matching the color will have all channels set to 0 */ static void @@ -1676,7 +1676,8 @@ convert( return (Imaging)ImagingError_ValueError("conversion not supported"); #else static char buf[100]; - snprintf(buf, 100, "conversion from %.10s to %.10s not supported", imIn->mode, mode); + snprintf( + buf, 100, "conversion from %.10s to %.10s not supported", imIn->mode, mode); return (Imaging)ImagingError_ValueError(buf); #endif } @@ -1720,25 +1721,24 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, int r, int g, int b) { return (Imaging)ImagingError_ModeError(); } - if (strcmp(imIn->mode, "RGB") == 0 && (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBa") == 0)) { + if (strcmp(imIn->mode, "RGB") == 0 && + (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBa") == 0)) { convert = rgb2rgba; if (strcmp(mode, "RGBa") == 0) { premultiplied = 1; } - } else if (strcmp(imIn->mode, "RGB") == 0 && (strcmp(mode, "LA") == 0 || strcmp(mode, "La") == 0)) { + } else if ( + strcmp(imIn->mode, "RGB") == 0 && + (strcmp(mode, "LA") == 0 || strcmp(mode, "La") == 0)) { convert = rgb2la; source_transparency = 1; if (strcmp(mode, "La") == 0) { premultiplied = 1; } - } else if ((strcmp(imIn->mode, "1") == 0 || - strcmp(imIn->mode, "I") == 0 || - strcmp(imIn->mode, "I;16") == 0 || - strcmp(imIn->mode, "L") == 0 - ) && ( - strcmp(mode, "RGBA") == 0 || - strcmp(mode, "LA") == 0 - )) { + } else if ( + (strcmp(imIn->mode, "1") == 0 || strcmp(imIn->mode, "I") == 0 || + strcmp(imIn->mode, "I;16") == 0 || strcmp(imIn->mode, "L") == 0) && + (strcmp(mode, "RGBA") == 0 || strcmp(mode, "LA") == 0)) { if (strcmp(imIn->mode, "1") == 0) { convert = bit2rgb; } else if (strcmp(imIn->mode, "I") == 0) { diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 0ccf22d58dd..133696dd86f 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -48,7 +48,7 @@ * This guarantees that ROUND_UP|DOWN(f) == -ROUND_UP|DOWN(-f) */ #define ROUND_UP(f) ((int)((f) >= 0.0 ? floor((f) + 0.5F) : -floor(fabs(f) + 0.5F))) -#define ROUND_DOWN(f) ((int)((f) >= 0.0 ? ceil((f)-0.5F) : -ceil(fabs(f) - 0.5F))) +#define ROUND_DOWN(f) ((int)((f) >= 0.0 ? ceil((f) - 0.5F) : -ceil(fabs(f) - 0.5F))) /* -------------------------------------------------------------------- */ /* Primitives */ @@ -439,7 +439,14 @@ draw_horizontal_lines( * Filled polygon draw function using scan line algorithm. */ static inline int -polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, int hasAlpha) { +polygon_generic( + Imaging im, + int n, + Edge *e, + int ink, + int eofill, + hline_handler hline, + int hasAlpha) { Edge **edge_table; float *xx; int edge_count = 0; @@ -499,7 +506,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h // Needed to draw consistent polygons xx[j] = xx[j - 1]; j++; - } else if (current->dx != 0 && roundf(xx[j-1]) == xx[j-1]) { + } else if (current->dx != 0 && roundf(xx[j - 1]) == xx[j - 1]) { // Connect discontiguous corners for (k = 0; k < i; k++) { Edge *other_edge = edge_table[k]; @@ -510,23 +517,38 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h // Check if the two edges join to make a corner if (((ymin == current->ymin && ymin == other_edge->ymin) || (ymin == current->ymax && ymin == other_edge->ymax)) && - xx[j-1] == (ymin - other_edge->y0) * other_edge->dx + other_edge->x0) { + xx[j - 1] == (ymin - other_edge->y0) * other_edge->dx + + other_edge->x0) { // Determine points from the edges on the next row // Or if this is the last row, check the previous row int offset = ymin == ymax ? -1 : 1; - adjacent_line_x = (ymin + offset - current->y0) * current->dx + current->x0; - adjacent_line_x_other_edge = (ymin + offset - other_edge->y0) * other_edge->dx + other_edge->x0; + adjacent_line_x = + (ymin + offset - current->y0) * current->dx + + current->x0; + adjacent_line_x_other_edge = + (ymin + offset - other_edge->y0) * other_edge->dx + + other_edge->x0; if (ymin == current->ymax) { if (current->dx > 0) { - xx[k] = fmax(adjacent_line_x, adjacent_line_x_other_edge) + 1; + xx[k] = fmax( + adjacent_line_x, + adjacent_line_x_other_edge) + + 1; } else { - xx[k] = fmin(adjacent_line_x, adjacent_line_x_other_edge) - 1; + xx[k] = fmin( + adjacent_line_x, + adjacent_line_x_other_edge) - + 1; } } else { if (current->dx > 0) { - xx[k] = fmin(adjacent_line_x, adjacent_line_x_other_edge); + xx[k] = fmin( + adjacent_line_x, adjacent_line_x_other_edge); } else { - xx[k] = fmax(adjacent_line_x, adjacent_line_x_other_edge) + 1; + xx[k] = fmax( + adjacent_line_x, + adjacent_line_x_other_edge) + + 1; } } break; @@ -552,7 +574,8 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h int x_start = ROUND_UP(xx[i - 1]); if (x_pos > x_start) { - // Line would be partway through x_pos, so increase the starting point + // Line would be partway through x_pos, so increase the starting + // point x_start = x_pos; if (x_end < x_start) { // Line would now end before it started @@ -776,7 +799,8 @@ ImagingDrawRectangle( } int -ImagingDrawPolygon(Imaging im, int count, int *xy, const void *ink_, int fill, int width, int op) { +ImagingDrawPolygon( + Imaging im, int count, int *xy, const void *ink_, int fill, int width, int op) { int i, n, x0, y0, x1, y1; DRAW *draw; INT32 ink; @@ -803,7 +827,7 @@ ImagingDrawPolygon(Imaging im, int count, int *xy, const void *ink_, int fill, i if (y0 == y1 && i != 0 && y0 == xy[i * 2 - 1]) { // This is a horizontal line, // that immediately follows another horizontal line - Edge *last_e = &e[n-1]; + Edge *last_e = &e[n - 1]; if (x1 > x0 && x0 > xy[i * 2 - 2]) { // They are both increasing in x last_e->xmax = x1; @@ -826,14 +850,24 @@ ImagingDrawPolygon(Imaging im, int count, int *xy, const void *ink_, int fill, i /* Outline */ if (width == 1) { for (i = 0; i < count - 1; i++) { - draw->line(im, xy[i * 2], xy[i * 2 + 1], xy[i * 2 + 2], xy[i * 2 + 3], ink); + draw->line( + im, xy[i * 2], xy[i * 2 + 1], xy[i * 2 + 2], xy[i * 2 + 3], ink); } draw->line(im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink); } else { for (i = 0; i < count - 1; i++) { - ImagingDrawWideLine(im, xy[i * 2], xy[i * 2 + 1], xy[i * 2 + 2], xy[i * 2 + 3], ink_, width, op); + ImagingDrawWideLine( + im, + xy[i * 2], + xy[i * 2 + 1], + xy[i * 2 + 2], + xy[i * 2 + 3], + ink_, + width, + op); } - ImagingDrawWideLine(im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op); + ImagingDrawWideLine( + im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op); } } diff --git a/src/libImaging/Filter.c b/src/libImaging/Filter.c index 4dcd368ca80..85de77fcbbc 100644 --- a/src/libImaging/Filter.c +++ b/src/libImaging/Filter.c @@ -106,7 +106,7 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin) { void ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) { -#define KERNEL1x3(in0, x, kernel, d) \ +#define KERNEL1x3(in0, x, kernel, d) \ (_i2f(in0[x - d]) * (kernel)[0] + _i2f(in0[x]) * (kernel)[1] + \ _i2f(in0[x + d]) * (kernel)[2]) @@ -224,10 +224,9 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) { void ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) { -#define KERNEL1x5(in0, x, kernel, d) \ - (_i2f(in0[x - d - d]) * (kernel)[0] + \ - _i2f(in0[x - d]) * (kernel)[1] + _i2f(in0[x]) * (kernel)[2] + \ - _i2f(in0[x + d]) * (kernel)[3] + \ +#define KERNEL1x5(in0, x, kernel, d) \ + (_i2f(in0[x - d - d]) * (kernel)[0] + _i2f(in0[x - d]) * (kernel)[1] + \ + _i2f(in0[x]) * (kernel)[2] + _i2f(in0[x + d]) * (kernel)[3] + \ _i2f(in0[x + d + d]) * (kernel)[4]) int x = 0, y = 0; diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c index d6e4ea0ff9d..debe7ddd82a 100644 --- a/src/libImaging/FliDecode.c +++ b/src/libImaging/FliDecode.c @@ -232,7 +232,8 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt /* Note, have to check Data + size, not just ptr + size) */ if (data + (state->xsize * state->ysize) > ptr + bytes) { /* not enough data for frame */ - /* UNDONE Unclear that we're actually going to leave the buffer at the right place. */ + /* UNDONE Unclear that we're actually going to leave the buffer at + * the right place. */ return ptr - buf; /* bytes consumed */ } for (y = 0; y < state->ysize; y++) { @@ -251,7 +252,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt return -1; } advance = I32(ptr); - if (advance == 0 ) { + if (advance == 0) { // If there's no advance, we're in an infinite loop state->errcode = IMAGING_CODEC_BROKEN; return -1; diff --git a/src/libImaging/Geometry.c b/src/libImaging/Geometry.c index 0c591579217..cf3bc997942 100644 --- a/src/libImaging/Geometry.c +++ b/src/libImaging/Geometry.c @@ -565,13 +565,13 @@ bilinear_filter32RGB(void *out, Imaging im, double xin, double yin) { #undef BILINEAR_HEAD #undef BILINEAR_BODY -#define BICUBIC(v, v1, v2, v3, v4, d) \ - { \ - double p1 = v2; \ - double p2 = -v1 + v3; \ - double p3 = 2 * (v1 - v2) + v3 - v4; \ - double p4 = -v1 + v2 - v3 + v4; \ - v = p1 + (d) * (p2 + (d) * (p3 + (d)*p4)); \ +#define BICUBIC(v, v1, v2, v3, v4, d) \ + { \ + double p1 = v2; \ + double p2 = -v1 + v3; \ + double p3 = 2 * (v1 - v2) + v3 - v4; \ + double p4 = -v1 + v2 - v3 + v4; \ + v = p1 + (d) * (p2 + (d) * (p3 + (d) * p4)); \ } #define BICUBIC_HEAD(type) \ @@ -966,7 +966,7 @@ affine_fixed( ysize = (int)imIn->ysize; /* use 16.16 fixed point arithmetics */ -#define FIX(v) FLOOR((v)*65536.0 + 0.5) +#define FIX(v) FLOOR((v) * 65536.0 + 0.5) a0 = FIX(a[0]); a1 = FIX(a[1]); diff --git a/src/libImaging/GetBBox.c b/src/libImaging/GetBBox.c index 86c687ca0a8..bd2a2778cdb 100644 --- a/src/libImaging/GetBBox.c +++ b/src/libImaging/GetBBox.c @@ -58,11 +58,11 @@ ImagingGetBBox(Imaging im, int bbox[4], int alpha_only) { INT32 mask = 0xffffffff; if (im->bands == 3) { ((UINT8 *)&mask)[3] = 0; - } else if (alpha_only && ( - strcmp(im->mode, "RGBa") == 0 || strcmp(im->mode, "RGBA") == 0 || - strcmp(im->mode, "La") == 0 || strcmp(im->mode, "LA") == 0 || - strcmp(im->mode, "PA") == 0 - )) { + } else if ( + alpha_only && + (strcmp(im->mode, "RGBa") == 0 || strcmp(im->mode, "RGBA") == 0 || + strcmp(im->mode, "La") == 0 || strcmp(im->mode, "LA") == 0 || + strcmp(im->mode, "PA") == 0)) { #ifdef WORDS_BIGENDIAN mask = 0x000000ff; #else diff --git a/src/libImaging/Gif.h b/src/libImaging/Gif.h index 5d7e2bdaa96..8edfbc2edc0 100644 --- a/src/libImaging/Gif.h +++ b/src/libImaging/Gif.h @@ -9,10 +9,10 @@ /* Max size for a LZW code word. */ -#define GIFBITS 12 +#define GIFBITS 12 -#define GIFTABLE (1<next_code = st->end_code + 1; st->max_code = 2 * st->clear_code - 1; st->code_width = st->bits + 1; memset(st->codes, 0, sizeof(st->codes)); } -static void glzwe_init(GIFENCODERSTATE *st) { +static void +glzwe_init(GIFENCODERSTATE *st) { st->clear_code = 1 << st->bits; st->end_code = st->clear_code + 1; glzwe_reset(st); @@ -64,156 +72,157 @@ static void glzwe_init(GIFENCODERSTATE *st) { st->code_buffer = 0; } -static int glzwe(GIFENCODERSTATE *st, const UINT8 *in_ptr, UINT8 *out_ptr, - UINT32 *in_avail, UINT32 *out_avail, - UINT32 end_of_data) { +static int +glzwe( + GIFENCODERSTATE *st, + const UINT8 *in_ptr, + UINT8 *out_ptr, + UINT32 *in_avail, + UINT32 *out_avail, + UINT32 end_of_data) { switch (st->entry_state) { - - case LZW_TRY_IN1: -get_first_byte: - if (!*in_avail) { - if (end_of_data) { - goto end_of_data; - } - st->entry_state = LZW_TRY_IN1; - return GLZW_NO_INPUT_AVAIL; - } - st->head = *in_ptr++; - (*in_avail)--; - - case LZW_TRY_IN2: -encode_loop: - if (!*in_avail) { - if (end_of_data) { - st->code = st->head; - st->put_state = PUT_LAST_HEAD; - goto put_code; + case LZW_TRY_IN1: + get_first_byte: + if (!*in_avail) { + if (end_of_data) { + goto end_of_data; + } + st->entry_state = LZW_TRY_IN1; + return GLZW_NO_INPUT_AVAIL; } - st->entry_state = LZW_TRY_IN2; - return GLZW_NO_INPUT_AVAIL; - } - st->tail = *in_ptr++; - (*in_avail)--; - - /* Knuth TAOCP vol 3 sec. 6.4 algorithm D. */ - /* Hash found experimentally to be pretty good. */ - /* This works ONLY with TABLE_SIZE a power of 2. */ - st->probe = ((st->head ^ (st->tail << 6)) * 31) & (TABLE_SIZE - 1); - while (st->codes[st->probe]) { - if ((st->codes[st->probe] & 0xFFFFF) == - ((st->head << 8) | st->tail)) { - st->head = st->codes[st->probe] >> 20; - goto encode_loop; - } else { - /* Reprobe decrement must be non-zero and relatively prime to table - * size. So, any odd positive number for power-of-2 size. */ - if ((st->probe -= ((st->tail << 2) | 1)) < 0) { - st->probe += TABLE_SIZE; + st->head = *in_ptr++; + (*in_avail)--; + + case LZW_TRY_IN2: + encode_loop: + if (!*in_avail) { + if (end_of_data) { + st->code = st->head; + st->put_state = PUT_LAST_HEAD; + goto put_code; } + st->entry_state = LZW_TRY_IN2; + return GLZW_NO_INPUT_AVAIL; } - } - /* Key not found, probe is at empty slot. */ - st->code = st->head; - st->put_state = PUT_HEAD; - goto put_code; -insert_code_or_clear: /* jump here after put_code */ - if (st->next_code < CODE_LIMIT) { - st->codes[st->probe] = (st->next_code << 20) | - (st->head << 8) | st->tail; - if (st->next_code > st->max_code) { - st->max_code = st->max_code * 2 + 1; - st->code_width++; + st->tail = *in_ptr++; + (*in_avail)--; + + /* Knuth TAOCP vol 3 sec. 6.4 algorithm D. */ + /* Hash found experimentally to be pretty good. */ + /* This works ONLY with TABLE_SIZE a power of 2. */ + st->probe = ((st->head ^ (st->tail << 6)) * 31) & (TABLE_SIZE - 1); + while (st->codes[st->probe]) { + if ((st->codes[st->probe] & 0xFFFFF) == ((st->head << 8) | st->tail)) { + st->head = st->codes[st->probe] >> 20; + goto encode_loop; + } else { + /* Reprobe decrement must be non-zero and relatively prime to table + * size. So, any odd positive number for power-of-2 size. */ + if ((st->probe -= ((st->tail << 2) | 1)) < 0) { + st->probe += TABLE_SIZE; + } + } } - st->next_code++; - } else { - st->code = st->clear_code; - st->put_state = PUT_CLEAR; + /* Key not found, probe is at empty slot. */ + st->code = st->head; + st->put_state = PUT_HEAD; goto put_code; -reset_after_clear: /* jump here after put_code */ - glzwe_reset(st); - } - st->head = st->tail; - goto encode_loop; - - case LZW_INITIAL: - glzwe_reset(st); - st->code = st->clear_code; - st->put_state = PUT_INIT_CLEAR; -put_code: - st->code_bits_left = st->code_width; -check_buf_bits: - if (!st->buf_bits_left) { /* out buffer full */ - - case LZW_TRY_OUT1: - if (!*out_avail) { - st->entry_state = LZW_TRY_OUT1; - return GLZW_NO_OUTPUT_AVAIL; + insert_code_or_clear: /* jump here after put_code */ + if (st->next_code < CODE_LIMIT) { + st->codes[st->probe] = + (st->next_code << 20) | (st->head << 8) | st->tail; + if (st->next_code > st->max_code) { + st->max_code = st->max_code * 2 + 1; + st->code_width++; + } + st->next_code++; + } else { + st->code = st->clear_code; + st->put_state = PUT_CLEAR; + goto put_code; + reset_after_clear: /* jump here after put_code */ + glzwe_reset(st); } - *out_ptr++ = st->code_buffer; - (*out_avail)--; - st->code_buffer = 0; - st->buf_bits_left = 8; - } - /* code bits to pack */ - UINT32 n = st->buf_bits_left < st->code_bits_left - ? st->buf_bits_left : st->code_bits_left; - st->code_buffer |= - (st->code & ((1 << n) - 1)) << (8 - st->buf_bits_left); - st->code >>= n; - st->buf_bits_left -= n; - st->code_bits_left -= n; - if (st->code_bits_left) { - goto check_buf_bits; - } - switch (st->put_state) { - case PUT_INIT_CLEAR: - goto get_first_byte; - case PUT_HEAD: - goto insert_code_or_clear; - case PUT_CLEAR: - goto reset_after_clear; - case PUT_LAST_HEAD: - goto end_of_data; - case PUT_END: - goto flush_code_buffer; - default: - return GLZW_INTERNAL_ERROR; - } + st->head = st->tail; + goto encode_loop; -end_of_data: - st->code = st->end_code; - st->put_state = PUT_END; - goto put_code; -flush_code_buffer: /* jump here after put_code */ - if (st->buf_bits_left < 8) { + case LZW_INITIAL: + glzwe_reset(st); + st->code = st->clear_code; + st->put_state = PUT_INIT_CLEAR; + put_code: + st->code_bits_left = st->code_width; + check_buf_bits: + if (!st->buf_bits_left) { /* out buffer full */ + + case LZW_TRY_OUT1: + if (!*out_avail) { + st->entry_state = LZW_TRY_OUT1; + return GLZW_NO_OUTPUT_AVAIL; + } + *out_ptr++ = st->code_buffer; + (*out_avail)--; + st->code_buffer = 0; + st->buf_bits_left = 8; + } + /* code bits to pack */ + UINT32 n = st->buf_bits_left < st->code_bits_left ? st->buf_bits_left + : st->code_bits_left; + st->code_buffer |= (st->code & ((1 << n) - 1)) << (8 - st->buf_bits_left); + st->code >>= n; + st->buf_bits_left -= n; + st->code_bits_left -= n; + if (st->code_bits_left) { + goto check_buf_bits; + } + switch (st->put_state) { + case PUT_INIT_CLEAR: + goto get_first_byte; + case PUT_HEAD: + goto insert_code_or_clear; + case PUT_CLEAR: + goto reset_after_clear; + case PUT_LAST_HEAD: + goto end_of_data; + case PUT_END: + goto flush_code_buffer; + default: + return GLZW_INTERNAL_ERROR; + } - case LZW_TRY_OUT2: - if (!*out_avail) { - st->entry_state = LZW_TRY_OUT2; - return GLZW_NO_OUTPUT_AVAIL; + end_of_data: + st->code = st->end_code; + st->put_state = PUT_END; + goto put_code; + flush_code_buffer: /* jump here after put_code */ + if (st->buf_bits_left < 8) { + case LZW_TRY_OUT2: + if (!*out_avail) { + st->entry_state = LZW_TRY_OUT2; + return GLZW_NO_OUTPUT_AVAIL; + } + *out_ptr++ = st->code_buffer; + (*out_avail)--; } - *out_ptr++ = st->code_buffer; - (*out_avail)--; - } - st->entry_state = LZW_FINISHED; - return GLZW_OK; + st->entry_state = LZW_FINISHED; + return GLZW_OK; - case LZW_FINISHED: - return GLZW_OK; + case LZW_FINISHED: + return GLZW_OK; - default: - return GLZW_INTERNAL_ERROR; + default: + return GLZW_INTERNAL_ERROR; } } /* -END- GIF LZW encoder. */ int -ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { - UINT8* ptr; - UINT8* sub_block_ptr; - UINT8* sub_block_limit; - UINT8* buf_limit; - GIFENCODERSTATE *context = (GIFENCODERSTATE*) state->context; +ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { + UINT8 *ptr; + UINT8 *sub_block_ptr; + UINT8 *sub_block_limit; + UINT8 *buf_limit; + GIFENCODERSTATE *context = (GIFENCODERSTATE *)state->context; int r; UINT32 in_avail, in_used; @@ -278,9 +287,9 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { return ptr - buf; } sub_block_ptr = ptr; - sub_block_limit = sub_block_ptr + - (256 < buf_limit - sub_block_ptr ? - 256 : buf_limit - sub_block_ptr); + sub_block_limit = + sub_block_ptr + + (256 < buf_limit - sub_block_ptr ? 256 : buf_limit - sub_block_ptr); *ptr++ = 0; } @@ -301,9 +310,9 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { /* get another line of data */ state->shuffle( state->buffer, - (UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->xsize - ); + (UINT8 *)im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + state->xsize); state->x = 0; /* step forward, according to the interlace settings */ @@ -331,10 +340,15 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { } } - in_avail = state->xsize - state->x; /* bytes left in line */ + in_avail = state->xsize - state->x; /* bytes left in line */ out_avail = sub_block_limit - ptr; /* bytes left in sub-block */ - r = glzwe(context, &state->buffer[state->x], ptr, &in_avail, - &out_avail, state->state == FINISH); + r = glzwe( + context, + &state->buffer[state->x], + ptr, + &in_avail, + &out_avail, + state->state == FINISH); out_used = sub_block_limit - ptr - out_avail; *sub_block_ptr += out_used; ptr += out_used; diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index afcd2229bde..1f2c03e934f 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -108,15 +108,15 @@ struct ImagingMemoryInstance { #define IMAGING_PIXEL_1(im, x, y) ((im)->image8[(y)][(x)]) #define IMAGING_PIXEL_L(im, x, y) ((im)->image8[(y)][(x)]) -#define IMAGING_PIXEL_LA(im, x, y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_LA(im, x, y) ((im)->image[(y)][(x) * 4]) #define IMAGING_PIXEL_P(im, x, y) ((im)->image8[(y)][(x)]) -#define IMAGING_PIXEL_PA(im, x, y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_PA(im, x, y) ((im)->image[(y)][(x) * 4]) #define IMAGING_PIXEL_I(im, x, y) ((im)->image32[(y)][(x)]) #define IMAGING_PIXEL_F(im, x, y) (((FLOAT32 *)(im)->image32[y])[x]) -#define IMAGING_PIXEL_RGB(im, x, y) ((im)->image[(y)][(x)*4]) -#define IMAGING_PIXEL_RGBA(im, x, y) ((im)->image[(y)][(x)*4]) -#define IMAGING_PIXEL_CMYK(im, x, y) ((im)->image[(y)][(x)*4]) -#define IMAGING_PIXEL_YCbCr(im, x, y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_RGB(im, x, y) ((im)->image[(y)][(x) * 4]) +#define IMAGING_PIXEL_RGBA(im, x, y) ((im)->image[(y)][(x) * 4]) +#define IMAGING_PIXEL_CMYK(im, x, y) ((im)->image[(y)][(x) * 4]) +#define IMAGING_PIXEL_YCbCr(im, x, y) ((im)->image[(y)][(x) * 4]) #define IMAGING_PIXEL_UINT8(im, x, y) ((im)->image8[(y)][(x)]) #define IMAGING_PIXEL_INT32(im, x, y) ((im)->image32[(y)][(x)]) @@ -161,7 +161,7 @@ typedef struct ImagingMemoryArena { int stats_reallocated_blocks; /* Number of blocks which were actually reallocated after retrieving */ int stats_freed_blocks; /* Number of freed blocks */ -} * ImagingMemoryArena; +} *ImagingMemoryArena; /* Objects */ /* ------- */ @@ -309,7 +309,8 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn); extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn); extern Imaging -ImagingGaussianBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int passes); +ImagingGaussianBlur( + Imaging imOut, Imaging imIn, float xradius, float yradius, int passes); extern Imaging ImagingGetBand(Imaging im, int band); extern Imaging @@ -487,7 +488,8 @@ ImagingDrawPieslice( extern int ImagingDrawPoint(Imaging im, int x, int y, const void *ink, int op); extern int -ImagingDrawPolygon(Imaging im, int points, int *xy, const void *ink, int fill, int width, int op); +ImagingDrawPolygon( + Imaging im, int points, int *xy, const void *ink, int fill, int width, int op); extern int ImagingDrawRectangle( Imaging im, diff --git a/src/libImaging/ImagingUtils.h b/src/libImaging/ImagingUtils.h index 0c0c1eda917..714458ad02a 100644 --- a/src/libImaging/ImagingUtils.h +++ b/src/libImaging/ImagingUtils.h @@ -21,7 +21,7 @@ #define DIV255(a, tmp) (tmp = (a) + 128, SHIFTFORDIV255(tmp)) -#define BLEND(mask, in1, in2, tmp1) DIV255(in1 *(255 - mask) + in2 * mask, tmp1) +#define BLEND(mask, in1, in2, tmp1) DIV255(in1 * (255 - mask) + in2 * mask, tmp1) #define PREBLEND(mask, in1, in2, tmp1) (MULDIV255(in1, (255 - mask), tmp1) + in2) diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c index 78a09bb8333..dd066c10b7a 100644 --- a/src/libImaging/Jpeg2KDecode.c +++ b/src/libImaging/Jpeg2KDecode.c @@ -183,9 +183,9 @@ j2ku_gray_i( UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; for (x = 0; x < w; ++x) { UINT16 pixel = j2ku_shift(offset + *data++, shift); - #ifdef WORDS_BIGENDIAN - pixel = (pixel >> 8) | (pixel << 8); - #endif +#ifdef WORDS_BIGENDIAN + pixel = (pixel >> 8) | (pixel << 8); +#endif *row++ = pixel; } } @@ -778,7 +778,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) { color_space = OPJ_CLRSPC_SYCC; break; } - break; + break; } } @@ -864,7 +864,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) { a, and then a malicious file could have a smaller tile_bytes */ - for (n=0; n < tile_info.nb_comps; n++) { + for (n = 0; n < tile_info.nb_comps; n++) { // see csize /acsize calcs int csize = (image->comps[n].prec + 7) >> 3; csize = (csize == 3) ? 4 : csize; diff --git a/src/libImaging/Jpeg2KEncode.c b/src/libImaging/Jpeg2KEncode.c index 3295373fd64..7f1aeaddb9b 100644 --- a/src/libImaging/Jpeg2KEncode.c +++ b/src/libImaging/Jpeg2KEncode.c @@ -383,8 +383,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) { float *pq; if (len > 0) { - if ((size_t)len > - sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) { + if ((size_t)len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) { len = sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0]); } @@ -464,7 +463,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) { } if (!context->num_resolutions) { - while (tile_width < (1U << (params.numresolution - 1U)) || tile_height < (1U << (params.numresolution - 1U))) { + while (tile_width < (1U << (params.numresolution - 1U)) || + tile_height < (1U << (params.numresolution - 1U))) { params.numresolution -= 1; } } diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 00f3d5f74db..bcbe65aa4d6 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -145,8 +145,8 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { case JCS_EXT_RGBX: #endif switch (context->subsampling) { - case -1: /* Default */ - case 0: /* No subsampling */ + case -1: /* Default */ + case 0: /* No subsampling */ break; default: /* Would subsample the green and blue @@ -305,7 +305,11 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { case 4: if (context->comment) { - jpeg_write_marker(&context->cinfo, JPEG_COM, (unsigned char *)context->comment, context->comment_size); + jpeg_write_marker( + &context->cinfo, + JPEG_COM, + (unsigned char *)context->comment, + context->comment_size); } state->state++; @@ -342,7 +346,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { } jpeg_finish_compress(&context->cinfo); -cleanup: + cleanup: /* Clean up */ if (context->comment) { free(context->comment); diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c index 6684b11efe3..a018225b26e 100644 --- a/src/libImaging/Paste.c +++ b/src/libImaging/Paste.c @@ -432,11 +432,10 @@ fill_mask_L( } } else { - int alpha_channel = strcmp(imOut->mode, "RGBa") == 0 || - strcmp(imOut->mode, "RGBA") == 0 || - strcmp(imOut->mode, "La") == 0 || - strcmp(imOut->mode, "LA") == 0 || - strcmp(imOut->mode, "PA") == 0; + int alpha_channel = + strcmp(imOut->mode, "RGBa") == 0 || strcmp(imOut->mode, "RGBA") == 0 || + strcmp(imOut->mode, "La") == 0 || strcmp(imOut->mode, "LA") == 0 || + strcmp(imOut->mode, "PA") == 0; for (y = 0; y < ysize; y++) { UINT8 *out = (UINT8 *)imOut->image[y + dy] + dx * pixelsize; UINT8 *mask = (UINT8 *)imMask->image[y + sy] + sx; diff --git a/src/libImaging/Point.c b/src/libImaging/Point.c index 8883578cbff..dd06f3940d1 100644 --- a/src/libImaging/Point.c +++ b/src/libImaging/Point.c @@ -134,7 +134,7 @@ ImagingPoint(Imaging imIn, const char *mode, const void *table) { ImagingSectionCookie cookie; Imaging imOut; im_point_context context; - void (*point)(Imaging imIn, Imaging imOut, im_point_context * context); + void (*point)(Imaging imIn, Imaging imOut, im_point_context *context); if (!imIn) { return (Imaging)ImagingError_ModeError(); diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 2582830c4a4..cdc614536da 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -260,8 +260,7 @@ mergesort_pixels(PixelList *head, int i) { return head; } for (c = t = head; c && t; - c = c->next[i], t = (t->next[i]) ? t->next[i]->next[i] : NULL) - ; + c = c->next[i], t = (t->next[i]) ? t->next[i]->next[i] : NULL); if (c) { if (c->prev[i]) { c->prev[i]->next[i] = NULL; @@ -354,12 +353,10 @@ splitlists( for (_i = 0; _i < 3; _i++) { for (_nextCount[_i] = 0, _nextTest = h[_i]; _nextTest && _nextTest->next[_i]; - _nextTest = _nextTest->next[_i], _nextCount[_i]++) - ; + _nextTest = _nextTest->next[_i], _nextCount[_i]++); for (_prevCount[_i] = 0, _prevTest = t[_i]; _prevTest && _prevTest->prev[_i]; - _prevTest = _prevTest->prev[_i], _prevCount[_i]++) - ; + _prevTest = _prevTest->prev[_i], _prevCount[_i]++); if (_nextTest != t[_i]) { printf("next-list of axis %d does not end at tail\n", _i); exit(1); @@ -368,10 +365,8 @@ splitlists( printf("prev-list of axis %d does not end at head\n", _i); exit(1); } - for (; _nextTest && _nextTest->prev[_i]; _nextTest = _nextTest->prev[_i]) - ; - for (; _prevTest && _prevTest->next[_i]; _prevTest = _prevTest->next[_i]) - ; + for (; _nextTest && _nextTest->prev[_i]; _nextTest = _nextTest->prev[_i]); + for (; _prevTest && _prevTest->next[_i]; _prevTest = _prevTest->next[_i]); if (_nextTest != h[_i]) { printf("next-list of axis %d does not loop back to head\n", _i); exit(1); @@ -548,22 +543,18 @@ split(BoxNode *node) { for (_i = 0; _i < 3; _i++) { for (_nextCount[_i] = 0, _nextTest = node->head[_i]; _nextTest && _nextTest->next[_i]; - _nextTest = _nextTest->next[_i], _nextCount[_i]++) - ; + _nextTest = _nextTest->next[_i], _nextCount[_i]++); for (_prevCount[_i] = 0, _prevTest = node->tail[_i]; _prevTest && _prevTest->prev[_i]; - _prevTest = _prevTest->prev[_i], _prevCount[_i]++) - ; + _prevTest = _prevTest->prev[_i], _prevCount[_i]++); if (_nextTest != node->tail[_i]) { printf("next-list of axis %d does not end at tail\n", _i); } if (_prevTest != node->head[_i]) { printf("prev-list of axis %d does not end at head\n", _i); } - for (; _nextTest && _nextTest->prev[_i]; _nextTest = _nextTest->prev[_i]) - ; - for (; _prevTest && _prevTest->next[_i]; _prevTest = _prevTest->next[_i]) - ; + for (; _nextTest && _nextTest->prev[_i]; _nextTest = _nextTest->prev[_i]); + for (; _prevTest && _prevTest->next[_i]; _prevTest = _prevTest->next[_i]); if (_nextTest != node->head[_i]) { printf("next-list of axis %d does not loop back to head\n", _i); } @@ -668,8 +659,7 @@ median_cut(PixelList *hl[3], uint32_t imPixelCount, int nPixels) { return NULL; } for (i = 0; i < 3; i++) { - for (tl[i] = hl[i]; tl[i] && tl[i]->next[i]; tl[i] = tl[i]->next[i]) - ; + for (tl[i] = hl[i]; tl[i] && tl[i]->next[i]; tl[i] = tl[i]->next[i]); root->head[i] = hl[i]; root->tail[i] = tl[i]; } @@ -832,16 +822,9 @@ build_distance_tables( } for (i = 0; i < nEntries; i++) { for (j = 0; j < nEntries; j++) { - dwi[j] = (DistanceWithIndex){ - &(avgDist[i * nEntries + j]), - j - }; + dwi[j] = (DistanceWithIndex){&(avgDist[i * nEntries + j]), j}; } - qsort( - dwi, - nEntries, - sizeof(DistanceWithIndex), - _distance_index_cmp); + qsort(dwi, nEntries, sizeof(DistanceWithIndex), _distance_index_cmp); for (j = 0; j < nEntries; j++) { avgDistSortKey[i * nEntries + j] = dwi[j].distance; } @@ -1213,7 +1196,7 @@ k_means( compute_palette_from_quantized_pixels( pixelData, nPixels, paletteData, nPaletteEntries, avg, count, qp); if (!build_distance_tables( - avgDist, avgDistSortKey, paletteData, nPaletteEntries)) { + avgDist, avgDistSortKey, paletteData, nPaletteEntries)) { goto error_3; } built = 1; @@ -1452,15 +1435,17 @@ quantize( hashtable_insert(h2, pixelData[i], bestmatch); } if (qp[i] != bestmatch) { - printf ("discrepancy in matching algorithms pixel %d [%d %d] %f %f\n", - i,qp[i],bestmatch, - sqrt((double)(_SQR(pixelData[i].c.r-p[qp[i]].c.r)+ - _SQR(pixelData[i].c.g-p[qp[i]].c.g)+ - _SQR(pixelData[i].c.b-p[qp[i]].c.b))), - sqrt((double)(_SQR(pixelData[i].c.r-p[bestmatch].c.r)+ - _SQR(pixelData[i].c.g-p[bestmatch].c.g)+ - _SQR(pixelData[i].c.b-p[bestmatch].c.b))) - ); + printf( + "discrepancy in matching algorithms pixel %d [%d %d] %f %f\n", + i, + qp[i], + bestmatch, + sqrt((double)(_SQR(pixelData[i].c.r - p[qp[i]].c.r) + + _SQR(pixelData[i].c.g - p[qp[i]].c.g) + + _SQR(pixelData[i].c.b - p[qp[i]].c.b))), + sqrt((double)(_SQR(pixelData[i].c.r - p[bestmatch].c.r) + + _SQR(pixelData[i].c.g - p[bestmatch].c.g) + + _SQR(pixelData[i].c.b - p[bestmatch].c.b)))); } } hashtable_free(h2); diff --git a/src/libImaging/QuantOctree.c b/src/libImaging/QuantOctree.c index 5e79bce358a..1331a30ad45 100644 --- a/src/libImaging/QuantOctree.c +++ b/src/libImaging/QuantOctree.c @@ -38,7 +38,7 @@ typedef struct _ColorBucket { uint64_t g; uint64_t b; uint64_t a; -} * ColorBucket; +} *ColorBucket; typedef struct _ColorCube { unsigned int rBits, gBits, bBits, aBits; @@ -47,7 +47,7 @@ typedef struct _ColorCube { unsigned long size; ColorBucket buckets; -} * ColorCube; +} *ColorCube; #define MAX(a, b) (a) > (b) ? (a) : (b) diff --git a/src/libImaging/Reduce.c b/src/libImaging/Reduce.c index 60928d2bc36..61566f0c506 100644 --- a/src/libImaging/Reduce.c +++ b/src/libImaging/Reduce.c @@ -2,7 +2,7 @@ #include -#define ROUND_UP(f) ((int)((f) >= 0.0 ? (f) + 0.5F : (f)-0.5F)) +#define ROUND_UP(f) ((int)((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F)) UINT32 division_UINT32(int divider, int result_bits) { diff --git a/src/libImaging/Resample.c b/src/libImaging/Resample.c index cf79d8a4e4d..59c27b3f421 100644 --- a/src/libImaging/Resample.c +++ b/src/libImaging/Resample.c @@ -2,7 +2,7 @@ #include -#define ROUND_UP(f) ((int)((f) >= 0.0 ? (f) + 0.5F : (f)-0.5F)) +#define ROUND_UP(f) ((int)((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F)) struct filter { double (*filter)(double x); diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c index 4eef44ba510..89dedb5252f 100644 --- a/src/libImaging/SgiRleDecode.c +++ b/src/libImaging/SgiRleDecode.c @@ -113,7 +113,8 @@ expandrow(UINT8 *dest, UINT8 *src, int n, int z, int xsize, UINT8 *end_of_buffer } static int -expandrow2(UINT8 *dest, const UINT8 *src, int n, int z, int xsize, UINT8 *end_of_buffer) { +expandrow2( + UINT8 *dest, const UINT8 *src, int n, int z, int xsize, UINT8 *end_of_buffer) { UINT8 pixel, count; int x = 0; @@ -197,7 +198,6 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t return -1; } - /* decoder initialization */ state->count = 0; state->y = 0; @@ -252,7 +252,7 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t c->rlelength, im->bands, im->xsize, - &ptr[c->bufsize-1]); + &ptr[c->bufsize - 1]); } else { status = expandrow2( &state->buffer[c->channo * 2], @@ -260,7 +260,7 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t c->rlelength, im->bands, im->xsize, - &ptr[c->bufsize-1]); + &ptr[c->bufsize - 1]); } if (status == -1) { state->errcode = IMAGING_CODEC_OVERRUN; @@ -268,7 +268,6 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t } else if (status == 1) { goto sgi_finish_decode; } - } /* store decompressed data in image */ diff --git a/src/libImaging/Storage.c b/src/libImaging/Storage.c index b1b03c515ad..b27195a3587 100644 --- a/src/libImaging/Storage.c +++ b/src/libImaging/Storage.c @@ -418,9 +418,8 @@ ImagingAllocateArray(Imaging im, int dirty, int block_size) { } im->blocks[current_block] = block; /* Bulletproof code from libc _int_memalign */ - aligned_ptr = (char *)( - ((size_t) (block.ptr + arena->alignment - 1)) & - -((Py_ssize_t) arena->alignment)); + aligned_ptr = (char *)(((size_t)(block.ptr + arena->alignment - 1)) & + -((Py_ssize_t)arena->alignment)); } im->image[y] = aligned_ptr + aligned_linesize * line_in_block; diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index e3b81590ec2..858de9332f5 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -60,7 +60,11 @@ _tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) { dump_state(state); if (state->loc > state->eof) { - TIFFError("_tiffReadProc", "Invalid Read at loc %" PRIu64 ", eof: %" PRIu64, state->loc, state->eof); + TIFFError( + "_tiffReadProc", + "Invalid Read at loc %" PRIu64 ", eof: %" PRIu64, + state->loc, + state->eof); return 0; } to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc); @@ -217,7 +221,12 @@ ImagingLibTiffInit(ImagingCodecState state, int fp, uint32_t offset) { } int -_pickUnpackers(Imaging im, ImagingCodecState state, TIFF *tiff, uint16_t planarconfig, ImagingShuffler *unpackers) { +_pickUnpackers( + Imaging im, + ImagingCodecState state, + TIFF *tiff, + uint16_t planarconfig, + ImagingShuffler *unpackers) { // if number of bands is 1, there is no difference with contig case if (planarconfig == PLANARCONFIG_SEPARATE && im->bands > 1) { uint16_t bits_per_sample = 8; @@ -232,10 +241,14 @@ _pickUnpackers(Imaging im, ImagingCodecState state, TIFF *tiff, uint16_t planarc // We'll pick appropriate set of unpackers depending on planar_configuration // It does not matter if data is RGB(A), CMYK or LUV really, // we just copy it plane by plane - unpackers[0] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "R;16N" : "R", NULL); - unpackers[1] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "G;16N" : "G", NULL); - unpackers[2] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "B;16N" : "B", NULL); - unpackers[3] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "A;16N" : "A", NULL); + unpackers[0] = + ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "R;16N" : "R", NULL); + unpackers[1] = + ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "G;16N" : "G", NULL); + unpackers[2] = + ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "B;16N" : "B", NULL); + unpackers[3] = + ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "A;16N" : "A", NULL); return im->bands; } else { @@ -247,10 +260,10 @@ _pickUnpackers(Imaging im, ImagingCodecState state, TIFF *tiff, uint16_t planarc int _decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) { - // To avoid dealing with YCbCr subsampling and other complications, let libtiff handle it - // Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle - // all of the conversion. Metadata read from the TIFFRGBAImage could - // be different from the metadata that the base tiff returns. + // To avoid dealing with YCbCr subsampling and other complications, let libtiff + // handle it Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle all + // of the conversion. Metadata read from the TIFFRGBAImage could be different from + // the metadata that the base tiff returns. INT32 current_row; UINT8 *new_data; @@ -259,17 +272,16 @@ _decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) { TIFFRGBAImage img; char emsg[1024] = ""; - // Since using TIFFRGBAImage* functions, we can read whole tiff into rastrr in one call - // Let's select smaller block size. Multiplying image width by (tile length OR rows per strip) - // gives us manageable block size in pixels + // Since using TIFFRGBAImage* functions, we can read whole tiff into rastrr in one + // call Let's select smaller block size. Multiplying image width by (tile length OR + // rows per strip) gives us manageable block size in pixels if (TIFFIsTiled(tiff)) { ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_TILELENGTH, &rows_per_block); - } - else { + } else { ret = TIFFGetFieldDefaulted(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_block); } - if (ret != 1 || rows_per_block==(UINT32)(-1)) { + if (ret != 1 || rows_per_block == (UINT32)(-1)) { rows_per_block = state->ysize; } @@ -357,7 +369,12 @@ _decodeAsRGBA(Imaging im, ImagingCodecState state, TIFF *tiff) { } int -_decodeTile(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, ImagingShuffler *unpackers) { +_decodeTile( + Imaging im, + ImagingCodecState state, + TIFF *tiff, + int planes, + ImagingShuffler *unpackers) { INT32 x, y, tile_y, current_tile_length, current_tile_width; UINT32 tile_width, tile_length; tsize_t tile_bytes_size, row_byte_size; @@ -396,7 +413,8 @@ _decodeTile(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imaging if (tile_bytes_size > ((tile_length * state->bits / planes + 7) / 8) * tile_width) { // If the tile size as expected by LibTiff isn't what we're expecting, abort. - // man: TIFFTileSize returns the equivalent size for a tile of data as it would be returned in a + // man: TIFFTileSize returns the equivalent size for a tile of data as it + // would be returned in a // call to TIFFReadTile ... state->errcode = IMAGING_CODEC_BROKEN; return -1; @@ -428,19 +446,24 @@ _decodeTile(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imaging TRACE(("Read tile at %dx%d; \n\n", x, y)); - current_tile_width = min((INT32) tile_width, state->xsize - x); - current_tile_length = min((INT32) tile_length, state->ysize - y); + current_tile_width = min((INT32)tile_width, state->xsize - x); + current_tile_length = min((INT32)tile_length, state->ysize - y); // iterate over each line in the tile and stuff data into image for (tile_y = 0; tile_y < current_tile_length; tile_y++) { - TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width)); + TRACE( + ("Writing tile data at %dx%d using tile_width: %d; \n", + tile_y + y, + x, + current_tile_width)); // UINT8 * bbb = state->buffer + tile_y * row_byte_size; - // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); + // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], + // ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); - shuffler((UINT8*) im->image[tile_y + y] + x * im->pixelsize, - state->buffer + tile_y * row_byte_size, - current_tile_width - ); + shuffler( + (UINT8 *)im->image[tile_y + y] + x * im->pixelsize, + state->buffer + tile_y * row_byte_size, + current_tile_width); } } } @@ -450,7 +473,12 @@ _decodeTile(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imaging } int -_decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, ImagingShuffler *unpackers) { +_decodeStrip( + Imaging im, + ImagingCodecState state, + TIFF *tiff, + int planes, + ImagingShuffler *unpackers) { INT32 strip_row = 0; UINT8 *new_data; UINT32 rows_per_strip; @@ -458,7 +486,7 @@ _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imagin tsize_t strip_size, row_byte_size, unpacker_row_byte_size; ret = TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip); - if (ret != 1 || rows_per_strip==(UINT32)(-1)) { + if (ret != 1 || rows_per_strip == (UINT32)(-1)) { rows_per_strip = state->ysize; } @@ -478,7 +506,8 @@ _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imagin unpacker_row_byte_size = (state->xsize * state->bits / planes + 7) / 8; if (strip_size > (unpacker_row_byte_size * rows_per_strip)) { // If the strip size as expected by LibTiff isn't what we're expecting, abort. - // man: TIFFStripSize returns the equivalent size for a strip of data as it would be returned in a + // man: TIFFStripSize returns the equivalent size for a strip of data as it + // would be returned in a // call to TIFFReadEncodedStrip ... state->errcode = IMAGING_CODEC_BROKEN; return -1; @@ -513,8 +542,13 @@ _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imagin int plane; for (plane = 0; plane < planes; plane++) { ImagingShuffler shuffler = unpackers[plane]; - if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, plane), (tdata_t)state->buffer, strip_size) == -1) { - TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))); + if (TIFFReadEncodedStrip( + tiff, + TIFFComputeStrip(tiff, state->y, plane), + (tdata_t)state->buffer, + strip_size) == -1) { + TRACE( + ("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))); state->errcode = IMAGING_CODEC_BROKEN; return -1; } @@ -523,16 +557,17 @@ _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, int planes, Imagin // iterate over each row in the strip and stuff data into image for (strip_row = 0; - strip_row < min((INT32) rows_per_strip, state->ysize - state->y); + strip_row < min((INT32)rows_per_strip, state->ysize - state->y); strip_row++) { TRACE(("Writing data into line %d ; \n", state->y + strip_row)); - // UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip); - // TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); + // UINT8 * bbb = state->buffer + strip_row * (state->bytes / + // rows_per_strip); TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], + // ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); shuffler( - (UINT8*) im->image[state->y + state->yoff + strip_row] + - state->xoff * im->pixelsize, + (UINT8 *)im->image[state->y + state->yoff + strip_row] + + state->xoff * im->pixelsize, state->buffer + strip_row * row_byte_size, state->xsize); } @@ -666,7 +701,6 @@ ImagingLibTiffDecode( goto decode_err; } - TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression); TIFFGetFieldDefaulted(tiff, TIFFTAG_PLANARCONFIG, &planarconfig); @@ -675,16 +709,17 @@ ImagingLibTiffDecode( // Let LibTiff read them as RGBA readAsRGBA = photometric == PHOTOMETRIC_YCBCR; - if (readAsRGBA && compression == COMPRESSION_JPEG && planarconfig == PLANARCONFIG_CONTIG) { - // If using new JPEG compression, let libjpeg do RGB conversion for performance reasons + if (readAsRGBA && compression == COMPRESSION_JPEG && + planarconfig == PLANARCONFIG_CONTIG) { + // If using new JPEG compression, let libjpeg do RGB conversion for performance + // reasons TIFFSetField(tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); readAsRGBA = 0; } if (readAsRGBA) { _decodeAsRGBA(im, state, tiff); - } - else { + } else { planes = _pickUnpackers(im, state, tiff, planarconfig, unpackers); if (planes <= 0) { goto decode_err; @@ -692,8 +727,7 @@ ImagingLibTiffDecode( if (TIFFIsTiled(tiff)) { _decodeTile(im, state, tiff, planes, unpackers); - } - else { + } else { _decodeStrip(im, state, tiff, planes, unpackers); } @@ -702,20 +736,20 @@ ImagingLibTiffDecode( // so we have to convert it to RGBA if (planes > 3 && strcmp(im->mode, "RGBA") == 0) { uint16_t extrasamples; - uint16_t* sampleinfo; + uint16_t *sampleinfo; ImagingShuffler shuffle; INT32 y; - TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); + TIFFGetFieldDefaulted( + tiff, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); - if (extrasamples >= 1 && - (sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED || sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA) - ) { + if (extrasamples >= 1 && (sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED || + sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA)) { shuffle = ImagingFindUnpacker("RGBA", "RGBa", NULL); for (y = state->yoff; y < state->ysize; y++) { - UINT8* ptr = (UINT8*) im->image[y + state->yoff] + - state->xoff * im->pixelsize; + UINT8 *ptr = (UINT8 *)im->image[y + state->yoff] + + state->xoff * im->pixelsize; shuffle(ptr, ptr, state->xsize); } } @@ -723,7 +757,7 @@ ImagingLibTiffDecode( } } - decode_err: +decode_err: // TIFFClose in libtiff calls tif_closeproc and TIFFCleanup if (clientstate->fp) { // Pillow will manage the closing of the file rather than libtiff diff --git a/src/libImaging/TiffDecode.h b/src/libImaging/TiffDecode.h index 02454ba0396..212b7dee6b0 100644 --- a/src/libImaging/TiffDecode.h +++ b/src/libImaging/TiffDecode.h @@ -30,7 +30,7 @@ typedef struct { * Should be uint32 for libtiff 3.9.x * uint64 for libtiff 4.0.x */ - TIFF *tiff; /* Used in write */ + TIFF *tiff; /* Used in write */ toff_t eof; int flrealloc; /* may we realloc */ } TIFFSTATE; diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index e351aa2f19b..1b84cd68f85 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1437,90 +1437,90 @@ band3I(UINT8 *out, const UINT8 *in, int pixels) { } static void -band016B(UINT8* out, const UINT8* in, int pixels) -{ +band016B(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 0 only, big endian */ for (i = 0; i < pixels; i++) { out[0] = in[0]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band116B(UINT8* out, const UINT8* in, int pixels) -{ +band116B(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 1 only, big endian */ for (i = 0; i < pixels; i++) { out[1] = in[0]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band216B(UINT8* out, const UINT8* in, int pixels) -{ +band216B(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 2 only, big endian */ for (i = 0; i < pixels; i++) { out[2] = in[0]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band316B(UINT8* out, const UINT8* in, int pixels) -{ +band316B(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 3 only, big endian */ for (i = 0; i < pixels; i++) { out[3] = in[0]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band016L(UINT8* out, const UINT8* in, int pixels) -{ +band016L(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 0 only, little endian */ for (i = 0; i < pixels; i++) { out[0] = in[1]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band116L(UINT8* out, const UINT8* in, int pixels) -{ +band116L(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 1 only, little endian */ for (i = 0; i < pixels; i++) { out[1] = in[1]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band216L(UINT8* out, const UINT8* in, int pixels) -{ +band216L(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 2 only, little endian */ for (i = 0; i < pixels; i++) { out[2] = in[1]; - out += 4; in += 2; + out += 4; + in += 2; } } static void -band316L(UINT8* out, const UINT8* in, int pixels) -{ +band316L(UINT8 *out, const UINT8 *in, int pixels) { int i; /* band 3 only, little endian */ for (i = 0; i < pixels; i++) { out[3] = in[1]; - out += 4; in += 2; + out += 4; + in += 2; } } @@ -1687,7 +1687,6 @@ static struct { {"RGB", "G;16N", 16, band116L}, {"RGB", "B;16N", 16, band216L}, - {"RGBA", "R;16N", 16, band016L}, {"RGBA", "G;16N", 16, band116L}, {"RGBA", "B;16N", 16, band216L}, diff --git a/src/path.c b/src/path.c index cc0698c4d2a..6bc90abed86 100644 --- a/src/path.c +++ b/src/path.c @@ -162,24 +162,24 @@ PyPath_Flatten(PyObject *data, double **pxy) { return -1; } -#define assign_item_to_array(op, decref) \ -if (PyFloat_Check(op)) { \ - xy[j++] = PyFloat_AS_DOUBLE(op); \ -} else if (PyLong_Check(op)) { \ - xy[j++] = (float)PyLong_AS_LONG(op); \ -} else if (PyNumber_Check(op)) { \ - xy[j++] = PyFloat_AsDouble(op); \ -} else if (PyArg_ParseTuple(op, "dd", &x, &y)) { \ - xy[j++] = x; \ - xy[j++] = y; \ -} else { \ - PyErr_SetString(PyExc_ValueError, "incorrect coordinate type"); \ - if (decref) { \ - Py_DECREF(op); \ - } \ - free(xy); \ - return -1; \ -} +#define assign_item_to_array(op, decref) \ + if (PyFloat_Check(op)) { \ + xy[j++] = PyFloat_AS_DOUBLE(op); \ + } else if (PyLong_Check(op)) { \ + xy[j++] = (float)PyLong_AS_LONG(op); \ + } else if (PyNumber_Check(op)) { \ + xy[j++] = PyFloat_AsDouble(op); \ + } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { \ + xy[j++] = x; \ + xy[j++] = y; \ + } else { \ + PyErr_SetString(PyExc_ValueError, "incorrect coordinate type"); \ + if (decref) { \ + Py_DECREF(op); \ + } \ + free(xy); \ + return -1; \ + } /* Copy table to path array */ if (PyList_Check(data)) { From 0099de0ed904c2021850fbb5a19908c36f908890 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:00:14 +0300 Subject: [PATCH 112/300] Add deprecation helper for Image.new with BGR; modes --- Tests/test_image.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index ed80be503fb..e8339424d99 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -63,14 +63,19 @@ image_mode_names = [name for name, _ in image_modes] +# Deprecation helper +def helper_image_new(mode: str, size: tuple[int, int]) -> Image.Image: + if mode.startswith("BGR;"): + with pytest.warns(DeprecationWarning): + return Image.new(mode, size) + else: + return Image.new(mode, size) + + class TestImage: @pytest.mark.parametrize("mode", image_mode_names) def test_image_modes_success(self, mode: str) -> None: - if mode.startswith("BGR;"): - with pytest.warns(DeprecationWarning): - Image.new(mode, (1, 1)) - else: - Image.new(mode, (1, 1)) + helper_image_new(mode, (1, 1)) @pytest.mark.parametrize("mode", ("", "bad", "very very long")) def test_image_modes_fail(self, mode: str) -> None: @@ -1066,29 +1071,17 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() - if mode.startswith("BGR;"): - with pytest.warns(DeprecationWarning): - reloaded = Image.new(mode, im.size) - else: - reloaded = Image.new(mode, im.size) + reloaded = helper_image_new(mode, im.size) reloaded.frombytes(source_bytes) assert reloaded.tobytes() == source_bytes @pytest.mark.parametrize(("mode", "pixelsize"), image_modes) def test_getdata_putdata(self, mode: str, pixelsize: int) -> None: - if mode.startswith("BGR;"): - with pytest.warns(DeprecationWarning): - im = Image.new(mode, (2, 2)) - else: - im = Image.new(mode, (2, 2)) + im = helper_image_new(mode, (2, 2)) source_bytes = bytes(range(im.width * im.height * pixelsize)) im.frombytes(source_bytes) - if mode.startswith("BGR;"): - with pytest.warns(DeprecationWarning): - reloaded = Image.new(mode, im.size) - else: - reloaded = Image.new(mode, im.size) + reloaded = helper_image_new(mode, im.size) reloaded.putdata(im.getdata()) assert_image_equal(im, reloaded) From a4080a72494042183cff6fbadeba3026fb6fa711 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 25 Apr 2024 08:51:33 -0500 Subject: [PATCH 113/300] clean up comments in test_image_access.py --- Tests/test_image_access.py | 40 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 50afb2a2368..2cd2e0c34e5 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -33,7 +33,7 @@ class AccessTest: - # initial value + # Initial value _init_cffi_access = Image.USE_CFFI_ACCESS _need_cffi_access = False @@ -151,7 +151,7 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: self.color(mode) if expected_color_int is None else expected_color_int ) - # check putpixel + # Check putpixel im = Image.new(mode, (1, 1), None) im.putpixel((0, 0), expected_color) actual_color = im.getpixel((0, 0)) @@ -160,7 +160,7 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: f"expected {expected_color} got {actual_color}" ) - # check putpixel negative index + # Check putpixel negative index im.putpixel((-1, -1), expected_color) actual_color = im.getpixel((-1, -1)) assert actual_color == expected_color, ( @@ -168,7 +168,7 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: f"expected {expected_color} got {actual_color}" ) - # check 0x0 image with None initial color + # Check 0x0 image with None initial color im = Image.new(mode, (0, 0), None) assert im.load() is not None error = ValueError if self._need_cffi_access else IndexError @@ -176,13 +176,13 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: im.putpixel((0, 0), expected_color) with pytest.raises(error): im.getpixel((0, 0)) - # check negative index + # Check negative index with pytest.raises(error): im.putpixel((-1, -1), expected_color) with pytest.raises(error): im.getpixel((-1, -1)) - # check initial color + # Check initial color im = Image.new(mode, (1, 1), expected_color) actual_color = im.getpixel((0, 0)) assert actual_color == expected_color, ( @@ -190,18 +190,18 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: f"expected {expected_color} got {actual_color}" ) - # check initial color negative index + # Check initial color negative index actual_color = im.getpixel((-1, -1)) assert actual_color == expected_color, ( f"initial color failed with negative index for mode {mode}, " f"expected {expected_color} got {actual_color}" ) - # check 0x0 image with initial color + # Check 0x0 image with initial color im = Image.new(mode, (0, 0), expected_color) with pytest.raises(error): im.getpixel((0, 0)) - # check negative index + # Check negative index with pytest.raises(error): im.getpixel((-1, -1)) @@ -216,7 +216,7 @@ def test_list(self) -> None: @pytest.mark.parametrize("mode", ("I;16", "I;16B")) @pytest.mark.parametrize("expected_color", (2**15 - 1, 2**15, 2**15 + 1, 2**16 - 1)) def test_signedness(self, mode: str, expected_color: int) -> None: - # see https://github.com/python-pillow/Pillow/issues/452 + # See https://github.com/python-pillow/Pillow/issues/452 # pixelaccess is using signed int* instead of uint* self.check(mode, expected_color) @@ -276,13 +276,6 @@ def test_get_vs_c(self) -> None: im = Image.new(mode, (10, 10), 40000) self._test_get_access(im) - # These don't actually appear to be modes that I can actually make, - # as unpack sets them directly into the I mode. - # im = Image.new('I;32L', (10, 10), -2**10) - # self._test_get_access(im) - # im = Image.new('I;32B', (10, 10), 2**10) - # self._test_get_access(im) - def _test_set_access(self, im: Image.Image, color: tuple[int, ...] | float) -> None: """Are we writing the correct bits into the image? @@ -314,23 +307,18 @@ def test_set_vs_c(self) -> None: self._test_set_access(hopper("LA"), (128, 128)) self._test_set_access(hopper("1"), 255) self._test_set_access(hopper("P"), 128) - # self._test_set_access(i, (128, 128)) #PA -- undone how to make + self._test_set_access(hopper("PA"), (128, 128)) self._test_set_access(hopper("F"), 1024.0) for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"): im = Image.new(mode, (10, 10), 40000) self._test_set_access(im, 45000) - # im = Image.new('I;32L', (10, 10), -(2**10)) - # self._test_set_access(im, -(2**13)+1) - # im = Image.new('I;32B', (10, 10), 2**10) - # self._test_set_access(im, 2**13-1) - @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_not_implemented(self) -> None: assert PyAccess.new(hopper("BGR;15")) is None - # ref https://github.com/python-pillow/Pillow/pull/2009 + # Ref https://github.com/python-pillow/Pillow/pull/2009 def test_reference_counting(self) -> None: size = 10 @@ -339,7 +327,7 @@ def test_reference_counting(self) -> None: with pytest.warns(DeprecationWarning): px = Image.new("L", (size, 1), 0).load() for i in range(size): - # pixels can contain garbage if image is released + # Pixels can contain garbage if image is released assert px[i, 0] == 0 @pytest.mark.parametrize("mode", ("P", "PA")) @@ -456,7 +444,7 @@ def test_embeddable(self) -> None: env = os.environ.copy() env["PATH"] = sys.prefix + ";" + env["PATH"] - # do not display the Windows Error Reporting dialog + # Do not display the Windows Error Reporting dialog getattr(ctypes, "windll").kernel32.SetErrorMode(0x0002) process = subprocess.Popen(["embed_pil.exe"], env=env) From c0cb417a44ac705e573e56582e7d9979dccf93ec Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 25 Apr 2024 21:08:24 +0300 Subject: [PATCH 114/300] Add semicolons to fix indent --- src/_imagingcms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index ba8c810057b..dbf7057c572 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -385,7 +385,7 @@ _buildTransform( iRenderingIntent, cmsFLAGS); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build transform"); @@ -419,7 +419,7 @@ _buildProofTransform( iProofIntent, cmsFLAGS); - Py_END_ALLOW_THREADS + Py_END_ALLOW_THREADS; if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build proof transform"); From 1420e725664114680ce72ad5b9542229faa39a0f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 18:08:50 +0000 Subject: [PATCH 115/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_imagingcms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index dbf7057c572..1a18525d071 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -387,7 +387,7 @@ _buildTransform( Py_END_ALLOW_THREADS; - if (!hTransform) { + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build transform"); } @@ -421,7 +421,7 @@ _buildProofTransform( Py_END_ALLOW_THREADS; - if (!hTransform) { + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build proof transform"); } From c3ded3abdaa64d4ba75445b43d09d4fcf3129d73 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 26 Apr 2024 09:13:00 +1000 Subject: [PATCH 116/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 85dc0b43c24..c5df1f8f7c4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Deprecate BGR;15, BGR;16 and BGR;24 modes #7978 + [radarhere, hugovk] + +- Fix ImagingAccess for I;16N on big-endian #7921 + [Yay295, radarhere] + - Support reading P mode TIFF images with padding #7996 [radarhere] From 8cc48b24fe83dc81e6a5e6a83f287d333d74a44f Mon Sep 17 00:00:00 2001 From: Cees Timmerman Date: Fri, 26 Apr 2024 17:17:44 +0200 Subject: [PATCH 117/300] Update ExifTags.py Fixed typo. No other instances in this repo. --- src/PIL/ExifTags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ExifTags.py b/src/PIL/ExifTags.py index 60a4d9774ae..39b4aa55262 100644 --- a/src/PIL/ExifTags.py +++ b/src/PIL/ExifTags.py @@ -346,7 +346,7 @@ class Interop(IntEnum): InteropVersion = 2 RelatedImageFileFormat = 4096 RelatedImageWidth = 4097 - RleatedImageHeight = 4098 + RelatedImageHeight = 4098 class IFD(IntEnum): From 86fb383739597acafc5e452e38eb3b87370d5eb1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 27 Apr 2024 14:08:36 +1000 Subject: [PATCH 118/300] Corrected big-endian check --- Tests/test_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index b7f81408000..e1490d6a083 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -1050,7 +1050,7 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: @pytest.mark.parametrize("mode", modes) def test_getdata_putdata(self, mode: str) -> None: - if is_big_endian and mode == "BGR;15": + if is_big_endian() and mode == "BGR;15": pytest.xfail("Known failure of BGR;15 on big-endian") im = hopper(mode) reloaded = helper_image_new(mode, im.size) From 39da704c61b4ae5ec9c4117d34a8eda8f058a18e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 28 Apr 2024 07:10:15 +1000 Subject: [PATCH 119/300] Updated libimagequant to 4.3.1 --- depends/install_imagequant.sh | 2 +- docs/installation/building-from-source.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 973b4374fed..9dd7742ed34 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -2,7 +2,7 @@ # install libimagequant archive_name=libimagequant -archive_version=4.3.0 +archive_version=4.3.1 archive=$archive_name-$archive_version diff --git a/docs/installation/building-from-source.rst b/docs/installation/building-from-source.rst index 961312b14ad..7f7dfa6ff24 100644 --- a/docs/installation/building-from-source.rst +++ b/docs/installation/building-from-source.rst @@ -68,7 +68,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-4.3** + * Pillow has been tested with libimagequant **2.6-4.3.1** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. From fd8c6a629594968d02b845a4fa2fa52540a86c7e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 28 Apr 2024 13:51:32 +1000 Subject: [PATCH 120/300] Do not indent goto labels --- .clang-format | 1 + src/libImaging/GifEncode.c | 16 ++++++++-------- src/libImaging/JpegEncode.c | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.clang-format b/.clang-format index be32e6d1a8a..3199e330b96 100644 --- a/.clang-format +++ b/.clang-format @@ -9,6 +9,7 @@ BinPackParameters: false BreakBeforeBraces: Attach ColumnLimit: 88 DerivePointerAlignment: false +IndentGotoLabels: false IndentWidth: 4 Language: Cpp PointerAlignment: Right diff --git a/src/libImaging/GifEncode.c b/src/libImaging/GifEncode.c index 831ce432c31..9e91944fecf 100644 --- a/src/libImaging/GifEncode.c +++ b/src/libImaging/GifEncode.c @@ -82,7 +82,7 @@ glzwe( UINT32 end_of_data) { switch (st->entry_state) { case LZW_TRY_IN1: - get_first_byte: +get_first_byte: if (!*in_avail) { if (end_of_data) { goto end_of_data; @@ -94,7 +94,7 @@ glzwe( (*in_avail)--; case LZW_TRY_IN2: - encode_loop: +encode_loop: if (!*in_avail) { if (end_of_data) { st->code = st->head; @@ -127,7 +127,7 @@ glzwe( st->code = st->head; st->put_state = PUT_HEAD; goto put_code; - insert_code_or_clear: /* jump here after put_code */ +insert_code_or_clear: /* jump here after put_code */ if (st->next_code < CODE_LIMIT) { st->codes[st->probe] = (st->next_code << 20) | (st->head << 8) | st->tail; @@ -140,7 +140,7 @@ glzwe( st->code = st->clear_code; st->put_state = PUT_CLEAR; goto put_code; - reset_after_clear: /* jump here after put_code */ +reset_after_clear: /* jump here after put_code */ glzwe_reset(st); } st->head = st->tail; @@ -150,9 +150,9 @@ glzwe( glzwe_reset(st); st->code = st->clear_code; st->put_state = PUT_INIT_CLEAR; - put_code: +put_code: st->code_bits_left = st->code_width; - check_buf_bits: +check_buf_bits: if (!st->buf_bits_left) { /* out buffer full */ case LZW_TRY_OUT1: @@ -190,11 +190,11 @@ glzwe( return GLZW_INTERNAL_ERROR; } - end_of_data: +end_of_data: st->code = st->end_code; st->put_state = PUT_END; goto put_code; - flush_code_buffer: /* jump here after put_code */ +flush_code_buffer: /* jump here after put_code */ if (st->buf_bits_left < 8) { case LZW_TRY_OUT2: if (!*out_avail) { diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index bcbe65aa4d6..ba8353c2d86 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -346,7 +346,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { } jpeg_finish_compress(&context->cinfo); - cleanup: +cleanup: /* Clean up */ if (context->comment) { free(context->comment); From 5597f618a3b5cf8e208dded44a2107313c47122d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 28 Apr 2024 02:49:42 -0600 Subject: [PATCH 121/300] Change comment style Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/libImaging/GifEncode.c | 4 ++-- src/libImaging/TiffDecode.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libImaging/GifEncode.c b/src/libImaging/GifEncode.c index 9e91944fecf..45b67616d47 100644 --- a/src/libImaging/GifEncode.c +++ b/src/libImaging/GifEncode.c @@ -116,8 +116,8 @@ glzwe( st->head = st->codes[st->probe] >> 20; goto encode_loop; } else { - /* Reprobe decrement must be non-zero and relatively prime to table - * size. So, any odd positive number for power-of-2 size. */ + // Reprobe decrement must be non-zero and relatively prime to table + // size. So, any odd positive number for power-of-2 size. if ((st->probe -= ((st->tail << 2) | 1)) < 0) { st->probe += TABLE_SIZE; } diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index 858de9332f5..e6b57e0a796 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -414,8 +414,7 @@ _decodeTile( if (tile_bytes_size > ((tile_length * state->bits / planes + 7) / 8) * tile_width) { // If the tile size as expected by LibTiff isn't what we're expecting, abort. // man: TIFFTileSize returns the equivalent size for a tile of data as it - // would be returned in a - // call to TIFFReadTile ... + // would be returned in a call to TIFFReadTile ... state->errcode = IMAGING_CODEC_BROKEN; return -1; } @@ -507,8 +506,7 @@ _decodeStrip( if (strip_size > (unpacker_row_byte_size * rows_per_strip)) { // If the strip size as expected by LibTiff isn't what we're expecting, abort. // man: TIFFStripSize returns the equivalent size for a strip of data as it - // would be returned in a - // call to TIFFReadEncodedStrip ... + // would be returned in a call to TIFFReadEncodedStrip ... state->errcode = IMAGING_CODEC_BROKEN; return -1; } From 996c053d8995893bcdac7673f0469091990c8f25 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 28 Apr 2024 22:49:56 +1000 Subject: [PATCH 122/300] Change comment style --- src/libImaging/FliDecode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c index debe7ddd82a..6b2518d35ce 100644 --- a/src/libImaging/FliDecode.c +++ b/src/libImaging/FliDecode.c @@ -231,9 +231,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt } /* Note, have to check Data + size, not just ptr + size) */ if (data + (state->xsize * state->ysize) > ptr + bytes) { - /* not enough data for frame */ - /* UNDONE Unclear that we're actually going to leave the buffer at - * the right place. */ + // not enough data for frame + // UNDONE Unclear that we're actually going to leave the buffer at + // the right place. return ptr - buf; /* bytes consumed */ } for (y = 0; y < state->ysize; y++) { From d01e43e796e3d35e6c562145cef13f3e1ffc646d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Apr 2024 09:11:33 +1000 Subject: [PATCH 123/300] Removed direct invocation of setup.py --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 477d92609d1..1f9b4a370df 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ .PHONY: clean clean: - python3 setup.py clean rm src/PIL/*.so || true rm -r build || true find . -name __pycache__ | xargs rm -r || true From 36869833c723a6ff54de74ec5c5d630a770d6d1d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 28 Apr 2024 23:06:39 +1000 Subject: [PATCH 124/300] Added Ubuntu 24.04 --- .github/workflows/test-docker.yml | 13 +++++++------ .github/workflows/test-valgrind.yml | 4 ++-- docs/installation/platform-support.rst | 4 +++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 8f4a4d09009..c53f23a9f28 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -36,8 +36,8 @@ jobs: docker: [ # Run slower jobs first to give them a headstart and reduce waiting time ubuntu-22.04-jammy-arm64v8, - ubuntu-22.04-jammy-ppc64le, - ubuntu-22.04-jammy-s390x, + ubuntu-24.04-noble-ppc64le, + ubuntu-24.04-noble-s390x, # Then run the remainder alpine, amazon-2-amd64, @@ -52,14 +52,15 @@ jobs: gentoo, ubuntu-20.04-focal-amd64, ubuntu-22.04-jammy-amd64, + ubuntu-24.04-noble-amd64, ] dockerTag: [main] include: - docker: "ubuntu-22.04-jammy-arm64v8" qemu-arch: "aarch64" - - docker: "ubuntu-22.04-jammy-ppc64le" + - docker: "ubuntu-24.04-noble-ppc64le" qemu-arch: "ppc64le" - - docker: "ubuntu-22.04-jammy-s390x" + - docker: "ubuntu-24.04-noble-s390x" qemu-arch: "s390x" name: ${{ matrix.docker }} @@ -81,8 +82,8 @@ jobs: - name: Docker build run: | - # The Pillow user in the docker container is UID 1000 - sudo chown -R 1000 $GITHUB_WORKSPACE + # The Pillow user in the docker container is UID 1001 + sudo chown -R 1001 $GITHUB_WORKSPACE docker run --name pillow_container -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }} sudo chown -R runner $GITHUB_WORKSPACE diff --git a/.github/workflows/test-valgrind.yml b/.github/workflows/test-valgrind.yml index 59bb958ec1c..63aec586b79 100644 --- a/.github/workflows/test-valgrind.yml +++ b/.github/workflows/test-valgrind.yml @@ -50,7 +50,7 @@ jobs: - name: Build and Run Valgrind run: | - # The Pillow user in the docker container is UID 1000 - sudo chown -R 1000 $GITHUB_WORKSPACE + # The Pillow user in the docker container is UID 1001 + sudo chown -R 1001 $GITHUB_WORKSPACE docker run --name pillow_container -e "PILLOW_VALGRIND_TEST=true" -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }} sudo chown -R runner $GITHUB_WORKSPACE diff --git a/docs/installation/platform-support.rst b/docs/installation/platform-support.rst index c08a53a4329..888966c51cb 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -47,7 +47,9 @@ These platforms are built and tested for every change. | Ubuntu Linux 22.04 LTS (Jammy) | 3.8, 3.9, 3.10, 3.11, | x86-64 | | | 3.12, 3.13, PyPy3 | | | +----------------------------+---------------------+ -| | 3.10 | arm64v8, ppc64le, | +| | 3.10 | arm64v8 | ++----------------------------------+----------------------------+---------------------+ +| Ubuntu Linux 24.04 LTS (Noble) | 3.12 | x86-64, ppc64le, | | | | s390x | +----------------------------------+----------------------------+---------------------+ | Windows Server 2016 | 3.8 | x86-64 | From 65d73ea970c31c33e23a55273dbdea24376efe04 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Apr 2024 18:54:16 +1000 Subject: [PATCH 125/300] Python 3.8 and 3.9 are tested on macOS 13 --- docs/installation/platform-support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation/platform-support.rst b/docs/installation/platform-support.rst index c08a53a4329..02c4093564c 100644 --- a/docs/installation/platform-support.rst +++ b/docs/installation/platform-support.rst @@ -37,7 +37,7 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| macOS 12 Monterey | 3.8, 3.9 | x86-64 | +| macOS 13 Ventura | 3.8, 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | macOS 14 Sonoma | 3.10, 3.11, 3.12, 3.13, | arm64 | | | PyPy3 | | From 2250fbeb9a8a1b61c9ec8e7df0902a0c35bc495e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Apr 2024 20:19:05 +1000 Subject: [PATCH 126/300] Added type hints --- src/PIL/Image.py | 12 +++++++----- src/PIL/ImageFile.py | 2 +- src/PIL/JpegImagePlugin.py | 8 +++++--- src/PIL/PngImagePlugin.py | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index a17edfa391c..33b3da9a615 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, cast +from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -877,7 +877,7 @@ def load(self): return self.pyaccess return self.im.pixel_access(self.readonly) - def verify(self): + def verify(self) -> None: """ Verifies the contents of a file. For data read from a file, this method attempts to determine if the file is broken, without @@ -1267,7 +1267,9 @@ def _crop(self, im, box): return im.crop((x0, y0, x1, y1)) - def draft(self, mode, size): + def draft( + self, mode: str, size: tuple[int, int] + ) -> tuple[str, tuple[int, int, float, float]] | None: """ Configures the image file loader so it returns a version of the image that as closely as possible matches the given mode and @@ -1290,7 +1292,7 @@ def draft(self, mode, size): """ pass - def _expand(self, xmargin, ymargin=None): + def _expand(self, xmargin: int, ymargin: int | None = None) -> Image: if ymargin is None: ymargin = xmargin self.load() @@ -3450,7 +3452,7 @@ def eval(image, *args): return image.point(args[0]) -def merge(mode, bands): +def merge(mode: str, bands: Sequence[Image]) -> Image: """ Merge a set of single band images into a new multiband image. diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 0283fa2fd42..27885e654eb 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -163,7 +163,7 @@ def __setstate__(self, state): self.tile = [] super().__setstate__(state) - def verify(self): + def verify(self) -> None: """Check file integrity""" # raise exception if something's wrong. must be called diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index e3c0083e944..715a358a3b7 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -424,13 +424,15 @@ def load_read(self, read_bytes): return s - def draft(self, mode, size): + def draft( + self, mode: str, size: tuple[int, int] + ) -> tuple[str, tuple[int, int, float, float]] | None: if len(self.tile) != 1: - return + return None # Protect from second call if self.decoderconfig: - return + return None d, e, o, a = self.tile[0] scale = 1 diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 8b81e54ea77..012e0b61bff 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -783,7 +783,7 @@ def text(self): self.seek(frame) return self._text - def verify(self): + def verify(self) -> None: """Verify PNG file""" if self.fp is None: From 5f805c39cccd6ccc5c695a885e274f8a2b4624f9 Mon Sep 17 00:00:00 2001 From: Nulano Date: Mon, 29 Apr 2024 23:19:36 +0200 Subject: [PATCH 127/300] Added type hints for PixelAccess methods and others --- docs/reference/ImageDraw.rst | 18 +----------- docs/reference/PixelAccess.rst | 41 ++------------------------ docs/reference/PyAccess.rst | 1 + src/PIL/Image.py | 53 +++++++++++++++++++++++++--------- src/PIL/ImageDraw.py | 13 +++++++-- src/PIL/ImageGrab.py | 15 +++++++--- src/PIL/PyAccess.py | 48 ++++++++++++++++++------------ 7 files changed, 97 insertions(+), 92 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 4ccfacae75e..e7339ecbe03 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -679,23 +679,7 @@ Methods :param hints: An optional list of hints. :returns: A (drawing context, drawing resource factory) tuple. -.. py:method:: floodfill(image, xy, value, border=None, thresh=0) - - .. warning:: This method is experimental. - - Fills a bounded region with a given color. - - :param image: Target image. - :param xy: Seed position (a 2-item coordinate tuple). - :param value: Fill color. - :param border: Optional border value. If given, the region consists of - pixels with a color different from the border color. If not given, - the region consists of pixels having the same color as the seed - pixel. - :param thresh: Optional threshold value which specifies a maximum - tolerable difference of a pixel value from the 'background' in - order for it to be replaced. Useful for filling regions of non- - homogeneous, but similar, colors. +.. autofunction:: PIL.ImageDraw.floodfill .. _BCP 47 language code: https://www.w3.org/International/articles/language-tags/ .. _OpenType docs: https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist diff --git a/docs/reference/PixelAccess.rst b/docs/reference/PixelAccess.rst index 04d6f5dcd58..026f488d8e1 100644 --- a/docs/reference/PixelAccess.rst +++ b/docs/reference/PixelAccess.rst @@ -44,42 +44,7 @@ Access using negative indexes is also possible. :: ----------------------------- .. class:: PixelAccess + :canonical: PIL.Image.PixelAccess - .. method:: __setitem__(self, xy, color): - - Modifies the pixel at x,y. The color is given as a single - numerical value for single band images, and a tuple for - multi-band images - - :param xy: The pixel coordinate, given as (x, y). - :param color: The pixel value according to its mode. e.g. tuple (r, g, b) for RGB mode) - - .. method:: __getitem__(self, xy): - - Returns the pixel at x,y. The pixel is returned as a single - value for single band images or a tuple for multiple band - images - - :param xy: The pixel coordinate, given as (x, y). - :returns: a pixel value for single band images, a tuple of - pixel values for multiband images. - - .. method:: putpixel(self, xy, color): - - Modifies the pixel at x,y. The color is given as a single - numerical value for single band images, and a tuple for - multi-band images. In addition to this, RGB and RGBA tuples - are accepted for P and PA images. - - :param xy: The pixel coordinate, given as (x, y). - :param color: The pixel value according to its mode. e.g. tuple (r, g, b) for RGB mode) - - .. method:: getpixel(self, xy): - - Returns the pixel at x,y. The pixel is returned as a single - value for single band images or a tuple for multiple band - images - - :param xy: The pixel coordinate, given as (x, y). - :returns: a pixel value for single band images, a tuple of - pixel values for multiband images. + .. automethod:: PIL.Image.PixelAccess.__getitem__ + .. automethod:: PIL.Image.PixelAccess.__setitem__ diff --git a/docs/reference/PyAccess.rst b/docs/reference/PyAccess.rst index ed58ca3a591..04b2a47eed3 100644 --- a/docs/reference/PyAccess.rst +++ b/docs/reference/PyAccess.rst @@ -44,3 +44,4 @@ Access using negative indexes is also possible. :: .. autoclass:: PIL.PyAccess.PyAccess() :members: + :special-members: __getitem__, __setitem__ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index a17edfa391c..c1b8a2b2faf 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, cast +from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, SupportsInt, cast # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -482,6 +482,31 @@ def _getscaleoffset(expr): # Implementation wrapper +class PixelAccess(Protocol): + def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: + """ + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multi-band images. + + :param xy: The pixel coordinate, given as (x, y). + :returns: a pixel value for single band images, a tuple of + pixel values for multiband images. + """ + raise NotImplementedError() + + def __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> None: + """ + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images. + + :param xy: The pixel coordinate, given as (x, y). + :param color: The pixel value according to its mode, + e.g. tuple (r, g, b) for RGB mode. + """ + raise NotImplementedError() + + class Image: """ This class represents an image object. To create @@ -834,7 +859,7 @@ def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None: msg = "cannot decode image data" raise ValueError(msg) - def load(self): + def load(self) -> PixelAccess | None: """ Allocates storage for the image and loads the pixel data. In normal cases, you don't need to call this method, since the @@ -847,7 +872,7 @@ def load(self): operations. See :ref:`file-handling` for more information. :returns: An image access object. - :rtype: :ref:`PixelAccess` or :py:class:`PIL.PyAccess` + :rtype: :py:class:`.PixelAccess` or :py:class:`.PyAccess` """ if self.im is not None and self.palette and self.palette.dirty: # realize palette @@ -876,6 +901,7 @@ def load(self): if self.pyaccess: return self.pyaccess return self.im.pixel_access(self.readonly) + return None def verify(self): """ @@ -1485,7 +1511,7 @@ def _reload_exif(self) -> None: self._exif._loaded = False self.getexif() - def get_child_images(self): + def get_child_images(self) -> list[ImageFile.ImageFile]: child_images = [] exif = self.getexif() ifds = [] @@ -1509,10 +1535,7 @@ def get_child_images(self): fp = self.fp thumbnail_offset = ifd.get(513) if thumbnail_offset is not None: - try: - thumbnail_offset += self._exif_offset - except AttributeError: - pass + thumbnail_offset += getattr(self, "_exif_offset", 0) self.fp.seek(thumbnail_offset) data = self.fp.read(ifd.get(514)) fp = io.BytesIO(data) @@ -1578,7 +1601,7 @@ def has_transparency_data(self) -> bool: or "transparency" in self.info ) - def apply_transparency(self): + def apply_transparency(self) -> None: """ If a P mode image has a "transparency" key in the info dictionary, remove the key and instead apply the transparency to the palette. @@ -1590,6 +1613,7 @@ def apply_transparency(self): from . import ImagePalette palette = self.getpalette("RGBA") + assert palette is not None transparency = self.info["transparency"] if isinstance(transparency, bytes): for i, alpha in enumerate(transparency): @@ -1601,7 +1625,9 @@ def apply_transparency(self): del self.info["transparency"] - def getpixel(self, xy): + def getpixel( + self, xy: tuple[SupportsInt, SupportsInt] + ) -> float | tuple[int, ...] | None: """ Returns the pixel value at a given position. @@ -1865,7 +1891,7 @@ def point(self, data): lut = [round(i) for i in lut] return self._new(self.im.point(lut, mode)) - def putalpha(self, alpha): + def putalpha(self, alpha: Image | int) -> None: """ Adds or replaces the alpha layer in this image. If the image does not have an alpha layer, it's converted to "LA" or "RGBA". @@ -1912,6 +1938,7 @@ def putalpha(self, alpha): alpha = alpha.convert("L") else: # constant alpha + alpha = cast(int, alpha) # see python/typing#1013 try: self.im.fillband(band, alpha) except (AttributeError, ValueError): @@ -1975,7 +2002,7 @@ def putpalette(self, data, rawmode="RGB") -> None: self.palette.mode = "RGB" self.load() # install new palette - def putpixel(self, xy, value): + def putpixel(self, xy: tuple[int, int], value: float | tuple[int, ...]) -> None: """ Modifies the pixel at the given position. The color is given as a single numerical value for single-band images, and a tuple for @@ -2015,7 +2042,7 @@ def putpixel(self, xy, value): value = value[:3] value = self.palette.getcolor(value, self) if self.mode == "PA": - value = (value, alpha) + value = (value, alpha) # type: ignore[assignment] return self.im.putpixel(xy, value) def remap_palette(self, dest_map, source_palette=None): diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index d3efe64865e..5c179000c97 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -898,9 +898,17 @@ def getdraw(im=None, hints=None): return im, handler -def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None: +def floodfill( + image: Image.Image, + xy: tuple[int, int], + value: float | tuple[int, ...], + border: float | tuple[int, ...] | None = None, + thresh: float = 0, +) -> None: """ - (experimental) Fills a bounded region with a given color. + .. warning:: This method is experimental. + + Fills a bounded region with a given color. :param image: Target image. :param xy: Seed position (a 2-item coordinate tuple). See @@ -918,6 +926,7 @@ def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None: # based on an implementation by Eric S. Raymond # amended by yo1995 @20180806 pixel = image.load() + assert pixel is not None x, y = xy try: background = pixel[x, y] diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 3f3be706d96..f52dd301d14 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -22,11 +22,17 @@ import subprocess import sys import tempfile +from typing import Union, cast from . import Image -def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None): +def grab( + bbox: tuple[int, int, int, int] | None = None, + include_layered_windows: bool = False, + all_screens: bool = False, + xdisplay: str | None = None, +) -> Image.Image: if xdisplay is None: if sys.platform == "darwin": fh, filepath = tempfile.mkstemp(".png") @@ -36,7 +42,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N left, top, right, bottom = bbox args += ["-R", f"{left},{top},{right-left},{bottom-top}"] subprocess.call(args + ["-x", filepath]) - im = Image.open(filepath) + im: Image.Image = Image.open(filepath) im.load() os.unlink(filepath) if bbox: @@ -63,6 +69,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N left, top, right, bottom = bbox im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) return im + xdisplay = cast(Union[str, None], xdisplay) # type: ignore[redundant-cast, unused-ignore] try: if not Image.core.HAVE_XCB: msg = "Pillow was built without XCB support" @@ -77,7 +84,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N fh, filepath = tempfile.mkstemp(".png") os.close(fh) subprocess.call(["gnome-screenshot", "-f", filepath]) - im = Image.open(filepath) + im: Image.Image = Image.open(filepath) im.load() os.unlink(filepath) if bbox: @@ -94,7 +101,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N return im -def grabclipboard(): +def grabclipboard() -> Image.Image | list[str] | None: if sys.platform == "darwin": fh, filepath = tempfile.mkstemp(".png") os.close(fh) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 2c831913d69..af95956662d 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -22,6 +22,7 @@ import logging import sys +from typing import TYPE_CHECKING from ._deprecate import deprecate @@ -50,7 +51,7 @@ class PyAccess: - def __init__(self, img, readonly=False): + def __init__(self, img: Image.Image, readonly: bool = False) -> None: deprecate("PyAccess", 11) vals = dict(img.im.unsafe_ptrs) self.readonly = readonly @@ -70,14 +71,15 @@ def __init__(self, img, readonly=False): # logger.debug("%s", vals) self._post_init() - def _post_init(self): + def _post_init(self) -> None: pass - def __setitem__(self, xy, color): + def __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> None: """ Modifies the pixel at x,y. The color is given as a single numerical value for single band images, and a tuple for - multi-band images + multi-band images. In addition to this, RGB and RGBA tuples + are accepted for P and PA images. :param xy: The pixel coordinate, given as (x, y). See :ref:`coordinate-system`. @@ -104,11 +106,11 @@ def __setitem__(self, xy, color): color = color[:3] color = self._palette.getcolor(color, self._img) if self._im.mode == "PA": - color = (color, alpha) + color = (color, alpha) # type: ignore[assignment] return self.set_pixel(x, y, color) - def __getitem__(self, xy): + def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: """ Returns the pixel at x,y. The pixel is returned as a single value for single band images or a tuple for multiple band @@ -130,13 +132,19 @@ def __getitem__(self, xy): putpixel = __setitem__ getpixel = __getitem__ - def check_xy(self, xy): + def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]: (x, y) = xy if not (0 <= x < self.xsize and 0 <= y < self.ysize): msg = "pixel location out of range" raise ValueError(msg) return xy + def get_pixel(self, x: int, y: int) -> float | tuple[int, ...]: + raise NotImplementedError() + + def set_pixel(self, x: int, y: int, color: float | tuple[int, ...]) -> None: + raise NotImplementedError() + class _PyAccess32_2(PyAccess): """PA, LA, stored in first and last bytes of a 32 bit word""" @@ -144,7 +152,7 @@ class _PyAccess32_2(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.a @@ -161,7 +169,7 @@ class _PyAccess32_3(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.g, pixel.b @@ -180,7 +188,7 @@ class _PyAccess32_4(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.g, pixel.b, pixel.a @@ -199,7 +207,7 @@ class _PyAccess8(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = self.image8 - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -217,7 +225,7 @@ class _PyAccessI16_N(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("unsigned short **", self.image) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -235,7 +243,7 @@ class _PyAccessI16_L(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_I16 **", self.image) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: pixel = self.pixels[y][x] return pixel.l + pixel.r * 256 @@ -256,7 +264,7 @@ class _PyAccessI16_B(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_I16 **", self.image) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: pixel = self.pixels[y][x] return pixel.l * 256 + pixel.r @@ -277,7 +285,7 @@ class _PyAccessI32_N(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = self.image32 - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -296,7 +304,7 @@ def reverse(self, i): chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0] return ffi.cast("int *", chars)[0] - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.reverse(self.pixels[y][x]) def set_pixel(self, x, y, color): @@ -309,7 +317,7 @@ class _PyAccessF(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("float **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> float: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -357,9 +365,13 @@ def set_pixel(self, x, y, color): mode_map["I;32B"] = _PyAccessI32_N -def new(img, readonly=False): +def new(img: Image.Image, readonly: bool = False) -> PyAccess | None: access_type = mode_map.get(img.mode, None) if not access_type: logger.debug("PyAccess Not Implemented: %s", img.mode) return None return access_type(img, readonly) + + +if TYPE_CHECKING: + from . import Image From 74b87ae7486e475c032044a648c33605514844ed Mon Sep 17 00:00:00 2001 From: Nulano Date: Tue, 30 Apr 2024 16:32:29 +0200 Subject: [PATCH 128/300] Move import to top of file --- src/PIL/PyAccess.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index af95956662d..cc8e2e35958 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -49,6 +49,9 @@ logger = logging.getLogger(__name__) +if TYPE_CHECKING: + from . import Image + class PyAccess: def __init__(self, img: Image.Image, readonly: bool = False) -> None: @@ -371,7 +374,3 @@ def new(img: Image.Image, readonly: bool = False) -> PyAccess | None: logger.debug("PyAccess Not Implemented: %s", img.mode) return None return access_type(img, readonly) - - -if TYPE_CHECKING: - from . import Image From c2cb9445141476283ff0a94be06ad70d4422e535 Mon Sep 17 00:00:00 2001 From: Nulano Date: Tue, 30 Apr 2024 16:32:44 +0200 Subject: [PATCH 129/300] Ignore incorrect mypy warning --- src/PIL/ImageGrab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index f52dd301d14..f16e9554433 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -84,7 +84,7 @@ def grab( fh, filepath = tempfile.mkstemp(".png") os.close(fh) subprocess.call(["gnome-screenshot", "-f", filepath]) - im: Image.Image = Image.open(filepath) + im: Image.Image = Image.open(filepath) # type: ignore[no-redef, unused-ignore] im.load() os.unlink(filepath) if bbox: From e8cddfbc6a2a6167e395fc8cfad9ea29a38f108b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 May 2024 08:45:39 +1000 Subject: [PATCH 130/300] Updated codecov/codecov-action to v4 --- .github/workflows/test-cygwin.yml | 3 ++- .github/workflows/test-docker.yml | 3 ++- .github/workflows/test-mingw.yml | 3 ++- .github/workflows/test-windows.yml | 3 ++- .github/workflows/test.yml | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 9674a466570..7972730ca3a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -132,11 +132,12 @@ jobs: bash.exe .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: GHA_Cygwin name: Cygwin Python 3.${{ matrix.python-minor-version }} + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index c53f23a9f28..6afed74db31 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -100,11 +100,12 @@ jobs: MATRIX_DOCKER: ${{ matrix.docker }} - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: flags: GHA_Docker name: ${{ matrix.docker }} gcov: true + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml index a07a27c4631..a773ca45304 100644 --- a/.github/workflows/test-mingw.yml +++ b/.github/workflows/test-mingw.yml @@ -85,8 +85,9 @@ jobs: python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: GHA_Windows name: "MSYS2 MinGW" + token: ${{ secrets.CODECOV_ORG_TOKEN }} diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 40994c60a8d..9edc1517350 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -213,11 +213,12 @@ jobs: shell: pwsh - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: file: ./coverage.xml flags: GHA_Windows name: ${{ runner.os }} Python ${{ matrix.python-version }} + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4573fde906d..aa5646caf44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -150,11 +150,12 @@ jobs: .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-action@v3.1.5 + uses: codecov/codecov-action@v4 with: flags: ${{ matrix.os == 'ubuntu-latest' && 'GHA_Ubuntu' || 'GHA_macOS' }} name: ${{ matrix.os }} Python ${{ matrix.python-version }} gcov: true + token: ${{ secrets.CODECOV_ORG_TOKEN }} success: permissions: From ac1eb57c03e182752e1207cd477300650fce4dc0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 May 2024 09:43:50 +1000 Subject: [PATCH 131/300] Install git --- .github/workflows/test-cygwin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 7972730ca3a..1269ef8cb4a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -55,6 +55,7 @@ jobs: packages: > gcc-g++ ghostscript + git ImageMagick jpeg libfreetype-devel From 6036d81d973e7b0a4613bb06d4a2d79310eef799 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 May 2024 20:51:54 +1000 Subject: [PATCH 132/300] Added type hints --- src/PIL/BlpImagePlugin.py | 6 +++--- src/PIL/BmpImagePlugin.py | 4 ++-- src/PIL/DcxImagePlugin.py | 4 ++-- src/PIL/DdsImagePlugin.py | 2 +- src/PIL/EpsImagePlugin.py | 4 ++-- src/PIL/FliImagePlugin.py | 4 ++-- src/PIL/GifImagePlugin.py | 12 ++++++------ src/PIL/Hdf5StubImagePlugin.py | 2 +- src/PIL/IcnsImagePlugin.py | 2 +- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/ImImagePlugin.py | 4 ++-- src/PIL/Image.py | 7 +++++-- src/PIL/ImageFile.py | 10 +++++----- src/PIL/ImageFilter.py | 5 ++++- src/PIL/ImageWin.py | 4 ++-- src/PIL/MicImagePlugin.py | 4 ++-- src/PIL/MpoImagePlugin.py | 4 ++-- src/PIL/PngImagePlugin.py | 8 ++++---- src/PIL/PsdImagePlugin.py | 9 ++++----- src/PIL/SpiderImagePlugin.py | 6 +++--- src/PIL/TiffImagePlugin.py | 20 ++++++++++---------- src/PIL/WalImageFile.py | 2 +- src/PIL/WebPImagePlugin.py | 4 ++-- 23 files changed, 67 insertions(+), 62 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 8d351ce9100..bdf54baae13 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -253,7 +253,7 @@ class BlpImageFile(ImageFile.ImageFile): format = "BLP" format_description = "Blizzard Mipmap Format" - def _open(self): + def _open(self) -> None: self.magic = self.fp.read(4) self.fp.seek(5, os.SEEK_CUR) @@ -333,7 +333,7 @@ def _read_bgra(self, palette): class BLP1Decoder(_BLPBaseDecoder): - def _load(self): + def _load(self) -> None: if self._blp_compression == Format.JPEG: self._decode_jpeg_stream() @@ -418,7 +418,7 @@ def _load(self): class BLPEncoder(ImageFile.PyEncoder): _pushes_fd = True - def _write_palette(self): + def _write_palette(self) -> bytes: data = b"" palette = self.im.getpalette("RGBA", "RGBA") for i in range(len(palette) // 4): diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 9ce0fed88fe..c5d1cd40d3b 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -283,7 +283,7 @@ def _bitmap(self, header=0, offset=0): ) ] - def _open(self): + def _open(self) -> None: """Open file, check magic number and read header""" # read 14 bytes: magic number, filesize, reserved, header final offset head_data = self.fp.read(14) @@ -376,7 +376,7 @@ class DibImageFile(BmpImageFile): format = "DIB" format_description = "Windows Bitmap" - def _open(self): + def _open(self) -> None: self._bitmap() diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index b24c16329d3..1c455b032cd 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -63,7 +63,7 @@ def _open(self): self.is_animated = self.n_frames > 1 self.seek(0) - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return self.frame = frame @@ -71,7 +71,7 @@ def seek(self, frame): self.fp.seek(self._offset[frame]) PcxImageFile._open(self) - def tell(self): + def tell(self) -> int: return self.frame diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 3032e4aec52..59ee0f8a06d 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -331,7 +331,7 @@ class DdsImageFile(ImageFile.ImageFile): format = "DDS" format_description = "DirectDraw Surface" - def _open(self): + def _open(self) -> None: if not _accept(self.fp.read(4)): msg = "not a DDS file" raise SyntaxError(msg) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index ec6705742ab..b57daca56d4 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -178,7 +178,7 @@ def seek(self, offset, whence=io.SEEK_SET): self.char = None self.fp.seek(offset, whence) - def readline(self): + def readline(self) -> str: s = [self.char or b""] self.char = None @@ -212,7 +212,7 @@ class EpsImageFile(ImageFile.ImageFile): mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} - def _open(self): + def _open(self) -> None: (length, offset) = self._find_offset(self.fp) # go to offset - start of "%!PS" diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 7a233d01584..eea2c0c951f 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -123,7 +123,7 @@ def _palette(self, palette, shift): palette[i] = (r, g, b) i += 1 - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return if frame < self.__frame: @@ -162,7 +162,7 @@ def _seek(self, frame): self.__offset += framesize - def tell(self): + def tell(self) -> int: return self.__frame diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 93be7fefb86..26e5958191b 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -76,7 +76,7 @@ class GifImageFile(ImageFile.ImageFile): global_palette = None - def data(self): + def data(self) -> bytes | None: s = self.fp.read(1) if s and s[0]: return self.fp.read(s[0]) @@ -88,7 +88,7 @@ def _is_palette_needed(self, p): return True return False - def _open(self): + def _open(self) -> None: # Screen s = self.fp.read(13) if not _accept(s): @@ -147,7 +147,7 @@ def is_animated(self): self.seek(current) return self._is_animated - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return if frame < self.__frame: @@ -417,7 +417,7 @@ def _rgb(color): elif k in self.info: del self.info[k] - def load_prepare(self): + def load_prepare(self) -> None: temp_mode = "P" if self._frame_palette else "L" self._prev_im = None if self.__frame == 0: @@ -437,7 +437,7 @@ def load_prepare(self): super().load_prepare() - def load_end(self): + def load_end(self) -> None: if self.__frame == 0: if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: if self._frame_transparency is not None: @@ -463,7 +463,7 @@ def load_end(self): else: self.im.paste(frame_im, self.dispose_extent) - def tell(self): + def tell(self) -> int: return self.__frame diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index f50e6bf1625..afbfd16393c 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -37,7 +37,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile): format = "HDF5" format_description = "HDF5" - def _open(self): + def _open(self) -> None: offset = self.fp.tell() if not _accept(self.fp.read(8)): diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index c2c9508633d..0a86ba883f1 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -252,7 +252,7 @@ class IcnsImageFile(ImageFile.ImageFile): format = "ICNS" format_description = "Mac OS icns resource" - def _open(self): + def _open(self) -> None: self.icns = IcnsFile(self.fp) self._mode = "RGBA" self.info["sizes"] = self.icns.itersizes() diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index 82b190eb8f4..eacffbae645 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -302,7 +302,7 @@ class IcoImageFile(ImageFile.ImageFile): format = "ICO" format_description = "Windows Icon" - def _open(self): + def _open(self) -> None: self.ico = IcoFile(self.fp) self.info["sizes"] = self.ico.sizes() self.size = self.ico.entry[0]["dim"] diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 4613e40b60f..0de7d6492b1 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -278,7 +278,7 @@ def n_frames(self): def is_animated(self): return self.info[FRAMES] > 1 - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return @@ -296,7 +296,7 @@ def seek(self, frame): self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] - def tell(self): + def tell(self) -> int: return self.frame diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 33b3da9a615..0f2e146bb5d 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1298,7 +1298,10 @@ def _expand(self, xmargin: int, ymargin: int | None = None) -> Image: self.load() return self._new(self.im.expand(xmargin, ymargin)) - def filter(self, filter): + if TYPE_CHECKING: + from . import ImageFilter + + def filter(self, filter: ImageFilter.Filter | type[ImageFilter.Filter]) -> Image: """ Filters this image using the given filter. For a list of available filters, see the :py:mod:`~PIL.ImageFilter` module. @@ -1310,7 +1313,7 @@ def filter(self, filter): self.load() - if isinstance(filter, Callable): + if callable(filter): filter = filter() if not hasattr(filter, "filter"): msg = "filter argument should be ImageFilter.Filter instance or class" diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 27885e654eb..b93e2ad2ce6 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -311,7 +311,7 @@ def load(self): return Image.Image.load(self) - def load_prepare(self): + def load_prepare(self) -> None: # create image memory if necessary if not self.im or self.im.mode != self.mode or self.im.size != self.size: self.im = Image.core.new(self.mode, self.size) @@ -319,7 +319,7 @@ def load_prepare(self): if self.mode == "P": Image.Image.load(self) - def load_end(self): + def load_end(self) -> None: # may be overridden pass @@ -390,7 +390,7 @@ class Parser: offset = 0 finished = 0 - def reset(self): + def reset(self) -> None: """ (Consumer) Reset the parser. Note that you can only call this method immediately after you've created a parser; parser @@ -605,7 +605,7 @@ def _safe_read(fp, size): class PyCodecState: - def __init__(self): + def __init__(self) -> None: self.xsize = 0 self.ysize = 0 self.xoff = 0 @@ -634,7 +634,7 @@ def init(self, args): """ self.args = args - def cleanup(self): + def cleanup(self) -> None: """ Override to perform codec specific cleanup diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index b2c4950d6a9..fa9ebd9defb 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -16,11 +16,14 @@ # from __future__ import annotations +import abc import functools class Filter: - pass + @abc.abstractmethod + def filter(self, image): + pass class MultibandFilter(Filter): diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 75910d2d9ab..2c439038dde 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -204,7 +204,7 @@ def ui_handle_clear(self, dc, x0, y0, x1, y1): def ui_handle_damage(self, x0, y0, x1, y1): pass - def ui_handle_destroy(self): + def ui_handle_destroy(self) -> None: pass def ui_handle_repair(self, dc, x0, y0, x1, y1): @@ -213,7 +213,7 @@ def ui_handle_repair(self, dc, x0, y0, x1, y1): def ui_handle_resize(self, width, height): pass - def mainloop(self): + def mainloop(self) -> None: Image.core.eventloop() diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 96de386a851..5aef94dfbff 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -38,7 +38,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): format_description = "Microsoft Image Composer" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: # read the OLE directory and see if this is a likely # to be a Microsoft Image Composer file @@ -88,7 +88,7 @@ def seek(self, frame): def tell(self): return self.frame - def close(self): + def close(self) -> None: self.__fp.close() self.ole.close() super().close() diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index ac9820bbf68..fb6620e7563 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -127,7 +127,7 @@ def _after_jpeg_open(self, mpheader=None): def load_seek(self, pos): self._fp.seek(pos) - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return self.fp = self._fp @@ -149,7 +149,7 @@ def seek(self, frame): self.tile = [("jpeg", (0, 0) + self.size, self.offset, self.tile[0][-1])] self.__frame = frame - def tell(self): + def tell(self) -> int: return self.__frame @staticmethod diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 012e0b61bff..39faa0f78b1 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -800,7 +800,7 @@ def verify(self) -> None: self.fp.close() self.fp = None - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return if frame < self.__frame: @@ -909,10 +909,10 @@ def _seek(self, frame, rewind=False): else: self.dispose = None - def tell(self): + def tell(self) -> int: return self.__frame - def load_prepare(self): + def load_prepare(self) -> None: """internal: prepare to read PNG file""" if self.info.get("interlace"): @@ -954,7 +954,7 @@ def load_read(self, read_bytes): return self.fp.read(read_bytes) - def load_end(self): + def load_end(self) -> None: """internal: finished reading image data""" if self.__idat != 0: self.fp.read(self.__idat) diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index b15918313be..86c1a67630e 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -57,7 +57,7 @@ class PsdImageFile(ImageFile.ImageFile): format_description = "Adobe Photoshop" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: read = self.fp.read # @@ -141,23 +141,22 @@ def _open(self): self.frame = 1 self._min_frame = 1 - def seek(self, layer): + def seek(self, layer: int) -> None: if not self._seek_check(layer): return # seek to given layer (1..max) try: - name, mode, bbox, tile = self.layers[layer - 1] + _, mode, _, tile = self.layers[layer - 1] self._mode = mode self.tile = tile self.frame = layer self.fp = self._fp - return name, bbox except IndexError as e: msg = "no such layer" raise EOFError(msg) from e - def tell(self): + def tell(self) -> int: # return layer number (0=image, 1..max=layers) return self.frame diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 86582fb128c..01a39e97c6d 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -97,7 +97,7 @@ class SpiderImageFile(ImageFile.ImageFile): format_description = "Spider 2D image" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: # check header n = 27 * 4 # read 27 float values f = self.fp.read(n) @@ -165,13 +165,13 @@ def is_animated(self): return self._nimages > 1 # 1st image index is zero (although SPIDER imgnumber starts at 1) - def tell(self): + def tell(self) -> int: if self.imgnumber < 1: return 0 else: return self.imgnumber - 1 - def seek(self, frame): + def seek(self, frame: int) -> None: if self.istack == 0: msg = "attempt to seek in a non-stack file" raise EOFError(msg) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index c78c223b365..1be717de1cb 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1143,7 +1143,7 @@ def n_frames(self): self.seek(current) return self._n_frames - def seek(self, frame): + def seek(self, frame: int) -> None: """Select a given frame as current image""" if not self._seek_check(frame): return @@ -1198,7 +1198,7 @@ def _seek(self, frame): self.__frame = frame self._setup() - def tell(self): + def tell(self) -> int: """Return the current frame number""" return self.__frame @@ -1237,7 +1237,7 @@ def load(self): return self._load_libtiff() return super().load() - def load_end(self): + def load_end(self) -> None: # allow closing if we're on the first frame, there's no next # This is the ImageFile.load path only, libtiff specific below. if not self.is_animated: @@ -1942,7 +1942,7 @@ def __init__(self, fn, new=False): self.beginning = self.f.tell() self.setup() - def setup(self): + def setup(self) -> None: # Reset everything. self.f.seek(self.beginning, os.SEEK_SET) @@ -1967,7 +1967,7 @@ def setup(self): self.skipIFDs() self.goToEnd() - def finalize(self): + def finalize(self) -> None: if self.isFirst: return @@ -1990,7 +1990,7 @@ def finalize(self): self.f.seek(ifd_offset) self.fixIFD() - def newFrame(self): + def newFrame(self) -> None: # Call this to finish a frame. self.finalize() self.setup() @@ -2013,7 +2013,7 @@ def seek(self, offset, whence=io.SEEK_SET): self.f.seek(offset, whence) return self.tell() - def goToEnd(self): + def goToEnd(self) -> None: self.f.seek(0, os.SEEK_END) pos = self.f.tell() @@ -2029,7 +2029,7 @@ def setEndian(self, endian): self.shortFmt = self.endian + "H" self.tagFormat = self.endian + "HHL" - def skipIFDs(self): + def skipIFDs(self) -> None: while True: ifd_offset = self.readLong() if ifd_offset == 0: @@ -2084,11 +2084,11 @@ def writeLong(self, value): msg = f"wrote only {bytes_written} bytes but wanted 4" raise RuntimeError(msg) - def close(self): + def close(self) -> None: self.finalize() self.f.close() - def fixIFD(self): + def fixIFD(self) -> None: num_tags = self.readShort() for i in range(num_tags): diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index c5bf3e04cf7..fbd7be6ed5c 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -32,7 +32,7 @@ class WalImageFile(ImageFile.ImageFile): format = "WAL" format_description = "Quake2 Texture" - def _open(self): + def _open(self) -> None: self._mode = "P" # read header fields diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 9c8d53336ef..61ae9eae500 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -109,7 +109,7 @@ def getxmp(self): """ return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return @@ -174,7 +174,7 @@ def load(self): def load_seek(self, pos): pass - def tell(self): + def tell(self) -> int: if not _webp.HAVE_WEBPANIM: return super().tell() From 74063feadca98b847ee8e239531d2cfb73de12e5 Mon Sep 17 00:00:00 2001 From: mrKazzila Date: Sat, 4 May 2024 19:21:49 +0300 Subject: [PATCH 133/300] chore: add f-string formatting --- src/PIL/DdsImagePlugin.py | 8 ++++---- src/PIL/ImImagePlugin.py | 2 +- src/PIL/Image.py | 14 +++++++------- src/PIL/ImageMath.py | 4 ++-- src/PIL/ImageMode.py | 8 ++++---- src/PIL/ImageMorph.py | 2 +- src/PIL/ImageWin.py | 2 +- src/PIL/PalmImagePlugin.py | 2 +- src/PIL/PdfParser.py | 4 ++-- src/PIL/PngImagePlugin.py | 2 +- src/PIL/SpiderImagePlugin.py | 10 +++++----- src/PIL/TiffImagePlugin.py | 12 ++++++------ src/PIL/features.py | 4 ++-- 13 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 3032e4aec52..2496088af41 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -271,16 +271,16 @@ class D3DFMT(IntEnum): module = sys.modules[__name__] for item in DDSD: assert item.name is not None - setattr(module, "DDSD_" + item.name, item.value) + setattr(module, f"DDSD_{item.name}", item.value) for item1 in DDSCAPS: assert item1.name is not None - setattr(module, "DDSCAPS_" + item1.name, item1.value) + setattr(module, f"DDSCAPS_{item1.name}", item1.value) for item2 in DDSCAPS2: assert item2.name is not None - setattr(module, "DDSCAPS2_" + item2.name, item2.value) + setattr(module, f"DDSCAPS2_{item2.name}", item2.value) for item3 in DDPF: assert item3.name is not None - setattr(module, "DDPF_" + item3.name, item3.value) + setattr(module, f"DDPF_{item3.name}", item3.value) DDS_FOURCC = DDPF.FOURCC DDS_RGB = DDPF.RGB diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 4613e40b60f..77b3963871d 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -196,7 +196,7 @@ def _open(self): n += 1 else: - msg = "Syntax error in IM header: " + s.decode("ascii", "replace") + msg = f"Syntax error in IM header: {s.decode('ascii', 'replace')}" raise SyntaxError(msg) if not n: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 33b3da9a615..2184ef8ea9e 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -405,7 +405,7 @@ def _getdecoder(mode, decoder_name, args, extra=()): try: # get decoder - decoder = getattr(core, decoder_name + "_decoder") + decoder = getattr(core, f"{decoder_name}_decoder") except AttributeError as e: msg = f"decoder {decoder_name} not available" raise OSError(msg) from e @@ -428,7 +428,7 @@ def _getencoder(mode, encoder_name, args, extra=()): try: # get encoder - encoder = getattr(core, encoder_name + "_encoder") + encoder = getattr(core, f"{encoder_name}_encoder") except AttributeError as e: msg = f"encoder {encoder_name} not available" raise OSError(msg) from e @@ -603,7 +603,7 @@ def _dump( ) -> str: suffix = "" if format: - suffix = "." + format + suffix = f".{format}" if not file: f, filename = tempfile.mkstemp(suffix) @@ -2180,7 +2180,7 @@ def resize(self, size, resample=None, box=None, reducing_gap=None) -> Image: (Resampling.HAMMING, "Image.Resampling.HAMMING"), ) ] - msg += " Use " + ", ".join(filters[:-1]) + " or " + filters[-1] + msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" raise ValueError(msg) if reducing_gap is not None and reducing_gap < 1.0: @@ -2825,7 +2825,7 @@ def __transformer( (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), ) ] - msg += " Use " + ", ".join(filters[:-1]) + " or " + filters[-1] + msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" raise ValueError(msg) image.load() @@ -3223,8 +3223,8 @@ def fromqpixmap(im): ((1, 1, 3), "|u1"): ("RGB", "RGB"), ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), # shortcuts: - ((1, 1), _ENDIAN + "i4"): ("I", "I"), - ((1, 1), _ENDIAN + "f4"): ("F", "F"), + ((1, 1), f"{_ENDIAN}i4"): ("I", "I"), + ((1, 1), f"{_ENDIAN}f4"): ("F", "F"), } diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 77472a24c68..6664434ea45 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -61,7 +61,7 @@ def apply( out = Image.new(mode or im_1.mode, im_1.size, None) im_1.load() try: - op = getattr(_imagingmath, op + "_" + im_1.mode) + op = getattr(_imagingmath, f"{op}_{im_1.mode}") except AttributeError as e: msg = f"bad operand type for '{op}'" raise TypeError(msg) from e @@ -89,7 +89,7 @@ def apply( im_1.load() im_2.load() try: - op = getattr(_imagingmath, op + "_" + im_1.mode) + op = getattr(_imagingmath, f"{op}_{im_1.mode}") except AttributeError as e: msg = f"bad operand type for '{op}'" raise TypeError(msg) from e diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 7bd2afcf2fa..92a08d2cbcb 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -44,8 +44,8 @@ def getmode(mode: str) -> ModeDescriptor: # Bits need to be extended to bytes "1": ("L", "L", ("1",), "|b1"), "L": ("L", "L", ("L",), "|u1"), - "I": ("L", "I", ("I",), endian + "i4"), - "F": ("L", "F", ("F",), endian + "f4"), + "I": ("L", "I", ("I",), f"{endian}i4"), + "F": ("L", "F", ("F",), f"{endian}f4"), "P": ("P", "L", ("P",), "|u1"), "RGB": ("RGB", "L", ("R", "G", "B"), "|u1"), "RGBX": ("RGB", "L", ("R", "G", "B", "X"), "|u1"), @@ -78,8 +78,8 @@ def getmode(mode: str) -> ModeDescriptor: "I;16LS": "u2", "I;16BS": ">i2", - "I;16N": endian + "u2", - "I;16NS": endian + "i2", + "I;16N": f"{endian}u2", + "I;16NS": f"{endian}i2", "I;32": "u4", "I;32L": " Date: Sat, 4 May 2024 19:26:22 +0300 Subject: [PATCH 134/300] chore: update __repr__ for PdfName --- src/PIL/PdfParser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index d6f2ebd44c0..1485cafe0ac 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -225,7 +225,7 @@ def __hash__(self): return hash(self.name) def __repr__(self): - return f"PdfName({repr(self.name)})" + return f"{self.__class__.__name__}({repr(self.name)})" @classmethod def from_pdf_stream(cls, data): From 71b8d99b3699bab3e7217af7453199853671409f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 19:27:42 +0000 Subject: [PATCH 135/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/PdfParser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 1485cafe0ac..c1ed7879741 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -144,9 +144,7 @@ def __delitem__(self, key): elif key in self.deleted_entries: generation = self.deleted_entries[key] else: - msg = ( - f"object ID {key} cannot be deleted because it doesn't exist" - ) + msg = f"object ID {key} cannot be deleted because it doesn't exist" raise IndexError(msg) def __contains__(self, key): From b8e3e0a43059dc2c1a1b410ac87fca0772de3af1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 17:25:18 +0000 Subject: [PATCH 136/300] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.3.4 → v0.4.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.4...v0.4.3) - [github.com/psf/black-pre-commit-mirror: 24.3.0 → 24.4.2](https://github.com/psf/black-pre-commit-mirror/compare/24.3.0...24.4.2) - [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0) - [github.com/python-jsonschema/check-jsonschema: 0.28.1 → 0.28.2](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.1...0.28.2) - [github.com/tox-dev/pyproject-fmt: 1.7.0 → 1.8.0](https://github.com/tox-dev/pyproject-fmt/compare/1.7.0...1.8.0) --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51625eb4c89..1272913c9cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.4.3 hooks: - id: ruff args: [--exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.3.0 + rev: 24.4.2 hooks: - id: black @@ -29,7 +29,7 @@ repos: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable @@ -43,7 +43,7 @@ repos: exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/ - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.1 + rev: 0.28.2 hooks: - id: check-github-workflows - id: check-readthedocs @@ -55,7 +55,7 @@ repos: - id: sphinx-lint - repo: https://github.com/tox-dev/pyproject-fmt - rev: 1.7.0 + rev: 1.8.0 hooks: - id: pyproject-fmt From b17f1e507b1e44246b89938e5e4b5d53716751f0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 May 2024 14:01:08 +1000 Subject: [PATCH 137/300] Use f-strings --- Tests/test_file_eps.py | 4 +--- selftest.py | 4 ++-- src/PIL/IptcImagePlugin.py | 2 +- src/PIL/PdfParser.py | 5 +++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index d01884f96c5..1c21aa8ca67 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -336,9 +336,7 @@ def test_readline_psfile(tmp_path: Path) -> None: strings = ["something", "else", "baz", "bif"] def _test_readline(t: EpsImagePlugin.PSFile, ending: str) -> None: - ending = "Failure with line ending: %s" % ( - "".join("%s" % ord(s) for s in ending) - ) + ending = f"Failure with line ending: {''.join(str(ord(s)) for s in ending)}" assert t.readline().strip("\r\n") == "something", ending assert t.readline().strip("\r\n") == "else", ending assert t.readline().strip("\r\n") == "baz", ending diff --git a/selftest.py b/selftest.py index 661abcddb74..9e049367edc 100755 --- a/selftest.py +++ b/selftest.py @@ -165,9 +165,9 @@ def testimage() -> None: print("Running selftest:") status = doctest.testmod(sys.modules[__name__]) if status[0]: - print("*** %s tests of %d failed." % status) + print(f"*** {status[0]} tests of {status[1]} failed.") exit_status = 1 else: - print("--- %s tests passed." % status[1]) + print(f"--- {status[1]} tests passed.") sys.exit(exit_status) diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index 4096094348a..73df83bfb31 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -57,7 +57,7 @@ def dump(c: Sequence[int | bytes]) -> None: """.. deprecated:: 10.2.0""" deprecate("IptcImagePlugin.dump", 12) for i in c: - print("%02x" % _i8(i), end=" ") + print(f"{_i8(i):02x}", end=" ") print() diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index c1ed7879741..65db70e133f 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -825,8 +825,9 @@ def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): try: stream_len = int(result[b"Length"]) except (TypeError, KeyError, ValueError) as e: - msg = "bad or missing Length in stream dict (%r)" % result.get( - b"Length", None + msg = ( + "bad or missing Length in stream dict " + f"({result.get(b'Length')})" ) raise PdfFormatError(msg) from e stream_data = data[m.end() : m.end() + stream_len] From 7d81cbd0ede0dd9e516f7c3b5e2a42988dd105b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 May 2024 13:59:30 +1000 Subject: [PATCH 138/300] Do not use percent format --- Tests/test_image_access.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 02c75073add..f37ae60960b 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -415,7 +415,9 @@ def test_embeddable(self) -> None: int main(int argc, char* argv[]) { - char *home = "%s"; + char *home = \"""" + + sys.prefix.replace("\\", "\\\\") + + """\"; wchar_t *whome = Py_DecodeLocale(home, NULL); Py_SetPythonHome(whome); @@ -432,7 +434,6 @@ def test_embeddable(self) -> None: return 0; } """ - % sys.prefix.replace("\\", "\\\\") ) compiler = getattr(build_ext, "new_compiler")() From c92f59d758e0a1e308b148f31dd3b7f3f68f94b4 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 7 May 2024 14:30:34 +0200 Subject: [PATCH 139/300] Add various type annotations --- src/PIL/Image.py | 58 +++++++++++++++++++++++++++++------------- src/PIL/ImageDraw.py | 19 +++++++------- src/PIL/ImageFont.py | 31 ++++++++++++---------- src/PIL/_imagingft.pyi | 37 ++++++++++++++++++++++++++- 4 files changed, 104 insertions(+), 41 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2184ef8ea9e..f81e95695a8 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast +from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast, overload # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -481,6 +481,8 @@ def _getscaleoffset(expr): # -------------------------------------------------------------------- # Implementation wrapper +class _GetDataTransform(Protocol): + def getdata(self) -> tuple[Transform, Sequence[int]]: ... class Image: """ @@ -1687,7 +1689,7 @@ def entropy(self, mask=None, extrema=None): return self.im.entropy(extrema) return self.im.entropy() - def paste(self, im, box=None, mask=None) -> None: + def paste(self, im: Image | str | int | tuple[int, ...], box: tuple[int, int, int, int] | tuple[int, int] | None = None, mask: Image | None = None) -> None: """ Pastes another image into this image. The box argument is either a 2-tuple giving the upper left corner, a 4-tuple defining the @@ -2122,7 +2124,7 @@ def _get_safe_box(self, size, resample, box): min(self.size[1], math.ceil(box[3] + support_y)), ) - def resize(self, size, resample=None, box=None, reducing_gap=None) -> Image: + def resize(self, size: tuple[int, int], resample: Resampling | None = None, box: tuple[float, float, float, float] | None = None, reducing_gap: float | None = None) -> Image: """ Returns a resized copy of this image. @@ -2228,7 +2230,7 @@ def resize(self, size, resample=None, box=None, reducing_gap=None) -> Image: return self._new(self.im.resize(size, resample, box)) - def reduce(self, factor, box=None): + def reduce(self, factor: int | tuple[int, int], box: tuple[int, int, int, int] | None = None) -> Image: """ Returns a copy of the image reduced ``factor`` times. If the size of the image is not dividable by ``factor``, @@ -2263,13 +2265,13 @@ def reduce(self, factor, box=None): def rotate( self, - angle, - resample=Resampling.NEAREST, - expand=0, - center=None, - translate=None, - fillcolor=None, - ): + angle: float, + resample: Resampling = Resampling.NEAREST, + expand: bool = False, + center: tuple[int, int] | None = None, + translate: tuple[int, int] | None = None, + fillcolor: float | tuple[float, ...] | str | None = None, + ) -> Image: """ Returns a rotated copy of this image. This method returns a copy of this image, rotated the given number of degrees counter @@ -2576,7 +2578,7 @@ def tell(self) -> int: """ return 0 - def thumbnail(self, size, resample=Resampling.BICUBIC, reducing_gap=2.0): + def thumbnail(self, size: tuple[int, int], resample: Resampling = Resampling.BICUBIC, reducing_gap: float = 2.0) -> None: """ Make this image into a thumbnail. This method modifies the image to contain a thumbnail version of itself, no larger than @@ -2664,14 +2666,34 @@ def round_aspect(number, key): # FIXME: the different transform methods need further explanation # instead of bloating the method docs, add a separate chapter. + @overload + def transform( + self, + size: tuple[int, int], + method: Transform | ImageTransformHandler, + data: Sequence[int], + resample: Resampling = Resampling.NEAREST, + fill: int = 1, + fillcolor: float | tuple[float, ...] | str | None = None, + ) -> Image: ... + @overload def transform( self, - size, - method, - data=None, - resample=Resampling.NEAREST, - fill=1, - fillcolor=None, + size: tuple[int, int], + method: _GetDataTransform, + data: None = None, + resample: Resampling = Resampling.NEAREST, + fill: int = 1, + fillcolor: float | tuple[float, ...] | str | None = None, + ) -> Image: ... + def transform( + self, + size: tuple[int, int], + method: Transform | ImageTransformHandler | _GetDataTransform, + data: Sequence[int] | None = None, + resample: Resampling = Resampling.NEAREST, + fill: int = 1, + fillcolor: float | tuple[float, ...] | str | None = None, ) -> Image: """ Transforms this image. This method creates a new image with the diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index d3efe64865e..579489fdeed 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -34,10 +34,11 @@ import math import numbers import struct -from typing import Sequence, cast +from typing import AnyStr, Sequence, cast from . import Image, ImageColor from ._typing import Coords +from .ImageFont import FreeTypeFont, ImageFont """ A simple 2D drawing interface for PIL images. @@ -92,7 +93,7 @@ def __init__(self, im: Image.Image, mode: str | None = None) -> None: self.fontmode = "L" # aliasing is okay for other modes self.fill = False - def getfont(self): + def getfont(self) -> FreeTypeFont | ImageFont: """ Get the current default font. @@ -450,12 +451,12 @@ def draw_corners(pieslice) -> None: right[3] -= r + 1 self.draw.draw_rectangle(right, ink, 1) - def _multiline_check(self, text) -> bool: + def _multiline_check(self, text: str | bytes) -> bool: split_character = "\n" if isinstance(text, str) else b"\n" return split_character in text - def _multiline_split(self, text) -> list[str | bytes]: + def _multiline_split(self, text: AnyStr) -> list[AnyStr]: split_character = "\n" if isinstance(text, str) else b"\n" return text.split(split_character) @@ -469,7 +470,7 @@ def _multiline_spacing(self, font, spacing, stroke_width): def text( self, - xy, + xy: tuple[int, int], text, fill=None, font=None, @@ -591,7 +592,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None) -> None: def multiline_text( self, - xy, + xy: tuple[int, int], text, fill=None, font=None, @@ -678,15 +679,15 @@ def multiline_text( def textlength( self, - text, - font=None, + text: str, + font: FreeTypeFont | ImageFont | None = None, direction=None, features=None, language=None, embedded_color=False, *, font_size=None, - ): + ) -> float: """Get the length of a given string, in pixels with 1/64 precision.""" if self._multiline_check(text): msg = "can't measure length of multiline text" diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 256c581df0c..536ee5fe607 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -33,12 +33,15 @@ import warnings from enum import IntEnum from io import BytesIO -from typing import BinaryIO +from typing import TYPE_CHECKING, BinaryIO from . import Image from ._typing import StrOrBytesPath from ._util import is_directory, is_path +if TYPE_CHECKING: + from _imagingft import Font + class Layout(IntEnum): BASIC = 0 @@ -56,7 +59,7 @@ class Layout(IntEnum): core = DeferredError.new(ex) -def _string_length_check(text): +def _string_length_check(text: str | bytes) -> None: if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH: msg = "too many characters in string" raise ValueError(msg) @@ -81,7 +84,9 @@ def _string_length_check(text): class ImageFont: """PIL font wrapper""" - def _load_pilfont(self, filename): + font: Font + + def _load_pilfont(self, filename: str) -> None: with open(filename, "rb") as fp: image = None for ext in (".png", ".gif", ".pbm"): @@ -153,7 +158,7 @@ def getmask(self, text, mode="", *args, **kwargs): Image._decompression_bomb_check(self.font.getsize(text)) return self.font.getmask(text, mode) - def getbbox(self, text, *args, **kwargs): + def getbbox(self, text: str, *args: object, **kwargs: object) -> tuple[int, int, int, int]: """ Returns bounding box (in pixels) of given text. @@ -171,7 +176,7 @@ def getbbox(self, text, *args, **kwargs): width, height = self.font.getsize(text) return 0, 0, width, height - def getlength(self, text, *args, **kwargs): + def getlength(self, text: str, *args: object, **kwargs: object) -> int: """ Returns length (in pixels) of given text. This is the amount by which following text should be offset. @@ -254,7 +259,7 @@ def __setstate__(self, state): path, size, index, encoding, layout_engine = state self.__init__(path, size, index, encoding, layout_engine) - def getname(self): + def getname(self) -> tuple[str, str]: """ :return: A tuple of the font family (e.g. Helvetica) and the font style (e.g. Bold) @@ -269,7 +274,7 @@ def getmetrics(self): """ return self.font.ascent, self.font.descent - def getlength(self, text, mode="", direction=None, features=None, language=None): + def getlength(self, text: str, mode="", direction=None, features=None, language=None) -> float: """ Returns length (in pixels with 1/64 precision) of given text when rendered in font with provided direction, features, and language. @@ -343,14 +348,14 @@ def getlength(self, text, mode="", direction=None, features=None, language=None) def getbbox( self, - text, + text: str, mode="", direction=None, features=None, language=None, stroke_width=0, anchor=None, - ): + ) -> tuple[int, int, int, int]: """ Returns bounding box (in pixels) of given text relative to given anchor when rendered in font with provided direction, features, and language. @@ -725,7 +730,7 @@ def getlength(self, text, *args, **kwargs): return self.font.getlength(text, *args, **kwargs) -def load(filename): +def load(filename: str) -> ImageFont: """ Load a font file. This function loads a font object from the given bitmap font file, and returns the corresponding font object. @@ -739,7 +744,7 @@ def load(filename): return f -def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): +def truetype(font: StrOrBytesPath | BinaryIO | None = None, size: float = 10, index: int = 0, encoding: str = "", layout_engine: Layout | None = None) -> FreeTypeFont: """ Load a TrueType or OpenType font from a file or file-like object, and create a font object. @@ -800,7 +805,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): :exception ValueError: If the font size is not greater than zero. """ - def freetype(font): + def freetype(font: StrOrBytesPath | BinaryIO | None) -> FreeTypeFont: return FreeTypeFont(font, size, index, encoding, layout_engine) try: @@ -850,7 +855,7 @@ def freetype(font): raise -def load_path(filename): +def load_path(filename: str | bytes) -> ImageFont: """ Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a bitmap font along the Python path. diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index e27843e5338..2c2ea9a54a7 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -1,3 +1,38 @@ -from typing import Any +from typing import Any, TypedDict + +class _Axis(TypedDict): + minimum: int | None + default: int | None + maximum: int | None + name: str | None + + +class Font: + @property + def family(self) -> str | None: ... + @property + def style(self) -> str | None: ... + @property + def ascent(self) -> int: ... + @property + def descent(self) -> int: ... + @property + def height(self) -> int: ... + @property + def x_ppem(self) -> int: ... + @property + def y_ppem(self) -> int: ... + @property + def glyphs(self) -> int: ... + + def render(self, string: str, fill, mode = ..., dir = ..., features = ..., lang = ..., stroke_width = ..., anchor = ..., foreground_ink_long = ..., x_start = ..., y_start = ..., /) -> tuple[Any, tuple[int, int]]: ... + def getsize(self, string: str, mode = ..., dir = ..., features = ..., lang = ..., anchor = ..., /) -> tuple[tuple[int, int], tuple[int, int]]: ... + def getlength(self, string: str, mode = ..., dir = ..., features = ..., lang = ..., /) -> int: ... + def getvarnames(self) -> list[str]: ... + def getvaraxes(self) -> list[_Axis]: ... + def setvarname(self, instance_index: int, /) -> None: ... + def setvaraxes(self, axes: list[float], /) -> None: ... + +def getfont(filename: str | bytes | bytearray, size, index = ..., encoding = ..., font_bytes = ..., layout_engine = ...) -> Font: ... def __getattr__(name: str) -> Any: ... From 1aa3886ed76b3f8fc60d604a34dffe573b491c20 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 12:33:59 +0000 Subject: [PATCH 140/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/Image.py | 30 ++++++++++++++++++++++++++---- src/PIL/ImageFont.py | 16 +++++++++++++--- src/PIL/_imagingft.pyi | 36 +++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f81e95695a8..9f55ea9242c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -481,9 +481,11 @@ def _getscaleoffset(expr): # -------------------------------------------------------------------- # Implementation wrapper + class _GetDataTransform(Protocol): def getdata(self) -> tuple[Transform, Sequence[int]]: ... + class Image: """ This class represents an image object. To create @@ -1689,7 +1691,12 @@ def entropy(self, mask=None, extrema=None): return self.im.entropy(extrema) return self.im.entropy() - def paste(self, im: Image | str | int | tuple[int, ...], box: tuple[int, int, int, int] | tuple[int, int] | None = None, mask: Image | None = None) -> None: + def paste( + self, + im: Image | str | int | tuple[int, ...], + box: tuple[int, int, int, int] | tuple[int, int] | None = None, + mask: Image | None = None, + ) -> None: """ Pastes another image into this image. The box argument is either a 2-tuple giving the upper left corner, a 4-tuple defining the @@ -2124,7 +2131,13 @@ def _get_safe_box(self, size, resample, box): min(self.size[1], math.ceil(box[3] + support_y)), ) - def resize(self, size: tuple[int, int], resample: Resampling | None = None, box: tuple[float, float, float, float] | None = None, reducing_gap: float | None = None) -> Image: + def resize( + self, + size: tuple[int, int], + resample: Resampling | None = None, + box: tuple[float, float, float, float] | None = None, + reducing_gap: float | None = None, + ) -> Image: """ Returns a resized copy of this image. @@ -2230,7 +2243,11 @@ def resize(self, size: tuple[int, int], resample: Resampling | None = None, box: return self._new(self.im.resize(size, resample, box)) - def reduce(self, factor: int | tuple[int, int], box: tuple[int, int, int, int] | None = None) -> Image: + def reduce( + self, + factor: int | tuple[int, int], + box: tuple[int, int, int, int] | None = None, + ) -> Image: """ Returns a copy of the image reduced ``factor`` times. If the size of the image is not dividable by ``factor``, @@ -2578,7 +2595,12 @@ def tell(self) -> int: """ return 0 - def thumbnail(self, size: tuple[int, int], resample: Resampling = Resampling.BICUBIC, reducing_gap: float = 2.0) -> None: + def thumbnail( + self, + size: tuple[int, int], + resample: Resampling = Resampling.BICUBIC, + reducing_gap: float = 2.0, + ) -> None: """ Make this image into a thumbnail. This method modifies the image to contain a thumbnail version of itself, no larger than diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 536ee5fe607..fb7e1d8b614 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -158,7 +158,9 @@ def getmask(self, text, mode="", *args, **kwargs): Image._decompression_bomb_check(self.font.getsize(text)) return self.font.getmask(text, mode) - def getbbox(self, text: str, *args: object, **kwargs: object) -> tuple[int, int, int, int]: + def getbbox( + self, text: str, *args: object, **kwargs: object + ) -> tuple[int, int, int, int]: """ Returns bounding box (in pixels) of given text. @@ -274,7 +276,9 @@ def getmetrics(self): """ return self.font.ascent, self.font.descent - def getlength(self, text: str, mode="", direction=None, features=None, language=None) -> float: + def getlength( + self, text: str, mode="", direction=None, features=None, language=None + ) -> float: """ Returns length (in pixels with 1/64 precision) of given text when rendered in font with provided direction, features, and language. @@ -744,7 +748,13 @@ def load(filename: str) -> ImageFont: return f -def truetype(font: StrOrBytesPath | BinaryIO | None = None, size: float = 10, index: int = 0, encoding: str = "", layout_engine: Layout | None = None) -> FreeTypeFont: +def truetype( + font: StrOrBytesPath | BinaryIO | None = None, + size: float = 10, + index: int = 0, + encoding: str = "", + layout_engine: Layout | None = None, +) -> FreeTypeFont: """ Load a TrueType or OpenType font from a file or file-like object, and create a font object. diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index 2c2ea9a54a7..987e7fd6f49 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -6,7 +6,6 @@ class _Axis(TypedDict): maximum: int | None name: str | None - class Font: @property def family(self) -> str | None: ... @@ -24,15 +23,38 @@ class Font: def y_ppem(self) -> int: ... @property def glyphs(self) -> int: ... - - def render(self, string: str, fill, mode = ..., dir = ..., features = ..., lang = ..., stroke_width = ..., anchor = ..., foreground_ink_long = ..., x_start = ..., y_start = ..., /) -> tuple[Any, tuple[int, int]]: ... - def getsize(self, string: str, mode = ..., dir = ..., features = ..., lang = ..., anchor = ..., /) -> tuple[tuple[int, int], tuple[int, int]]: ... - def getlength(self, string: str, mode = ..., dir = ..., features = ..., lang = ..., /) -> int: ... + def render( + self, + string: str, + fill, + mode=..., + dir=..., + features=..., + lang=..., + stroke_width=..., + anchor=..., + foreground_ink_long=..., + x_start=..., + y_start=..., + /, + ) -> tuple[Any, tuple[int, int]]: ... + def getsize( + self, string: str, mode=..., dir=..., features=..., lang=..., anchor=..., / + ) -> tuple[tuple[int, int], tuple[int, int]]: ... + def getlength( + self, string: str, mode=..., dir=..., features=..., lang=..., / + ) -> int: ... def getvarnames(self) -> list[str]: ... def getvaraxes(self) -> list[_Axis]: ... def setvarname(self, instance_index: int, /) -> None: ... def setvaraxes(self, axes: list[float], /) -> None: ... -def getfont(filename: str | bytes | bytearray, size, index = ..., encoding = ..., font_bytes = ..., layout_engine = ...) -> Font: ... - +def getfont( + filename: str | bytes | bytearray, + size, + index=..., + encoding=..., + font_bytes=..., + layout_engine=..., +) -> Font: ... def __getattr__(name: str) -> Any: ... From d44e9fccb16c63005fbffce06c16a0afc2b26667 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 7 May 2024 14:53:26 +0200 Subject: [PATCH 141/300] Various fixes --- src/PIL/Image.py | 46 ++++++++++++++++++++++++++++---------------- src/PIL/ImageFont.py | 2 +- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9f55ea9242c..f6f070feef3 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -483,7 +483,7 @@ def _getscaleoffset(expr): class _GetDataTransform(Protocol): - def getdata(self) -> tuple[Transform, Sequence[int]]: ... + def getdata(self) -> tuple[Transform, Sequence[float]]: ... class Image: @@ -2134,7 +2134,7 @@ def _get_safe_box(self, size, resample, box): def resize( self, size: tuple[int, int], - resample: Resampling | None = None, + resample: int | None = None, box: tuple[float, float, float, float] | None = None, reducing_gap: float | None = None, ) -> Image: @@ -2202,13 +2202,13 @@ def resize( msg = "reducing_gap must be 1.0 or greater" raise ValueError(msg) - size = tuple(size) + size = cast(tuple[int, int], tuple(size)) self.load() if box is None: box = (0, 0) + self.size else: - box = tuple(box) + box = cast(tuple[float, float, float, float], tuple(box)) if self.size == size and box == (0, 0) + self.size: return self.copy() @@ -2266,7 +2266,7 @@ def reduce( if box is None: box = (0, 0) + self.size else: - box = tuple(box) + box = cast(tuple[int, int, int, int], tuple(box)) if factor == (1, 1) and box == (0, 0) + self.size: return self.copy() @@ -2283,7 +2283,7 @@ def reduce( def rotate( self, angle: float, - resample: Resampling = Resampling.NEAREST, + resample: int = Resampling.NEAREST, expand: bool = False, center: tuple[int, int] | None = None, translate: tuple[int, int] | None = None, @@ -2598,7 +2598,7 @@ def tell(self) -> int: def thumbnail( self, size: tuple[int, int], - resample: Resampling = Resampling.BICUBIC, + resample: int = Resampling.BICUBIC, reducing_gap: float = 2.0, ) -> None: """ @@ -2661,20 +2661,22 @@ def round_aspect(number, key): box = None if reducing_gap is not None: - size = preserve_aspect_ratio() - if size is None: + preserved_size = preserve_aspect_ratio() + if preserved_size is None: return + size = preserved_size - res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap)) + res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap)) # type: ignore[arg-type] if res is not None: box = res[1] if box is None: self.load() # load() may have changed the size of the image - size = preserve_aspect_ratio() - if size is None: + preserved_size = preserve_aspect_ratio() + if preserved_size is None: return + size = preserved_size if self.size != size: im = self.resize(size, resample, box=box, reducing_gap=reducing_gap) @@ -2693,8 +2695,8 @@ def transform( self, size: tuple[int, int], method: Transform | ImageTransformHandler, - data: Sequence[int], - resample: Resampling = Resampling.NEAREST, + data: Sequence[float], + resample: int = Resampling.NEAREST, fill: int = 1, fillcolor: float | tuple[float, ...] | str | None = None, ) -> Image: ... @@ -2704,7 +2706,17 @@ def transform( size: tuple[int, int], method: _GetDataTransform, data: None = None, - resample: Resampling = Resampling.NEAREST, + resample: int = Resampling.NEAREST, + fill: int = 1, + fillcolor: float | tuple[float, ...] | str | None = None, + ) -> Image: ... + @overload + def transform( + self, + size: tuple[int, int], + method: Transform | ImageTransformHandler | _GetDataTransform, + data: Sequence[float] | None = None, + resample: int = Resampling.NEAREST, fill: int = 1, fillcolor: float | tuple[float, ...] | str | None = None, ) -> Image: ... @@ -2712,8 +2724,8 @@ def transform( self, size: tuple[int, int], method: Transform | ImageTransformHandler | _GetDataTransform, - data: Sequence[int] | None = None, - resample: Resampling = Resampling.NEAREST, + data: Sequence[float] | None = None, + resample: int = Resampling.NEAREST, fill: int = 1, fillcolor: float | tuple[float, ...] | str | None = None, ) -> Image: diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index fb7e1d8b614..a1b722765a3 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -40,7 +40,7 @@ from ._util import is_directory, is_path if TYPE_CHECKING: - from _imagingft import Font + from ._imagingft import Font class Layout(IntEnum): From d63caf266d2561b1646ed378761332e0855dd73d Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 7 May 2024 15:59:20 +0200 Subject: [PATCH 142/300] Various fixes --- src/PIL/Image.py | 44 +++++++-------------------------------- src/PIL/ImageDraw.py | 24 ++++++++++----------- src/PIL/ImageFont.py | 13 +++++++++--- src/PIL/ImageTransform.py | 4 ++-- src/PIL/_imaging.pyi | 15 +++++++++++++ 5 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f6f070feef3..9b0c24ec09a 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast, overload +from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -483,7 +483,9 @@ def _getscaleoffset(expr): class _GetDataTransform(Protocol): - def getdata(self) -> tuple[Transform, Sequence[float]]: ... + def getdata( + self, + ) -> tuple[Transform, Sequence[Any]]: ... class Image: @@ -2690,41 +2692,11 @@ def round_aspect(number, key): # FIXME: the different transform methods need further explanation # instead of bloating the method docs, add a separate chapter. - @overload - def transform( - self, - size: tuple[int, int], - method: Transform | ImageTransformHandler, - data: Sequence[float], - resample: int = Resampling.NEAREST, - fill: int = 1, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: ... - @overload - def transform( - self, - size: tuple[int, int], - method: _GetDataTransform, - data: None = None, - resample: int = Resampling.NEAREST, - fill: int = 1, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: ... - @overload - def transform( - self, - size: tuple[int, int], - method: Transform | ImageTransformHandler | _GetDataTransform, - data: Sequence[float] | None = None, - resample: int = Resampling.NEAREST, - fill: int = 1, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: ... def transform( self, size: tuple[int, int], method: Transform | ImageTransformHandler | _GetDataTransform, - data: Sequence[float] | None = None, + data: Sequence[Any] | None = None, resample: int = Resampling.NEAREST, fill: int = 1, fillcolor: float | tuple[float, ...] | str | None = None, @@ -2803,7 +2775,7 @@ def getdata(self): im.info = self.info.copy() if method == Transform.MESH: # list of quads - for box, quad in data: + for box, quad in cast(Sequence[tuple[float, float]], data): im.__transformer( box, self, Transform.QUAD, quad, resample, fillcolor is None ) @@ -2961,7 +2933,7 @@ def transform( self, size: tuple[int, int], image: Image, - **options: dict[str, str | int | tuple[int, ...] | list[int]], + **options: dict[str, str | int | tuple[int, ...] | list[int]] | int, ) -> Image: pass @@ -3830,7 +3802,7 @@ def _get_ifd_dict(self, offset, group=None): return self._fixup_dict(info) def _get_head(self): - version = b"\x2B" if self.bigtiff else b"\x2A" + version = b"\x2b" if self.bigtiff else b"\x2a" if self.endian == "<": head = b"II" + version + b"\x00" + o32le(8) else: diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 579489fdeed..ec8a9a67d5f 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -118,7 +118,7 @@ def getfont(self) -> FreeTypeFont | ImageFont: self.font = ImageFont.load_default() return self.font - def _getfont(self, font_size: float | None): + def _getfont(self, font_size: float | None) -> FreeTypeFont | ImageFont: if font_size is not None: from . import ImageFont @@ -451,13 +451,13 @@ def draw_corners(pieslice) -> None: right[3] -= r + 1 self.draw.draw_rectangle(right, ink, 1) - def _multiline_check(self, text: str | bytes) -> bool: - split_character = "\n" if isinstance(text, str) else b"\n" + def _multiline_check(self, text: AnyStr) -> bool: + split_character = cast(AnyStr, "\n" if isinstance(text, str) else b"\n") return split_character in text def _multiline_split(self, text: AnyStr) -> list[AnyStr]: - split_character = "\n" if isinstance(text, str) else b"\n" + split_character = cast(AnyStr, "\n" if isinstance(text, str) else b"\n") return text.split(split_character) @@ -470,10 +470,10 @@ def _multiline_spacing(self, font, spacing, stroke_width): def text( self, - xy: tuple[int, int], - text, + xy: tuple[float, float], + text: str, fill=None, - font=None, + font: FreeTypeFont | ImageFont | None = None, anchor=None, spacing=4, align="left", @@ -527,7 +527,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None) -> None: coord.append(int(xy[i])) start.append(math.modf(xy[i])[0]) try: - mask, offset = font.getmask2( + mask, offset = font.getmask2( # type: ignore[union-attr,misc] text, mode, direction=direction, @@ -543,7 +543,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None) -> None: coord = [coord[0] + offset[0], coord[1] + offset[1]] except AttributeError: try: - mask = font.getmask( + mask = font.getmask( # type: ignore[misc] text, mode, direction, @@ -592,7 +592,7 @@ def draw_text(ink, stroke_width=0, stroke_offset=None) -> None: def multiline_text( self, - xy: tuple[int, int], + xy: tuple[float, float], text, fill=None, font=None, @@ -625,7 +625,7 @@ def multiline_text( font = self._getfont(font_size) widths = [] - max_width = 0 + max_width: float = 0 lines = self._multiline_split(text) line_spacing = self._multiline_spacing(font, spacing, stroke_width) for line in lines: @@ -779,7 +779,7 @@ def multiline_textbbox( font = self._getfont(font_size) widths = [] - max_width = 0 + max_width: float = 0 lines = self._multiline_split(text) line_spacing = self._multiline_spacing(font, spacing, stroke_width) for line in lines: diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index a1b722765a3..9eca3bc9877 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -35,11 +35,14 @@ from io import BytesIO from typing import TYPE_CHECKING, BinaryIO +from PIL import ImageFile + from . import Image from ._typing import StrOrBytesPath from ._util import is_directory, is_path if TYPE_CHECKING: + from ._imaging import ImagingFont from ._imagingft import Font @@ -84,11 +87,11 @@ def _string_length_check(text: str | bytes) -> None: class ImageFont: """PIL font wrapper""" - font: Font + font: ImagingFont def _load_pilfont(self, filename: str) -> None: with open(filename, "rb") as fp: - image = None + image: ImageFile.ImageFile | None = None for ext in (".png", ".gif", ".pbm"): if image: image.close() @@ -198,6 +201,8 @@ def getlength(self, text: str, *args: object, **kwargs: object) -> int: class FreeTypeFont: """FreeType font wrapper (requires _imagingft service)""" + font: Font + def __init__( self, font: StrOrBytesPath | BinaryIO | None = None, @@ -261,7 +266,7 @@ def __setstate__(self, state): path, size, index, encoding, layout_engine = state self.__init__(path, size, index, encoding, layout_engine) - def getname(self) -> tuple[str, str]: + def getname(self) -> tuple[str | None, str | None]: """ :return: A tuple of the font family (e.g. Helvetica) and the font style (e.g. Bold) @@ -876,6 +881,7 @@ def load_path(filename: str | bytes) -> ImageFont: """ for directory in sys.path: if is_directory(directory): + assert isinstance(directory, str) if not isinstance(filename, str): filename = filename.decode("utf-8") try: @@ -900,6 +906,7 @@ def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: :return: A font object. """ + f: FreeTypeFont | ImageFont if core.__class__.__name__ == "module" or size is not None: f = truetype( BytesIO( diff --git a/src/PIL/ImageTransform.py b/src/PIL/ImageTransform.py index 6aa82dadd9c..80a6116b7cf 100644 --- a/src/PIL/ImageTransform.py +++ b/src/PIL/ImageTransform.py @@ -14,7 +14,7 @@ # from __future__ import annotations -from typing import Sequence +from typing import Any, Sequence from . import Image @@ -34,7 +34,7 @@ def transform( self, size: tuple[int, int], image: Image.Image, - **options: dict[str, str | int | tuple[int, ...] | list[int]], + **options: Any, ) -> Image.Image: """Perform the transform. Called from :py:meth:`.Image.transform`.""" # can be overridden diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index e27843e5338..d85eb84fa69 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -1,3 +1,18 @@ from typing import Any +from typing_extensions import Buffer + +class ImagingCore: + def __getattr__(self, name: str) -> Any: ... + +class ImagingFont: + def __getattr__(self, name: str) -> Any: ... + +class ImagingDraw: + def __getattr__(self, name: str) -> Any: ... + +class PixelAccess: + def __getattr__(self, name: str) -> Any: ... + +def font(image, glyphdata: Buffer) -> ImagingFont: ... def __getattr__(name: str) -> Any: ... From ed0867abecd7f4ce8eb300c4896229fcedeee6c4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2024 06:30:43 +1000 Subject: [PATCH 143/300] Set stream length for later use --- src/PIL/PdfParser.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 65db70e133f..c43f2da7baa 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -823,12 +823,10 @@ def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): m = cls.re_stream_start.match(data, offset) if m: try: - stream_len = int(result[b"Length"]) - except (TypeError, KeyError, ValueError) as e: - msg = ( - "bad or missing Length in stream dict " - f"({result.get(b'Length')})" - ) + stream_len_str = result.get(b"Length") + stream_len = int(stream_len_str) + except (TypeError, ValueError) as e: + msg = f"bad or missing Length in stream dict ({stream_len_str})" raise PdfFormatError(msg) from e stream_data = data[m.end() : m.end() + stream_len] m = cls.re_stream_end.match(data, m.end() + stream_len) From a3356879fd5b0685b3741d6f43802626a3f5d2e0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 May 2024 17:57:36 +1000 Subject: [PATCH 144/300] Use f-string --- Tests/test_image_access.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index f37ae60960b..e55a4d9c1fb 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -409,15 +409,14 @@ def test_embeddable(self) -> None: from setuptools.command import build_ext with open("embed_pil.c", "w", encoding="utf-8") as fh: + home = sys.prefix.replace("\\", "\\\\") fh.write( - """ + f""" #include "Python.h" int main(int argc, char* argv[]) -{ - char *home = \"""" - + sys.prefix.replace("\\", "\\\\") - + """\"; +{{ + char *home = "{home}"; wchar_t *whome = Py_DecodeLocale(home, NULL); Py_SetPythonHome(whome); @@ -432,7 +431,7 @@ def test_embeddable(self) -> None: PyMem_RawFree(whome); return 0; -} +}} """ ) From ef35d7926439e6fe8c36abc0846f859aaf3a893d Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 8 May 2024 12:14:37 +0200 Subject: [PATCH 145/300] Python 3.8 compatibility --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9b0c24ec09a..31e6fdb83a8 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2204,7 +2204,7 @@ def resize( msg = "reducing_gap must be 1.0 or greater" raise ValueError(msg) - size = cast(tuple[int, int], tuple(size)) + size = cast("tuple[int, int]", tuple(size)) self.load() if box is None: From 7ae8d37138c8678e4a84210aa899df422fadaab1 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 8 May 2024 12:14:59 +0200 Subject: [PATCH 146/300] Make `GetDataTransform` public --- src/PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 31e6fdb83a8..ed1621e6244 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -482,7 +482,7 @@ def _getscaleoffset(expr): # Implementation wrapper -class _GetDataTransform(Protocol): +class GetDataTransform(Protocol): def getdata( self, ) -> tuple[Transform, Sequence[Any]]: ... @@ -2695,7 +2695,7 @@ def round_aspect(number, key): def transform( self, size: tuple[int, int], - method: Transform | ImageTransformHandler | _GetDataTransform, + method: Transform | ImageTransformHandler | GetDataTransform, data: Sequence[Any] | None = None, resample: int = Resampling.NEAREST, fill: int = 1, From 296050f3823c4648e6e7eb351e433343eddc9cee Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 8 May 2024 12:26:45 +0200 Subject: [PATCH 147/300] More Python 3.8 compatibility --- src/PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ed1621e6244..8348ea257d8 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2210,7 +2210,7 @@ def resize( if box is None: box = (0, 0) + self.size else: - box = cast(tuple[float, float, float, float], tuple(box)) + box = cast("tuple[float, float, float, float]", tuple(box)) if self.size == size and box == (0, 0) + self.size: return self.copy() @@ -2268,7 +2268,7 @@ def reduce( if box is None: box = (0, 0) + self.size else: - box = cast(tuple[int, int, int, int], tuple(box)) + box = cast("tuple[int, int, int, int]", tuple(box)) if factor == (1, 1) and box == (0, 0) + self.size: return self.copy() From bb8718e58162cdcd6a9b80eca45f7b2c8321bca9 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 8 May 2024 12:54:44 +0200 Subject: [PATCH 148/300] Hopefully the last Python 3.8 instance :/ --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8348ea257d8..f39580996a3 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2775,7 +2775,7 @@ def getdata(self): im.info = self.info.copy() if method == Transform.MESH: # list of quads - for box, quad in cast(Sequence[tuple[float, float]], data): + for box, quad in cast("Sequence[tuple[float, float]]", data): im.__transformer( box, self, Transform.QUAD, quad, resample, fillcolor is None ) From 47580f257b1ae7c9b461108d1bf4ba7d2b65f1ac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 9 May 2024 08:51:12 +1000 Subject: [PATCH 149/300] Updated libjpeg-turbo to 3.0.3 --- .github/workflows/wheels-dependencies.sh | 2 +- winbuild/build_prepare.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 0d45d5a209d..930289c2ace 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -18,7 +18,7 @@ ARCHIVE_SDIR=pillow-depends-main FREETYPE_VERSION=2.13.2 HARFBUZZ_VERSION=8.4.0 LIBPNG_VERSION=1.6.43 -JPEGTURBO_VERSION=3.0.2 +JPEGTURBO_VERSION=3.0.3 OPENJPEG_VERSION=2.5.2 XZ_VERSION=5.4.5 TIFF_VERSION=4.6.0 diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0d6da77549f..9875d71e7b6 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -114,7 +114,7 @@ def cmd_msbuild( "FREETYPE": "2.13.2", "FRIBIDI": "1.0.13", "HARFBUZZ": "8.4.0", - "JPEGTURBO": "3.0.2", + "JPEGTURBO": "3.0.3", "LCMS2": "2.16", "LIBPNG": "1.6.43", "LIBWEBP": "1.3.2", From 431fe0dcc8ff8a28fbe89c1668d7090f247aaed8 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 10 May 2024 11:46:35 +0200 Subject: [PATCH 150/300] Rename protocol to SupportsGetData --- src/PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f39580996a3..154862a6fbc 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -482,7 +482,7 @@ def _getscaleoffset(expr): # Implementation wrapper -class GetDataTransform(Protocol): +class SupportsGetData(Protocol): def getdata( self, ) -> tuple[Transform, Sequence[Any]]: ... @@ -2695,7 +2695,7 @@ def round_aspect(number, key): def transform( self, size: tuple[int, int], - method: Transform | ImageTransformHandler | GetDataTransform, + method: Transform | ImageTransformHandler | SupportsGetData, data: Sequence[Any] | None = None, resample: int = Resampling.NEAREST, fill: int = 1, From 9b44abb6b7f77043ac337fe8171d0ecdbb4b7882 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 10 May 2024 11:48:36 +0200 Subject: [PATCH 151/300] Add SupportsGetData to documentation --- docs/reference/Image.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 0d9b4d93d77..c0d9095cd3c 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -365,6 +365,12 @@ Classes .. autoclass:: PIL.Image.ImagePointHandler .. autoclass:: PIL.Image.ImageTransformHandler +Protocols +--------- + +.. autoclass:: SupportsGetData + :show-inheritance: + Constants --------- From 57399ce204d79c74c80612c622bb788e20d786e8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 10 May 2024 22:43:56 +1000 Subject: [PATCH 152/300] Parse _version contents instead of using exec() --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 7d8e1c1ee21..abdd87ea252 100644 --- a/setup.py +++ b/setup.py @@ -23,8 +23,7 @@ def get_version(): version_file = "src/PIL/_version.py" with open(version_file, encoding="utf-8") as f: - exec(compile(f.read(), version_file, "exec")) - return locals()["__version__"] + return f.read().split('"')[1] configuration = {} From 18b87c8515941f7131b764d4293e3cdf638ba2ff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 May 2024 10:48:09 +1000 Subject: [PATCH 153/300] Added type hints --- src/PIL/BufrStubImagePlugin.py | 2 +- src/PIL/CurImagePlugin.py | 2 +- src/PIL/FpxImagePlugin.py | 2 +- src/PIL/FtexImagePlugin.py | 2 +- src/PIL/GbrImagePlugin.py | 2 +- src/PIL/GribStubImagePlugin.py | 2 +- src/PIL/ImImagePlugin.py | 2 +- src/PIL/ImageTk.py | 4 ++-- src/PIL/Jpeg2KImagePlugin.py | 4 ++-- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MpoImagePlugin.py | 2 +- src/PIL/PSDraw.py | 2 +- src/PIL/PdfParser.py | 16 ++++++++-------- src/PIL/PngImagePlugin.py | 6 +++--- src/PIL/PyAccess.py | 2 +- src/PIL/QoiImagePlugin.py | 2 +- src/PIL/WebPImagePlugin.py | 2 +- src/PIL/WmfImagePlugin.py | 2 +- src/PIL/XpmImagePlugin.py | 2 +- 19 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 1cbd50d1997..271db725852 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -37,7 +37,7 @@ class BufrStubImageFile(ImageFile.StubImageFile): format = "BUFR" format_description = "BUFR" - def _open(self): + def _open(self) -> None: offset = self.fp.tell() if not _accept(self.fp.read(4)): diff --git a/src/PIL/CurImagePlugin.py b/src/PIL/CurImagePlugin.py index b8790e20963..85e2145e766 100644 --- a/src/PIL/CurImagePlugin.py +++ b/src/PIL/CurImagePlugin.py @@ -37,7 +37,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): format = "CUR" format_description = "Windows Cursor" - def _open(self): + def _open(self) -> None: offset = self.fp.tell() # check magic diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index cfaf862399d..4ba93bb3912 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -237,7 +237,7 @@ def load(self): return ImageFile.ImageFile.load(self) - def close(self): + def close(self) -> None: self.ole.close() super().close() diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index a746959a3a8..7fcf8137684 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -71,7 +71,7 @@ class FtexImageFile(ImageFile.ImageFile): format = "FTEX" format_description = "Texture File Format (IW2:EOC)" - def _open(self): + def _open(self) -> None: if not _accept(self.fp.read(4)): msg = "not an FTEX file" raise SyntaxError(msg) diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index 62197e36c98..93e89b1e655 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -41,7 +41,7 @@ class GbrImageFile(ImageFile.ImageFile): format = "GBR" format_description = "GIMP brush file" - def _open(self): + def _open(self) -> None: header_size = i32(self.fp.read(4)) if header_size < 20: msg = "not a GIMP brush" diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index a80fe0a234a..13bdfa616e8 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -37,7 +37,7 @@ class GribStubImageFile(ImageFile.StubImageFile): format = "GRIB" format_description = "GRIB" - def _open(self): + def _open(self) -> None: offset = self.fp.tell() if not _accept(self.fp.read(8)): diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 9c16159e958..a325f8552d5 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -119,7 +119,7 @@ class ImImageFile(ImageFile.ImageFile): format_description = "IFUNC Image Memory" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: # Quick rejection: if there's not an LF among the first # 100 bytes, this is (probably) not a text header. diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 10b2cc69a1e..2f9d7f505ba 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -128,7 +128,7 @@ def __init__(self, image=None, size=None, **kw): if image: self.paste(image) - def __del__(self): + def __del__(self) -> None: name = self.__photo.name self.__photo.name = None try: @@ -219,7 +219,7 @@ def __init__(self, image=None, **kw): kw["data"] = image.tobitmap() self.__photo = tkinter.BitmapImage(**kw) - def __del__(self): + def __del__(self) -> None: name = self.__photo.name self.__photo.name = None try: diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 697bad22120..81ef32253bd 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -215,7 +215,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile): format = "JPEG2000" format_description = "JPEG 2000 (ISO 15444)" - def _open(self): + def _open(self) -> None: sig = self.fp.read(4) if sig == b"\xff\x4f\xff\x51": self.codec = "j2k" @@ -267,7 +267,7 @@ def _open(self): ) ] - def _parse_comment(self): + def _parse_comment(self) -> None: hdr = self.fp.read(2) length = _binary.i16be(hdr) self.fp.seek(length - 2, os.SEEK_CUR) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 715a358a3b7..7a3c99b6c30 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -462,7 +462,7 @@ def draft( box = (0, 0, original_size[0] / scale, original_size[1] / scale) return self.mode, box - def load_djpeg(self): + def load_djpeg(self) -> None: # ALTERNATIVE: handle JPEGs via the IJG command line utilities f, path = tempfile.mkstemp() diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index fb6620e7563..eba35fb4d8d 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -100,7 +100,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): format_description = "MPO (CIPA DC-007)" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: self.fp.seek(0) # prep the fp in order to pass the JPEG test JpegImagePlugin.JpegImageFile._open(self) self._after_jpeg_open() diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index 848fc2f716a..49c06ce1363 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -54,7 +54,7 @@ def begin_document(self, id=None): self.fp.write(b"%%EndProlog\n") self.isofont = {} - def end_document(self): + def end_document(self) -> None: """Ends printing. (Write PostScript DSC footer.)""" self.fp.write(b"%%EndDocument\nrestore showpage\n%%End\n") if hasattr(self.fp, "flush"): diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index c43f2da7baa..077c9ec8ba7 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -409,28 +409,28 @@ def __exit__(self, exc_type, exc_value, traceback): self.close() return False # do not suppress exceptions - def start_writing(self): + def start_writing(self) -> None: self.close_buf() self.seek_end() - def close_buf(self): + def close_buf(self) -> None: try: self.buf.close() except AttributeError: pass self.buf = None - def close(self): + def close(self) -> None: if self.should_close_buf: self.close_buf() if self.f is not None and self.should_close_file: self.f.close() self.f = None - def seek_end(self): + def seek_end(self) -> None: self.f.seek(0, os.SEEK_END) - def write_header(self): + def write_header(self) -> None: self.f.write(b"%PDF-1.4\n") def write_comment(self, s): @@ -450,7 +450,7 @@ def write_catalog(self): ) return self.root_ref - def rewrite_pages(self): + def rewrite_pages(self) -> None: pages_tree_nodes_to_delete = [] for i, page_ref in enumerate(self.orig_pages): page_info = self.cached_objects[page_ref] @@ -529,7 +529,7 @@ def write_obj(self, ref, *objs, **dict_obj): f.write(b"endobj\n") return ref - def del_root(self): + def del_root(self) -> None: if self.root_ref is None: return del self.xref_table[self.root_ref.object_id] @@ -547,7 +547,7 @@ def get_buf_from_file(f): except ValueError: # cannot mmap an empty file return b"" - def read_pdf_info(self): + def read_pdf_info(self) -> None: self.file_size_total = len(self.buf) self.file_size_this = self.file_size_total - self.start_offset self.read_trailer() diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 1547edde5b1..76e0abc317f 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -179,7 +179,7 @@ def __enter__(self): def __exit__(self, *args): self.close() - def close(self): + def close(self) -> None: self.queue = self.fp = None def push(self, cid, pos, length): @@ -370,14 +370,14 @@ def check_text_memory(self, chunklen): ) raise ValueError(msg) - def save_rewind(self): + def save_rewind(self) -> None: self.rewind_state = { "info": self.im_info.copy(), "tile": self.im_tile, "seq_num": self._seq_num, } - def rewind(self): + def rewind(self) -> None: self.im_info = self.rewind_state["info"].copy() self.im_tile = self.rewind_state["tile"] self._seq_num = self.rewind_state["seq_num"] diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 2c831913d69..a9da90613e7 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -70,7 +70,7 @@ def __init__(self, img, readonly=False): # logger.debug("%s", vals) self._post_init() - def _post_init(self): + def _post_init(self) -> None: pass def __setitem__(self, xy, color): diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index 2875b8d752b..cea8b60da81 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -21,7 +21,7 @@ class QoiImageFile(ImageFile.ImageFile): format = "QOI" format_description = "Quite OK Image" - def _open(self): + def _open(self) -> None: if not _accept(self.fp.read(4)): msg = "not a QOI file" raise SyntaxError(msg) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 61ae9eae500..052f253cf4e 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -43,7 +43,7 @@ class WebPImageFile(ImageFile.ImageFile): __loaded = 0 __logical_frame = 0 - def _open(self): + def _open(self) -> None: if not _webp.HAVE_WEBPANIM: # Legacy mode data, width, height, self._mode, icc_profile, exif = _webp.WebPDecode( diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 7f045ec7da4..b0328657b03 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -79,7 +79,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): format = "WMF" format_description = "Windows Metafile" - def _open(self): + def _open(self) -> None: self._inch = None # check placable header diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index a638547af61..88d14e9c2bd 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -36,7 +36,7 @@ class XpmImageFile(ImageFile.ImageFile): format = "XPM" format_description = "X11 Pixel Map" - def _open(self): + def _open(self) -> None: if not _accept(self.fp.read(9)): msg = "not an XPM file" raise SyntaxError(msg) From 13cf2bc70f4bb5de7c0a083303d4a232104d7852 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 May 2024 11:16:52 +1000 Subject: [PATCH 154/300] Moved SupportsArrayInterface under Protocols heading --- docs/reference/Image.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index c0d9095cd3c..d917a3c9271 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -78,8 +78,6 @@ Constructing images ^^^^^^^^^^^^^^^^^^^ .. autofunction:: new -.. autoclass:: SupportsArrayInterface - :show-inheritance: .. autofunction:: fromarray .. autofunction:: frombytes .. autofunction:: frombuffer @@ -368,6 +366,8 @@ Classes Protocols --------- +.. autoclass:: SupportsArrayInterface + :show-inheritance: .. autoclass:: SupportsGetData :show-inheritance: From 6310280428a49ea5495953a824b1dfa85a4d5223 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sat, 11 May 2024 10:44:52 +0200 Subject: [PATCH 155/300] Move an import behind the TYPE_CHECKING flag Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageFont.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 9eca3bc9877..f2936bae67d 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -35,13 +35,12 @@ from io import BytesIO from typing import TYPE_CHECKING, BinaryIO -from PIL import ImageFile - from . import Image from ._typing import StrOrBytesPath from ._util import is_directory, is_path if TYPE_CHECKING: + from . import ImageFile from ._imaging import ImagingFont from ._imagingft import Font From 6d6dfd176cf00a864a62dff6dd881099cc3bcec8 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sat, 11 May 2024 10:46:20 +0200 Subject: [PATCH 156/300] Revert unnecessary formatting change --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 154862a6fbc..53f38f0b241 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3802,7 +3802,7 @@ def _get_ifd_dict(self, offset, group=None): return self._fixup_dict(info) def _get_head(self): - version = b"\x2b" if self.bigtiff else b"\x2a" + version = b"\x2B" if self.bigtiff else b"\x2A" if self.endian == "<": head = b"II" + version + b"\x00" + o32le(8) else: From db4714c280c96ea4b61f1b3360c55e730ffaf62d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 12 May 2024 21:20:46 +1000 Subject: [PATCH 157/300] Removed helper.py modes --- Tests/helper.py | 27 --------------------------- Tests/test_image.py | 9 ++++----- Tests/test_image_access.py | 13 +++++++------ 3 files changed, 11 insertions(+), 38 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 1297c1c4309..5fd4fe3327e 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -29,33 +29,6 @@ uploader = "github_actions" -modes = ( - "1", - "L", - "LA", - "La", - "P", - "PA", - "F", - "I", - "I;16", - "I;16L", - "I;16B", - "I;16N", - "RGB", - "RGBA", - "RGBa", - "RGBX", - "BGR;15", - "BGR;16", - "BGR;24", - "CMYK", - "YCbCr", - "HSV", - "LAB", -) - - def upload(a: Image.Image, b: Image.Image) -> str | None: if uploader == "show": # local img.show for errors. diff --git a/Tests/test_image.py b/Tests/test_image.py index e1490d6a083..742d0dfe406 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -31,7 +31,6 @@ is_big_endian, is_win32, mark_if_feature_version, - modes, skip_unless_feature, ) @@ -46,7 +45,7 @@ def helper_image_new(mode: str, size: tuple[int, int]) -> Image.Image: class TestImage: - @pytest.mark.parametrize("mode", modes) + @pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"]) def test_image_modes_success(self, mode: str) -> None: helper_image_new(mode, (1, 1)) @@ -1027,7 +1026,7 @@ def test_close_graceful(self, caplog: pytest.LogCaptureFixture) -> None: class TestImageBytes: - @pytest.mark.parametrize("mode", modes) + @pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"]) def test_roundtrip_bytes_constructor(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() @@ -1039,7 +1038,7 @@ def test_roundtrip_bytes_constructor(self, mode: str) -> None: reloaded = Image.frombytes(mode, im.size, source_bytes) assert reloaded.tobytes() == source_bytes - @pytest.mark.parametrize("mode", modes) + @pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"]) def test_roundtrip_bytes_method(self, mode: str) -> None: im = hopper(mode) source_bytes = im.tobytes() @@ -1048,7 +1047,7 @@ def test_roundtrip_bytes_method(self, mode: str) -> None: reloaded.frombytes(source_bytes) assert reloaded.tobytes() == source_bytes - @pytest.mark.parametrize("mode", modes) + @pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"]) def test_getdata_putdata(self, mode: str) -> None: if is_big_endian() and mode == "BGR;15": pytest.xfail("Known failure of BGR;15 on big-endian") diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index e55a4d9c1fb..9d600667937 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -10,7 +10,7 @@ from PIL import Image -from .helper import assert_image_equal, hopper, is_win32, modes +from .helper import assert_image_equal, hopper, is_win32 # CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2 # https://github.com/eliben/pycparser/pull/198#issuecomment-317001670 @@ -205,12 +205,13 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None: with pytest.raises(error): im.getpixel((-1, -1)) - @pytest.mark.parametrize("mode", modes) + @pytest.mark.parametrize("mode", Image.MODES) def test_basic(self, mode: str) -> None: - if mode.startswith("BGR;"): - with pytest.warns(DeprecationWarning): - self.check(mode) - else: + self.check(mode) + + @pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24")) + def test_deprecated(self, mode: str) -> None: + with pytest.warns(DeprecationWarning): self.check(mode) def test_list(self) -> None: From 00e5e43da42ec9cd4da6c80ae269536a975f1217 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 12 May 2024 11:43:08 +0000 Subject: [PATCH 158/300] chore(deps): update dependency cibuildwheel to v2.18.0 --- .ci/requirements-cibw.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/requirements-cibw.txt b/.ci/requirements-cibw.txt index 45c2af975ae..8d39ea9bbab 100644 --- a/.ci/requirements-cibw.txt +++ b/.ci/requirements-cibw.txt @@ -1 +1 @@ -cibuildwheel==2.17.0 +cibuildwheel==2.18.0 From a8d154877d92d549f1d18411383441d1c9238e7c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 May 2024 18:47:51 +1000 Subject: [PATCH 159/300] Added type hints --- src/PIL/FliImagePlugin.py | 2 +- src/PIL/GifImagePlugin.py | 10 +++++----- src/PIL/GimpPaletteFile.py | 2 +- src/PIL/ImImagePlugin.py | 4 ++-- src/PIL/ImageDraw.py | 7 +++++-- src/PIL/ImageFilter.py | 2 +- src/PIL/ImagePalette.py | 6 +++--- src/PIL/ImageTk.py | 12 ++++++------ src/PIL/Jpeg2KImagePlugin.py | 4 ++-- src/PIL/PdfParser.py | 20 ++++++++++---------- src/PIL/SpiderImagePlugin.py | 10 +++++++--- src/PIL/TiffImagePlugin.py | 14 +++++++------- src/PIL/WebPImagePlugin.py | 2 +- 13 files changed, 51 insertions(+), 44 deletions(-) diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index eea2c0c951f..dceb839279a 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -132,7 +132,7 @@ def seek(self, frame: int) -> None: for f in range(self.__frame + 1, frame + 1): self._seek(f) - def _seek(self, frame): + def _seek(self, frame: int) -> None: if frame == 0: self.__frame = -1 self._fp.seek(self.__rewind) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 26e5958191b..eede4154994 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -82,7 +82,7 @@ def data(self) -> bytes | None: return self.fp.read(s[0]) return None - def _is_palette_needed(self, p): + def _is_palette_needed(self, p: bytes) -> bool: for i in range(0, len(p), 3): if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): return True @@ -474,7 +474,7 @@ def tell(self) -> int: RAWMODE = {"1": "L", "L": "L", "P": "P"} -def _normalize_mode(im): +def _normalize_mode(im: Image.Image) -> Image.Image: """ Takes an image (or frame), returns an image in a mode that is appropriate for saving in a Gif. @@ -887,7 +887,7 @@ def _get_optimize(im, info): return used_palette_colors -def _get_color_table_size(palette_bytes): +def _get_color_table_size(palette_bytes: bytes) -> int: # calculate the palette size for the header if not palette_bytes: return 0 @@ -897,7 +897,7 @@ def _get_color_table_size(palette_bytes): return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 -def _get_header_palette(palette_bytes): +def _get_header_palette(palette_bytes: bytes) -> bytes: """ Returns the palette, null padded to the next power of 2 (*3) bytes suitable for direct inclusion in the GIF header @@ -915,7 +915,7 @@ def _get_header_palette(palette_bytes): return palette_bytes -def _get_palette_bytes(im): +def _get_palette_bytes(im: Image.Image) -> bytes: """ Gets the palette for inclusion in the gif header diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index a3109ebaa1b..2274f1a8bfe 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -53,5 +53,5 @@ def __init__(self, fp): self.palette = b"".join(self.palette) - def getpalette(self): + def getpalette(self) -> tuple[bytes, str]: return self.palette, self.rawmode diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index a325f8552d5..8e949ebaf9d 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -271,11 +271,11 @@ def _open(self) -> None: self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] @property - def n_frames(self): + def n_frames(self) -> int: return self.info[FRAMES] @property - def is_animated(self): + def is_animated(self) -> bool: return self.info[FRAMES] > 1 def seek(self, frame: int) -> None: diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index d3efe64865e..42f2ee8c799 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -34,7 +34,7 @@ import math import numbers import struct -from typing import Sequence, cast +from typing import TYPE_CHECKING, Sequence, cast from . import Image, ImageColor from ._typing import Coords @@ -92,7 +92,10 @@ def __init__(self, im: Image.Image, mode: str | None = None) -> None: self.fontmode = "L" # aliasing is okay for other modes self.fill = False - def getfont(self): + if TYPE_CHECKING: + from . import ImageFont + + def getfont(self) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: """ Get the current default font. diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index fa9ebd9defb..678bd29a2d7 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -544,7 +544,7 @@ def transform(self, callback, with_normals=False, channels=None, target_mode=Non _copy_table=False, ) - def __repr__(self): + def __repr__(self) -> str: r = [ f"{self.__class__.__name__} from {self.table.__class__.__name__}", "size={:d}x{:d}x{:d}".format(*self.size), diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 770d10025c8..ae5c5dec0da 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -66,7 +66,7 @@ def colors(self): def colors(self, colors): self._colors = colors - def copy(self): + def copy(self) -> ImagePalette: new = ImagePalette() new.mode = self.mode @@ -77,7 +77,7 @@ def copy(self): return new - def getdata(self): + def getdata(self) -> tuple[str, bytes]: """ Get palette contents in format suitable for the low-level ``im.putpalette`` primitive. @@ -88,7 +88,7 @@ def getdata(self): return self.rawmode, self.palette return self.mode, self.tobytes() - def tobytes(self): + def tobytes(self) -> bytes: """Convert palette to bytes. .. warning:: This method is experimental. diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 2f9d7f505ba..6e2e7db1e19 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -136,7 +136,7 @@ def __del__(self) -> None: except Exception: pass # ignore internal errors - def __str__(self): + def __str__(self) -> str: """ Get the Tkinter photo image identifier. This method is automatically called by Tkinter whenever a PhotoImage object is passed to a Tkinter @@ -146,7 +146,7 @@ def __str__(self): """ return str(self.__photo) - def width(self): + def width(self) -> int: """ Get the width of the image. @@ -154,7 +154,7 @@ def width(self): """ return self.__size[0] - def height(self): + def height(self) -> int: """ Get the height of the image. @@ -227,7 +227,7 @@ def __del__(self) -> None: except Exception: pass # ignore internal errors - def width(self): + def width(self) -> int: """ Get the width of the image. @@ -235,7 +235,7 @@ def width(self): """ return self.__size[0] - def height(self): + def height(self) -> int: """ Get the height of the image. @@ -243,7 +243,7 @@ def height(self): """ return self.__size[1] - def __str__(self): + def __str__(self) -> str: """ Get the Tkinter bitmap image identifier. This method is automatically called by Tkinter whenever a BitmapImage object is passed to a Tkinter diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 81ef32253bd..ce6342bdba2 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -63,12 +63,12 @@ def read_fields(self, field_format): data = self._read_bytes(size) return struct.unpack(field_format, data) - def read_boxes(self): + def read_boxes(self) -> BoxReader: size = self.remaining_in_box data = self._read_bytes(size) return BoxReader(io.BytesIO(data), size) - def has_next_box(self): + def has_next_box(self) -> bool: if self.has_length: return self.fp.tell() + self.remaining_in_box < self.length else: diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 077c9ec8ba7..68501d625d4 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -87,10 +87,10 @@ class IndirectReferenceTuple(NamedTuple): class IndirectReference(IndirectReferenceTuple): - def __str__(self): + def __str__(self) -> str: return f"{self.object_id} {self.generation} R" - def __bytes__(self): + def __bytes__(self) -> bytes: return self.__str__().encode("us-ascii") def __eq__(self, other): @@ -108,7 +108,7 @@ def __hash__(self): class IndirectObjectDef(IndirectReference): - def __str__(self): + def __str__(self) -> str: return f"{self.object_id} {self.generation} obj" @@ -150,7 +150,7 @@ def __delitem__(self, key): def __contains__(self, key): return key in self.existing_entries or key in self.new_entries - def __len__(self): + def __len__(self) -> int: return len( set(self.existing_entries.keys()) | set(self.new_entries.keys()) @@ -211,7 +211,7 @@ def __init__(self, name): else: self.name = name.encode("us-ascii") - def name_as_str(self): + def name_as_str(self) -> str: return self.name.decode("us-ascii") def __eq__(self, other): @@ -222,7 +222,7 @@ def __eq__(self, other): def __hash__(self): return hash(self.name) - def __repr__(self): + def __repr__(self) -> str: return f"{self.__class__.__name__}({repr(self.name)})" @classmethod @@ -231,7 +231,7 @@ def from_pdf_stream(cls, data): allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} - def __bytes__(self): + def __bytes__(self) -> bytes: result = bytearray(b"/") for b in self.name: if b in self.allowed_chars: @@ -242,7 +242,7 @@ def __bytes__(self): class PdfArray(List[Any]): - def __bytes__(self): + def __bytes__(self) -> bytes: return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" @@ -286,7 +286,7 @@ def __getattr__(self, key): value = time.gmtime(calendar.timegm(value) + offset) return value - def __bytes__(self): + def __bytes__(self) -> bytes: out = bytearray(b"<<") for key, value in self.items(): if value is None: @@ -304,7 +304,7 @@ class PdfBinary: def __init__(self, data): self.data = data - def __bytes__(self): + def __bytes__(self) -> bytes: return b"<%s>" % b"".join(b"%02X" % b for b in self.data) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 21509b2d99f..5b8ad47f0cd 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -37,6 +37,7 @@ import os import struct import sys +from typing import TYPE_CHECKING from . import Image, ImageFile @@ -157,11 +158,11 @@ def _open(self) -> None: self._fp = self.fp # FIXME: hack @property - def n_frames(self): + def n_frames(self) -> int: return self._nimages @property - def is_animated(self): + def is_animated(self) -> bool: return self._nimages > 1 # 1st image index is zero (although SPIDER imgnumber starts at 1) @@ -191,8 +192,11 @@ def convert2byte(self, depth=255): b = -m * minimum return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + if TYPE_CHECKING: + from . import ImageTk + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 - def tkPhotoImage(self): + def tkPhotoImage(self) -> ImageTk.PhotoImage: from . import ImageTk return ImageTk.PhotoImage(self.convert2byte(), palette=256) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index a7a7e28bdbc..54faa59c55f 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -381,7 +381,7 @@ def limit_rational(self, max_denominator): f = self._val.limit_denominator(max_denominator) return f.numerator, f.denominator - def __repr__(self): + def __repr__(self) -> str: return str(float(self._val)) def __hash__(self): @@ -603,7 +603,7 @@ def reset(self): self._next = None self._offset = None - def __str__(self): + def __str__(self) -> str: return str(dict(self)) def named(self): @@ -617,7 +617,7 @@ def named(self): for code, value in self.items() } - def __len__(self): + def __len__(self) -> int: return len(set(self._tagdata) | set(self._tags_v2)) def __getitem__(self, tag): @@ -1041,7 +1041,7 @@ def from_v2(cls, original): ifd.next = original.next # an indicator for multipage tiffs return ifd - def to_v2(self): + def to_v2(self) -> ImageFileDirectory_v2: """Returns an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` instance with the same data as is contained in the original @@ -1061,7 +1061,7 @@ def to_v2(self): def __contains__(self, tag): return tag in self._tags_v1 or tag in self._tagdata - def __len__(self): + def __len__(self) -> int: return len(set(self._tagdata) | set(self._tags_v1)) def __iter__(self): @@ -1154,7 +1154,7 @@ def seek(self, frame: int) -> None: Image._decompression_bomb_check(self.size) self.im = Image.core.new(self.mode, self.size) - def _seek(self, frame): + def _seek(self, frame: int) -> None: self.fp = self._fp # reset buffered io handle in case fp @@ -2003,7 +2003,7 @@ def __exit__(self, exc_type, exc_value, traceback): self.close() return False - def tell(self): + def tell(self) -> int: return self.f.tell() - self.offsetOfNewPage def seek(self, offset, whence=io.SEEK_SET): diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 052f253cf4e..4b8cfe65c7e 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -144,7 +144,7 @@ def _get_next(self): timestamp -= duration return data, timestamp, duration - def _seek(self, frame): + def _seek(self, frame: int) -> None: if self.__physical_frame == frame: return # Nothing to do if frame < self.__physical_frame: From b15ce5a4bce4c5ec5d2816e271e0496e7198ae06 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 May 2024 21:51:16 +1000 Subject: [PATCH 160/300] Do not detect Ultra HDR images as MPO --- Tests/images/ultrahdr.jpg | Bin 0 -> 520842 bytes Tests/test_file_mpo.py | 5 +++++ src/PIL/JpegImagePlugin.py | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 Tests/images/ultrahdr.jpg diff --git a/Tests/images/ultrahdr.jpg b/Tests/images/ultrahdr.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1c5cc4adfe07ff4bbf80e79a784caf287c3b867f GIT binary patch literal 520842 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%0AWLXd`zuo~ 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index a5018870058..39b9c60b756 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -226,6 +226,11 @@ def test_eoferror() -> None: im.seek(n_frames - 1) +def test_ultra_hdr() -> None: + with Image.open("Tests/images/ultrahdr.jpg") as im: + assert im.format == "JPEG" + + @pytest.mark.parametrize("test_file", test_files) def test_image_grab(test_file: str) -> None: with Image.open(test_file) as im: diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 7a3c99b6c30..10940179a12 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -843,6 +843,10 @@ def jpeg_factory(fp=None, filename=None): try: mpheader = im._getmp() if mpheader[45057] > 1: + for segment, content in im.applist: + if segment == "APP1" and b' hdrgm:Version="' in content: + # Ultra HDR images are not yet supported + return im # It's actually an MPO from .MpoImagePlugin import MpoImageFile From e9b15f8091431de34d1a5f82cc0cf954d2cf2d6c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 14 May 2024 10:09:44 +1000 Subject: [PATCH 161/300] Updated harfbuzz to 8.5.0 --- .github/workflows/wheels-dependencies.sh | 2 +- winbuild/build_prepare.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 0d45d5a209d..c5b279a339d 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -16,7 +16,7 @@ ARCHIVE_SDIR=pillow-depends-main # Package versions for fresh source builds FREETYPE_VERSION=2.13.2 -HARFBUZZ_VERSION=8.4.0 +HARFBUZZ_VERSION=8.5.0 LIBPNG_VERSION=1.6.43 JPEGTURBO_VERSION=3.0.2 OPENJPEG_VERSION=2.5.2 diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0d6da77549f..b654ee8da97 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -113,7 +113,7 @@ def cmd_msbuild( "BROTLI": "1.1.0", "FREETYPE": "2.13.2", "FRIBIDI": "1.0.13", - "HARFBUZZ": "8.4.0", + "HARFBUZZ": "8.5.0", "JPEGTURBO": "3.0.2", "LCMS2": "2.16", "LIBPNG": "1.6.43", From e419fd7ab4a71bc269a9c624c92d8955b372aaed Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 15 May 2024 20:19:09 +1000 Subject: [PATCH 162/300] Added type hints --- src/PIL/DdsImagePlugin.py | 2 +- src/PIL/EpsImagePlugin.py | 4 +-- src/PIL/FtexImagePlugin.py | 2 +- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/ImageFile.py | 4 +-- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MpoImagePlugin.py | 2 +- src/PIL/PngImagePlugin.py | 74 +++++++++++++++++++++----------------- src/PIL/WebPImagePlugin.py | 2 +- src/PIL/XpmImagePlugin.py | 7 ++-- src/PIL/features.py | 27 +++++++------- 11 files changed, 65 insertions(+), 63 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index b2ddbe44fdc..1575f2d88b6 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -472,7 +472,7 @@ def _open(self) -> None: else: self.tile = [ImageFile._Tile("raw", extents, 0, rawmode or self.mode)] - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: pass diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index b57daca56d4..5a44baa491e 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -42,7 +42,7 @@ gs_windows_binary = None -def has_ghostscript(): +def has_ghostscript() -> bool: global gs_binary, gs_windows_binary if gs_binary is None: if sys.platform.startswith("win"): @@ -404,7 +404,7 @@ def load(self, scale=1, transparency=False): self.tile = [] return Image.Image.load(self) - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: # we can't incrementally load, so force ImageFile.parser to # use our custom load method by defining this method. pass diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index 7fcf8137684..5acbb4912f7 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -103,7 +103,7 @@ def _open(self) -> None: self.fp.close() self.fp = BytesIO(data) - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: pass diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index eacffbae645..cea093f9c6c 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -341,7 +341,7 @@ def load(self): self.size = im.size - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: # Flag the ImageFile.Parser so that it # just does all the decode at the end. pass diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index b93e2ad2ce6..33467fc4fbe 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -324,11 +324,11 @@ def load_end(self) -> None: pass # may be defined for contained formats - # def load_seek(self, pos): + # def load_seek(self, pos: int) -> None: # pass # may be defined for blocked formats (e.g. PNG) - # def load_read(self, read_bytes): + # def load_read(self, read_bytes: int) -> bytes: # pass def _seek_check(self, frame): diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 7a3c99b6c30..909911dfe37 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -408,7 +408,7 @@ def _open(self): msg = "no marker found" raise SyntaxError(msg) - def load_read(self, read_bytes): + def load_read(self, read_bytes: int) -> bytes: """ internal: read more image data For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index eba35fb4d8d..766e1290ced 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -124,7 +124,7 @@ def _after_jpeg_open(self, mpheader=None): # for now we can only handle reading and individual frame extraction self.readonly = 1 - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: self._fp.seek(pos) def seek(self, frame: int) -> None: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 76e0abc317f..c74cbccf1bf 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -39,6 +39,7 @@ import warnings import zlib from enum import IntEnum +from typing import IO from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence from ._binary import i16be as i16 @@ -149,14 +150,15 @@ def _crc32(data, seed=0): class ChunkStream: - def __init__(self, fp): - self.fp = fp - self.queue = [] + def __init__(self, fp: IO[bytes]) -> None: + self.fp: IO[bytes] | None = fp + self.queue: list[tuple[bytes, int, int]] | None = [] - def read(self): + def read(self) -> tuple[bytes, int, int]: """Fetch a new chunk. Returns header information.""" cid = None + assert self.fp is not None if self.queue: cid, pos, length = self.queue.pop() self.fp.seek(pos) @@ -173,7 +175,7 @@ def read(self): return cid, pos, length - def __enter__(self): + def __enter__(self) -> ChunkStream: return self def __exit__(self, *args): @@ -182,7 +184,8 @@ def __exit__(self, *args): def close(self) -> None: self.queue = self.fp = None - def push(self, cid, pos, length): + def push(self, cid: bytes, pos: int, length: int) -> None: + assert self.queue is not None self.queue.append((cid, pos, length)) def call(self, cid, pos, length): @@ -191,7 +194,7 @@ def call(self, cid, pos, length): logger.debug("STREAM %r %s %s", cid, pos, length) return getattr(self, f"chunk_{cid.decode('ascii')}")(pos, length) - def crc(self, cid, data): + def crc(self, cid: bytes, data: bytes) -> None: """Read and verify checksum""" # Skip CRC checks for ancillary chunks if allowed to load truncated @@ -201,6 +204,7 @@ def crc(self, cid, data): self.crc_skip(cid, data) return + assert self.fp is not None try: crc1 = _crc32(data, _crc32(cid)) crc2 = i32(self.fp.read(4)) @@ -211,12 +215,13 @@ def crc(self, cid, data): msg = f"broken PNG file (incomplete checksum in {repr(cid)})" raise SyntaxError(msg) from e - def crc_skip(self, cid, data): + def crc_skip(self, cid: bytes, data: bytes) -> None: """Read checksum""" + assert self.fp is not None self.fp.read(4) - def verify(self, endchunk=b"IEND"): + def verify(self, endchunk: bytes = b"IEND") -> list[bytes]: # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. @@ -361,7 +366,7 @@ def __init__(self, fp): self.text_memory = 0 - def check_text_memory(self, chunklen): + def check_text_memory(self, chunklen: int) -> None: self.text_memory += chunklen if self.text_memory > MAX_TEXT_MEMORY: msg = ( @@ -382,7 +387,7 @@ def rewind(self) -> None: self.im_tile = self.rewind_state["tile"] self._seq_num = self.rewind_state["seq_num"] - def chunk_iCCP(self, pos, length): + def chunk_iCCP(self, pos: int, length: int) -> bytes: # ICC profile s = ImageFile._safe_read(self.fp, length) # according to PNG spec, the iCCP chunk contains: @@ -409,7 +414,7 @@ def chunk_iCCP(self, pos, length): self.im_info["icc_profile"] = icc_profile return s - def chunk_IHDR(self, pos, length): + def chunk_IHDR(self, pos: int, length: int) -> bytes: # image header s = ImageFile._safe_read(self.fp, length) if length < 13: @@ -446,14 +451,14 @@ def chunk_IEND(self, pos, length): msg = "end of PNG image" raise EOFError(msg) - def chunk_PLTE(self, pos, length): + def chunk_PLTE(self, pos: int, length: int) -> bytes: # palette s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": self.im_palette = "RGB", s return s - def chunk_tRNS(self, pos, length): + def chunk_tRNS(self, pos: int, length: int) -> bytes: # transparency s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": @@ -473,13 +478,13 @@ def chunk_tRNS(self, pos, length): self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4) return s - def chunk_gAMA(self, pos, length): + def chunk_gAMA(self, pos: int, length: int) -> bytes: # gamma setting s = ImageFile._safe_read(self.fp, length) self.im_info["gamma"] = i32(s) / 100000.0 return s - def chunk_cHRM(self, pos, length): + def chunk_cHRM(self, pos: int, length: int) -> bytes: # chromaticity, 8 unsigned ints, actual value is scaled by 100,000 # WP x,y, Red x,y, Green x,y Blue x,y @@ -488,7 +493,7 @@ def chunk_cHRM(self, pos, length): self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) return s - def chunk_sRGB(self, pos, length): + def chunk_sRGB(self, pos: int, length: int) -> bytes: # srgb rendering intent, 1 byte # 0 perceptual # 1 relative colorimetric @@ -504,7 +509,7 @@ def chunk_sRGB(self, pos, length): self.im_info["srgb"] = s[0] return s - def chunk_pHYs(self, pos, length): + def chunk_pHYs(self, pos: int, length: int) -> bytes: # pixels per unit s = ImageFile._safe_read(self.fp, length) if length < 9: @@ -521,7 +526,7 @@ def chunk_pHYs(self, pos, length): self.im_info["aspect"] = px, py return s - def chunk_tEXt(self, pos, length): + def chunk_tEXt(self, pos: int, length: int) -> bytes: # text s = ImageFile._safe_read(self.fp, length) try: @@ -540,7 +545,7 @@ def chunk_tEXt(self, pos, length): return s - def chunk_zTXt(self, pos, length): + def chunk_zTXt(self, pos: int, length: int) -> bytes: # compressed text s = ImageFile._safe_read(self.fp, length) try: @@ -574,7 +579,7 @@ def chunk_zTXt(self, pos, length): return s - def chunk_iTXt(self, pos, length): + def chunk_iTXt(self, pos: int, length: int) -> bytes: # international text r = s = ImageFile._safe_read(self.fp, length) try: @@ -614,13 +619,13 @@ def chunk_iTXt(self, pos, length): return s - def chunk_eXIf(self, pos, length): + def chunk_eXIf(self, pos: int, length: int) -> bytes: s = ImageFile._safe_read(self.fp, length) self.im_info["exif"] = b"Exif\x00\x00" + s return s # APNG chunks - def chunk_acTL(self, pos, length): + def chunk_acTL(self, pos: int, length: int) -> bytes: s = ImageFile._safe_read(self.fp, length) if length < 8: if ImageFile.LOAD_TRUNCATED_IMAGES: @@ -640,7 +645,7 @@ def chunk_acTL(self, pos, length): self.im_custom_mimetype = "image/apng" return s - def chunk_fcTL(self, pos, length): + def chunk_fcTL(self, pos: int, length: int) -> bytes: s = ImageFile._safe_read(self.fp, length) if length < 26: if ImageFile.LOAD_TRUNCATED_IMAGES: @@ -669,7 +674,7 @@ def chunk_fcTL(self, pos, length): self.im_info["blend"] = s[25] return s - def chunk_fdAT(self, pos, length): + def chunk_fdAT(self, pos: int, length: int) -> bytes: if length < 4: if ImageFile.LOAD_TRUNCATED_IMAGES: s = ImageFile._safe_read(self.fp, length) @@ -701,7 +706,7 @@ class PngImageFile(ImageFile.ImageFile): format = "PNG" format_description = "Portable network graphics" - def _open(self): + def _open(self) -> None: if not _accept(self.fp.read(8)): msg = "not a PNG file" raise SyntaxError(msg) @@ -711,8 +716,8 @@ def _open(self): # # Parse headers up to the first IDAT or fDAT chunk - self.private_chunks = [] - self.png = PngStream(self.fp) + self.private_chunks: list[tuple[bytes, bytes] | tuple[bytes, bytes, bool]] = [] + self.png: PngStream | None = PngStream(self.fp) while True: # @@ -793,6 +798,7 @@ def verify(self) -> None: # back up to beginning of IDAT block self.fp.seek(self.tile[0][2] - 8) + assert self.png is not None self.png.verify() self.png.close() @@ -921,9 +927,10 @@ def load_prepare(self) -> None: self.__idat = self.__prepare_idat # used by load_read() ImageFile.ImageFile.load_prepare(self) - def load_read(self, read_bytes): + def load_read(self, read_bytes: int) -> bytes: """internal: read more image data""" + assert self.png is not None while self.__idat == 0: # end of chunk, skip forward to next one @@ -956,6 +963,7 @@ def load_read(self, read_bytes): def load_end(self) -> None: """internal: finished reading image data""" + assert self.png is not None if self.__idat != 0: self.fp.read(self.__idat) while True: @@ -1079,7 +1087,7 @@ def __init__(self, fp, chunk): self.fp = fp self.chunk = chunk - def write(self, data): + def write(self, data: bytes) -> None: self.chunk(self.fp, b"IDAT", data) @@ -1091,7 +1099,7 @@ def __init__(self, fp, chunk, seq_num): self.chunk = chunk self.seq_num = seq_num - def write(self, data): + def write(self, data: bytes) -> None: self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) self.seq_num += 1 @@ -1436,10 +1444,10 @@ def getchunks(im, **params): class collector: data = [] - def write(self, data): + def write(self, data: bytes) -> None: pass - def append(self, chunk): + def append(self, chunk: bytes) -> None: self.data.append(chunk) def append(fp, cid, *data): diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 4b8cfe65c7e..cae124e9f4c 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -171,7 +171,7 @@ def load(self): return super().load() - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: pass def tell(self) -> int: diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index 88d14e9c2bd..8d56331e6aa 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -103,16 +103,13 @@ def _open(self) -> None: self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))] - def load_read(self, read_bytes): + def load_read(self, read_bytes: int) -> bytes: # # load all image data in one chunk xsize, ysize = self.size - s = [None] * ysize - - for i in range(ysize): - s[i] = self.fp.readline()[1 : xsize + 1].ljust(xsize) + s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)] return b"".join(s) diff --git a/src/PIL/features.py b/src/PIL/features.py index 95c6c84cce3..16c749f148b 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -18,7 +18,7 @@ } -def check_module(feature): +def check_module(feature: str) -> bool: """ Checks if a module is available. @@ -42,7 +42,7 @@ def check_module(feature): return False -def version_module(feature): +def version_module(feature: str) -> str | None: """ :param feature: The module to check for. :returns: @@ -54,13 +54,10 @@ def version_module(feature): module, ver = modules[feature] - if ver is None: - return None - return getattr(__import__(module, fromlist=[ver]), ver) -def get_supported_modules(): +def get_supported_modules() -> list[str]: """ :returns: A list of all supported modules. """ @@ -75,7 +72,7 @@ def get_supported_modules(): } -def check_codec(feature): +def check_codec(feature: str) -> bool: """ Checks if a codec is available. @@ -92,7 +89,7 @@ def check_codec(feature): return f"{codec}_encoder" in dir(Image.core) -def version_codec(feature): +def version_codec(feature: str) -> str | None: """ :param feature: The codec to check for. :returns: @@ -113,7 +110,7 @@ def version_codec(feature): return version -def get_supported_codecs(): +def get_supported_codecs() -> list[str]: """ :returns: A list of all supported codecs. """ @@ -133,7 +130,7 @@ def get_supported_codecs(): } -def check_feature(feature): +def check_feature(feature: str) -> bool | None: """ Checks if a feature is available. @@ -157,7 +154,7 @@ def check_feature(feature): return None -def version_feature(feature): +def version_feature(feature: str) -> str | None: """ :param feature: The feature to check for. :returns: The version number as a string, or ``None`` if not available. @@ -174,14 +171,14 @@ def version_feature(feature): return getattr(__import__(module, fromlist=[ver]), ver) -def get_supported_features(): +def get_supported_features() -> list[str]: """ :returns: A list of all supported features. """ return [f for f in features if check_feature(f)] -def check(feature): +def check(feature: str) -> bool | None: """ :param feature: A module, codec, or feature name. :returns: @@ -199,7 +196,7 @@ def check(feature): return False -def version(feature): +def version(feature: str) -> str | None: """ :param feature: The module, codec, or feature to check for. @@ -215,7 +212,7 @@ def version(feature): return None -def get_supported(): +def get_supported() -> list[str]: """ :returns: A list of all supported modules, features, and codecs. """ From 1036aa74a7d91eda68d3f3f0bf4f59a87c020b0b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 16 May 2024 17:47:35 +1000 Subject: [PATCH 163/300] Added Image.WARN_POSSIBLE_FORMATS --- Tests/test_image.py | 11 +++++++++++ docs/reference/Image.rst | 5 +++++ src/PIL/Image.py | 15 ++++++++------- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 742d0dfe406..7ec4195e5cf 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -116,6 +116,17 @@ def test_open_formats(self) -> None: assert im.mode == "RGB" assert im.size == (128, 128) + def test_open_verbose_failure(self) -> None: + im = io.BytesIO(b"") + Image.WARN_POSSIBLE_FORMATS = True + try: + with pytest.warns(UserWarning): + with pytest.raises(UnidentifiedImageError): + with Image.open(im): + pass + finally: + Image.WARN_POSSIBLE_FORMATS = False + def test_width_height(self) -> None: im = Image.new("RGB", (1, 2)) assert im.width == 1 diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 0d9b4d93d77..f25c9cd9d46 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -374,6 +374,11 @@ Constants Set to 89,478,485, approximately 0.25GB for a 24-bit (3 bpp) image. See :py:meth:`~PIL.Image.open` for more information about how this is used. +.. data:: WARN_POSSIBLE_FORMATS + + Set to false. If true, when an image cannot be identified, warnings will be raised + from formats that attempted to read the data. + Transpose methods ^^^^^^^^^^^^^^^^^ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 958b95e3b0f..89c64998141 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -76,6 +76,8 @@ class DecompressionBombError(Exception): pass +WARN_POSSIBLE_FORMATS: bool = False + # Limit to around a quarter gigabyte for a 24-bit (3 bpp) image MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3) @@ -3344,7 +3346,7 @@ def open( preinit() - accept_warnings: list[str] = [] + warning_messages: list[str] = [] def _open_core( fp: IO[bytes], @@ -3360,16 +3362,15 @@ def _open_core( factory, accept = OPEN[i] result = not accept or accept(prefix) if isinstance(result, str): - accept_warnings.append(result) + warning_messages.append(result) elif result: fp.seek(0) im = factory(fp, filename) _decompression_bomb_check(im.size) return im - except (SyntaxError, IndexError, TypeError, struct.error): - # Leave disabled by default, spams the logs with image - # opening failures that are entirely expected. - # logger.debug("", exc_info=True) + except (SyntaxError, IndexError, TypeError, struct.error) as e: + if WARN_POSSIBLE_FORMATS: + warning_messages.append(i + " opening failed. " + str(e)) continue except BaseException: if exclusive_fp: @@ -3395,7 +3396,7 @@ def _open_core( if exclusive_fp: fp.close() - for message in accept_warnings: + for message in warning_messages: warnings.warn(message) msg = "cannot identify image file %r" % (filename if filename else fp) raise UnidentifiedImageError(msg) From 3062ec4dd2343258e91c240645d20d1c5b6cecf1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 16 May 2024 22:55:03 +1000 Subject: [PATCH 164/300] Fix type errors --- src/_imagingcms.c | 2 +- src/display.c | 2 +- src/libImaging/Dib.c | 2 +- src/libImaging/TiffDecode.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 63d78f84daa..4b0e21d7a4e 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -622,7 +622,7 @@ cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) { static PyObject * cms_get_display_profile_win32(PyObject *self, PyObject *args) { char filename[MAX_PATH]; - cmsUInt32Number filename_size; + DWORD filename_size; BOOL ok; HANDLE handle = 0; diff --git a/src/display.c b/src/display.c index 6b66ddafb87..abf94f1e1a7 100644 --- a/src/display.c +++ b/src/display.c @@ -716,7 +716,7 @@ PyImaging_DrawWmf(PyObject *self, PyObject *args) { HDC dc; RECT rect; PyObject *buffer = NULL; - char *ptr; + void *ptr; char *data; Py_ssize_t datasize; diff --git a/src/libImaging/Dib.c b/src/libImaging/Dib.c index 1b5bfe1324e..5194bfca3cb 100644 --- a/src/libImaging/Dib.c +++ b/src/libImaging/Dib.c @@ -95,7 +95,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) { } dib->bitmap = - CreateDIBSection(dib->dc, dib->info, DIB_RGB_COLORS, &dib->bits, NULL, 0); + CreateDIBSection(dib->dc, dib->info, DIB_RGB_COLORS, (void **)&dib->bits, NULL, 0); if (!dib->bitmap) { free(dib->info); free(dib); diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index e3b81590ec2..4874dd26ab5 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -973,7 +973,7 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt } if (state->state == 1 && !clientstate->fp) { - int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes); + int read = (int)_tiffReadProc((thandle_t)clientstate, (tdata_t)buffer, (tsize_t)bytes); TRACE( ("Buffer: %p: %c%c%c%c\n", buffer, From 2a703a2d4c94eefb224de1cbd7c2209af643a258 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 May 2024 08:04:20 +1000 Subject: [PATCH 165/300] Removed continue --- src/PIL/Image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 89c64998141..514a543c2d3 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3371,7 +3371,6 @@ def _open_core( except (SyntaxError, IndexError, TypeError, struct.error) as e: if WARN_POSSIBLE_FORMATS: warning_messages.append(i + " opening failed. " + str(e)) - continue except BaseException: if exclusive_fp: fp.close() From 7554e2cfbf25ea3a9b2da322a36b06be2a840b56 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 May 2024 08:44:04 +1000 Subject: [PATCH 166/300] Use monkeypatch --- Tests/test_image.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 7ec4195e5cf..a03e5eced07 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -116,16 +116,14 @@ def test_open_formats(self) -> None: assert im.mode == "RGB" assert im.size == (128, 128) - def test_open_verbose_failure(self) -> None: + def test_open_verbose_failure(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(Image, "WARN_POSSIBLE_FORMATS", True) + im = io.BytesIO(b"") - Image.WARN_POSSIBLE_FORMATS = True - try: - with pytest.warns(UserWarning): - with pytest.raises(UnidentifiedImageError): - with Image.open(im): - pass - finally: - Image.WARN_POSSIBLE_FORMATS = False + with pytest.warns(UserWarning): + with pytest.raises(UnidentifiedImageError): + with Image.open(im): + pass def test_width_height(self) -> None: im = Image.new("RGB", (1, 2)) From 8a3a72e51d57360df6e91f6f73e73707f181069f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 18 May 2024 16:06:50 +1000 Subject: [PATCH 167/300] Added type hints --- docs/reference/ImageFile.rst | 4 ++++ src/PIL/BlpImagePlugin.py | 22 +++++++++--------- src/PIL/BufrStubImagePlugin.py | 4 ++-- src/PIL/DcxImagePlugin.py | 4 ++-- src/PIL/GimpPaletteFile.py | 9 ++++---- src/PIL/GribStubImagePlugin.py | 4 ++-- src/PIL/Hdf5StubImagePlugin.py | 8 ++++--- src/PIL/Image.py | 4 +++- src/PIL/ImageFile.py | 10 +++++++++ src/PIL/ImageFilter.py | 17 ++++++++------ src/PIL/ImageFont.py | 4 ++-- src/PIL/ImagePalette.py | 14 ++++++------ src/PIL/ImageWin.py | 22 ++++++++++-------- src/PIL/JpegImagePlugin.py | 15 +++++++------ src/PIL/PSDraw.py | 41 ++++++++++++++++++++-------------- src/PIL/PdfParser.py | 10 ++++----- src/PIL/PngImagePlugin.py | 8 +++---- src/PIL/PyAccess.py | 12 ++++++---- src/PIL/TiffImagePlugin.py | 2 +- src/PIL/WebPImagePlugin.py | 5 +++-- src/PIL/WmfImagePlugin.py | 10 ++++----- 21 files changed, 135 insertions(+), 94 deletions(-) diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst index 047990f1c2a..e59c7311a99 100644 --- a/docs/reference/ImageFile.rst +++ b/docs/reference/ImageFile.rst @@ -57,6 +57,10 @@ Classes :undoc-members: :show-inheritance: +.. autoclass:: PIL.ImageFile.StubHandler() + :members: + :show-inheritance: + .. autoclass:: PIL.ImageFile.StubImageFile() :members: :show-inheritance: diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index bdf54baae13..782e28cf5df 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -55,7 +55,7 @@ class AlphaEncoding(IntEnum): DXT5 = 7 -def unpack_565(i): +def unpack_565(i: int) -> tuple[int, int, int]: return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 @@ -284,7 +284,8 @@ def decode(self, buffer): raise OSError(msg) from e return -1, 0 - def _read_blp_header(self): + def _read_blp_header(self) -> None: + assert self.fd is not None self.fd.seek(4) (self._blp_compression,) = struct.unpack(" bytes: return ImageFile._safe_read(self.fd, length) - def _read_palette(self): + def _read_palette(self) -> list[tuple[int, int, int, int]]: ret = [] for i in range(256): try: @@ -349,29 +350,30 @@ def _load(self) -> None: msg = f"Unsupported BLP compression {repr(self._blp_encoding)}" raise BLPFormatError(msg) - def _decode_jpeg_stream(self): + def _decode_jpeg_stream(self) -> None: from .JpegImagePlugin import JpegImageFile (jpeg_header_size,) = struct.unpack(" None: palette = self._read_palette() + assert self.fd is not None self.fd.seek(self._blp_offsets[0]) if self._blp_compression == 1: diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 271db725852..826e89dafea 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -15,7 +15,7 @@ _handler = None -def register_handler(handler): +def register_handler(handler: ImageFile.StubHandler) -> None: """ Install application-specific BUFR image handler. @@ -54,7 +54,7 @@ def _open(self) -> None: if loader: loader.open(self) - def _load(self): + def _load(self) -> ImageFile.StubHandler | None: return _handler diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index 1c455b032cd..f67f27d73bb 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -42,7 +42,7 @@ class DcxImageFile(PcxImageFile): format_description = "Intel DCX" _close_exclusive_fp_after_loading = False - def _open(self): + def _open(self) -> None: # Header s = self.fp.read(4) if not _accept(s): @@ -58,7 +58,7 @@ def _open(self): self._offset.append(offset) self._fp = self.fp - self.frame = None + self.frame = -1 self.n_frames = len(self._offset) self.is_animated = self.n_frames > 1 self.seek(0) diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index 2274f1a8bfe..4cad0ebeea1 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -16,6 +16,7 @@ from __future__ import annotations import re +from typing import IO from ._binary import o8 @@ -25,8 +26,8 @@ class GimpPaletteFile: rawmode = "RGB" - def __init__(self, fp): - self.palette = [o8(i) * 3 for i in range(256)] + def __init__(self, fp: IO[bytes]) -> None: + palette = [o8(i) * 3 for i in range(256)] if fp.readline()[:12] != b"GIMP Palette": msg = "not a GIMP palette file" @@ -49,9 +50,9 @@ def __init__(self, fp): msg = "bad palette entry" raise ValueError(msg) - self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) + palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) - self.palette = b"".join(self.palette) + self.palette = b"".join(palette) def getpalette(self) -> tuple[bytes, str]: return self.palette, self.rawmode diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index 13bdfa616e8..c27cffab63e 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -15,7 +15,7 @@ _handler = None -def register_handler(handler): +def register_handler(handler: ImageFile.StubHandler) -> None: """ Install application-specific GRIB image handler. @@ -54,7 +54,7 @@ def _open(self) -> None: if loader: loader.open(self) - def _load(self): + def _load(self) -> ImageFile.StubHandler | None: return _handler diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index afbfd16393c..c8d7866a312 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -10,12 +10,14 @@ # from __future__ import annotations +from typing import IO + from . import Image, ImageFile _handler = None -def register_handler(handler): +def register_handler(handler: ImageFile.StubHandler) -> None: """ Install application-specific HDF5 image handler. @@ -54,11 +56,11 @@ def _open(self) -> None: if loader: loader.open(self) - def _load(self): + def _load(self) -> ImageFile.StubHandler | None: return _handler -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "HDF5 save handler not installed" raise OSError(msg) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 958b95e3b0f..38ff0bfe4d6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1948,7 +1948,9 @@ def putalpha(self, alpha): self.im.putband(alpha.im, band) - def putdata(self, data, scale=1.0, offset=0.0): + def putdata( + self, data: Sequence[float], scale: float = 1.0, offset: float = 0.0 + ) -> None: """ Copies pixel data from a flattened sequence object into the image. The values should start at the upper left corner (0, 0), continue to the diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 33467fc4fbe..f0e49238760 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -28,6 +28,7 @@ # from __future__ import annotations +import abc import io import itertools import struct @@ -347,6 +348,15 @@ def _seek_check(self, frame): return self.tell() != frame +class StubHandler: + def open(self, im: StubImageFile) -> None: + pass + + @abc.abstractmethod + def load(self, im: StubImageFile) -> Image.Image: + pass + + class StubImageFile(ImageFile): """ Base class for stub image loaders. diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 678bd29a2d7..43e700b7b30 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -18,6 +18,7 @@ import abc import functools +from typing import Sequence class Filter: @@ -79,7 +80,7 @@ class RankFilter(Filter): name = "Rank" - def __init__(self, size, rank): + def __init__(self, size: int, rank: int) -> None: self.size = size self.rank = rank @@ -101,7 +102,7 @@ class MedianFilter(RankFilter): name = "Median" - def __init__(self, size=3): + def __init__(self, size: int = 3) -> None: self.size = size self.rank = size * size // 2 @@ -116,7 +117,7 @@ class MinFilter(RankFilter): name = "Min" - def __init__(self, size=3): + def __init__(self, size: int = 3) -> None: self.size = size self.rank = 0 @@ -131,7 +132,7 @@ class MaxFilter(RankFilter): name = "Max" - def __init__(self, size=3): + def __init__(self, size: int = 3) -> None: self.size = size self.rank = size * size - 1 @@ -147,7 +148,7 @@ class ModeFilter(Filter): name = "Mode" - def __init__(self, size=3): + def __init__(self, size: int = 3) -> None: self.size = size def filter(self, image): @@ -165,7 +166,7 @@ class GaussianBlur(MultibandFilter): name = "GaussianBlur" - def __init__(self, radius=2): + def __init__(self, radius: float | Sequence[float] = 2) -> None: self.radius = radius def filter(self, image): @@ -228,7 +229,9 @@ class UnsharpMask(MultibandFilter): name = "UnsharpMask" - def __init__(self, radius=2, percent=150, threshold=3): + def __init__( + self, radius: float = 2, percent: int = 150, threshold: int = 3 + ) -> None: self.radius = radius self.percent = percent self.threshold = threshold diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 256c581df0c..5446bc0c09b 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -261,7 +261,7 @@ def getname(self): """ return self.font.family, self.font.style - def getmetrics(self): + def getmetrics(self) -> tuple[int, int]: """ :return: A tuple of the font ascent (the distance from the baseline to the highest outline point) and descent (the distance from the @@ -628,7 +628,7 @@ def font_variant( layout_engine=layout_engine or self.layout_engine, ) - def get_variation_names(self): + def get_variation_names(self) -> list[bytes]: """ :returns: A list of the named styles in a variation font. :exception OSError: If the font is not a variation font. diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index ae5c5dec0da..057ccd1d7c4 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -18,7 +18,7 @@ from __future__ import annotations import array -from typing import Sequence +from typing import IO, Sequence from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile @@ -166,7 +166,7 @@ def getcolor(self, color, image=None) -> int: msg = f"unknown color specifier: {repr(color)}" raise ValueError(msg) - def save(self, fp): + def save(self, fp: str | IO[str]) -> None: """Save palette to text file. .. warning:: This method is experimental. @@ -213,29 +213,29 @@ def make_linear_lut(black, white): raise NotImplementedError(msg) # FIXME -def make_gamma_lut(exp): +def make_gamma_lut(exp: float) -> list[int]: return [int(((i / 255.0) ** exp) * 255.0 + 0.5) for i in range(256)] -def negative(mode="RGB"): +def negative(mode: str = "RGB") -> ImagePalette: palette = list(range(256 * len(mode))) palette.reverse() return ImagePalette(mode, [i // len(mode) for i in palette]) -def random(mode="RGB"): +def random(mode: str = "RGB") -> ImagePalette: from random import randint palette = [randint(0, 255) for _ in range(256 * len(mode))] return ImagePalette(mode, palette) -def sepia(white="#fff0c0"): +def sepia(white: str = "#fff0c0") -> ImagePalette: bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)] return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)]) -def wedge(mode="RGB"): +def wedge(mode: str = "RGB") -> ImagePalette: palette = list(range(256 * len(mode))) return ImagePalette(mode, [i // len(mode) for i in palette]) diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 77e57a4157b..6c29e2590cf 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -28,10 +28,10 @@ class HDC: methods. """ - def __init__(self, dc): + def __init__(self, dc: int) -> None: self.dc = dc - def __int__(self): + def __int__(self) -> int: return self.dc @@ -42,10 +42,10 @@ class HWND: methods, instead of a DC. """ - def __init__(self, wnd): + def __init__(self, wnd: int) -> None: self.wnd = wnd - def __int__(self): + def __int__(self) -> int: return self.wnd @@ -149,7 +149,9 @@ def query_palette(self, handle): result = self.image.query_palette(handle) return result - def paste(self, im, box=None): + def paste( + self, im: Image.Image, box: tuple[int, int, int, int] | None = None + ) -> None: """ Paste a PIL image into the bitmap image. @@ -169,16 +171,16 @@ def paste(self, im, box=None): else: self.image.paste(im.im) - def frombytes(self, buffer): + def frombytes(self, buffer: bytes) -> None: """ Load display memory contents from byte data. :param buffer: A buffer containing display data (usually data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) """ - return self.image.frombytes(buffer) + self.image.frombytes(buffer) - def tobytes(self): + def tobytes(self) -> bytes: """ Copy display memory contents to bytes object. @@ -190,7 +192,9 @@ def tobytes(self): class Window: """Create a Window with the given title size.""" - def __init__(self, title="PIL", width=None, height=None): + def __init__( + self, title: str = "PIL", width: int | None = None, height: int | None = None + ) -> None: self.hwnd = Image.core.createwindow( title, self.__dispatcher, width or 0, height or 0 ) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 909911dfe37..9fea4e7d133 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -42,6 +42,7 @@ import sys import tempfile import warnings +from typing import Any from . import Image, ImageFile from ._binary import i16be as i16 @@ -54,7 +55,7 @@ # Parser -def Skip(self, marker): +def Skip(self: JpegImageFile, marker: int) -> None: n = i16(self.fp.read(2)) - 2 ImageFile._safe_read(self.fp, n) @@ -191,7 +192,7 @@ def APP(self, marker): self.info["dpi"] = 72, 72 -def COM(self, marker): +def COM(self: JpegImageFile, marker: int) -> None: # # Comment marker. Store these in the APP dictionary. n = i16(self.fp.read(2)) - 2 @@ -202,7 +203,7 @@ def COM(self, marker): self.applist.append(("COM", s)) -def SOF(self, marker): +def SOF(self: JpegImageFile, marker: int) -> None: # # Start of frame marker. Defines the size and mode of the # image. JPEG is colour blind, so we use some simple @@ -250,7 +251,7 @@ def SOF(self, marker): self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2])) -def DQT(self, marker): +def DQT(self: JpegImageFile, marker: int) -> None: # # Define quantization table. Note that there might be more # than one table in each marker. @@ -493,13 +494,13 @@ def load_djpeg(self) -> None: self.tile = [] - def _getexif(self): + def _getexif(self) -> dict[str, Any] | None: return _getexif(self) def _getmp(self): return _getmp(self) - def getxmp(self): + def getxmp(self) -> dict[str, Any]: """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. @@ -515,7 +516,7 @@ def getxmp(self): return {} -def _getexif(self): +def _getexif(self) -> dict[str, Any] | None: if "exif" not in self.info: return None return self.getexif()._get_merged_dict() diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index 49c06ce1363..4e2b9788ef3 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -17,6 +17,7 @@ from __future__ import annotations import sys +from typing import TYPE_CHECKING from . import EpsImagePlugin @@ -38,7 +39,7 @@ def __init__(self, fp=None): fp = sys.stdout self.fp = fp - def begin_document(self, id=None): + def begin_document(self, id: str | None = None) -> None: """Set up printing of a document. (Write PostScript DSC header.)""" # FIXME: incomplete self.fp.write( @@ -52,7 +53,7 @@ def begin_document(self, id=None): self.fp.write(EDROFF_PS) self.fp.write(VDI_PS) self.fp.write(b"%%EndProlog\n") - self.isofont = {} + self.isofont: dict[bytes, int] = {} def end_document(self) -> None: """Ends printing. (Write PostScript DSC footer.)""" @@ -60,22 +61,24 @@ def end_document(self) -> None: if hasattr(self.fp, "flush"): self.fp.flush() - def setfont(self, font, size): + def setfont(self, font: str, size: int) -> None: """ Selects which font to use. :param font: A PostScript font name :param size: Size in points. """ - font = bytes(font, "UTF-8") - if font not in self.isofont: + font_bytes = bytes(font, "UTF-8") + if font_bytes not in self.isofont: # reencode font - self.fp.write(b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font, font)) - self.isofont[font] = 1 + self.fp.write( + b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font_bytes, font_bytes) + ) + self.isofont[font_bytes] = 1 # rough - self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font)) + self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font_bytes)) - def line(self, xy0, xy1): + def line(self, xy0: tuple[int, int], xy1: tuple[int, int]) -> None: """ Draws a line between the two points. Coordinates are given in PostScript point coordinates (72 points per inch, (0, 0) is the lower @@ -83,7 +86,7 @@ def line(self, xy0, xy1): """ self.fp.write(b"%d %d %d %d Vl\n" % (*xy0, *xy1)) - def rectangle(self, box): + def rectangle(self, box: tuple[int, int, int, int]) -> None: """ Draws a rectangle. @@ -92,18 +95,22 @@ def rectangle(self, box): """ self.fp.write(b"%d %d M 0 %d %d Vr\n" % box) - def text(self, xy, text): + def text(self, xy: tuple[int, int], text: str) -> None: """ Draws text at the given position. You must use :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. """ - text = bytes(text, "UTF-8") - text = b"\\(".join(text.split(b"(")) - text = b"\\)".join(text.split(b")")) - xy += (text,) - self.fp.write(b"%d %d M (%s) S\n" % xy) + text_bytes = bytes(text, "UTF-8") + text_bytes = b"\\(".join(text_bytes.split(b"(")) + text_bytes = b"\\)".join(text_bytes.split(b")")) + self.fp.write(b"%d %d M (%s) S\n" % (xy + (text_bytes,))) - def image(self, box, im, dpi=None): + if TYPE_CHECKING: + from . import Image + + def image( + self, box: tuple[int, int, int, int], im: Image.Image, dpi: int | None = None + ) -> None: """Draw a PIL image, centered in the given box.""" # default resolution depends on mode if not dpi: diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 68501d625d4..a6c24e67179 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -13,7 +13,7 @@ # see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set # on page 656 -def encode_text(s): +def encode_text(s: str) -> bytes: return codecs.BOM_UTF16_BE + s.encode("utf_16_be") @@ -103,7 +103,7 @@ def __eq__(self, other): def __ne__(self, other): return not (self == other) - def __hash__(self): + def __hash__(self) -> int: return hash((self.object_id, self.generation)) @@ -219,7 +219,7 @@ def __eq__(self, other): isinstance(other, PdfName) and other.name == self.name ) or other == self.name - def __hash__(self): + def __hash__(self) -> int: return hash(self.name) def __repr__(self) -> str: @@ -402,7 +402,7 @@ def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): if f: self.seek_end() - def __enter__(self): + def __enter__(self) -> PdfParser: return self def __exit__(self, exc_type, exc_value, traceback): @@ -436,7 +436,7 @@ def write_header(self) -> None: def write_comment(self, s): self.f.write(f"% {s}\n".encode()) - def write_catalog(self): + def write_catalog(self) -> IndirectReference: self.del_root() self.root_ref = self.next_object_id(self.f.tell()) self.pages_ref = self.next_object_id(0) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c74cbccf1bf..f7ccc8381fa 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -39,7 +39,7 @@ import warnings import zlib from enum import IntEnum -from typing import IO +from typing import IO, Any from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence from ._binary import i16be as i16 @@ -1019,7 +1019,7 @@ def load_end(self) -> None: if self.pyaccess: self.pyaccess = None - def _getexif(self): + def _getexif(self) -> dict[str, Any] | None: if "exif" not in self.info: self.load() if "exif" not in self.info and "Raw profile type exif" not in self.info: @@ -1032,7 +1032,7 @@ def getexif(self): return super().getexif() - def getxmp(self): + def getxmp(self) -> dict[str, Any]: """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. @@ -1234,7 +1234,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) seq_num = fdat_chunks.seq_num -def _save_all(im, fp, filename): +def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: _save(im, fp, filename, save_all=True) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index a9da90613e7..f476713ca7e 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -22,6 +22,7 @@ import logging import sys +from typing import TYPE_CHECKING from ._deprecate import deprecate @@ -48,9 +49,12 @@ logger = logging.getLogger(__name__) +if TYPE_CHECKING: + from . import Image + class PyAccess: - def __init__(self, img, readonly=False): + def __init__(self, img: Image.Image, readonly: bool = False) -> None: deprecate("PyAccess", 11) vals = dict(img.im.unsafe_ptrs) self.readonly = readonly @@ -130,7 +134,7 @@ def __getitem__(self, xy): putpixel = __setitem__ getpixel = __getitem__ - def check_xy(self, xy): + def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]: (x, y) = xy if not (0 <= x < self.xsize and 0 <= y < self.ysize): msg = "pixel location out of range" @@ -161,7 +165,7 @@ class _PyAccess32_3(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.g, pixel.b @@ -180,7 +184,7 @@ class _PyAccess32_4(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.g, pixel.b, pixel.a diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 54faa59c55f..f3fa3c24ccd 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1202,7 +1202,7 @@ def tell(self) -> int: """Return the current frame number""" return self.__frame - def getxmp(self): + def getxmp(self) -> dict[str, Any]: """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index cae124e9f4c..ff7402dca32 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -1,6 +1,7 @@ from __future__ import annotations from io import BytesIO +from typing import Any from . import Image, ImageFile @@ -95,12 +96,12 @@ def _open(self) -> None: # Initialize seek state self._reset(reset=False) - def _getexif(self): + def _getexif(self) -> dict[str, Any] | None: if "exif" not in self.info: return None return self.getexif()._get_merged_dict() - def getxmp(self): + def getxmp(self) -> dict[str, Any]: """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index b0328657b03..fab3e26c587 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -28,7 +28,7 @@ _handler = None -def register_handler(handler): +def register_handler(handler: ImageFile.StubHandler) -> None: """ Install application-specific WMF image handler. @@ -41,12 +41,12 @@ def register_handler(handler): if hasattr(Image.core, "drawwmf"): # install default handler (windows only) - class WmfHandler: - def open(self, im): + class WmfHandler(ImageFile.StubHandler): + def open(self, im: ImageFile.StubImageFile) -> None: im._mode = "RGB" self.bbox = im.info["wmf_bbox"] - def load(self, im): + def load(self, im: ImageFile.StubImageFile) -> Image.Image: im.fp.seek(0) # rewind return Image.frombytes( "RGB", @@ -147,7 +147,7 @@ def _open(self) -> None: if loader: loader.open(self) - def _load(self): + def _load(self) -> ImageFile.StubHandler | None: return _handler def load(self, dpi=None): From b2316f46cb4fc084fc15cb2848eca8b19cbc4329 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sat, 18 May 2024 11:22:57 +0200 Subject: [PATCH 168/300] Use just `str` for `_string_length_check` Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageFont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index f2936bae67d..747c0c05077 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -61,7 +61,7 @@ class Layout(IntEnum): core = DeferredError.new(ex) -def _string_length_check(text: str | bytes) -> None: +def _string_length_check(text: str) -> None: if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH: msg = "too many characters in string" raise ValueError(msg) From 82910a5e4f1f12472dd85ce31090e7abeefe9814 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 19 May 2024 22:00:45 +1000 Subject: [PATCH 169/300] Lint fixes --- src/libImaging/Dib.c | 4 ++-- src/libImaging/TiffDecode.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libImaging/Dib.c b/src/libImaging/Dib.c index 5194bfca3cb..269be10587b 100644 --- a/src/libImaging/Dib.c +++ b/src/libImaging/Dib.c @@ -94,8 +94,8 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) { return (ImagingDIB)ImagingError_MemoryError(); } - dib->bitmap = - CreateDIBSection(dib->dc, dib->info, DIB_RGB_COLORS, (void **)&dib->bits, NULL, 0); + dib->bitmap = CreateDIBSection( + dib->dc, dib->info, DIB_RGB_COLORS, (void **)&dib->bits, NULL, 0); if (!dib->bitmap) { free(dib->info); free(dib); diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index 8b4d7aeacd1..abffdeabcd5 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -1005,7 +1005,8 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt } if (state->state == 1 && !clientstate->fp) { - int read = (int)_tiffReadProc((thandle_t)clientstate, (tdata_t)buffer, (tsize_t)bytes); + int read = + (int)_tiffReadProc((thandle_t)clientstate, (tdata_t)buffer, (tsize_t)bytes); TRACE( ("Buffer: %p: %c%c%c%c\n", buffer, From fb21c50fb664cefff2ef68ea972f5079d49f227e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 May 2024 10:46:32 +1000 Subject: [PATCH 170/300] Restore original thread state --- src/display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display.c b/src/display.c index abf94f1e1a7..990f4b0a5c9 100644 --- a/src/display.c +++ b/src/display.c @@ -618,7 +618,7 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) { if (callback) { /* restore thread state */ PyEval_SaveThread(); - PyThreadState_Swap(threadstate); + PyThreadState_Swap(current_threadstate); } return status; From 0f1a0fc5018cfda48a1e9368d4f421734eca4a18 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 20 May 2024 23:11:50 +1000 Subject: [PATCH 171/300] Improved consistency of XMP handling --- Tests/test_file_jpeg.py | 1 + Tests/test_file_png.py | 1 + Tests/test_file_tiff.py | 1 + Tests/test_file_webp_metadata.py | 1 + Tests/test_image.py | 4 ++++ docs/reference/Image.rst | 1 + docs/releasenotes/8.3.0.rst | 6 +++--- src/PIL/Image.py | 16 ++++++++++++---- src/PIL/ImageOps.py | 3 +++ src/PIL/JpegImagePlugin.py | 17 ++--------------- src/PIL/PngImagePlugin.py | 15 ++------------- src/PIL/TiffImagePlugin.py | 13 ++++--------- src/PIL/WebPImagePlugin.py | 9 --------- 13 files changed, 35 insertions(+), 53 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 5d2157651df..c895c3a8d06 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -943,6 +943,7 @@ def test_getxmp(self) -> None: ): assert im.getxmp() == {} else: + assert "xmp" in im.info xmp = im.getxmp() description = xmp["xmpmeta"]["RDF"]["Description"] diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 19462dcb5a4..eb2c8a5f145 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -683,6 +683,7 @@ def test_getxmp(self) -> None: ): assert im.getxmp() == {} else: + assert "xmp" in im.info xmp = im.getxmp() description = xmp["xmpmeta"]["RDF"]["Description"] diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 8821fb46a84..56b9148d167 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -759,6 +759,7 @@ def test_getxmp(self) -> None: ): assert im.getxmp() == {} else: + assert "xmp" in im.info xmp = im.getxmp() description = xmp["xmpmeta"]["RDF"]["Description"] diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 8759412408c..c3df4ad7bcb 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -129,6 +129,7 @@ def test_getxmp() -> None: ): assert im.getxmp() == {} else: + assert "xmp" in im.info assert ( im.getxmp()["xmpmeta"]["xmptk"] == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " diff --git a/Tests/test_image.py b/Tests/test_image.py index 742d0dfe406..042c0dccbb4 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -897,6 +897,10 @@ def test_exif_hide_offsets(self) -> None: assert tag not in exif.get_ifd(0x8769) assert exif.get_ifd(0xA005) + def test_empty_xmp(self) -> None: + with Image.open("Tests/images/hopper.gif") as im: + assert im.getxmp() == {} + @pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0))) def test_zero_tobytes(self, size: tuple[int, int]) -> None: im = Image.new("RGB", size) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 0d9b4d93d77..767c833b39c 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -197,6 +197,7 @@ This helps to get the bounding box coordinates of the input image:: .. automethod:: PIL.Image.Image.getpalette .. automethod:: PIL.Image.Image.getpixel .. automethod:: PIL.Image.Image.getprojection +.. automethod:: PIL.Image.Image.getxmp .. automethod:: PIL.Image.Image.histogram .. automethod:: PIL.Image.Image.paste .. automethod:: PIL.Image.Image.point diff --git a/docs/releasenotes/8.3.0.rst b/docs/releasenotes/8.3.0.rst index 9f46cc1e9e9..4ef914f6471 100644 --- a/docs/releasenotes/8.3.0.rst +++ b/docs/releasenotes/8.3.0.rst @@ -18,9 +18,9 @@ is not secure. - :py:meth:`~PIL.Image.Image.getexif` has used ``xml`` to potentially retrieve orientation data since Pillow 7.2.0. It has been refactored to use ``re`` instead. -- :py:meth:`~PIL.JpegImagePlugin.JpegImageFile.getxmp` was added in Pillow 8.2.0. It - will now use ``defusedxml`` instead. If the dependency is not present, an empty - dictionary will be returned and a warning raised. +- ``getxmp()`` was added to :py:class:`~PIL.JpegImagePlugin.JpegImageFile` in Pillow + 8.2.0. It will now use ``defusedxml`` instead. If the dependency is not present, an + empty dictionary will be returned and a warning raised. Deprecations ============ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 958b95e3b0f..7d54b124bc0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1439,7 +1439,14 @@ def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]: return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands)) return self.im.getextrema() - def _getxmp(self, xmp_tags): + def getxmp(self): + """ + Returns a dictionary containing the XMP tags. + Requires defusedxml to be installed. + + :returns: XMP tags in a dictionary. + """ + def get_name(tag): return re.sub("^{[^}]+}", "", tag) @@ -1466,9 +1473,10 @@ def get_value(element): if ElementTree is None: warnings.warn("XMP data cannot be read without defusedxml dependency") return {} - else: - root = ElementTree.fromstring(xmp_tags) - return {get_name(root.tag): get_value(root)} + if "xmp" not in self.info: + return {} + root = ElementTree.fromstring(self.info["xmp"]) + return {get_name(root.tag): get_value(root)} def getexif(self) -> Exif: """ diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 33db8fa50c7..48bde39fe26 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -717,6 +717,9 @@ def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image exif_image.info["XML:com.adobe.xmp"] = re.sub( pattern, "", exif_image.info["XML:com.adobe.xmp"] ) + exif_image.info["xmp"] = re.sub( + pattern.encode(), b"", exif_image.info["xmp"] + ) if not in_place: return transposed_image elif not in_place: diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 909911dfe37..be6b6959a09 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -94,6 +94,8 @@ def APP(self, marker): else: self.info["exif"] = s self._exif_offset = self.fp.tell() - n + 6 + elif marker == 0xFFE1 and s[:29] == b"http://ns.adobe.com/xap/1.0/\x00": + self.info["xmp"] = s.split(b"\x00")[1] elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change @@ -499,21 +501,6 @@ def _getexif(self): def _getmp(self): return _getmp(self) - def getxmp(self): - """ - Returns a dictionary containing the XMP tags. - Requires defusedxml to be installed. - - :returns: XMP tags in a dictionary. - """ - - for segment, content in self.applist: - if segment == "APP1": - marker, xmp_tags = content.split(b"\x00")[:2] - if marker == b"http://ns.adobe.com/xap/1.0/": - return self._getxmp(xmp_tags) - return {} - def _getexif(self): if "exif" not in self.info: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c74cbccf1bf..14622349b81 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -606,6 +606,8 @@ def chunk_iTXt(self, pos: int, length: int) -> bytes: return s else: return s + if k == b"XML:com.adobe.xmp": + self.im_info["xmp"] = v try: k = k.decode("latin-1", "strict") lang = lang.decode("utf-8", "strict") @@ -1032,19 +1034,6 @@ def getexif(self): return super().getexif() - def getxmp(self): - """ - Returns a dictionary containing the XMP tags. - Requires defusedxml to be installed. - - :returns: XMP tags in a dictionary. - """ - return ( - self._getxmp(self.info["XML:com.adobe.xmp"]) - if "XML:com.adobe.xmp" in self.info - else {} - ) - # -------------------------------------------------------------------- # PNG writer diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 54faa59c55f..7ba71b58edd 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1192,6 +1192,10 @@ def _seek(self, frame: int) -> None: self.__frame += 1 self.fp.seek(self._frame_pos[frame]) self.tag_v2.load(self.fp) + if XMP in self.tag_v2: + self.info["xmp"] = self.tag_v2[XMP] + elif "xmp" in self.info: + del self.info["xmp"] self._reload_exif() # fill the legacy tag/ifd entries self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) @@ -1202,15 +1206,6 @@ def tell(self) -> int: """Return the current frame number""" return self.__frame - def getxmp(self): - """ - Returns a dictionary containing the XMP tags. - Requires defusedxml to be installed. - - :returns: XMP tags in a dictionary. - """ - return self._getxmp(self.tag_v2[XMP]) if XMP in self.tag_v2 else {} - def get_photoshop_blocks(self): """ Returns a dictionary of Photoshop "Image Resource Blocks". diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index cae124e9f4c..43e1410c7a4 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -100,15 +100,6 @@ def _getexif(self): return None return self.getexif()._get_merged_dict() - def getxmp(self): - """ - Returns a dictionary containing the XMP tags. - Requires defusedxml to be installed. - - :returns: XMP tags in a dictionary. - """ - return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} - def seek(self, frame: int) -> None: if not self._seek_check(frame): return From ea9dc1e4a5c5c508bbb5c7cbd29f207836e77007 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 21:30:03 +0000 Subject: [PATCH 172/300] chore(deps): update dependency cibuildwheel to v2.18.1 --- .ci/requirements-cibw.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/requirements-cibw.txt b/.ci/requirements-cibw.txt index 8d39ea9bbab..7e257b75cf3 100644 --- a/.ci/requirements-cibw.txt +++ b/.ci/requirements-cibw.txt @@ -1 +1 @@ -cibuildwheel==2.18.0 +cibuildwheel==2.18.1 From 3cc26e9ea614def5a5efdcbcfd646ff50e3936e2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 21 May 2024 12:55:48 +1000 Subject: [PATCH 173/300] Added Python 3.13 wheels --- .github/workflows/wheels-test.sh | 2 +- .github/workflows/wheels.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheels-test.sh b/.github/workflows/wheels-test.sh index 3fbf3be695b..a3376ac929c 100755 --- a/.github/workflows/wheels-test.sh +++ b/.github/workflows/wheels-test.sh @@ -12,7 +12,7 @@ elif [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then else yum install -y fribidi fi -if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ]; then +if [ "${AUDITWHEEL_POLICY::9}" != "musllinux" ] && !([[ "$OSTYPE" == "darwin"* ]] && [[ $(python3 --version) == *"3.13."* ]]); then python3 -m pip install numpy fi diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b2fbd314083..3d6099c1cf5 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -46,6 +46,7 @@ jobs: - cp310 - cp311 - cp312 + - cp313 spec: - manylinux2014 - manylinux_2_28 @@ -80,6 +81,7 @@ jobs: CIBW_ARCHS: "aarch64" # Likewise, select only one Python version per job to speed this up. CIBW_BUILD: "${{ matrix.python-version }}-${{ matrix.spec == 'musllinux' && 'musllinux' || 'manylinux' }}*" + CIBW_PRERELEASE_PYTHONS: True # Extra options for manylinux. CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.spec }} CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: ${{ matrix.spec }} @@ -133,6 +135,7 @@ jobs: CIBW_BUILD: ${{ matrix.build }} CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }} CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }} + CIBW_PRERELEASE_PYTHONS: True CIBW_SKIP: pp38-* CIBW_TEST_SKIP: cp38-macosx_arm64 MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }} @@ -204,6 +207,7 @@ jobs: CIBW_ARCHS: ${{ matrix.cibw_arch }} CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd" CIBW_CACHE_PATH: "C:\\cibw" + CIBW_PRERELEASE_PYTHONS: True CIBW_SKIP: pp38-* CIBW_TEST_SKIP: "*-win_arm64" CIBW_TEST_COMMAND: 'docker run --rm From d461ff8cef83116abffbd24145459ee1300c520d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 May 2024 11:50:10 +1000 Subject: [PATCH 174/300] Added release notes --- docs/releasenotes/10.4.0.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 3150bf4e02f..41f33102fdd 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -53,7 +53,9 @@ TODO Other Changes ============= -TODO -^^^^ +Python 3.13 beta +^^^^^^^^^^^^^^^^ -TODO +To help others prepare for Python 3.13, wheels have been built against the 3.13 beta as +a preview. This is not official support for Python 3.13, but simply an opportunity for +users to test how Pillow works with the beta and report any problems. From 33e304ed66851ed7cbd6e9018764ec9ec089c25f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 May 2024 19:43:00 +1000 Subject: [PATCH 175/300] Use @cached_property --- src/PIL/GifImagePlugin.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index eede4154994..962a9283464 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -30,6 +30,7 @@ import os import subprocess from enum import IntEnum +from functools import cached_property from . import ( Image, @@ -112,8 +113,7 @@ def _open(self) -> None: self._fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() - self._n_frames = None - self._is_animated = None + self._n_frames: int | None = None self._seek(0) # get ready to read first frame @property @@ -128,24 +128,23 @@ def n_frames(self): self.seek(current) return self._n_frames - @property - def is_animated(self): - if self._is_animated is None: - if self._n_frames is not None: - self._is_animated = self._n_frames != 1 - else: - current = self.tell() - if current: - self._is_animated = True - else: - try: - self._seek(1, False) - self._is_animated = True - except EOFError: - self._is_animated = False - - self.seek(current) - return self._is_animated + @cached_property + def is_animated(self) -> bool: + if self._n_frames is not None: + return self._n_frames != 1 + + current = self.tell() + if current: + return True + + try: + self._seek(1, False) + is_animated = True + except EOFError: + is_animated = False + + self.seek(current) + return is_animated def seek(self, frame: int) -> None: if not self._seek_check(frame): From 2dedeef832eb170dd8490ca12aa496ade95de478 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 May 2024 20:05:07 +1000 Subject: [PATCH 176/300] Support unpacking more rawmodes to RGBA palette --- Tests/test_image_putpalette.py | 1 + src/PIL/DdsImagePlugin.py | 1 + src/PIL/GifImagePlugin.py | 2 +- src/PIL/Image.py | 12 ++++++------ src/_imaging.c | 6 +++--- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index cc7cf58f0fc..75d9d2fc116 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -79,6 +79,7 @@ def test_putpalette_with_alpha_values() -> None: ( ("RGBA", (1, 2, 3, 4)), ("RGBAX", (1, 2, 3, 4, 0)), + ("ARGB", (4, 1, 2, 3)), ), ) def test_rgba_palette(mode: str, palette: tuple[int, ...]) -> None: diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 1575f2d88b6..285e4b9db92 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -379,6 +379,7 @@ def _open(self) -> None: elif pfflags & DDPF.PALETTEINDEXED8: self._mode = "P" self.palette = ImagePalette.raw("RGBA", self.fp.read(1024)) + self.palette.mode = "RGBA" elif pfflags & DDPF.FOURCC: offset = header_size + 4 if fourcc == D3DFMT.DXT1: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index eede4154994..151feb17d9f 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -429,7 +429,7 @@ def load_prepare(self) -> None: self._prev_im = self.im if self._frame_palette: self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) - self.im.putpalette(*self._frame_palette.getdata()) + self.im.putpalette("RGB", *self._frame_palette.getdata()) else: self.im = None self._mode = temp_mode diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 958b95e3b0f..7a957e76960 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -873,7 +873,7 @@ def load(self): if self.im is not None and self.palette and self.palette.dirty: # realize palette mode, arr = self.palette.getdata() - self.im.putpalette(mode, arr) + self.im.putpalette(self.palette.mode, mode, arr) self.palette.dirty = 0 self.palette.rawmode = None if "transparency" in self.info and mode in ("LA", "PA"): @@ -883,9 +883,9 @@ def load(self): self.im.putpalettealphas(self.info["transparency"]) self.palette.mode = "RGBA" else: - palette_mode = "RGBA" if mode.startswith("RGBA") else "RGB" - self.palette.mode = palette_mode - self.palette.palette = self.im.getpalette(palette_mode, palette_mode) + self.palette.palette = self.im.getpalette( + self.palette.mode, self.palette.mode + ) if self.im is not None: if cffi and USE_CFFI_ACCESS: @@ -1998,7 +1998,7 @@ def putpalette(self, data, rawmode="RGB") -> None: palette = ImagePalette.raw(rawmode, data) self._mode = "PA" if "A" in self.mode else "P" self.palette = palette - self.palette.mode = "RGB" + self.palette.mode = "RGBA" if "A" in rawmode else "RGB" self.load() # install new palette def putpixel(self, xy, value): @@ -2113,7 +2113,7 @@ def remap_palette(self, dest_map, source_palette=None): # m_im.putpalette(mapping_palette, 'L') # converts to 'P' # or just force it. # UNDONE -- this is part of the general issue with palettes - m_im.im.putpalette(palette_mode + ";L", m_im.palette.tobytes()) + m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes()) m_im = m_im.convert("L") diff --git a/src/_imaging.c b/src/_imaging.c index c565c21bb15..d2d29bfdf04 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1725,10 +1725,11 @@ _putpalette(ImagingObject *self, PyObject *args) { ImagingShuffler unpack; int bits; - char *rawmode, *palette_mode; + char *palette_mode, *rawmode; UINT8 *palette; Py_ssize_t palettesize; - if (!PyArg_ParseTuple(args, "sy#", &rawmode, &palette, &palettesize)) { + if (!PyArg_ParseTuple( + args, "ssy#", &palette_mode, &rawmode, &palette, &palettesize)) { return NULL; } @@ -1738,7 +1739,6 @@ _putpalette(ImagingObject *self, PyObject *args) { return NULL; } - palette_mode = strncmp("RGBA", rawmode, 4) == 0 ? "RGBA" : "RGB"; unpack = ImagingFindUnpacker(palette_mode, rawmode, &bits); if (!unpack) { PyErr_SetString(PyExc_ValueError, wrong_raw_mode); From 804fe7bbf86826a34067846b8ca29837ccc8d2af Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 May 2024 17:50:50 +1000 Subject: [PATCH 177/300] Do not read layers when opening --- Tests/test_file_psd.py | 24 +++++++++++++++--------- src/PIL/PsdImagePlugin.py | 30 +++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 484a1be8f83..91539e0e4a3 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -4,7 +4,7 @@ import pytest -from PIL import Image, PsdImagePlugin, UnidentifiedImageError +from PIL import Image, PsdImagePlugin from .helper import assert_image_equal_tofile, assert_image_similar, hopper, is_pypy @@ -150,14 +150,6 @@ def test_combined_larger_than_size() -> None: @pytest.mark.parametrize( "test_file,raises", [ - ( - "Tests/images/timeout-1ee28a249896e05b83840ae8140622de8e648ba9.psd", - UnidentifiedImageError, - ), - ( - "Tests/images/timeout-598843abc37fc080ec36a2699ebbd44f795d3a6f.psd", - UnidentifiedImageError, - ), ("Tests/images/timeout-c8efc3fded6426986ba867a399791bae544f59bc.psd", OSError), ("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError), ], @@ -167,3 +159,17 @@ def test_crashes(test_file: str, raises) -> None: with pytest.raises(raises): with Image.open(f): pass + + +@pytest.mark.parametrize( + "test_file", + [ + "Tests/images/timeout-1ee28a249896e05b83840ae8140622de8e648ba9.psd", + "Tests/images/timeout-598843abc37fc080ec36a2699ebbd44f795d3a6f.psd", + ], +) +def test_layer_crashes(test_file: str) -> None: + with open(test_file, "rb") as f: + with Image.open(f) as im: + with pytest.raises(SyntaxError): + im.layers diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 86c1a67630e..edf698bf02d 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -18,6 +18,7 @@ from __future__ import annotations import io +from functools import cached_property from . import Image, ImageFile, ImagePalette from ._binary import i8 @@ -118,18 +119,17 @@ def _open(self) -> None: # # layer and mask information - self.layers = [] + self._layers_position = None size = i32(read(4)) if size: end = self.fp.tell() + size size = i32(read(4)) if size: - _layer_data = io.BytesIO(ImageFile._safe_read(self.fp, size)) - self.layers = _layerinfo(_layer_data, size) + self._layers_position = self.fp.tell() + self._layers_size = size self.fp.seek(end) - self.n_frames = len(self.layers) - self.is_animated = self.n_frames > 1 + self._n_frames: int | None = None # # image descriptor @@ -141,6 +141,26 @@ def _open(self) -> None: self.frame = 1 self._min_frame = 1 + @cached_property + def layers(self): + layers = [] + if self._layers_position is not None: + self._fp.seek(self._layers_position) + _layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size)) + layers = _layerinfo(_layer_data, self._layers_size) + self._n_frames = len(layers) + return layers + + @property + def n_frames(self) -> int: + if self._n_frames is None: + self._n_frames = len(self.layers) + return self._n_frames + + @property + def is_animated(self) -> bool: + return len(self.layers) > 1 + def seek(self, layer: int) -> None: if not self._seek_check(layer): return From 92d1879a776be3b109a26786d6611cab9ef6181c Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 23 May 2024 13:27:53 -0500 Subject: [PATCH 178/300] add mypy task to makefile --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 1f9b4a370df..94f7565d826 100644 --- a/Makefile +++ b/Makefile @@ -118,3 +118,8 @@ lint-fix: python3 -m black . python3 -c "import ruff" > /dev/null 2>&1 || python3 -m pip install ruff python3 -m ruff --fix . + +.PHONY: mypy +mypy: + python3 -c "import tox" > /dev/null 2>&1 || python3 -m pip install tox + python3 -m tox -e mypy From 16cd3584548d8b5f4cd81d8460805b6604530a6f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 May 2024 20:32:52 +1000 Subject: [PATCH 179/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c5df1f8f7c4..8a531d1e2a9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Add mypy target to Makefile #8077 + [Yay295] + +- Added more modes to Image.MODES #7984 + [radarhere] + - Deprecate BGR;15, BGR;16 and BGR;24 modes #7978 [radarhere, hugovk] From 2c9b5f03607d083665f5880506197405197f34ae Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 May 2024 06:20:03 +1000 Subject: [PATCH 180/300] Updated Ghostscript to 10.3.1 --- .appveyor.yml | 2 +- .github/workflows/test-windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 57a8fa5a06d..6470dbc4c67 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -34,7 +34,7 @@ install: - xcopy /S /Y c:\test-images-main\* c:\pillow\tests\images - curl -fsSL -o nasm-win64.zip https://raw.githubusercontent.com/python-pillow/pillow-depends/main/nasm-2.16.01-win64.zip - 7z x nasm-win64.zip -oc:\ -- choco install ghostscript --version=10.3.0 +- choco install ghostscript --version=10.3.1 - path c:\nasm-2.16.01;C:\Program Files\gs\gs10.00.0\bin;%PATH% - cd c:\pillow\winbuild\ - ps: | diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 9edc1517350..ee265774b2e 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -86,7 +86,7 @@ jobs: choco install nasm --no-progress echo "C:\Program Files\NASM" >> $env:GITHUB_PATH - choco install ghostscript --version=10.3.0 --no-progress + choco install ghostscript --version=10.3.1 --no-progress echo "C:\Program Files\gs\gs10.00.0\bin" >> $env:GITHUB_PATH # Install extra test images From 1a6b0bb6b5423dba1607dfee6d4ea437a58cc246 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 May 2024 19:30:20 +1000 Subject: [PATCH 181/300] Removed documentation of unused argument --- src/PIL/ImageFont.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 256c581df0c..ad5f7545925 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -160,10 +160,6 @@ def getbbox(self, text, *args, **kwargs): .. versionadded:: 9.2.0 :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. :return: ``(left, top, right, bottom)`` bounding box """ From 1b878189d857bcc823f887cf94650a5f0eaa182a Mon Sep 17 00:00:00 2001 From: Yay295 Date: Sun, 26 May 2024 02:25:45 -0500 Subject: [PATCH 182/300] don't reuse variable name --- src/PIL/PngImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c74cbccf1bf..31706cef96b 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1294,7 +1294,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): # get the corresponding PNG mode try: - rawmode, mode = _OUTMODES[mode] + rawmode, rawmode_depth_type = _OUTMODES[mode] except KeyError as e: msg = f"cannot write mode {mode} as PNG" raise OSError(msg) from e @@ -1309,7 +1309,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): b"IHDR", o32(size[0]), # 0: size o32(size[1]), - mode, # 8: depth/type + rawmode_depth_type, # 8: depth/type b"\0", # 10: compression b"\0", # 11: filter category b"\0", # 12: interlace flag From 82d992690572a270f4a8280cf48318fb8f70f2c2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 May 2024 21:18:14 +1000 Subject: [PATCH 183/300] Split depth/type into bit depth and color type --- src/PIL/PngImagePlugin.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 31706cef96b..0d5751962c0 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1050,22 +1050,22 @@ def getxmp(self): # PNG writer _OUTMODES = { - # supported PIL modes, and corresponding rawmodes/bits/color combinations - "1": ("1", b"\x01\x00"), - "L;1": ("L;1", b"\x01\x00"), - "L;2": ("L;2", b"\x02\x00"), - "L;4": ("L;4", b"\x04\x00"), - "L": ("L", b"\x08\x00"), - "LA": ("LA", b"\x08\x04"), - "I": ("I;16B", b"\x10\x00"), - "I;16": ("I;16B", b"\x10\x00"), - "I;16B": ("I;16B", b"\x10\x00"), - "P;1": ("P;1", b"\x01\x03"), - "P;2": ("P;2", b"\x02\x03"), - "P;4": ("P;4", b"\x04\x03"), - "P": ("P", b"\x08\x03"), - "RGB": ("RGB", b"\x08\x02"), - "RGBA": ("RGBA", b"\x08\x06"), + # supported PIL modes, and corresponding rawmode, bit depth and color type + "1": ("1", b"\x01", b"\x00"), + "L;1": ("L;1", b"\x01", b"\x00"), + "L;2": ("L;2", b"\x02", b"\x00"), + "L;4": ("L;4", b"\x04", b"\x00"), + "L": ("L", b"\x08", b"\x00"), + "LA": ("LA", b"\x08", b"\x04"), + "I": ("I;16B", b"\x10", b"\x00"), + "I;16": ("I;16B", b"\x10", b"\x00"), + "I;16B": ("I;16B", b"\x10", b"\x00"), + "P;1": ("P;1", b"\x01", b"\x03"), + "P;2": ("P;2", b"\x02", b"\x03"), + "P;4": ("P;4", b"\x04", b"\x03"), + "P": ("P", b"\x08", b"\x03"), + "RGB": ("RGB", b"\x08", b"\x02"), + "RGBA": ("RGBA", b"\x08", b"\x06"), } @@ -1294,7 +1294,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): # get the corresponding PNG mode try: - rawmode, rawmode_depth_type = _OUTMODES[mode] + rawmode, bit_depth, color_type = _OUTMODES[mode] except KeyError as e: msg = f"cannot write mode {mode} as PNG" raise OSError(msg) from e @@ -1309,7 +1309,8 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): b"IHDR", o32(size[0]), # 0: size o32(size[1]), - rawmode_depth_type, # 8: depth/type + bit_depth, + color_type, b"\0", # 10: compression b"\0", # 11: filter category b"\0", # 12: interlace flag From 2c4a6e1179a7e2460d3dae9f4558173c547996a7 Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 00:23:16 +0200 Subject: [PATCH 184/300] Add function and documentation to draw circle --- docs/reference/ImageDraw.rst | 13 +++++++++++++ src/PIL/ImageDraw.py | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 4ccfacae75e..6987adc881c 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -240,6 +240,19 @@ Methods .. versionadded:: 5.3.0 +.. py:method:: ImageDraw.circle(xy, radius, fill=None, outline=None, width=1) + + Draws a circle given the center coordinates and a radius. + + :param xy: One point to define the circle center. Sequence: + ``[x, y]`` + :param radius: Radius of the circle + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + :param width: The line width, in pixels. + + .. versionadded:: ?.?.? + .. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None) Draws a line between the coordinates in the ``xy`` list. diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 42f2ee8c799..4b42b32d0d3 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -181,6 +181,15 @@ def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None: if ink is not None and ink != fill and width != 0: self.draw.draw_ellipse(xy, ink, 0, width) + def circle(self, xy: Coords, radius, fill=None, outline=None, width=1) -> None: + """Draw a circle given center coordinates and a radius.""" + ink, fill = self._getink(outline, fill) + ellipse_xy = (xy[0]-radius, xy[1]-radius, xy[0]+radius, xy[1]+radius) + if fill is not None: + self.draw.draw_ellipse(ellipse_xy, fill, 1) + if ink is not None and ink != fill and width != 0: + self.draw.draw_ellipse(ellipse_xy, ink, 0, width) + def line(self, xy: Coords, fill=None, width=0, joint=None) -> None: """Draw a line, or a connected sequence of line segments.""" ink = self._getink(fill)[0] From 2ee3cef50ef6b1917ea3c03ad06849a2a599ee8a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 26 May 2024 22:25:13 +0000 Subject: [PATCH 185/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/ImageDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 4b42b32d0d3..03f2637aefc 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -184,7 +184,7 @@ def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None: def circle(self, xy: Coords, radius, fill=None, outline=None, width=1) -> None: """Draw a circle given center coordinates and a radius.""" ink, fill = self._getink(outline, fill) - ellipse_xy = (xy[0]-radius, xy[1]-radius, xy[0]+radius, xy[1]+radius) + ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) if fill is not None: self.draw.draw_ellipse(ellipse_xy, fill, 1) if ink is not None and ink != fill and width != 0: From 930c423db1b4d571755768b0502067676b30eb58 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 May 2024 17:08:13 +1000 Subject: [PATCH 186/300] Added ImageFont.load_default_imagefont() --- Tests/test_imagefontpil.py | 47 +++--- docs/reference/ImageFont.rst | 1 + src/PIL/ImageFont.py | 273 ++++++++++++++++++----------------- 3 files changed, 164 insertions(+), 157 deletions(-) diff --git a/Tests/test_imagefontpil.py b/Tests/test_imagefontpil.py index 3b1c14b4e18..acbaecab27b 100644 --- a/Tests/test_imagefontpil.py +++ b/Tests/test_imagefontpil.py @@ -9,51 +9,54 @@ from .helper import assert_image_equal_tofile -original_core = ImageFont.core - - -def setup_module() -> None: - if features.check_module("freetype2"): - ImageFont.core = _util.DeferredError(ImportError) - - -def teardown_module() -> None: - ImageFont.core = original_core +fonts = [ImageFont.load_default_imagefont()] +if not features.check_module("freetype2"): + default_font = ImageFont.load_default() + if isinstance(default_font, ImageFont.ImageFont): + fonts.append(default_font) -def test_default_font() -> None: +@pytest.mark.parametrize("font", fonts) +def test_default_font(font: ImageFont.ImageFont) -> None: # Arrange txt = 'This is a "better than nothing" default font.' im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) # Act - default_font = ImageFont.load_default() - draw.text((10, 10), txt, font=default_font) + draw.text((10, 10), txt, font=font) # Assert assert_image_equal_tofile(im, "Tests/images/default_font.png") -def test_size_without_freetype() -> None: - with pytest.raises(ImportError): - ImageFont.load_default(size=14) +def test_without_freetype() -> None: + original_core = ImageFont.core + if features.check_module("freetype2"): + ImageFont.core = _util.DeferredError(ImportError) + try: + assert isinstance(ImageFont.load_default(), ImageFont.ImageFont) + + with pytest.raises(ImportError): + ImageFont.load_default(size=14) + finally: + ImageFont.core = original_core -def test_unicode() -> None: +@pytest.mark.parametrize("font", fonts) +def test_unicode(font: ImageFont.ImageFont) -> None: # should not segfault, should return UnicodeDecodeError # issue #2826 - font = ImageFont.load_default() with pytest.raises(UnicodeEncodeError): font.getbbox("’") -def test_textbbox() -> None: +@pytest.mark.parametrize("font", fonts) +def test_textbbox(font: ImageFont.ImageFont) -> None: im = Image.new("RGB", (200, 200)) d = ImageDraw.Draw(im) - default_font = ImageFont.load_default() - assert d.textlength("test", font=default_font) == 24 - assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, 24, 11) + assert d.textlength("test", font=font) == 24 + assert d.textbbox((0, 0), "test", font=font) == (0, 0, 24, 11) def test_decompression_bomb() -> None: diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index 6edf4b05c55..edbdd9a32a6 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -53,6 +53,7 @@ Functions .. autofunction:: PIL.ImageFont.load_path .. autofunction:: PIL.ImageFont.truetype .. autofunction:: PIL.ImageFont.load_default +.. autofunction:: PIL.ImageFont.load_default_imagefont Methods ------- diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index ad5f7545925..87ff8315ecb 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -867,6 +867,142 @@ def load_path(filename): raise OSError(msg) +def load_default_imagefont() -> ImageFont: + f = ImageFont() + f._load_pilfont_data( + # courB08 + BytesIO( + base64.b64decode( + b""" +UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA +BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL +AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA +AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB +ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A +BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB +//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA +AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH +AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA +ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv +AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ +/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 +AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA +AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG +AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA +BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA +AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA +2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF +AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// ++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA +////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA +BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv +AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA +AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA +AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA +BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// +//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA +AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF +AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB +mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn +AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA +AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 +AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA +Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB +//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA +AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ +AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC +DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ +AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ ++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 +AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ +///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG +AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA +BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA +Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC +eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG +AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// ++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA +////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA +BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT +AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A +AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA +Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA +Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// +//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA +AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ +AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA +LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 +AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA +AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 +AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA +AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG +AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA +EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK +AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA +pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG +AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// ++QAGAAIAzgAKANUAEw== +""" + ) + ), + Image.open( + BytesIO( + base64.b64decode( + b""" +iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u +Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 +M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g +LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F +IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA +Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 +NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx +in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 +SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY +AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt +y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG +ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY +lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H +/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 +AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 +c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ +/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw +pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv +oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR +evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA +AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// +Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR +w7IkEbzhVQAAAABJRU5ErkJggg== +""" + ) + ) + ), + ) + return f + + def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: """If FreeType support is available, load a version of Aileron Regular, https://dotcolon.net/font/aileron, with a more limited character set. @@ -882,7 +1018,7 @@ def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: :return: A font object. """ if core.__class__.__name__ == "module" or size is not None: - f = truetype( + return truetype( BytesIO( base64.b64decode( b""" @@ -1112,137 +1248,4 @@ def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: 10 if size is None else size, layout_engine=Layout.BASIC, ) - else: - f = ImageFont() - f._load_pilfont_data( - # courB08 - BytesIO( - base64.b64decode( - b""" -UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA -BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL -AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA -AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB -ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A -BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB -//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA -AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH -AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA -ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv -AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ -/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 -AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA -AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG -AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA -BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA -AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA -2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF -AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// -+gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA -////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA -BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv -AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA -AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA -AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA -BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// -//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA -AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF -AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB -mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn -AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA -AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 -AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA -Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB -//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA -AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ -AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC -DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ -AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ -+wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 -AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ -///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG -AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA -BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA -Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC -eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG -AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// -+gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA -////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA -BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT -AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A -AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA -Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA -Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// -//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA -AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ -AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA -LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 -AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA -AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 -AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA -AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG -AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA -EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK -AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA -pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG -AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// -+QAGAAIAzgAKANUAEw== -""" - ) - ), - Image.open( - BytesIO( - base64.b64decode( - b""" -iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u -Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 -M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g -LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F -IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA -Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 -NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx -in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 -SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY -AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt -y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG -ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY -lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H -/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 -AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 -c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ -/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw -pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv -oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR -evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA -AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// -Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR -w7IkEbzhVQAAAABJRU5ErkJggg== -""" - ) - ) - ), - ) - return f + return load_default_imagefont() From f34360d1e3c8f29f1a0b0ccae62b5f7c95700440 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 May 2024 18:09:46 +1000 Subject: [PATCH 187/300] When saving multiple frames, convert to mode rather than raw mode --- Tests/test_file_png.py | 5 +++-- src/PIL/PngImagePlugin.py | 17 +++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 19462dcb5a4..55db5ef852f 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -655,11 +655,12 @@ def test_truncated_chunks(self, cid: bytes) -> None: png.call(cid, 0, 0) ImageFile.LOAD_TRUNCATED_IMAGES = False - def test_specify_bits(self, tmp_path: Path) -> None: + @pytest.mark.parametrize("save_all", (True, False)) + def test_specify_bits(self, save_all: bool, tmp_path: Path) -> None: im = hopper("P") out = str(tmp_path / "temp.png") - im.save(out, bits=4) + im.save(out, bits=4, save_all=save_all) with Image.open(out) as reloaded: assert len(reloaded.png.im_palette[1]) == 48 diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0d5751962c0..6b6c01d1b8e 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1104,7 +1104,7 @@ def write(self, data: bytes) -> None: self.seq_num += 1 -def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images): +def _write_multiple_frames(im, fp, chunk, mode, rawmode, default_image, append_images): duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) @@ -1119,10 +1119,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) frame_count = 0 for im_seq in chain: for im_frame in ImageSequence.Iterator(im_seq): - if im_frame.mode == rawmode: + if im_frame.mode == mode: im_frame = im_frame.copy() else: - im_frame = im_frame.convert(rawmode) + im_frame = im_frame.convert(mode) encoderinfo = im.encoderinfo.copy() if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] @@ -1184,8 +1184,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) # default image IDAT (if it exists) if default_image: - if im.mode != rawmode: - im = im.convert(rawmode) + if im.mode != mode: + im = im.convert(mode) ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) seq_num = 0 @@ -1262,6 +1262,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): size = im.size mode = im.mode + outmode = mode if mode == "P": # # attempt to minimize storage requirements for palette images @@ -1282,7 +1283,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): bits = 2 else: bits = 4 - mode = f"{mode};{bits}" + outmode += f";{bits}" # encoder options im.encoderconfig = ( @@ -1294,7 +1295,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): # get the corresponding PNG mode try: - rawmode, bit_depth, color_type = _OUTMODES[mode] + rawmode, bit_depth, color_type = _OUTMODES[outmode] except KeyError as e: msg = f"cannot write mode {mode} as PNG" raise OSError(msg) from e @@ -1415,7 +1416,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): if save_all: im = _write_multiple_frames( - im, fp, chunk, rawmode, default_image, append_images + im, fp, chunk, mode, rawmode, default_image, append_images ) if im: ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) From 8d9a4dda980f02ff93fa2301d906ef32602c5e07 Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 12:57:50 +0200 Subject: [PATCH 188/300] Update docs/reference/ImageDraw.rst - Set versionadded Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- docs/reference/ImageDraw.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 6987adc881c..51d5965dbca 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -251,7 +251,7 @@ Methods :param fill: Color to use for the fill. :param width: The line width, in pixels. - .. versionadded:: ?.?.? + .. versionadded:: 10.4.0 .. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None) From 38e6913579c3c7c04bb40384cd7eb797130c5dd8 Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 13:01:10 +0200 Subject: [PATCH 189/300] Simplify circle() by reusing ellipse() --- src/PIL/ImageDraw.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 03f2637aefc..b270cc6baf8 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -183,12 +183,8 @@ def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None: def circle(self, xy: Coords, radius, fill=None, outline=None, width=1) -> None: """Draw a circle given center coordinates and a radius.""" - ink, fill = self._getink(outline, fill) ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) - if fill is not None: - self.draw.draw_ellipse(ellipse_xy, fill, 1) - if ink is not None and ink != fill and width != 0: - self.draw.draw_ellipse(ellipse_xy, ink, 0, width) + self.ellipse(ellipse_xy, fill, outline, width) def line(self, xy: Coords, fill=None, width=0, joint=None) -> None: """Draw a line, or a connected sequence of line segments.""" From 35a700a1d4b2de51c8e13d4b60736f765b8b816b Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 13:14:04 +0200 Subject: [PATCH 190/300] Update 10.4.0.rst - Add PIL.ImageDraw.circle() API addition --- docs/releasenotes/10.4.0.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 41f33102fdd..3c42588014c 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -45,6 +45,10 @@ TODO API Additions ============= +Added PIL.ImageDraw.circle() +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Given the circle center coordinate pair and a radius, plots a circle using PIL.ImageDraw.ellipse() + TODO ^^^^ From 773ff20b762247a9fa717683f5e89349830def7e Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 13:18:47 +0200 Subject: [PATCH 191/300] Update docs/reference/ImageDraw.rst - move circle method up to indicate it is new Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- docs/reference/ImageDraw.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 51d5965dbca..ed762c8d69c 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -243,6 +243,8 @@ Methods .. py:method:: ImageDraw.circle(xy, radius, fill=None, outline=None, width=1) Draws a circle given the center coordinates and a radius. + + .. versionadded:: 10.4.0 :param xy: One point to define the circle center. Sequence: ``[x, y]`` From 034f3cbed522b862b07bf938bbce0a599018bc57 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 11:19:09 +0000 Subject: [PATCH 192/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/reference/ImageDraw.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index ed762c8d69c..4996acf7160 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -243,7 +243,7 @@ Methods .. py:method:: ImageDraw.circle(xy, radius, fill=None, outline=None, width=1) Draws a circle given the center coordinates and a radius. - + .. versionadded:: 10.4.0 :param xy: One point to define the circle center. Sequence: From 9b7556228e0f045495f6b3dd2fd22e84b65ab0eb Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 13:21:41 +0200 Subject: [PATCH 193/300] Update docs/reference/ImageDraw.rst - move versionadded Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- docs/reference/ImageDraw.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 4996acf7160..c4ce76cc07a 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -253,8 +253,6 @@ Methods :param fill: Color to use for the fill. :param width: The line width, in pixels. - .. versionadded:: 10.4.0 - .. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None) Draws a line between the coordinates in the ``xy`` list. From 8db5fbead1955ed6f1f4810e3b36c46483dcc62e Mon Sep 17 00:00:00 2001 From: void4 Date: Mon, 27 May 2024 13:27:56 +0200 Subject: [PATCH 194/300] Update src/PIL/ImageDraw.py - set circle argument xy to type Sequence[float] instead of Coords, radius to float Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageDraw.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index b270cc6baf8..17c17643066 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -181,7 +181,9 @@ def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None: if ink is not None and ink != fill and width != 0: self.draw.draw_ellipse(xy, ink, 0, width) - def circle(self, xy: Coords, radius, fill=None, outline=None, width=1) -> None: + def circle( + self, xy: Sequence[float], radius: float, fill=None, outline=None, width=1 + ) -> None: """Draw a circle given center coordinates and a radius.""" ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) self.ellipse(ellipse_xy, fill, outline, width) From 12cefd798e7b28d05af4a39abd601bc862081881 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 May 2024 21:48:38 +1000 Subject: [PATCH 195/300] Added method links to release notes --- docs/reference/ImageDraw.rst | 25 ++++++++++++------------- docs/releasenotes/10.4.0.rst | 9 ++++++--- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index c4ce76cc07a..1404869ca6c 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -227,6 +227,18 @@ Methods .. versionadded:: 5.3.0 +.. py:method:: ImageDraw.circle(xy, radius, fill=None, outline=None, width=1) + + Draws a circle with a given radius centering on a point. + + .. versionadded:: 10.4.0 + + :param xy: The point for the center of the circle, e.g. ``(x, y)``. + :param radius: Radius of the circle. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + :param width: The line width, in pixels. + .. py:method:: ImageDraw.ellipse(xy, fill=None, outline=None, width=1) Draws an ellipse inside the given bounding box. @@ -240,19 +252,6 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: ImageDraw.circle(xy, radius, fill=None, outline=None, width=1) - - Draws a circle given the center coordinates and a radius. - - .. versionadded:: 10.4.0 - - :param xy: One point to define the circle center. Sequence: - ``[x, y]`` - :param radius: Radius of the circle - :param outline: Color to use for the outline. - :param fill: Color to use for the fill. - :param width: The line width, in pixels. - .. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None) Draws a line between the coordinates in the ``xy`` list. diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 3c42588014c..e0d695a8bba 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -45,9 +45,12 @@ TODO API Additions ============= -Added PIL.ImageDraw.circle() -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Given the circle center coordinate pair and a radius, plots a circle using PIL.ImageDraw.ellipse() +ImageDraw.circle +^^^^^^^^^^^^^^^^ + +Added :py:meth:`~PIL.ImageDraw.ImageDraw.circle`. It provides the same functionality as +:py:meth:`~PIL.ImageDraw.ImageDraw.ellipse`, but instead of taking a bounding box, it +takes a center point and radius. TODO ^^^^ From cac1a04329a2bb0864075f0aca4ff6e2c48a45cf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 May 2024 21:59:32 +1000 Subject: [PATCH 196/300] Added test --- Tests/test_imagedraw.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 0a699e2ab6a..69d09e03d86 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -2,6 +2,7 @@ import contextlib import os.path +from typing import Sequence import pytest @@ -265,6 +266,21 @@ def test_chord_too_fat() -> None: assert_image_equal_tofile(im, "Tests/images/imagedraw_chord_too_fat.png") +@pytest.mark.parametrize("mode", ("RGB", "L")) +@pytest.mark.parametrize("xy", ((W / 2, H / 2), [W / 2, H / 2])) +def test_circle(mode: str, xy: Sequence[float]) -> None: + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + expected = f"Tests/images/imagedraw_ellipse_{mode}.png" + + # Act + draw.circle(xy, 25, fill="green", outline="blue") + + # Assert + assert_image_similar_tofile(im, expected, 1) + + @pytest.mark.parametrize("mode", ("RGB", "L")) @pytest.mark.parametrize("bbox", BBOX) def test_ellipse(mode: str, bbox: Coords) -> None: From 759ab28757430d0661cdbd32410c30f663b4ca9c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 28 May 2024 22:16:04 +1000 Subject: [PATCH 197/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8a531d1e2a9..dc4016d76f6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Added ImageDraw circle() #8085 + [void4, hugovk, radarhere] + - Add mypy target to Makefile #8077 [Yay295] From a6d1daeb4b5e2797a393492d82333870ba7660a7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 29 May 2024 22:51:02 +1000 Subject: [PATCH 198/300] Added type hints --- Tests/helper.py | 25 +++++++++++-------------- Tests/test_file_webp_animated.py | 10 ++++++---- Tests/test_imagecms.py | 19 ++++++++++++++----- Tests/test_imagefile.py | 6 ++++++ Tests/test_imagefont.py | 23 +++++++++++++++-------- 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 5fd4fe3327e..fe337c09f5c 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -174,12 +174,13 @@ def skip_unless_feature(feature: str) -> pytest.MarkDecorator: def skip_unless_feature_version( feature: str, required: str, reason: str | None = None ) -> pytest.MarkDecorator: - if not features.check(feature): + version = features.version(feature) + if version is None: return pytest.mark.skip(f"{feature} not available") if reason is None: reason = f"{feature} is older than {required}" version_required = parse_version(required) - version_available = parse_version(features.version(feature)) + version_available = parse_version(version) return pytest.mark.skipif(version_available < version_required, reason=reason) @@ -189,12 +190,13 @@ def mark_if_feature_version( version_blacklist: str, reason: str | None = None, ) -> pytest.MarkDecorator: - if not features.check(feature): + version = features.version(feature) + if version is None: return pytest.mark.pil_noop_mark() if reason is None: reason = f"{feature} is {version_blacklist}" version_required = parse_version(version_blacklist) - version_available = parse_version(features.version(feature)) + version_available = parse_version(version) if ( version_available.major == version_required.major and version_available.minor == version_required.minor @@ -220,16 +222,11 @@ def _get_mem_usage(self) -> float: from resource import RUSAGE_SELF, getrusage mem = getrusage(RUSAGE_SELF).ru_maxrss - if sys.platform == "darwin": - # man 2 getrusage: - # ru_maxrss - # This is the maximum resident set size utilized (in bytes). - return mem / 1024 # Kb - # linux - # man 2 getrusage - # ru_maxrss (since Linux 2.6.32) - # This is the maximum resident set size used (in kilobytes). - return mem # Kb + # man 2 getrusage: + # ru_maxrss + # This is the maximum resident set size utilized + # in bytes on macOS, in kilobytes on Linux + return mem / 1024 if sys.platform == "darwin" else mem def _test_leak(self, core: Callable[[], None]) -> None: start_mem = self._get_mem_usage() diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index 6a9337fa57f..ba931f8643c 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -52,8 +52,9 @@ def test_write_animation_L(tmp_path: Path) -> None: assert_image_similar(im, orig.convert("RGBA"), 32.9) if is_big_endian(): - webp = parse_version(features.version_module("webp")) - if webp < parse_version("1.2.2"): + version = features.version_module("webp") + assert version is not None + if parse_version(version) < parse_version("1.2.2"): pytest.skip("Fails with libwebp earlier than 1.2.2") orig.seek(orig.n_frames - 1) im.seek(im.n_frames - 1) @@ -78,8 +79,9 @@ def check(temp_file) -> None: # Compare second frame to original if is_big_endian(): - webp = parse_version(features.version_module("webp")) - if webp < parse_version("1.2.2"): + version = features.version_module("webp") + assert version is not None + if parse_version(version) < parse_version("1.2.2"): pytest.skip("Fails with libwebp earlier than 1.2.2") im.seek(1) im.load() diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index bf629fa79cc..8d2029c2183 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -60,10 +60,13 @@ def test_sanity() -> None: assert list(map(type, v)) == [str, str, str, str] # internal version number - assert re.search(r"\d+\.\d+(\.\d+)?$", features.version_module("littlecms2")) + version = features.version_module("littlecms2") + assert version is not None + assert re.search(r"\d+\.\d+(\.\d+)?$", version) skip_missing() i = ImageCms.profileToProfile(hopper(), SRGB, SRGB) + assert i is not None assert_image(i, "RGB", (128, 128)) i = hopper() @@ -72,23 +75,27 @@ def test_sanity() -> None: t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") i = ImageCms.applyTransform(hopper(), t) + assert i is not None assert_image(i, "RGB", (128, 128)) with hopper() as i: t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") ImageCms.applyTransform(hopper(), t, inPlace=True) + assert i is not None assert_image(i, "RGB", (128, 128)) p = ImageCms.createProfile("sRGB") o = ImageCms.getOpenProfile(SRGB) t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") i = ImageCms.applyTransform(hopper(), t) + assert i is not None assert_image(i, "RGB", (128, 128)) t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") assert t.inputMode == "RGB" assert t.outputMode == "RGB" i = ImageCms.applyTransform(hopper(), t) + assert i is not None assert_image(i, "RGB", (128, 128)) # test PointTransform convenience API @@ -260,7 +267,7 @@ def test_simple_lab() -> None: t = ImageCms.buildTransform(psRGB, pLab, "RGB", "LAB") i_lab = ImageCms.applyTransform(i, t) - + assert i_lab is not None assert i_lab.mode == "LAB" k = i_lab.getpixel((0, 0)) @@ -284,6 +291,7 @@ def test_lab_color() -> None: # Need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, and # have that mapping work back to a PIL mode (likely RGB). i = ImageCms.applyTransform(hopper(), t) + assert i is not None assert_image(i, "LAB", (128, 128)) # i.save('temp.lab.tif') # visually verified vs PS. @@ -298,6 +306,7 @@ def test_lab_srgb() -> None: with Image.open("Tests/images/hopper.Lab.tif") as img: img_srgb = ImageCms.applyTransform(img, t) + assert img_srgb is not None # img_srgb.save('temp.srgb.tif') # visually verified vs ps. @@ -317,11 +326,11 @@ def test_lab_roundtrip() -> None: t2 = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") i = ImageCms.applyTransform(hopper(), t) - + assert i is not None assert i.info["icc_profile"] == ImageCmsProfile(pLab).tobytes() out = ImageCms.applyTransform(i, t2) - + assert out is not None assert_image_similar(hopper(), out, 2) @@ -657,7 +666,7 @@ def test_auxiliary_channels_isolated() -> None: reference_image = ImageCms.applyTransform( source_image.convert(src_format[2]), reference_transform ) - + assert reference_image is not None assert_image_equal(test_image.convert(dst_format[2]), reference_image) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index ddcae80d68e..c9dba294359 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -202,6 +202,8 @@ def test_broken_datastream_without_errors(self) -> None: class MockPyDecoder(ImageFile.PyDecoder): + last: MockPyDecoder + def __init__(self, mode: str, *args: Any) -> None: MockPyDecoder.last = self @@ -213,6 +215,8 @@ def decode(self, buffer): class MockPyEncoder(ImageFile.PyEncoder): + last: MockPyEncoder | None + def __init__(self, mode: str, *args: Any) -> None: MockPyEncoder.last = self @@ -315,6 +319,7 @@ def test_setimage(self) -> None: im, fp, [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 0, "RGB")] ) + assert MockPyEncoder.last assert MockPyEncoder.last.state.xoff == xoff assert MockPyEncoder.last.state.yoff == yoff assert MockPyEncoder.last.state.xsize == xsize @@ -329,6 +334,7 @@ def test_extents_none(self) -> None: fp = BytesIO() ImageFile._save(im, fp, [("MOCK", None, 0, "RGB")]) + assert MockPyEncoder.last assert MockPyEncoder.last.state.xoff == 0 assert MockPyEncoder.last.state.yoff == 0 assert MockPyEncoder.last.state.xsize == 200 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 05b5d471691..4398f8a3055 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -34,7 +34,9 @@ def test_sanity() -> None: - assert re.search(r"\d+\.\d+\.\d+$", features.version_module("freetype2")) + version = features.version_module("freetype2") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) @pytest.fixture( @@ -547,11 +549,10 @@ def _test_fake_loading_font(path_to_fake: str, fontname: str) -> None: def loadable_font( filepath: str, size: int, index: int, encoding: str, *args: Any ): + _freeTypeFont = getattr(ImageFont, "_FreeTypeFont") if filepath == path_to_fake: - return ImageFont._FreeTypeFont( - FONT_PATH, size, index, encoding, *args - ) - return ImageFont._FreeTypeFont(filepath, size, index, encoding, *args) + return _freeTypeFont(FONT_PATH, size, index, encoding, *args) + return _freeTypeFont(filepath, size, index, encoding, *args) m.setattr(ImageFont, "FreeTypeFont", loadable_font) font = ImageFont.truetype(fontname) @@ -630,7 +631,9 @@ def test_complex_font_settings() -> None: def test_variation_get(font: ImageFont.FreeTypeFont) -> None: - freetype = parse_version(features.version_module("freetype2")) + version = features.version_module("freetype2") + assert version is not None + freetype = parse_version(version) if freetype < parse_version("2.9.1"): with pytest.raises(NotImplementedError): font.get_variation_names() @@ -700,7 +703,9 @@ def _check_text(font: ImageFont.FreeTypeFont, path: str, epsilon: float) -> None def test_variation_set_by_name(font: ImageFont.FreeTypeFont) -> None: - freetype = parse_version(features.version_module("freetype2")) + version = features.version_module("freetype2") + assert version is not None + freetype = parse_version(version) if freetype < parse_version("2.9.1"): with pytest.raises(NotImplementedError): font.set_variation_by_name("Bold") @@ -725,7 +730,9 @@ def test_variation_set_by_name(font: ImageFont.FreeTypeFont) -> None: def test_variation_set_by_axes(font: ImageFont.FreeTypeFont) -> None: - freetype = parse_version(features.version_module("freetype2")) + version = features.version_module("freetype2") + assert version is not None + freetype = parse_version(version) if freetype < parse_version("2.9.1"): with pytest.raises(NotImplementedError): font.set_variation_by_axes([100]) From e68cec640a0b0698fbfea9493089727edaf03379 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 May 2024 12:00:50 +1000 Subject: [PATCH 199/300] Added type hints --- Tests/oss-fuzz/test_fuzzers.py | 5 +++-- Tests/test_features.py | 10 +++++++--- Tests/test_file_jpeg.py | 4 +++- Tests/test_file_jpeg2k.py | 4 +++- Tests/test_file_libtiff.py | 4 +++- Tests/test_file_png.py | 6 +++--- Tests/test_file_webp.py | 4 +++- Tests/test_image_quantize.py | 5 +++-- Tests/test_image_reduce.py | 2 +- Tests/test_imageops_usm.py | 14 +++++++------- 10 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Tests/oss-fuzz/test_fuzzers.py b/Tests/oss-fuzz/test_fuzzers.py index 58d0213e85d..90eb8713a8b 100644 --- a/Tests/oss-fuzz/test_fuzzers.py +++ b/Tests/oss-fuzz/test_fuzzers.py @@ -12,8 +12,9 @@ if sys.platform.startswith("win32"): pytest.skip("Fuzzer is linux only", allow_module_level=True) -if features.check("libjpeg_turbo"): - version = packaging.version.parse(features.version("libjpeg_turbo")) +libjpeg_turbo_version = features.version("libjpeg_turbo") +if libjpeg_turbo_version is not None: + version = packaging.version.parse(libjpeg_turbo_version) if version.major == 2 and version.minor == 0: pytestmark = pytest.mark.valgrind_known_error( reason="Known failing with libjpeg_turbo 2.0" diff --git a/Tests/test_features.py b/Tests/test_features.py index 2d402ca9114..59fb4980923 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -30,7 +30,7 @@ def test_version() -> None: # Check the correctness of the convenience function # and the format of version numbers - def test(name: str, function: Callable[[str], bool]) -> None: + def test(name: str, function: Callable[[str], str | None]) -> None: version = features.version(name) if not features.check(name): assert version is None @@ -67,12 +67,16 @@ def test_webp_anim() -> None: @skip_unless_feature("libjpeg_turbo") def test_libjpeg_turbo_version() -> None: - assert re.search(r"\d+\.\d+\.\d+$", features.version("libjpeg_turbo")) + version = features.version("libjpeg_turbo") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) @skip_unless_feature("libimagequant") def test_libimagequant_version() -> None: - assert re.search(r"\d+\.\d+\.\d+$", features.version("libimagequant")) + version = features.version("libimagequant") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) @pytest.mark.parametrize("feature", features.modules) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 5d2157651df..f24faecaab2 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -70,7 +70,9 @@ def gen_random_image(self, size: tuple[int, int], mode: str = "RGB") -> Image.Im def test_sanity(self) -> None: # internal version number - assert re.search(r"\d+\.\d+$", features.version_codec("jpg")) + version = features.version_codec("jpg") + assert version is not None + assert re.search(r"\d+\.\d+$", version) with Image.open(TEST_FILE) as im: im.load() diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index a7cae563adb..5a208739f70 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -48,7 +48,9 @@ def roundtrip(im: Image.Image, **options: Any) -> Image.Image: def test_sanity() -> None: # Internal version number - assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("jpg_2000")) + version = features.version_codec("jpg_2000") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) with Image.open("Tests/images/test-card-lossless.jp2") as im: px = im.load() diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 11883ad24aa..6c13999a50a 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -52,7 +52,9 @@ def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> No class TestFileLibTiff(LibTiffTestCase): def test_version(self) -> None: - assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("libtiff")) + version = features.version_codec("libtiff") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) def test_g4_tiff(self, tmp_path: Path) -> None: """Test the ordinary file path load path""" diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 19462dcb5a4..c7c9f6fab0b 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -85,9 +85,9 @@ def get_chunks(self, filename: str) -> list[bytes]: def test_sanity(self, tmp_path: Path) -> None: # internal version number - assert re.search( - r"\d+(\.\d+){1,3}(\.zlib\-ng)?$", features.version_codec("zlib") - ) + version = features.version_codec("zlib") + assert version is not None + assert re.search(r"\d+(\.\d+){1,3}(\.zlib\-ng)?$", version) test_file = str(tmp_path / "temp.png") diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 249846da481..e2de84c71a1 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -49,7 +49,9 @@ def setup_method(self) -> None: def test_version(self) -> None: _webp.WebPDecoderVersion() _webp.WebPDecoderBuggyAlpha() - assert re.search(r"\d+\.\d+\.\d+$", features.version_module("webp")) + version = features.version_module("webp") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) def test_read_rgb(self) -> None: """ diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index e1aa6252b7a..2daaf5c3cbb 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -24,8 +24,9 @@ def test_sanity() -> None: def test_libimagequant_quantize() -> None: image = hopper() if is_ppc64le(): - libimagequant = parse_version(features.version_feature("libimagequant")) - if libimagequant < parse_version("4"): + version = features.version_feature("libimagequant") + assert version is not None + if parse_version(version) < parse_version("4"): pytest.skip("Fails with libimagequant earlier than 4.0.0 on ppc64le") converted = image.quantize(100, Image.Quantize.LIBIMAGEQUANT) assert converted.mode == "P" diff --git a/Tests/test_image_reduce.py b/Tests/test_image_reduce.py index fcf671daace..f6609a1a054 100644 --- a/Tests/test_image_reduce.py +++ b/Tests/test_image_reduce.py @@ -102,7 +102,7 @@ def test_unsupported_modes(mode: str) -> None: def get_image(mode: str) -> Image.Image: mode_info = ImageMode.getmode(mode) if mode_info.basetype == "L": - bands = [gradients_image] + bands: list[Image.Image] = [gradients_image] for _ in mode_info.bands[1:]: # rotate previous image band = bands[-1].transpose(Image.Transpose.ROTATE_90) diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index c15907a554a..104c620de01 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -4,11 +4,11 @@ import pytest -from PIL import Image, ImageFilter +from PIL import Image, ImageFile, ImageFilter @pytest.fixture -def test_images() -> Generator[dict[str, Image.Image], None, None]: +def test_images() -> Generator[dict[str, ImageFile.ImageFile], None, None]: ims = { "im": Image.open("Tests/images/hopper.ppm"), "snakes": Image.open("Tests/images/color_snakes.png"), @@ -20,7 +20,7 @@ def test_images() -> Generator[dict[str, Image.Image], None, None]: im.close() -def test_filter_api(test_images: dict[str, Image.Image]) -> None: +def test_filter_api(test_images: dict[str, ImageFile.ImageFile]) -> None: im = test_images["im"] test_filter = ImageFilter.GaussianBlur(2.0) @@ -34,7 +34,7 @@ def test_filter_api(test_images: dict[str, Image.Image]) -> None: assert i.size == (128, 128) -def test_usm_formats(test_images: dict[str, Image.Image]) -> None: +def test_usm_formats(test_images: dict[str, ImageFile.ImageFile]) -> None: im = test_images["im"] usm = ImageFilter.UnsharpMask @@ -52,7 +52,7 @@ def test_usm_formats(test_images: dict[str, Image.Image]) -> None: im.convert("YCbCr").filter(usm) -def test_blur_formats(test_images: dict[str, Image.Image]) -> None: +def test_blur_formats(test_images: dict[str, ImageFile.ImageFile]) -> None: im = test_images["im"] blur = ImageFilter.GaussianBlur @@ -70,7 +70,7 @@ def test_blur_formats(test_images: dict[str, Image.Image]) -> None: im.convert("YCbCr").filter(blur) -def test_usm_accuracy(test_images: dict[str, Image.Image]) -> None: +def test_usm_accuracy(test_images: dict[str, ImageFile.ImageFile]) -> None: snakes = test_images["snakes"] src = snakes.convert("RGB") @@ -79,7 +79,7 @@ def test_usm_accuracy(test_images: dict[str, Image.Image]) -> None: assert i.tobytes() == src.tobytes() -def test_blur_accuracy(test_images: dict[str, Image.Image]) -> None: +def test_blur_accuracy(test_images: dict[str, ImageFile.ImageFile]) -> None: snakes = test_images["snakes"] i = snakes.filter(ImageFilter.GaussianBlur(0.4)) From afc7d8d0b012b8be86e00411c5cb69de62478ee5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 May 2024 17:17:22 +1000 Subject: [PATCH 200/300] Added type hints --- Tests/test_font_leaks.py | 2 +- Tests/test_image.py | 20 +++++++++++++++----- Tests/test_imagecms.py | 26 ++++++++++++++------------ Tests/test_imagestat.py | 4 ++-- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 08a0e743100..3fb92a62edb 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -12,7 +12,7 @@ class TestTTypeFontLeak(PillowLeakTestCase): iterations = 10 mem_limit = 4096 # k - def _test_font(self, font: ImageFont.FreeTypeFont) -> None: + def _test_font(self, font: ImageFont.FreeTypeFont | ImageFont.ImageFont) -> None: im = Image.new("RGB", (255, 255), "white") draw = ImageDraw.ImageDraw(im) self._test_leak( diff --git a/Tests/test_image.py b/Tests/test_image.py index 742d0dfe406..c7694a0efda 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -99,10 +99,18 @@ def test_open_formats(self) -> None: JPGFILE = "Tests/images/hopper.jpg" with pytest.raises(TypeError): - with Image.open(PNGFILE, formats=123): + with Image.open(PNGFILE, formats=123): # type: ignore[arg-type] pass - for formats in [["JPEG"], ("JPEG",), ["jpeg"], ["Jpeg"], ["jPeG"], ["JpEg"]]: + format_list: list[list[str] | tuple[str, ...]] = [ + ["JPEG"], + ("JPEG",), + ["jpeg"], + ["Jpeg"], + ["jPeG"], + ["JpEg"], + ] + for formats in format_list: with pytest.raises(UnidentifiedImageError): with Image.open(PNGFILE, formats=formats): pass @@ -138,7 +146,7 @@ def test_invalid_image(self) -> None: def test_bad_mode(self) -> None: with pytest.raises(ValueError): - with Image.open("filename", "bad mode"): + with Image.open("filename", "bad mode"): # type: ignore[arg-type] pass def test_stringio(self) -> None: @@ -497,9 +505,11 @@ def test_effect_spread_zero(self) -> None: def test_check_size(self) -> None: # Checking that the _check_size function throws value errors when we want it to with pytest.raises(ValueError): - Image.new("RGB", 0) # not a tuple + # not a tuple + Image.new("RGB", 0) # type: ignore[arg-type] with pytest.raises(ValueError): - Image.new("RGB", (0,)) # Tuple too short + # tuple too short + Image.new("RGB", (0,)) # type: ignore[arg-type] with pytest.raises(ValueError): Image.new("RGB", (-1, -1)) # w,h < 0 diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 8d2029c2183..082eb8162e4 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -7,7 +7,7 @@ import sys from io import BytesIO from pathlib import Path -from typing import Any +from typing import Any, Literal, cast import pytest @@ -209,13 +209,13 @@ def test_exceptions() -> None: ImageCms.buildTransform("foo", "bar", "RGB", "RGB") with pytest.raises(ImageCms.PyCMSError, match="Invalid type for Profile"): - ImageCms.getProfileName(None) + ImageCms.getProfileName(None) # type: ignore[arg-type] skip_missing() # Python <= 3.9: "an integer is required (got type NoneType)" # Python > 3.9: "'NoneType' object cannot be interpreted as an integer" with pytest.raises(ImageCms.PyCMSError, match="integer"): - ImageCms.isIntentSupported(SRGB, None, None) + ImageCms.isIntentSupported(SRGB, None, None) # type: ignore[arg-type] def test_display_profile() -> None: @@ -239,7 +239,7 @@ def test_unsupported_color_space() -> None: "Color space not supported for on-the-fly profile creation (unsupported)" ), ): - ImageCms.createProfile("unsupported") + ImageCms.createProfile("unsupported") # type: ignore[arg-type] def test_invalid_color_temperature() -> None: @@ -352,7 +352,7 @@ def test_extended_information() -> None: p = o.profile def assert_truncated_tuple_equal( - tup1: tuple[Any, ...], tup2: tuple[Any, ...], digits: int = 10 + tup1: tuple[Any, ...] | None, tup2: tuple[Any, ...], digits: int = 10 ) -> None: # Helper function to reduce precision of tuples of floats # recursively and then check equality. @@ -368,6 +368,7 @@ def truncate_tuple(tuple_value: tuple[Any, ...]) -> tuple[Any, ...]: for val in tuple_value ) + assert tup1 is not None assert truncate_tuple(tup1) == truncate_tuple(tup2) assert p.attributes == 4294967296 @@ -513,22 +514,22 @@ def test_non_ascii_path(tmp_path: Path) -> None: def test_profile_typesafety() -> None: # does not segfault with pytest.raises(TypeError, match="Invalid type for Profile"): - ImageCms.ImageCmsProfile(0).tobytes() + ImageCms.ImageCmsProfile(0) # type: ignore[arg-type] with pytest.raises(TypeError, match="Invalid type for Profile"): - ImageCms.ImageCmsProfile(1).tobytes() + ImageCms.ImageCmsProfile(1) # type: ignore[arg-type] # also check core function with pytest.raises(TypeError): - ImageCms.core.profile_tobytes(0) + ImageCms.core.profile_tobytes(0) # type: ignore[arg-type] with pytest.raises(TypeError): - ImageCms.core.profile_tobytes(1) + ImageCms.core.profile_tobytes(1) # type: ignore[arg-type] if not is_pypy(): # core profile should not be directly instantiable with pytest.raises(TypeError): ImageCms.core.CmsProfile() with pytest.raises(TypeError): - ImageCms.core.CmsProfile(0) + ImageCms.core.CmsProfile(0) # type: ignore[call-arg] @pytest.mark.skipif(is_pypy(), reason="fails on PyPy") @@ -537,7 +538,7 @@ def test_transform_typesafety() -> None: with pytest.raises(TypeError): ImageCms.core.CmsTransform() with pytest.raises(TypeError): - ImageCms.core.CmsTransform(0) + ImageCms.core.CmsTransform(0) # type: ignore[call-arg] def assert_aux_channel_preserved( @@ -637,7 +638,8 @@ def test_auxiliary_channels_isolated() -> None: continue # convert with and without AUX data, test colors are equal - source_profile = ImageCms.createProfile(src_format[1]) + src_colorSpace = cast(Literal["LAB", "XYZ", "sRGB"], src_format[1]) + source_profile = ImageCms.createProfile(src_colorSpace) destination_profile = ImageCms.createProfile(dst_format[1]) source_image = src_format[3] test_transform = ImageCms.buildTransform( diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py index b1c1306c1ec..0dfbc5a2abb 100644 --- a/Tests/test_imagestat.py +++ b/Tests/test_imagestat.py @@ -25,10 +25,10 @@ def test_sanity() -> None: st.stddev with pytest.raises(AttributeError): - st.spam() + st.spam() # type: ignore[attr-defined] with pytest.raises(TypeError): - ImageStat.Stat(1) + ImageStat.Stat(1) # type: ignore[arg-type] def test_hopper() -> None: From 66ab7e0de22251b32eccd8938ac7f985579dc0ac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 1 Jun 2024 21:31:53 +1000 Subject: [PATCH 201/300] Added type hints --- Tests/test_file_gif.py | 5 +++-- Tests/test_file_jpeg.py | 4 ++-- Tests/test_file_libtiff.py | 3 ++- Tests/test_image.py | 4 +++- Tests/test_image_array.py | 2 +- Tests/test_image_crop.py | 26 +++++++++++++------------- Tests/test_image_filter.py | 4 ++-- Tests/test_image_getextrema.py | 2 +- Tests/test_imagecms.py | 8 ++++++-- Tests/test_imagedraw.py | 2 +- 10 files changed, 34 insertions(+), 26 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 48c70db8a89..4e790926bfc 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -1252,10 +1252,11 @@ def test_palette_save_L(tmp_path: Path) -> None: im = hopper("P") im_l = Image.frombytes("L", im.size, im.tobytes()) - palette = bytes(im.getpalette()) + palette = im.getpalette() + assert palette is not None out = str(tmp_path / "temp.gif") - im_l.save(out, palette=palette) + im_l.save(out, palette=bytes(palette)) with Image.open(out) as reloaded: assert_image_equal(reloaded.convert("RGB"), im.convert("RGB")) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index f24faecaab2..33f9ce00edc 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -154,7 +154,7 @@ def test_cmyk(self) -> None: assert k > 0.9 def test_rgb(self) -> None: - def getchannels(im: Image.Image) -> tuple[int, int, int]: + def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, int, int]: return tuple(v[0] for v in im.layer) im = hopper() @@ -443,7 +443,7 @@ def test_smooth(self) -> None: assert_image(im1, im2.mode, im2.size) def test_subsampling(self) -> None: - def getsampling(im: Image.Image): + def getsampling(im: JpegImagePlugin.JpegImageFile): layer = im.layer return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 6c13999a50a..22bcd285622 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -668,7 +668,8 @@ def save_bytesio(compression: str | None = None) -> None: pilim.save(buffer_io, format="tiff", compression=compression) buffer_io.seek(0) - assert_image_similar_tofile(pilim, buffer_io, 0) + with Image.open(buffer_io) as saved_im: + assert_image_similar(pilim, saved_im, 0) save_bytesio() save_bytesio("raw") diff --git a/Tests/test_image.py b/Tests/test_image.py index c7694a0efda..d6a739c79df 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -25,6 +25,7 @@ from .helper import ( assert_image_equal, assert_image_equal_tofile, + assert_image_similar, assert_image_similar_tofile, assert_not_all_same, hopper, @@ -193,7 +194,8 @@ def test_tempfile(self) -> None: with tempfile.TemporaryFile() as fp: im.save(fp, "JPEG") fp.seek(0) - assert_image_similar_tofile(im, fp, 20) + with Image.open(fp) as reloaded: + assert_image_similar(im, reloaded, 20) def test_unknown_extension(self, tmp_path: Path) -> None: im = hopper() diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 342bd8654e1..d7e6c562c39 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -86,8 +86,8 @@ def test(mode: str) -> tuple[str, tuple[int, int], bool]: assert test("RGBX") == ("RGBA", (128, 100), True) # Test mode is None with no "typestr" in the array interface + wrapped = Wrapper(hopper("L"), {"shape": (100, 128)}) with pytest.raises(TypeError): - wrapped = Wrapper(test("L"), {"shape": (100, 128)}) Image.fromarray(wrapped) diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index d095364ba75..07fec2e64af 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -18,7 +18,7 @@ def test_crop(mode: str) -> None: def test_wide_crop() -> None: - def crop(*bbox: int) -> tuple[int, ...]: + def crop(bbox: tuple[int, int, int, int]) -> tuple[int, ...]: i = im.crop(bbox) h = i.histogram() while h and not h[-1]: @@ -27,23 +27,23 @@ def crop(*bbox: int) -> tuple[int, ...]: im = Image.new("L", (100, 100), 1) - assert crop(0, 0, 100, 100) == (0, 10000) - assert crop(25, 25, 75, 75) == (0, 2500) + assert crop((0, 0, 100, 100)) == (0, 10000) + assert crop((25, 25, 75, 75)) == (0, 2500) # sides - assert crop(-25, 0, 25, 50) == (1250, 1250) - assert crop(0, -25, 50, 25) == (1250, 1250) - assert crop(75, 0, 125, 50) == (1250, 1250) - assert crop(0, 75, 50, 125) == (1250, 1250) + assert crop((-25, 0, 25, 50)) == (1250, 1250) + assert crop((0, -25, 50, 25)) == (1250, 1250) + assert crop((75, 0, 125, 50)) == (1250, 1250) + assert crop((0, 75, 50, 125)) == (1250, 1250) - assert crop(-25, 25, 125, 75) == (2500, 5000) - assert crop(25, -25, 75, 125) == (2500, 5000) + assert crop((-25, 25, 125, 75)) == (2500, 5000) + assert crop((25, -25, 75, 125)) == (2500, 5000) # corners - assert crop(-25, -25, 25, 25) == (1875, 625) - assert crop(75, -25, 125, 25) == (1875, 625) - assert crop(75, 75, 125, 125) == (1875, 625) - assert crop(-25, 75, 25, 125) == (1875, 625) + assert crop((-25, -25, 25, 25)) == (1875, 625) + assert crop((75, -25, 125, 25)) == (1875, 625) + assert crop((75, 75, 125, 125)) == (1875, 625) + assert crop((-25, 75, 25, 125)) == (1875, 625) @pytest.mark.parametrize("box", ((8, 2, 2, 8), (2, 8, 8, 2), (8, 8, 2, 2))) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 47f9ffa3db8..1f0644471ee 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -46,9 +46,9 @@ def test_sanity(filter_to_apply: ImageFilter.Filter, mode: str) -> None: @pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK")) def test_sanity_error(mode: str) -> None: + im = hopper(mode) with pytest.raises(TypeError): - im = hopper(mode) - im.filter("hello") + im.filter("hello") # type: ignore[arg-type] # crashes on small images diff --git a/Tests/test_image_getextrema.py b/Tests/test_image_getextrema.py index a5b974459a6..de5956f3e7e 100644 --- a/Tests/test_image_getextrema.py +++ b/Tests/test_image_getextrema.py @@ -6,7 +6,7 @@ def test_extrema() -> None: - def extrema(mode: str) -> tuple[int, int] | tuple[tuple[int, int], ...]: + def extrema(mode: str) -> tuple[float, float] | tuple[tuple[int, int], ...]: return hopper(mode).getextrema() assert extrema("1") == (0, 255) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 082eb8162e4..55f72c3b95c 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -247,7 +247,7 @@ def test_invalid_color_temperature() -> None: ImageCms.PyCMSError, match='Color temperature must be numeric, "invalid" not valid', ): - ImageCms.createProfile("LAB", "invalid") + ImageCms.createProfile("LAB", "invalid") # type: ignore[arg-type] @pytest.mark.parametrize("flag", ("my string", -1)) @@ -256,7 +256,7 @@ def test_invalid_flag(flag: str | int) -> None: with pytest.raises( ImageCms.PyCMSError, match="flags must be an integer between 0 and " ): - ImageCms.profileToProfile(im, "foo", "bar", flags=flag) + ImageCms.profileToProfile(im, "foo", "bar", flags=flag) # type: ignore[arg-type] def test_simple_lab() -> None: @@ -588,11 +588,13 @@ def create_test_image() -> Image.Image: ) # apply transform + result_image: Image.Image | None if transform_in_place: ImageCms.applyTransform(source_image, t, inPlace=True) result_image = source_image else: result_image = ImageCms.applyTransform(source_image, t, inPlace=False) + assert result_image is not None result_image_aux = result_image.getchannel(preserved_channel) assert_image_equal(source_image_aux, result_image_aux) @@ -650,6 +652,7 @@ def test_auxiliary_channels_isolated() -> None: ) # test conversion from aux-ful source + test_image: Image.Image | None if transform_in_place: test_image = source_image.copy() ImageCms.applyTransform(test_image, test_transform, inPlace=True) @@ -657,6 +660,7 @@ def test_auxiliary_channels_isolated() -> None: test_image = ImageCms.applyTransform( source_image, test_transform, inPlace=False ) + assert test_image is not None # reference conversion from aux-less source reference_transform = ImageCms.buildTransform( diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 69d09e03d86..c221fe00829 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1083,8 +1083,8 @@ def test_line_horizontal() -> None: ) +@pytest.mark.xfail(reason="failing test") def test_line_h_s1_w2() -> None: - pytest.skip("failing") img, draw = create_base_image_draw((20, 20)) draw.line((5, 5, 14, 6), BLACK, 2) assert_image_equal_tofile( From 54150f2061fa87cb5f629f28958d53c293ea4b90 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 2 Jun 2024 16:26:35 +1000 Subject: [PATCH 202/300] Corrected docstring --- src/PIL/ImageCms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 5f5c5df54e7..5915cc944c3 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -777,7 +777,7 @@ def createProfile( :param colorSpace: String, the color space of the profile you wish to create. Currently only "LAB", "XYZ", and "sRGB" are supported. - :param colorTemp: Positive integer for the white point for the profile, in + :param colorTemp: Positive number for the white point for the profile, in degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles, and is ignored for XYZ and sRGB. From 4aba0b8238db501b5f498fee9335f380a7d08f88 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 2 Jun 2024 16:27:05 +1000 Subject: [PATCH 203/300] Changed default colorTemp --- src/PIL/ImageCms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 5915cc944c3..ec9471f84b4 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -754,7 +754,7 @@ def applyTransform( def createProfile( - colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = -1 + colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0 ) -> core.CmsProfile: """ (pyCMS) Creates a profile. From 8dae9b618f39b8afd9ae2177dcce6a02ea227006 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 2 Jun 2024 17:53:52 +1000 Subject: [PATCH 204/300] Corrected type hint --- src/PIL/ImageCms.py | 2 +- src/PIL/_imagingcms.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index ec9471f84b4..19a79facc24 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -1089,7 +1089,7 @@ def isIntentSupported( raise PyCMSError(v) from v -def versions() -> tuple[str, str, str, str]: +def versions() -> tuple[str, str | None, str, str]: """ (pyCMS) Fetches versions. """ diff --git a/src/PIL/_imagingcms.pyi b/src/PIL/_imagingcms.pyi index f704047be3f..2abd6d0f7a9 100644 --- a/src/PIL/_imagingcms.pyi +++ b/src/PIL/_imagingcms.pyi @@ -2,7 +2,7 @@ import datetime import sys from typing import Literal, SupportsFloat, TypedDict -littlecms_version: str +littlecms_version: str | None _Tuple3f = tuple[float, float, float] _Tuple2x3f = tuple[_Tuple3f, _Tuple3f] From d566c04d5b9b2f7587015b110e588b073a24cf2d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 3 Jun 2024 14:20:01 +1000 Subject: [PATCH 205/300] Updated type hints --- src/PIL/Image.py | 22 +++++++--------- src/PIL/ImageDraw.py | 29 +++++++++++++++------ src/PIL/ImageFont.py | 53 +++++++++++++++++++------------------- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/_imaging.pyi | 4 +-- src/PIL/_imagingft.pyi | 23 ++++++++++++----- 6 files changed, 75 insertions(+), 58 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c02c7d6b6bf..2ea26877d13 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -506,7 +506,7 @@ def _getscaleoffset(expr): class SupportsGetData(Protocol): def getdata( self, - ) -> tuple[Transform, Sequence[Any]]: ... + ) -> tuple[Transform, Sequence[int]]: ... class Image: @@ -1295,7 +1295,7 @@ def _crop(self, im, box): return im.crop((x0, y0, x1, y1)) def draft( - self, mode: str, size: tuple[int, int] + self, mode: str | None, size: tuple[int, int] ) -> tuple[str, tuple[int, int, float, float]] | None: """ Configures the image file loader so it returns a version of the @@ -1719,7 +1719,7 @@ def entropy(self, mask=None, extrema=None): def paste( self, - im: Image | str | int | tuple[int, ...], + im: Image | str | float | tuple[int, ...], box: tuple[int, int, int, int] | tuple[int, int] | None = None, mask: Image | None = None, ) -> None: @@ -1750,7 +1750,7 @@ def paste( See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to combine images with respect to their alpha channels. - :param im: Source image or pixel value (integer or tuple). + :param im: Source image or pixel value (integer, float or tuple). :param box: An optional 4-tuple giving the region to paste into. If a 2-tuple is used instead, it's treated as the upper left corner. If omitted or None, the source is pasted into the @@ -2228,13 +2228,9 @@ def resize( msg = "reducing_gap must be 1.0 or greater" raise ValueError(msg) - size = cast("tuple[int, int]", tuple(size)) - self.load() if box is None: box = (0, 0) + self.size - else: - box = cast("tuple[float, float, float, float]", tuple(box)) if self.size == size and box == (0, 0) + self.size: return self.copy() @@ -2291,8 +2287,6 @@ def reduce( if box is None: box = (0, 0) + self.size - else: - box = cast("tuple[int, int, int, int]", tuple(box)) if factor == (1, 1) and box == (0, 0) + self.size: return self.copy() @@ -2692,7 +2686,9 @@ def round_aspect(number, key): return size = preserved_size - res = self.draft(None, (size[0] * reducing_gap, size[1] * reducing_gap)) # type: ignore[arg-type] + res = self.draft( + None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) + ) if res is not None: box = res[1] if box is None: @@ -2799,7 +2795,7 @@ def getdata(self): im.info = self.info.copy() if method == Transform.MESH: # list of quads - for box, quad in cast("Sequence[tuple[float, float]]", data): + for box, quad in data: im.__transformer( box, self, Transform.QUAD, quad, resample, fillcolor is None ) @@ -2957,7 +2953,7 @@ def transform( self, size: tuple[int, int], image: Image, - **options: dict[str, str | int | tuple[int, ...] | list[int]] | int, + **options: str | int | tuple[int, ...] | list[int], ) -> Image: pass diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 1887a393352..0663d9ddf85 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -456,14 +456,12 @@ def draw_corners(pieslice) -> None: self.draw.draw_rectangle(right, ink, 1) def _multiline_check(self, text: AnyStr) -> bool: - split_character = cast(AnyStr, "\n" if isinstance(text, str) else b"\n") + split_character = "\n" if isinstance(text, str) else b"\n" return split_character in text def _multiline_split(self, text: AnyStr) -> list[AnyStr]: - split_character = cast(AnyStr, "\n" if isinstance(text, str) else b"\n") - - return text.split(split_character) + return text.split("\n" if isinstance(text, str) else b"\n") def _multiline_spacing(self, font, spacing, stroke_width): return ( @@ -477,7 +475,12 @@ def text( xy: tuple[float, float], text: str, fill=None, - font: ImageFont.FreeTypeFont | ImageFont.ImageFont | None = None, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, anchor=None, spacing=4, align="left", @@ -597,9 +600,14 @@ def draw_text(ink, stroke_width=0, stroke_offset=None) -> None: def multiline_text( self, xy: tuple[float, float], - text, + text: str, fill=None, - font=None, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, anchor=None, spacing=4, align="left", @@ -684,7 +692,12 @@ def multiline_text( def textlength( self, text: str, - font: ImageFont.FreeTypeFont | ImageFont.ImageFont | None = None, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ) = None, direction=None, features=None, language=None, diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 747c0c05077..a9925483e40 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -33,11 +33,11 @@ import warnings from enum import IntEnum from io import BytesIO -from typing import TYPE_CHECKING, BinaryIO +from typing import IO, TYPE_CHECKING, Any, BinaryIO from . import Image from ._typing import StrOrBytesPath -from ._util import is_directory, is_path +from ._util import is_path if TYPE_CHECKING: from . import ImageFile @@ -61,7 +61,7 @@ class Layout(IntEnum): core = DeferredError.new(ex) -def _string_length_check(text: str) -> None: +def _string_length_check(text: str | bytes | bytearray) -> None: if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH: msg = "too many characters in string" raise ValueError(msg) @@ -113,7 +113,7 @@ def _load_pilfont(self, filename: str) -> None: self._load_pilfont_data(fp, image) image.close() - def _load_pilfont_data(self, file, image): + def _load_pilfont_data(self, file: IO[bytes], image: Image.Image) -> None: # read PILfont header if file.readline() != b"PILfont\n": msg = "Not a PILfont file" @@ -161,7 +161,7 @@ def getmask(self, text, mode="", *args, **kwargs): return self.font.getmask(text, mode) def getbbox( - self, text: str, *args: object, **kwargs: object + self, text: str | bytes | bytearray, *args: Any, **kwargs: Any ) -> tuple[int, int, int, int]: """ Returns bounding box (in pixels) of given text. @@ -180,7 +180,9 @@ def getbbox( width, height = self.font.getsize(text) return 0, 0, width, height - def getlength(self, text: str, *args: object, **kwargs: object) -> int: + def getlength( + self, text: str | bytes | bytearray, *args: Any, **kwargs: Any + ) -> int: """ Returns length (in pixels) of given text. This is the amount by which following text should be offset. @@ -357,13 +359,13 @@ def getlength( def getbbox( self, text: str, - mode="", - direction=None, - features=None, - language=None, - stroke_width=0, - anchor=None, - ) -> tuple[int, int, int, int]: + mode: str = "", + direction: str | None = None, + features: str | None = None, + language: str | None = None, + stroke_width: float = 0, + anchor: str | None = None, + ) -> tuple[float, float, float, float]: """ Returns bounding box (in pixels) of given text relative to given anchor when rendered in font with provided direction, features, and language. @@ -513,7 +515,7 @@ def getmask( def getmask2( self, - text, + text: str, mode="", direction=None, features=None, @@ -641,7 +643,7 @@ def font_variant( layout_engine=layout_engine or self.layout_engine, ) - def get_variation_names(self): + def get_variation_names(self) -> list[bytes]: """ :returns: A list of the named styles in a variation font. :exception OSError: If the font is not a variation font. @@ -683,10 +685,11 @@ def get_variation_axes(self): msg = "FreeType 2.9.1 or greater is required" raise NotImplementedError(msg) from e for axis in axes: - axis["name"] = axis["name"].replace(b"\x00", b"") + if axis["name"]: + axis["name"] = axis["name"].replace(b"\x00", b"") return axes - def set_variation_by_axes(self, axes): + def set_variation_by_axes(self, axes: list[float]) -> None: """ :param axes: A list of values for each axis. :exception OSError: If the font is not a variation font. @@ -731,7 +734,7 @@ def getbbox(self, text, *args, **kwargs): return 0, 0, height, width return 0, 0, width, height - def getlength(self, text, *args, **kwargs): + def getlength(self, text: str, *args, **kwargs) -> float: if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): msg = "text length is undefined for text rotated by 90 or 270 degrees" raise ValueError(msg) @@ -878,15 +881,13 @@ def load_path(filename: str | bytes) -> ImageFont: :return: A font object. :exception OSError: If the file could not be read. """ + if not isinstance(filename, str): + filename = filename.decode("utf-8") for directory in sys.path: - if is_directory(directory): - assert isinstance(directory, str) - if not isinstance(filename, str): - filename = filename.decode("utf-8") - try: - return load(os.path.join(directory, filename)) - except OSError: - pass + try: + return load(os.path.join(directory, filename)) + except OSError: + pass msg = "cannot find font file" raise OSError(msg) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 909911dfe37..e1c61f991c5 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -425,7 +425,7 @@ def load_read(self, read_bytes: int) -> bytes: return s def draft( - self, mode: str, size: tuple[int, int] + self, mode: str | None, size: tuple[int, int] ) -> tuple[str, tuple[int, int, float, float]] | None: if len(self.tile) != 1: return None diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index d85eb84fa69..1fe95441715 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -1,7 +1,5 @@ from typing import Any -from typing_extensions import Buffer - class ImagingCore: def __getattr__(self, name: str) -> Any: ... @@ -14,5 +12,5 @@ class ImagingDraw: class PixelAccess: def __getattr__(self, name: str) -> Any: ... -def font(image, glyphdata: Buffer) -> ImagingFont: ... +def font(image, glyphdata: bytes) -> ImagingFont: ... def __getattr__(name: str) -> Any: ... diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index 987e7fd6f49..b023efe0110 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -1,5 +1,7 @@ from typing import Any, TypedDict +from . import _imaging + class _Axis(TypedDict): minimum: int | None default: int | None @@ -37,21 +39,28 @@ class Font: x_start=..., y_start=..., /, - ) -> tuple[Any, tuple[int, int]]: ... + ) -> tuple[_imaging.ImagingCore, tuple[int, int]]: ... def getsize( - self, string: str, mode=..., dir=..., features=..., lang=..., anchor=..., / + self, + string: str | bytes | bytearray, + mode=..., + dir=..., + features=..., + lang=..., + anchor=..., + /, ) -> tuple[tuple[int, int], tuple[int, int]]: ... def getlength( self, string: str, mode=..., dir=..., features=..., lang=..., / - ) -> int: ... - def getvarnames(self) -> list[str]: ... - def getvaraxes(self) -> list[_Axis]: ... + ) -> float: ... + def getvarnames(self) -> list[bytes]: ... + def getvaraxes(self) -> list[_Axis] | None: ... def setvarname(self, instance_index: int, /) -> None: ... def setvaraxes(self, axes: list[float], /) -> None: ... def getfont( - filename: str | bytes | bytearray, - size, + filename: str | bytes, + size: float, index=..., encoding=..., font_bytes=..., From f5da04adb07f19bd79aededb2be7291d445e4f96 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 3 Jun 2024 21:58:02 +1000 Subject: [PATCH 206/300] Added type hints Co-authored-by: Nulano --- src/PIL/Image.py | 10 ++++------ src/PIL/ImageDraw.py | 8 +++++++- src/PIL/PyAccess.py | 41 ++++++++++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 958b95e3b0f..dd6984020b0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1511,7 +1511,7 @@ def _reload_exif(self) -> None: self._exif._loaded = False self.getexif() - def get_child_images(self): + def get_child_images(self) -> list[ImageFile.ImageFile]: child_images = [] exif = self.getexif() ifds = [] @@ -1535,10 +1535,7 @@ def get_child_images(self): fp = self.fp thumbnail_offset = ifd.get(513) if thumbnail_offset is not None: - try: - thumbnail_offset += self._exif_offset - except AttributeError: - pass + thumbnail_offset += getattr(self, "_exif_offset", 0) self.fp.seek(thumbnail_offset) data = self.fp.read(ifd.get(514)) fp = io.BytesIO(data) @@ -1604,7 +1601,7 @@ def has_transparency_data(self) -> bool: or "transparency" in self.info ) - def apply_transparency(self): + def apply_transparency(self) -> None: """ If a P mode image has a "transparency" key in the info dictionary, remove the key and instead apply the transparency to the palette. @@ -1616,6 +1613,7 @@ def apply_transparency(self): from . import ImagePalette palette = self.getpalette("RGBA") + assert palette is not None transparency = self.info["transparency"] if isinstance(transparency, bytes): for i, alpha in enumerate(transparency): diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 17c17643066..8fe179dd591 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -908,7 +908,13 @@ def getdraw(im=None, hints=None): return im, handler -def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None: +def floodfill( + image: Image.Image, + xy: tuple[int, int], + value: float | tuple[int, ...], + border: float | tuple[int, ...] | None = None, + thresh: float = 0, +) -> None: """ (experimental) Fills a bounded region with a given color. diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index a9da90613e7..fe12cb641e1 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -22,6 +22,7 @@ import logging import sys +from typing import TYPE_CHECKING from ._deprecate import deprecate @@ -48,9 +49,12 @@ logger = logging.getLogger(__name__) +if TYPE_CHECKING: + from . import Image + class PyAccess: - def __init__(self, img, readonly=False): + def __init__(self, img: Image.Image, readonly: bool = False) -> None: deprecate("PyAccess", 11) vals = dict(img.im.unsafe_ptrs) self.readonly = readonly @@ -77,7 +81,8 @@ def __setitem__(self, xy, color): """ Modifies the pixel at x,y. The color is given as a single numerical value for single band images, and a tuple for - multi-band images + multi-band images. In addition to this, RGB and RGBA tuples + are accepted for P and PA images. :param xy: The pixel coordinate, given as (x, y). See :ref:`coordinate-system`. @@ -108,7 +113,7 @@ def __setitem__(self, xy, color): return self.set_pixel(x, y, color) - def __getitem__(self, xy): + def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: """ Returns the pixel at x,y. The pixel is returned as a single value for single band images or a tuple for multiple band @@ -130,13 +135,19 @@ def __getitem__(self, xy): putpixel = __setitem__ getpixel = __getitem__ - def check_xy(self, xy): + def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]: (x, y) = xy if not (0 <= x < self.xsize and 0 <= y < self.ysize): msg = "pixel location out of range" raise ValueError(msg) return xy + def get_pixel(self, x: int, y: int) -> float | tuple[int, ...]: + raise NotImplementedError() + + def set_pixel(self, x: int, y: int, color: float | tuple[int, ...]) -> None: + raise NotImplementedError() + class _PyAccess32_2(PyAccess): """PA, LA, stored in first and last bytes of a 32 bit word""" @@ -144,7 +155,7 @@ class _PyAccess32_2(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.a @@ -161,7 +172,7 @@ class _PyAccess32_3(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.g, pixel.b @@ -180,7 +191,7 @@ class _PyAccess32_4(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]: pixel = self.pixels[y][x] return pixel.r, pixel.g, pixel.b, pixel.a @@ -199,7 +210,7 @@ class _PyAccess8(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = self.image8 - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -217,7 +228,7 @@ class _PyAccessI16_N(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("unsigned short **", self.image) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -235,7 +246,7 @@ class _PyAccessI16_L(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_I16 **", self.image) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: pixel = self.pixels[y][x] return pixel.l + pixel.r * 256 @@ -256,7 +267,7 @@ class _PyAccessI16_B(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("struct Pixel_I16 **", self.image) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: pixel = self.pixels[y][x] return pixel.l * 256 + pixel.r @@ -277,7 +288,7 @@ class _PyAccessI32_N(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = self.image32 - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -296,7 +307,7 @@ def reverse(self, i): chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0] return ffi.cast("int *", chars)[0] - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> int: return self.reverse(self.pixels[y][x]) def set_pixel(self, x, y, color): @@ -309,7 +320,7 @@ class _PyAccessF(PyAccess): def _post_init(self, *args, **kwargs): self.pixels = ffi.cast("float **", self.image32) - def get_pixel(self, x, y): + def get_pixel(self, x: int, y: int) -> float: return self.pixels[y][x] def set_pixel(self, x, y, color): @@ -357,7 +368,7 @@ def set_pixel(self, x, y, color): mode_map["I;32B"] = _PyAccessI32_N -def new(img, readonly=False): +def new(img: Image.Image, readonly: bool = False) -> PyAccess | None: access_type = mode_map.get(img.mode, None) if not access_type: logger.debug("PyAccess Not Implemented: %s", img.mode) From 322814d7ce8d48bcbf45ac912aecef445f6743b2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:24:10 +0000 Subject: [PATCH 207/300] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.3 → v0.4.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.3...v0.4.7) - [github.com/pre-commit/mirrors-clang-format: v18.1.4 → v18.1.5](https://github.com/pre-commit/mirrors-clang-format/compare/v18.1.4...v18.1.5) - [github.com/python-jsonschema/check-jsonschema: 0.28.2 → 0.28.4](https://github.com/python-jsonschema/check-jsonschema/compare/0.28.2...0.28.4) - [github.com/abravalheri/validate-pyproject: v0.16 → v0.18](https://github.com/abravalheri/validate-pyproject/compare/v0.16...v0.18) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e848eb67080..6a76e8c00cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.3 + rev: v0.4.7 hooks: - id: ruff args: [--exit-non-zero-on-fix] @@ -24,7 +24,7 @@ repos: exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$) - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v18.1.4 + rev: v18.1.5 hooks: - id: clang-format types: [c] @@ -50,7 +50,7 @@ repos: exclude: ^.github/.*TEMPLATE|^Tests/(fonts|images)/ - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.2 + rev: 0.28.4 hooks: - id: check-github-workflows - id: check-readthedocs @@ -67,7 +67,7 @@ repos: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.16 + rev: v0.18 hooks: - id: validate-pyproject From e9c9f19c26c3a361c4fa17947b6b5d860e516c2c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 4 Jun 2024 18:46:35 +1000 Subject: [PATCH 208/300] Do not use first frame duration for other frames when saving --- Tests/test_file_apng.py | 15 +++++++++++++-- src/PIL/PngImagePlugin.py | 13 ++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 1b393a3ff5d..e95850212a5 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -706,10 +706,21 @@ def test_different_modes_in_later_frames( assert reloaded.mode == mode -def test_apng_repeated_seeks_give_correct_info() -> None: +def test_different_durations(tmp_path: Path) -> None: + test_file = str(tmp_path / "temp.png") + with Image.open("Tests/images/apng/different_durations.png") as im: - for i in range(3): + for _ in range(3): im.seek(0) assert im.info["duration"] == 4000 + im.seek(1) assert im.info["duration"] == 1000 + + im.save(test_file, save_all=True) + + with Image.open(test_file) as reloaded: + assert reloaded.info["duration"] == 4000 + + reloaded.seek(1) + assert reloaded.info["duration"] == 1000 diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0d5751962c0..dde45e587e1 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1105,7 +1105,7 @@ def write(self, data: bytes) -> None: def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images): - duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) + duration = im.encoderinfo.get("duration") loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) @@ -1126,6 +1126,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) encoderinfo = im.encoderinfo.copy() if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] + elif duration is None and "duration" in im_frame.info: + encoderinfo["duration"] = im_frame.info["duration"] if isinstance(disposal, (list, tuple)): encoderinfo["disposal"] = disposal[frame_count] if isinstance(blend, (list, tuple)): @@ -1160,15 +1162,12 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) not bbox and prev_disposal == encoderinfo.get("disposal") and prev_blend == encoderinfo.get("blend") + and "duration" in encoderinfo ): - previous["encoderinfo"]["duration"] += encoderinfo.get( - "duration", duration - ) + previous["encoderinfo"]["duration"] += encoderinfo["duration"] continue else: bbox = None - if "duration" not in encoderinfo: - encoderinfo["duration"] = duration im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) if len(im_frames) == 1 and not default_image: @@ -1198,7 +1197,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) im_frame = im_frame.crop(bbox) size = im_frame.size encoderinfo = frame_data["encoderinfo"] - frame_duration = int(round(encoderinfo["duration"])) + frame_duration = int(round(encoderinfo.get("duration", 0))) frame_disposal = encoderinfo.get("disposal", disposal) frame_blend = encoderinfo.get("blend", blend) # frame control From 6e40601f69875e0734dce467e4a3b969649f65bf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 4 Jun 2024 20:37:09 +1000 Subject: [PATCH 209/300] Added type hints --- src/PIL/BlpImagePlugin.py | 3 ++- src/PIL/EpsImagePlugin.py | 2 +- src/PIL/FpxImagePlugin.py | 6 +++--- src/PIL/ImageEnhance.py | 13 ++++++++----- src/PIL/ImageFilter.py | 25 ++++++++++++++++--------- src/PIL/ImageMorph.py | 6 +++--- src/PIL/Jpeg2KImagePlugin.py | 6 +++--- src/PIL/PaletteFile.py | 2 +- src/PIL/QoiImagePlugin.py | 2 +- src/PIL/SpiderImagePlugin.py | 2 +- src/PIL/WebPImagePlugin.py | 2 +- 11 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 782e28cf5df..2db115ccc4f 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -35,6 +35,7 @@ import struct from enum import IntEnum from io import BytesIO +from typing import IO from . import Image, ImageFile @@ -448,7 +449,7 @@ def encode(self, bufsize): return len(data), 0, data -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if im.mode != "P": msg = "Unsupported BLP image mode" raise ValueError(msg) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 5a44baa491e..d24a2ba80f0 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -228,7 +228,7 @@ def _open(self) -> None: reading_trailer_comments = False trailer_reached = False - def check_required_header_comments(): + def check_required_header_comments() -> None: if "PS-Adobe" not in self.info: msg = 'EPS header missing "%!PS-Adobe" comment' raise SyntaxError(msg) diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 4ba93bb3912..b3e6c6e362b 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -70,7 +70,7 @@ def _open(self): self._open_index(1) - def _open_index(self, index=1): + def _open_index(self, index: int = 1) -> None: # # get the Image Contents Property Set @@ -85,7 +85,7 @@ def _open_index(self, index=1): size = max(self.size) i = 1 while size > 64: - size = size / 2 + size = size // 2 i += 1 self.maxid = i - 1 @@ -118,7 +118,7 @@ def _open_index(self, index=1): self._open_subimage(1, self.maxid) - def _open_subimage(self, index=1, subimage=0): + def _open_subimage(self, index: int = 1, subimage: int = 0) -> None: # # setup tile descriptors for a given subimage diff --git a/src/PIL/ImageEnhance.py b/src/PIL/ImageEnhance.py index 93a50d2a2b9..d7e99a96815 100644 --- a/src/PIL/ImageEnhance.py +++ b/src/PIL/ImageEnhance.py @@ -23,7 +23,10 @@ class _Enhance: - def enhance(self, factor): + image: Image.Image + degenerate: Image.Image + + def enhance(self, factor: float) -> Image.Image: """ Returns an enhanced image. @@ -46,7 +49,7 @@ class Color(_Enhance): the original image. """ - def __init__(self, image): + def __init__(self, image: Image.Image) -> None: self.image = image self.intermediate_mode = "L" if "A" in image.getbands(): @@ -63,7 +66,7 @@ class Contrast(_Enhance): gives a solid gray image. A factor of 1.0 gives the original image. """ - def __init__(self, image): + def __init__(self, image: Image.Image) -> None: self.image = image mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) self.degenerate = Image.new("L", image.size, mean).convert(image.mode) @@ -80,7 +83,7 @@ class Brightness(_Enhance): original image. """ - def __init__(self, image): + def __init__(self, image: Image.Image) -> None: self.image = image self.degenerate = Image.new(image.mode, image.size, 0) @@ -96,7 +99,7 @@ class Sharpness(_Enhance): original image, and a factor of 2.0 gives a sharpened image. """ - def __init__(self, image): + def __init__(self, image: Image.Image) -> None: self.image = image self.degenerate = image.filter(ImageFilter.SMOOTH) diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 43e700b7b30..02288e13576 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -18,7 +18,8 @@ import abc import functools -from typing import Sequence +from types import ModuleType +from typing import Any, Sequence class Filter: @@ -57,7 +58,13 @@ class Kernel(BuiltinFilter): name = "Kernel" - def __init__(self, size, kernel, scale=None, offset=0): + def __init__( + self, + size: tuple[int, int], + kernel: Sequence[float], + scale: float | None = None, + offset: float = 0, + ) -> None: if scale is None: # default scale is sum of kernel scale = functools.reduce(lambda a, b: a + b, kernel) @@ -194,10 +201,8 @@ class BoxBlur(MultibandFilter): name = "BoxBlur" - def __init__(self, radius): - xy = radius - if not isinstance(xy, (tuple, list)): - xy = (xy, xy) + def __init__(self, radius: float | Sequence[float]) -> None: + xy = radius if isinstance(radius, (tuple, list)) else (radius, radius) if xy[0] < 0 or xy[1] < 0: msg = "radius must be >= 0" raise ValueError(msg) @@ -381,7 +386,9 @@ class Color3DLUT(MultibandFilter): name = "Color 3D LUT" - def __init__(self, size, table, channels=3, target_mode=None, **kwargs): + def __init__( + self, size, table, channels: int = 3, target_mode: str | None = None, **kwargs + ): if channels not in (3, 4): msg = "Only 3 or 4 output channels are supported" raise ValueError(msg) @@ -395,7 +402,7 @@ def __init__(self, size, table, channels=3, target_mode=None, **kwargs): items = size[0] * size[1] * size[2] wrong_size = False - numpy = None + numpy: ModuleType | None = None if hasattr(table, "shape"): try: import numpy @@ -442,7 +449,7 @@ def __init__(self, size, table, channels=3, target_mode=None, **kwargs): self.table = table @staticmethod - def _check_size(size): + def _check_size(size: Any) -> list[int]: try: _, _, _ = size except ValueError as e: diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py index 6ee8c4f253d..6a43983d33d 100644 --- a/src/PIL/ImageMorph.py +++ b/src/PIL/ImageMorph.py @@ -200,7 +200,7 @@ def __init__( elif patterns is not None: self.lut = LutBuilder(patterns=patterns).build_lut() - def apply(self, image: Image.Image): + def apply(self, image: Image.Image) -> tuple[int, Image.Image]: """Run a single morphological operation on an image Returns a tuple of the number of changed pixels and the @@ -216,7 +216,7 @@ def apply(self, image: Image.Image): count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id) return count, outimage - def match(self, image: Image.Image): + def match(self, image: Image.Image) -> list[tuple[int, int]]: """Get a list of coordinates matching the morphological operation on an image. @@ -231,7 +231,7 @@ def match(self, image: Image.Image): raise ValueError(msg) return _imagingmorph.match(bytes(self.lut), image.im.id) - def get_on_pixels(self, image: Image.Image): + def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]: """Get a list of all turned on pixels in a binary image Returns a list of tuples of (x,y) coordinates diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index ce6342bdba2..e6395b1cb96 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -34,7 +34,7 @@ def __init__(self, fp, length=-1): self.length = length self.remaining_in_box = -1 - def _can_read(self, num_bytes): + def _can_read(self, num_bytes: int) -> bool: if self.has_length and self.fp.tell() + num_bytes > self.length: # Outside box: ensure we don't read past the known file length return False @@ -44,7 +44,7 @@ def _can_read(self, num_bytes): else: return True # No length known, just read - def _read_bytes(self, num_bytes): + def _read_bytes(self, num_bytes: int) -> bytes: if not self._can_read(num_bytes): msg = "Not enough data in header" raise SyntaxError(msg) @@ -74,7 +74,7 @@ def has_next_box(self) -> bool: else: return True - def next_box_type(self): + def next_box_type(self) -> bytes: # Skip the rest of the box if it has not been read if self.remaining_in_box > 0: self.fp.seek(self.remaining_in_box, os.SEEK_CUR) diff --git a/src/PIL/PaletteFile.py b/src/PIL/PaletteFile.py index dc31754020e..eaed5367c99 100644 --- a/src/PIL/PaletteFile.py +++ b/src/PIL/PaletteFile.py @@ -48,5 +48,5 @@ def __init__(self, fp): self.palette = b"".join(self.palette) - def getpalette(self): + def getpalette(self) -> tuple[bytes, str]: return self.palette, self.rawmode diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index cea8b60da81..f2cf06d0dd7 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -38,7 +38,7 @@ def _open(self) -> None: class QoiDecoder(ImageFile.PyDecoder): _pulls_fd = True - def _add_to_previous_pixels(self, value): + def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: self._previous_pixel = value r, g, b, a = value diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 5b8ad47f0cd..e5242395f92 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -233,7 +233,7 @@ def loadImageSeries(filelist=None): # For saving images in Spider format -def makeSpiderHeader(im): +def makeSpiderHeader(im: Image.Image) -> list[bytes]: nsam, nrow = im.size lenbyt = nsam * 4 # There are labrec records in the header labrec = int(1024 / lenbyt) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index ff7402dca32..463d6a62398 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -117,7 +117,7 @@ def seek(self, frame: int) -> None: # Set logical frame to requested position self.__logical_frame = frame - def _reset(self, reset=True): + def _reset(self, reset: bool = True) -> None: if reset: self._decoder.reset() self.__physical_frame = 0 From b3c534cc9aa1acb7d84d0be83c2919072e46af95 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Jun 2024 08:29:28 +1000 Subject: [PATCH 210/300] Added type hints --- src/PIL/BmpImagePlugin.py | 9 ++++++--- src/PIL/BufrStubImagePlugin.py | 4 +++- src/PIL/DdsImagePlugin.py | 3 ++- src/PIL/GifImagePlugin.py | 16 +++++++++------- src/PIL/GribStubImagePlugin.py | 4 +++- src/PIL/IcoImagePlugin.py | 6 ++++-- src/PIL/ImImagePlugin.py | 5 +++-- src/PIL/Image.py | 20 ++++++++++---------- src/PIL/MpoImagePlugin.py | 3 ++- src/PIL/PdfImagePlugin.py | 3 ++- src/PIL/SpiderImagePlugin.py | 6 +++--- src/PIL/TiffImagePlugin.py | 4 ++-- src/PIL/WmfImagePlugin.py | 4 +++- 13 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index c5d1cd40d3b..2df1d8d33bd 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -25,6 +25,7 @@ from __future__ import annotations import os +from typing import IO from . import Image, ImageFile, ImagePalette from ._binary import i16le as i16 @@ -52,7 +53,7 @@ def _accept(prefix: bytes) -> bool: return prefix[:2] == b"BM" -def _dib_accept(prefix): +def _dib_accept(prefix: bytes) -> bool: return i32(prefix) in [12, 40, 52, 56, 64, 108, 124] @@ -394,11 +395,13 @@ def _open(self) -> None: } -def _dib_save(im, fp, filename): +def _dib_save(im: Image.Image, fp: IO[bytes], filename: str) -> None: _save(im, fp, filename, False) -def _save(im, fp, filename, bitmap_header=True): +def _save( + im: Image.Image, fp: IO[bytes], filename: str, bitmap_header: bool = True +) -> None: try: rawmode, bits, colors = SAVE[im.mode] except KeyError as e: diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 826e89dafea..6f52204b829 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -10,6 +10,8 @@ # from __future__ import annotations +from typing import IO + from . import Image, ImageFile _handler = None @@ -58,7 +60,7 @@ def _load(self) -> ImageFile.StubHandler | None: return _handler -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "BUFR save handler not installed" raise OSError(msg) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 1575f2d88b6..a3efadb030d 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -16,6 +16,7 @@ import struct import sys from enum import IntEnum, IntFlag +from typing import IO from . import Image, ImageFile, ImagePalette from ._binary import i32le as i32 @@ -510,7 +511,7 @@ def decode(self, buffer): return -1, 0 -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if im.mode not in ("RGB", "RGBA", "L", "LA"): msg = f"cannot write mode {im.mode} as DDS" raise OSError(msg) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 962a9283464..e62852db31d 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -31,6 +31,7 @@ import subprocess from enum import IntEnum from functools import cached_property +from typing import IO from . import ( Image, @@ -336,14 +337,13 @@ def _seek(self, frame, update_image=True): self._mode = "RGB" self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) - def _rgb(color): + def _rgb(color: int) -> tuple[int, int, int]: if self._frame_palette: if color * 3 + 3 > len(self._frame_palette.palette): color = 0 - color = tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]) + return tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]) else: - color = (color, color, color) - return color + return (color, color, color) self.dispose_extent = frame_dispose_extent try: @@ -709,11 +709,13 @@ def _write_multiple_frames(im, fp, palette): return True -def _save_all(im, fp, filename): +def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: _save(im, fp, filename, save_all=True) -def _save(im, fp, filename, save_all=False): +def _save( + im: Image.Image, fp: IO[bytes], filename: str, save_all: bool = False +) -> None: # header if "palette" in im.encoderinfo or "palette" in im.info: palette = im.encoderinfo.get("palette", im.info.get("palette")) @@ -730,7 +732,7 @@ def _save(im, fp, filename, save_all=False): fp.flush() -def get_interlace(im): +def get_interlace(im: Image.Image) -> int: interlace = im.encoderinfo.get("interlace", 1) # workaround for @PIL153 diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index c27cffab63e..b24dcded2f9 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -10,6 +10,8 @@ # from __future__ import annotations +from typing import IO + from . import Image, ImageFile _handler = None @@ -58,7 +60,7 @@ def _load(self) -> ImageFile.StubHandler | None: return _handler -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "GRIB save handler not installed" raise OSError(msg) diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index cea093f9c6c..af94e5a2e7f 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -25,6 +25,7 @@ import warnings from io import BytesIO from math import ceil, log +from typing import IO from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin from ._binary import i16le as i16 @@ -39,7 +40,7 @@ _MAGIC = b"\0\0\1\0" -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: fp.write(_MAGIC) # (2+2) bmp = im.encoderinfo.get("bitmap_format") == "bmp" sizes = im.encoderinfo.get( @@ -194,7 +195,7 @@ def getimage(self, size, bpp=False): """ return self.frame(self.getentryindex(size, bpp)) - def frame(self, idx): + def frame(self, idx: int) -> Image.Image: """ Get an image from frame idx """ @@ -205,6 +206,7 @@ def frame(self, idx): data = self.buf.read(8) self.buf.seek(header["offset"]) + im: Image.Image if data[:8] == PngImagePlugin._MAGIC: # png frame im = PngImagePlugin.PngImageFile(self.buf) diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 8e949ebaf9d..c98cfb0984b 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -28,6 +28,7 @@ import os import re +from typing import IO, Any from . import Image, ImageFile, ImagePalette @@ -103,7 +104,7 @@ split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") -def number(s): +def number(s: Any) -> float: try: return int(s) except ValueError: @@ -325,7 +326,7 @@ def tell(self) -> int: } -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: try: image_type, rawmode = SAVE[im.mode] except KeyError as e: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6385f204bf2..8cb4b7e3292 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2875,7 +2875,7 @@ def transpose(self, method: Transpose) -> Image: self.load() return self._new(self.im.transpose(method)) - def effect_spread(self, distance): + def effect_spread(self, distance: int) -> Image: """ Randomly spread pixels in an image. @@ -3012,7 +3012,7 @@ def new( return im._new(core.fill(mode, size, color)) -def frombytes(mode, size, data, decoder_name="raw", *args) -> Image: +def frombytes(mode, size, data, decoder_name: str = "raw", *args) -> Image: """ Creates a copy of an image memory from pixel data in a buffer. @@ -3051,7 +3051,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args) -> Image: return im -def frombuffer(mode, size, data, decoder_name="raw", *args) -> Image: +def frombuffer(mode: str, size, data, decoder_name: str = "raw", *args) -> Image: """ Creates an image memory referencing pixel data in a byte buffer. @@ -3553,7 +3553,7 @@ def register_save(id: str, driver) -> None: SAVE[id.upper()] = driver -def register_save_all(id, driver) -> None: +def register_save_all(id: str, driver) -> None: """ Registers an image function to save all the frames of a multiframe format. This function should not be @@ -3565,7 +3565,7 @@ def register_save_all(id, driver) -> None: SAVE_ALL[id.upper()] = driver -def register_extension(id, extension) -> None: +def register_extension(id: str, extension: str) -> None: """ Registers an image extension. This function should not be used in application code. @@ -3576,7 +3576,7 @@ def register_extension(id, extension) -> None: EXTENSION[extension.lower()] = id.upper() -def register_extensions(id, extensions) -> None: +def register_extensions(id: str, extensions: list[str]) -> None: """ Registers image extensions. This function should not be used in application code. @@ -3588,7 +3588,7 @@ def register_extensions(id, extensions) -> None: register_extension(id, extension) -def registered_extensions(): +def registered_extensions() -> dict[str, str]: """ Returns a dictionary containing all file extensions belonging to registered plugins @@ -3650,7 +3650,7 @@ def effect_mandelbrot(size, extent, quality): return Image()._new(core.effect_mandelbrot(size, extent, quality)) -def effect_noise(size, sigma): +def effect_noise(size: tuple[int, int], sigma: float) -> Image: """ Generate Gaussian noise centered around 128. @@ -3661,7 +3661,7 @@ def effect_noise(size, sigma): return Image()._new(core.effect_noise(size, sigma)) -def linear_gradient(mode): +def linear_gradient(mode: str) -> Image: """ Generate 256x256 linear gradient from black to white, top to bottom. @@ -3670,7 +3670,7 @@ def linear_gradient(mode): return Image()._new(core.linear_gradient(mode)) -def radial_gradient(mode): +def radial_gradient(mode: str) -> Image: """ Generate 256x256 radial gradient from black to white, centre to edge. diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 766e1290ced..6716722f204 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -22,6 +22,7 @@ import itertools import os import struct +from typing import IO from . import ( Image, @@ -32,7 +33,7 @@ from ._binary import o32le -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: JpegImagePlugin._save(im, fp, filename) diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 1777f1f20db..ccd28f3434b 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -25,6 +25,7 @@ import math import os import time +from typing import IO from . import Image, ImageFile, ImageSequence, PdfParser, __version__, features @@ -39,7 +40,7 @@ # 5. page contents -def _save_all(im, fp, filename): +def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: _save(im, fp, filename, save_all=True) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index e5242395f92..98dd91c0e24 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -37,7 +37,7 @@ import os import struct import sys -from typing import TYPE_CHECKING +from typing import IO, TYPE_CHECKING from . import Image, ImageFile @@ -263,7 +263,7 @@ def makeSpiderHeader(im: Image.Image) -> list[bytes]: return [struct.pack("f", v) for v in hdr] -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if im.mode[0] != "F": im = im.convert("F") @@ -279,7 +279,7 @@ def _save(im, fp, filename): ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) -def _save_spider(im, fp, filename): +def _save_spider(im: Image.Image, fp: IO[bytes], filename: str) -> None: # get the filename extension and register it with Image ext = os.path.splitext(filename)[1] Image.register_extension(SpiderImageFile.format, ext) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index f3fa3c24ccd..04f36744bed 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1995,7 +1995,7 @@ def newFrame(self) -> None: self.finalize() self.setup() - def __enter__(self): + def __enter__(self) -> AppendingTiffWriter: return self def __exit__(self, exc_type, exc_value, traceback): @@ -2023,7 +2023,7 @@ def goToEnd(self) -> None: self.f.write(bytes(pad_bytes)) self.offsetOfNewPage = self.f.tell() - def setEndian(self, endian): + def setEndian(self, endian: str) -> None: self.endian = endian self.longFmt = f"{self.endian}L" self.shortFmt = f"{self.endian}H" diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index fab3e26c587..25a4545db52 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -20,6 +20,8 @@ # http://wvware.sourceforge.net/caolan/ora-wmf.html from __future__ import annotations +from typing import IO + from . import Image, ImageFile from ._binary import i16le as word from ._binary import si16le as short @@ -161,7 +163,7 @@ def load(self, dpi=None): return super().load() -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "WMF save handler not installed" raise OSError(msg) From 923d4e5e1a971ea64ce56c5b016a620be33d51eb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Jun 2024 22:27:23 +1000 Subject: [PATCH 211/300] Added type hints --- Tests/bench_cffi_access.py | 1 + Tests/test_features.py | 2 +- Tests/test_file_bmp.py | 2 +- Tests/test_file_bufrstub.py | 11 ++++++----- Tests/test_file_gribstub.py | 4 ++-- Tests/test_file_hdf5stub.py | 7 ++++--- Tests/test_file_jpeg.py | 2 +- Tests/test_file_webp.py | 4 +++- Tests/test_file_webp_animated.py | 2 +- Tests/test_file_wmf.py | 12 ++++++++---- Tests/test_image_access.py | 6 ++++++ Tests/test_image_rotate.py | 4 ++-- Tests/test_image_thumbnail.py | 4 +++- Tests/test_imageops_usm.py | 1 - Tests/test_qt_image_qapplication.py | 2 +- src/PIL/BufrStubImagePlugin.py | 2 +- src/PIL/GribStubImagePlugin.py | 2 +- src/PIL/Hdf5StubImagePlugin.py | 2 +- src/PIL/WmfImagePlugin.py | 2 +- 19 files changed, 44 insertions(+), 28 deletions(-) diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index d2a08c07bc4..c7d105836aa 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -44,6 +44,7 @@ def test_direct() -> None: caccess = im.im.pixel_access(False) access = PyAccess.new(im, False) + assert access is not None assert caccess[(0, 0)] == access[(0, 0)] print(f"Size: {im.width}x{im.height}") diff --git a/Tests/test_features.py b/Tests/test_features.py index 59fb4980923..de418115ee0 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -124,7 +124,7 @@ def test_unsupported_module() -> None: @pytest.mark.parametrize("supported_formats", (True, False)) -def test_pilinfo(supported_formats) -> None: +def test_pilinfo(supported_formats: bool) -> None: buf = io.StringIO() features.pilinfo(buf, supported_formats=supported_formats) out = buf.getvalue() diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index c7c9b24e763..2ff4160bd71 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -140,7 +140,7 @@ def test_load_dib() -> None: (124, "g/pal8v5.bmp"), ), ) -def test_dib_header_size(header_size, path): +def test_dib_header_size(header_size: int, path: str) -> None: image_path = "Tests/images/bmp/" + path with open(image_path, "rb") as fp: data = fp.read()[14:] diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py index 3dd24533aae..939e82e7717 100644 --- a/Tests/test_file_bufrstub.py +++ b/Tests/test_file_bufrstub.py @@ -1,10 +1,11 @@ from __future__ import annotations from pathlib import Path +from typing import IO import pytest -from PIL import BufrStubImagePlugin, Image +from PIL import BufrStubImagePlugin, Image, ImageFile from .helper import hopper @@ -50,20 +51,20 @@ def test_save(tmp_path: Path) -> None: def test_handler(tmp_path: Path) -> None: - class TestHandler: + class TestHandler(ImageFile.StubHandler): opened = False loaded = False saved = False - def open(self, im) -> None: + def open(self, im: ImageFile.StubImageFile) -> None: self.opened = True - def load(self, im): + def load(self, im: ImageFile.StubImageFile) -> Image.Image: self.loaded = True im.fp.close() return Image.new("RGB", (1, 1)) - def save(self, im, fp, filename) -> None: + def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: self.saved = True handler = TestHandler() diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index 096a5b88b21..86a9064fc46 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -5,7 +5,7 @@ import pytest -from PIL import GribStubImagePlugin, Image +from PIL import GribStubImagePlugin, Image, ImageFile from .helper import hopper @@ -51,7 +51,7 @@ def test_save(tmp_path: Path) -> None: def test_handler(tmp_path: Path) -> None: - class TestHandler: + class TestHandler(ImageFile.StubHandler): opened = False loaded = False saved = False diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index f871e2eff16..ee1544c51fa 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -1,11 +1,12 @@ from __future__ import annotations +from io import BytesIO from pathlib import Path from typing import IO import pytest -from PIL import Hdf5StubImagePlugin, Image +from PIL import Hdf5StubImagePlugin, Image, ImageFile TEST_FILE = "Tests/images/hdf5.h5" @@ -41,7 +42,7 @@ def test_load() -> None: def test_save() -> None: # Arrange with Image.open(TEST_FILE) as im: - dummy_fp = None + dummy_fp = BytesIO() dummy_filename = "dummy.filename" # Act / Assert: stub cannot save without an implemented handler @@ -52,7 +53,7 @@ def test_save() -> None: def test_handler(tmp_path: Path) -> None: - class TestHandler: + class TestHandler(ImageFile.StubHandler): opened = False loaded = False saved = False diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 33f9ce00edc..18dc752d8bd 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -171,7 +171,7 @@ def getchannels(im: JpegImagePlugin.JpegImageFile) -> tuple[int, int, int]: [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"], ) def test_dpi(self, test_image_path: str) -> None: - def test(xdpi: int, ydpi: int | None = None): + def test(xdpi: int, ydpi: int | None = None) -> tuple[int, int] | None: with Image.open(test_image_path) as im: im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) return im.info.get("dpi") diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index e2de84c71a1..1caf032f6e1 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -198,7 +198,9 @@ def test_file_pointer_could_be_reused(self) -> None: (0, (0,), (-1, 0, 1, 2), (253, 254, 255, 256)), ) @skip_unless_feature("webp_anim") - def test_invalid_background(self, background, tmp_path: Path) -> None: + def test_invalid_background( + self, background: int | tuple[int, ...], tmp_path: Path + ) -> None: temp_file = str(tmp_path / "temp.webp") im = hopper() with pytest.raises(OSError): diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index ba931f8643c..882dccb32f9 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -69,7 +69,7 @@ def test_write_animation_RGB(tmp_path: Path) -> None: are visually similar to the originals. """ - def check(temp_file) -> None: + def check(temp_file: str) -> None: with Image.open(temp_file) as im: assert im.n_frames == 2 diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index b43e3f2965f..79e707263d6 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -1,10 +1,11 @@ from __future__ import annotations from pathlib import Path +from typing import IO import pytest -from PIL import Image, WmfImagePlugin +from PIL import Image, ImageFile, WmfImagePlugin from .helper import assert_image_similar_tofile, hopper @@ -34,10 +35,13 @@ def test_load() -> None: def test_register_handler(tmp_path: Path) -> None: - class TestHandler: + class TestHandler(ImageFile.StubHandler): methodCalled = False - def save(self, im, fp, filename) -> None: + def load(self, im: ImageFile.StubImageFile) -> Image.Image: + return Image.new("RGB", (1, 1)) + + def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: self.methodCalled = True handler = TestHandler() @@ -70,7 +74,7 @@ def test_load_set_dpi() -> None: @pytest.mark.parametrize("ext", (".wmf", ".emf")) -def test_save(ext, tmp_path: Path) -> None: +def test_save(ext: str, tmp_path: Path) -> None: im = hopper() tmpfile = str(tmp_path / ("temp" + ext)) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 9d600667937..8abb1f69fc1 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -259,6 +259,7 @@ def _test_get_access(self, im: Image.Image) -> None: caccess = im.im.pixel_access(False) with pytest.warns(DeprecationWarning): access = PyAccess.new(im, False) + assert access is not None w, h = im.size for x in range(0, w, 10): @@ -289,6 +290,7 @@ def _test_set_access(self, im: Image.Image, color: tuple[int, ...] | float) -> N caccess = im.im.pixel_access(False) with pytest.warns(DeprecationWarning): access = PyAccess.new(im, False) + assert access is not None w, h = im.size for x in range(0, w, 10): @@ -299,6 +301,8 @@ def _test_set_access(self, im: Image.Image, color: tuple[int, ...] | float) -> N # Attempt to set the value on a read-only image with pytest.warns(DeprecationWarning): access = PyAccess.new(im, True) + assert access is not None + with pytest.raises(ValueError): access[(0, 0)] = color @@ -341,6 +345,8 @@ def test_p_putpixel_rgb_rgba(self, mode: str) -> None: im = Image.new(mode, (1, 1)) with pytest.warns(DeprecationWarning): access = PyAccess.new(im, False) + assert access is not None + access.putpixel((0, 0), color) if len(color) == 3: diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index c10c96da6f9..252a15db742 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -124,8 +124,8 @@ def test_fastpath_translate() -> None: def test_center() -> None: im = hopper() rotate(im, im.mode, 45, center=(0, 0)) - rotate(im, im.mode, 45, translate=(im.size[0] / 2, 0)) - rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] / 2, 0)) + rotate(im, im.mode, 45, translate=(im.size[0] // 2, 0)) + rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] // 2, 0)) def test_rotate_no_fill() -> None: diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 2ca1d2cfc03..1593eaaf7fa 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -111,7 +111,9 @@ def test_load_first_unless_jpeg() -> None: with Image.open("Tests/images/hopper.jpg") as im: draft = im.draft - def im_draft(mode: str, size: tuple[int, int]): + def im_draft( + mode: str, size: tuple[int, int] + ) -> tuple[str, tuple[int, int, float, float]] | None: result = draft(mode, size) assert result is not None diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 104c620de01..dbdd5b317d5 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -58,7 +58,6 @@ def test_blur_formats(test_images: dict[str, ImageFile.ImageFile]) -> None: blur = ImageFilter.GaussianBlur with pytest.raises(ValueError): im.convert("1").filter(blur) - blur(im.convert("L")) with pytest.raises(ValueError): im.convert("I").filter(blur) with pytest.raises(ValueError): diff --git a/Tests/test_qt_image_qapplication.py b/Tests/test_qt_image_qapplication.py index 3cd323553fa..28f66891c0d 100644 --- a/Tests/test_qt_image_qapplication.py +++ b/Tests/test_qt_image_qapplication.py @@ -46,7 +46,7 @@ def roundtrip(expected: Image.Image) -> None: @pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed") def test_sanity(tmp_path: Path) -> None: # Segfault test - app = QApplication([]) + app: QApplication | None = QApplication([]) ex = Example() assert app # Silence warning assert ex # Silence warning diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 6f52204b829..7388a2b8a3a 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -17,7 +17,7 @@ _handler = None -def register_handler(handler: ImageFile.StubHandler) -> None: +def register_handler(handler: ImageFile.StubHandler | None) -> None: """ Install application-specific BUFR image handler. diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index b24dcded2f9..d3655f4ddc0 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -17,7 +17,7 @@ _handler = None -def register_handler(handler: ImageFile.StubHandler) -> None: +def register_handler(handler: ImageFile.StubHandler | None) -> None: """ Install application-specific GRIB image handler. diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index c8d7866a312..b789c215fee 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -17,7 +17,7 @@ _handler = None -def register_handler(handler: ImageFile.StubHandler) -> None: +def register_handler(handler: ImageFile.StubHandler | None) -> None: """ Install application-specific HDF5 image handler. diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 25a4545db52..a68f705a03c 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -30,7 +30,7 @@ _handler = None -def register_handler(handler: ImageFile.StubHandler) -> None: +def register_handler(handler: ImageFile.StubHandler | None) -> None: """ Install application-specific WMF image handler. From 148f0d345f92261e12d28ebbbd3b92d027d667ca Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:38:38 +0300 Subject: [PATCH 212/300] Use Sphinx long options in Makefile --- docs/Makefile | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 6495e5866ff..8f13f1aea1c 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -9,9 +9,9 @@ PAPER = BUILDDIR = _build # Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +PAPEROPT_a4 = --define latex_paper_size=a4 +PAPEROPT_letter = --define latex_paper_size=letter +ALLSPHINXOPTS = --doctree-dir $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . @@ -51,42 +51,42 @@ install-sphinx: .PHONY: html html: $(MAKE) install-sphinx - $(SPHINXBUILD) -b html -W --keep-going $(ALLSPHINXOPTS) $(BUILDDIR)/html + $(SPHINXBUILD) --builder html --fail-on-warning --keep-going $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml dirhtml: $(MAKE) install-sphinx - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + $(SPHINXBUILD) --builder dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(MAKE) install-sphinx - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + $(SPHINXBUILD) --builder singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(MAKE) install-sphinx - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + $(SPHINXBUILD) --builder pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(MAKE) install-sphinx - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + $(SPHINXBUILD) --builder json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(MAKE) install-sphinx - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + $(SPHINXBUILD) --builder htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." @@ -94,7 +94,7 @@ htmlhelp: .PHONY: qthelp qthelp: $(MAKE) install-sphinx - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + $(SPHINXBUILD) --builder qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @@ -105,7 +105,7 @@ qthelp: .PHONY: devhelp devhelp: $(MAKE) install-sphinx - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + $(SPHINXBUILD) --builder devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @@ -116,14 +116,14 @@ devhelp: .PHONY: epub epub: $(MAKE) install-sphinx - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + $(SPHINXBUILD) --builder epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: latex latex: $(MAKE) install-sphinx - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + $(SPHINXBUILD) --builder latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ @@ -132,7 +132,7 @@ latex: .PHONY: latexpdf latexpdf: $(MAKE) install-sphinx - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + $(SPHINXBUILD) --builder latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." @@ -140,21 +140,21 @@ latexpdf: .PHONY: text text: $(MAKE) install-sphinx - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + $(SPHINXBUILD) --builder text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(MAKE) install-sphinx - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + $(SPHINXBUILD) --builder man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(MAKE) install-sphinx - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + $(SPHINXBUILD) --builder texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ @@ -163,7 +163,7 @@ texinfo: .PHONY: info info: $(MAKE) install-sphinx - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + $(SPHINXBUILD) --builder texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." @@ -171,21 +171,21 @@ info: .PHONY: gettext gettext: $(MAKE) install-sphinx - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + $(SPHINXBUILD) --builder gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(MAKE) install-sphinx - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + $(SPHINXBUILD) --builder changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(MAKE) install-sphinx - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck -j auto + $(SPHINXBUILD) --builder linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck -j auto @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." @@ -193,7 +193,7 @@ linkcheck: .PHONY: doctest doctest: $(MAKE) install-sphinx - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + $(SPHINXBUILD) --builder doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." From ac7967cfc0d554a494739891f468d8d3dd10595c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 7 Jun 2024 13:25:56 +1000 Subject: [PATCH 213/300] Do not preserve EXIFIFD tag by default --- Tests/test_file_libtiff.py | 13 +++++++++---- src/PIL/TiffImagePlugin.py | 12 ++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 22bcd285622..20c0bae9218 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -685,13 +685,18 @@ def test_save_ycbcr(self, tmp_path: Path) -> None: assert reloaded.tag_v2[530] == (1, 1) assert reloaded.tag_v2[532] == (0, 255, 128, 255, 128, 255) - def test_exif_ifd(self, tmp_path: Path) -> None: - outfile = str(tmp_path / "temp.tif") + def test_exif_ifd(self) -> None: + out = io.BytesIO() with Image.open("Tests/images/tiff_adobe_deflate.tif") as im: assert im.tag_v2[34665] == 125456 - im.save(outfile) + im.save(out, "TIFF") - with Image.open(outfile) as reloaded: + with Image.open(out) as reloaded: + assert 34665 not in reloaded.tag_v2 + + im.save(out, "TIFF", tiffinfo={34665: 125456}) + + with Image.open(out) as reloaded: if Image.core.libtiff_support_custom_tags: assert reloaded.tag_v2[34665] == 125456 diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 04f36744bed..ab5934816ba 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1816,11 +1816,15 @@ def _save(im, fp, filename): if hasattr(im, "tag"): legacy_ifd = im.tag.to_v2() - # SAMPLEFORMAT is determined by the image format and should not be copied - # from legacy_ifd. supplied_tags = {**getattr(im, "tag_v2", {}), **legacy_ifd} - if SAMPLEFORMAT in supplied_tags: - del supplied_tags[SAMPLEFORMAT] + for tag in ( + # IFD offset that may not be correct in the saved image + EXIFIFD, + # Determined by the image format and should not be copied from legacy_ifd. + SAMPLEFORMAT, + ): + if tag in supplied_tags: + del supplied_tags[tag] for tag, value in itertools.chain(ifd.items(), supplied_tags.items()): # Libtiff can only process certain core items without adding From 44805bcd1d34446af734052f91428844a604540b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 7 Jun 2024 16:49:03 +1000 Subject: [PATCH 214/300] Updated fribidi to 1.0.15 --- winbuild/build_prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0d6da77549f..7ec1eaa82b0 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -112,7 +112,7 @@ def cmd_msbuild( V = { "BROTLI": "1.1.0", "FREETYPE": "2.13.2", - "FRIBIDI": "1.0.13", + "FRIBIDI": "1.0.15", "HARFBUZZ": "8.4.0", "JPEGTURBO": "3.0.2", "LCMS2": "2.16", From 08b5a2e9a7d290f4cd2cfb35dd556d50145a7dea Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jun 2024 14:35:10 +1000 Subject: [PATCH 215/300] Corrected using a 1 mode mask with I;16* images --- Tests/images/imagedraw_polygon_width_I.tiff | Bin 0 -> 20122 bytes Tests/test_imagedraw.py | 13 ++++++++++ src/libImaging/Paste.c | 26 +++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 Tests/images/imagedraw_polygon_width_I.tiff diff --git a/Tests/images/imagedraw_polygon_width_I.tiff b/Tests/images/imagedraw_polygon_width_I.tiff new file mode 100644 index 0000000000000000000000000000000000000000..ce1917d61fe9f4a8d04e6d2bdf0cfd057c0d8e9d GIT binary patch literal 20122 zcmeI$K@Ng25QX8X7^6Gg=*ER>Z^Fh?cnU9%Ekk3GZXl3!nxDkjG==cKSJc?HPdSRn zNyP2N`O(|ITr%Fdrk_9i`DPf0b%$hp8P%4 DH7N{| literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index c221fe00829..505bba80c52 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -631,6 +631,19 @@ def test_polygon(points: Coords) -> None: assert_image_equal_tofile(im, "Tests/images/imagedraw_polygon.png") +@pytest.mark.parametrize("points", POINTS) +def test_polygon_width_I16(points: Coords) -> None: + # Arrange + im = Image.new("I;16", (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw.polygon(points, outline=0xFFFF, width=2) + + # Assert + assert_image_equal_tofile(im, "Tests/images/imagedraw_polygon_width_I.tiff") + + @pytest.mark.parametrize("mode", ("RGB", "L")) @pytest.mark.parametrize("kite_points", KITE_POINTS) def test_polygon_kite( diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c index a018225b26e..dc67cb41d2f 100644 --- a/src/libImaging/Paste.c +++ b/src/libImaging/Paste.c @@ -65,15 +65,32 @@ paste_mask_1( int x, y; if (imOut->image8) { + int in_i16 = strncmp(imIn->mode, "I;16", 4) == 0; + int out_i16 = strncmp(imOut->mode, "I;16", 4) == 0; for (y = 0; y < ysize; y++) { UINT8 *out = imOut->image8[y + dy] + dx; + if (out_i16) { + out += dx; + } UINT8 *in = imIn->image8[y + sy] + sx; + if (in_i16) { + in += sx; + } UINT8 *mask = imMask->image8[y + sy] + sx; for (x = 0; x < xsize; x++) { - if (*mask++) { + if (*mask) { *out = *in; } - out++, in++; + if (in_i16) { + in++; + } + if (out_i16) { + out++; + if (*mask) { + *out = *in; + } + } + out++, in++, mask++; } } @@ -415,15 +432,16 @@ fill_mask_L( unsigned int tmp1; if (imOut->image8) { + int i16 = strncmp(imOut->mode, "I;16", 4) == 0; for (y = 0; y < ysize; y++) { UINT8 *out = imOut->image8[y + dy] + dx; - if (strncmp(imOut->mode, "I;16", 4) == 0) { + if (i16) { out += dx; } UINT8 *mask = imMask->image8[y + sy] + sx; for (x = 0; x < xsize; x++) { *out = BLEND(*mask, *out, ink[0], tmp1); - if (strncmp(imOut->mode, "I;16", 4) == 0) { + if (i16) { out++; *out = BLEND(*mask, *out, ink[1], tmp1); } From d2603b779aec4810349621542b795e79c0144d8e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jun 2024 15:42:24 +1000 Subject: [PATCH 216/300] im color could be a tuple with a single float --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2ea26877d13..d9eb73e453c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1719,7 +1719,7 @@ def entropy(self, mask=None, extrema=None): def paste( self, - im: Image | str | float | tuple[int, ...], + im: Image | str | float | tuple[float, ...], box: tuple[int, int, int, int] | tuple[int, int] | None = None, mask: Image | None = None, ) -> None: From 45cdc53bbb609212590b0558061aa2991cc87a5d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jun 2024 18:01:26 +1000 Subject: [PATCH 217/300] Updated type hints --- Tests/test_image_rotate.py | 4 ++-- docs/handbook/concepts.rst | 6 ++++++ docs/reference/Image.rst | 1 - src/PIL/Image.py | 10 +++++----- src/PIL/ImageDraw.py | 11 ++++++----- src/PIL/ImageFont.py | 2 +- src/PIL/_imagingft.pyi | 2 +- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index c10c96da6f9..252a15db742 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -124,8 +124,8 @@ def test_fastpath_translate() -> None: def test_center() -> None: im = hopper() rotate(im, im.mode, 45, center=(0, 0)) - rotate(im, im.mode, 45, translate=(im.size[0] / 2, 0)) - rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] / 2, 0)) + rotate(im, im.mode, 45, translate=(im.size[0] // 2, 0)) + rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] // 2, 0)) def test_rotate_no_fill() -> None: diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index 5094dbf3f27..7da1078c14e 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -144,10 +144,12 @@ pixel, the Python Imaging Library provides different resampling *filters*. .. py:currentmodule:: PIL.Image .. data:: Resampling.NEAREST + :noindex: Pick one nearest pixel from the input image. Ignore all other input pixels. .. data:: Resampling.BOX + :noindex: Each pixel of source image contributes to one pixel of the destination image with identical weights. @@ -158,6 +160,7 @@ pixel, the Python Imaging Library provides different resampling *filters*. .. versionadded:: 3.4.0 .. data:: Resampling.BILINEAR + :noindex: For resize calculate the output pixel value using linear interpolation on all pixels that may contribute to the output value. @@ -165,6 +168,7 @@ pixel, the Python Imaging Library provides different resampling *filters*. in the input image is used. .. data:: Resampling.HAMMING + :noindex: Produces a sharper image than :data:`Resampling.BILINEAR`, doesn't have dislocations on local level like with :data:`Resampling.BOX`. @@ -174,6 +178,7 @@ pixel, the Python Imaging Library provides different resampling *filters*. .. versionadded:: 3.4.0 .. data:: Resampling.BICUBIC + :noindex: For resize calculate the output pixel value using cubic interpolation on all pixels that may contribute to the output value. @@ -181,6 +186,7 @@ pixel, the Python Imaging Library provides different resampling *filters*. in the input image is used. .. data:: Resampling.LANCZOS + :noindex: Calculate the output pixel value using a high-quality Lanczos filter (a truncated sinc) on all pixels that may contribute to the output value. diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index d917a3c9271..1c095a11453 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -424,7 +424,6 @@ See :ref:`concept-filters` for details. .. autoclass:: Resampling :members: :undoc-members: - :noindex: Dither modes ^^^^^^^^^^^^ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index d9eb73e453c..13d374345a6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2303,8 +2303,8 @@ def reduce( def rotate( self, angle: float, - resample: int = Resampling.NEAREST, - expand: bool = False, + resample: Resampling = Resampling.NEAREST, + expand: int | bool = False, center: tuple[int, int] | None = None, translate: tuple[int, int] | None = None, fillcolor: float | tuple[float, ...] | str | None = None, @@ -2617,8 +2617,8 @@ def tell(self) -> int: def thumbnail( self, - size: tuple[int, int], - resample: int = Resampling.BICUBIC, + size: tuple[float, float], + resample: Resampling = Resampling.BICUBIC, reducing_gap: float = 2.0, ) -> None: """ @@ -2953,7 +2953,7 @@ def transform( self, size: tuple[int, int], image: Image, - **options: str | int | tuple[int, ...] | list[int], + **options: Any, ) -> Image: pass diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 0663d9ddf85..9796189bb4b 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -95,7 +95,9 @@ def __init__(self, im: Image.Image, mode: str | None = None) -> None: if TYPE_CHECKING: from . import ImageFont - def getfont(self) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: + def getfont( + self, + ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: """ Get the current default font. @@ -122,14 +124,13 @@ def getfont(self) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: def _getfont( self, font_size: float | None - ) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: + ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: if font_size is not None: from . import ImageFont - font = ImageFont.load_default(font_size) + return ImageFont.load_default(font_size) else: - font = self.getfont() - return font + return self.getfont() def _getink(self, ink, fill=None) -> tuple[int | None, int | None]: if ink is None and fill is None: diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index a9925483e40..87261f51920 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -361,7 +361,7 @@ def getbbox( text: str, mode: str = "", direction: str | None = None, - features: str | None = None, + features: list[str] | None = None, language: str | None = None, stroke_width: float = 0, anchor: str | None = None, diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index b023efe0110..6e0ddd2f165 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -6,7 +6,7 @@ class _Axis(TypedDict): minimum: int | None default: int | None maximum: int | None - name: str | None + name: bytes | None class Font: @property From 985e6053810b7236e67dafcbfcd4b53e71e3fe3d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jun 2024 19:06:46 +1000 Subject: [PATCH 218/300] Renamed transform2 to transform --- src/PIL/Image.py | 2 +- src/_imaging.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 611d6960da1..f61acc1d317 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2883,7 +2883,7 @@ def __transformer( if image.mode in ("1", "P"): resample = Resampling.NEAREST - self.im.transform2(box, image.im, method, data, resample, fill) + self.im.transform(box, image.im, method, data, resample, fill) def transpose(self, method: Transpose) -> Image: """ diff --git a/src/_imaging.c b/src/_imaging.c index c565c21bb15..f398c6c7c72 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -2028,7 +2028,7 @@ im_setmode(ImagingObject *self, PyObject *args) { } static PyObject * -_transform2(ImagingObject *self, PyObject *args) { +_transform(ImagingObject *self, PyObject *args) { static const char *wrong_number = "wrong number of matrix entries"; Imaging imOut; @@ -3647,7 +3647,7 @@ static struct PyMethodDef methods[] = { {"resize", (PyCFunction)_resize, METH_VARARGS}, {"reduce", (PyCFunction)_reduce, METH_VARARGS}, {"transpose", (PyCFunction)_transpose, METH_VARARGS}, - {"transform2", (PyCFunction)_transform2, METH_VARARGS}, + {"transform", (PyCFunction)_transform, METH_VARARGS}, {"isblock", (PyCFunction)_isblock, METH_NOARGS}, From 0d73721c65d71e968a1a3b07d4252dd99b154772 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jun 2024 19:11:51 +1000 Subject: [PATCH 219/300] Allow float center for rotate operations --- src/PIL/Image.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 611d6960da1..1e46c4ee4a4 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2305,7 +2305,7 @@ def rotate( angle: float, resample: Resampling = Resampling.NEAREST, expand: int | bool = False, - center: tuple[int, int] | None = None, + center: tuple[float, float] | None = None, translate: tuple[int, int] | None = None, fillcolor: float | tuple[float, ...] | str | None = None, ) -> Image: @@ -2373,10 +2373,7 @@ def rotate( else: post_trans = translate if center is None: - # FIXME These should be rounded to ints? - rotn_center = (w / 2.0, h / 2.0) - else: - rotn_center = center + center = (w / 2, h / 2) angle = -math.radians(angle) matrix = [ @@ -2393,10 +2390,10 @@ def transform(x, y, matrix): return a * x + b * y + c, d * x + e * y + f matrix[2], matrix[5] = transform( - -rotn_center[0] - post_trans[0], -rotn_center[1] - post_trans[1], matrix + -center[0] - post_trans[0], -center[1] - post_trans[1], matrix ) - matrix[2] += rotn_center[0] - matrix[5] += rotn_center[1] + matrix[2] += center[0] + matrix[5] += center[1] if expand: # calculate output size From 14a32650ddf8af6016045170e6e7490e1cdcc0b0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jun 2024 22:26:28 +1000 Subject: [PATCH 220/300] Added type hints --- src/PIL/ImageColor.py | 53 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py index 5fb80b75310..9a15a8eb759 100644 --- a/src/PIL/ImageColor.py +++ b/src/PIL/ImageColor.py @@ -25,7 +25,7 @@ @lru_cache -def getrgb(color): +def getrgb(color: str) -> tuple[int, int, int] | tuple[int, int, int, int]: """ Convert a color string to an RGB or RGBA tuple. If the string cannot be parsed, this function raises a :py:exc:`ValueError` exception. @@ -44,8 +44,10 @@ def getrgb(color): if rgb: if isinstance(rgb, tuple): return rgb - colormap[color] = rgb = getrgb(rgb) - return rgb + rgb_tuple = getrgb(rgb) + assert len(rgb_tuple) == 3 + colormap[color] = rgb_tuple + return rgb_tuple # check for known string formats if re.match("#[a-f0-9]{3}$", color): @@ -88,15 +90,15 @@ def getrgb(color): if m: from colorsys import hls_to_rgb - rgb = hls_to_rgb( + rgb_floats = hls_to_rgb( float(m.group(1)) / 360.0, float(m.group(3)) / 100.0, float(m.group(2)) / 100.0, ) return ( - int(rgb[0] * 255 + 0.5), - int(rgb[1] * 255 + 0.5), - int(rgb[2] * 255 + 0.5), + int(rgb_floats[0] * 255 + 0.5), + int(rgb_floats[1] * 255 + 0.5), + int(rgb_floats[2] * 255 + 0.5), ) m = re.match( @@ -105,15 +107,15 @@ def getrgb(color): if m: from colorsys import hsv_to_rgb - rgb = hsv_to_rgb( + rgb_floats = hsv_to_rgb( float(m.group(1)) / 360.0, float(m.group(2)) / 100.0, float(m.group(3)) / 100.0, ) return ( - int(rgb[0] * 255 + 0.5), - int(rgb[1] * 255 + 0.5), - int(rgb[2] * 255 + 0.5), + int(rgb_floats[0] * 255 + 0.5), + int(rgb_floats[1] * 255 + 0.5), + int(rgb_floats[2] * 255 + 0.5), ) m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) @@ -124,7 +126,7 @@ def getrgb(color): @lru_cache -def getcolor(color, mode: str) -> tuple[int, ...]: +def getcolor(color: str, mode: str) -> int | tuple[int, ...]: """ Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is @@ -136,33 +138,34 @@ def getcolor(color, mode: str) -> tuple[int, ...]: :param color: A color string :param mode: Convert result to this mode - :return: ``(graylevel[, alpha]) or (red, green, blue[, alpha])`` + :return: ``graylevel, (graylevel, alpha) or (red, green, blue[, alpha])`` """ # same as getrgb, but converts the result to the given mode - color, alpha = getrgb(color), 255 - if len(color) == 4: - color, alpha = color[:3], color[3] + rgb, alpha = getrgb(color), 255 + if len(rgb) == 4: + alpha = rgb[3] + rgb = rgb[:3] if mode == "HSV": from colorsys import rgb_to_hsv - r, g, b = color + r, g, b = rgb h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255) return int(h * 255), int(s * 255), int(v * 255) elif Image.getmodebase(mode) == "L": - r, g, b = color + r, g, b = rgb # ITU-R Recommendation 601-2 for nonlinear RGB # scaled to 24 bits to match the convert's implementation. - color = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 + graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 if mode[-1] == "A": - return color, alpha - else: - if mode[-1] == "A": - return color + (alpha,) - return color + return graylevel, alpha + return graylevel + elif mode[-1] == "A": + return rgb + (alpha,) + return rgb -colormap = { +colormap: dict[str, str | tuple[int, int, int]] = { # X11 colour table from https://drafts.csswg.org/css-color-4/, with # gray/grey spelling issues fixed. This is a superset of HTML 4.0 # colour names used in CSS 1. From 56fa3c658a9c60facce7acbb3909855f7aea2340 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 9 Jun 2024 07:12:05 +1000 Subject: [PATCH 221/300] Added type hints --- src/PIL/GifImagePlugin.py | 15 +++++++++---- src/PIL/GimpGradientFile.py | 43 +++++++++++++++++++++++++++--------- src/PIL/Image.py | 2 +- src/PIL/ImageDraw2.py | 6 ++--- src/PIL/ImageTk.py | 6 ++--- src/PIL/Jpeg2KImagePlugin.py | 3 ++- src/PIL/PaletteFile.py | 10 +++++---- src/PIL/TiffImagePlugin.py | 4 ++-- 8 files changed, 61 insertions(+), 28 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index e62852db31d..f41bc2b321d 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -558,7 +558,11 @@ def _normalize_palette(im, palette, info): return im -def _write_single_frame(im, fp, palette): +def _write_single_frame( + im: Image.Image, + fp: IO[bytes], + palette: bytes | bytearray | list[int] | ImagePalette.ImagePalette, +) -> None: im_out = _normalize_mode(im) for k, v in im_out.info.items(): im.encoderinfo.setdefault(k, v) @@ -579,7 +583,9 @@ def _write_single_frame(im, fp, palette): fp.write(b"\0") # end of image data -def _getbbox(base_im, im_frame): +def _getbbox( + base_im: Image.Image, im_frame: Image.Image +) -> tuple[Image.Image, tuple[int, int, int, int]]: if _get_palette_bytes(im_frame) != _get_palette_bytes(base_im): im_frame = im_frame.convert("RGBA") base_im = base_im.convert("RGBA") @@ -790,7 +796,7 @@ def _write_local_header(fp, im, offset, flags): fp.write(o8(8)) # bits -def _save_netpbm(im, fp, filename): +def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str) -> None: # Unused by default. # To use, uncomment the register_save call at the end of the file. # @@ -821,6 +827,7 @@ def _save_netpbm(im, fp, filename): ) # Allow ppmquant to receive SIGPIPE if ppmtogif exits + assert quant_proc.stdout is not None quant_proc.stdout.close() retcode = quant_proc.wait() @@ -1080,7 +1087,7 @@ def getdata(im, offset=(0, 0), **params): class Collector: data = [] - def write(self, data): + def write(self, data: bytes) -> None: self.data.append(data) im.load() # make sure raster data is available diff --git a/src/PIL/GimpGradientFile.py b/src/PIL/GimpGradientFile.py index 2d8c78ea91a..92068b904e7 100644 --- a/src/PIL/GimpGradientFile.py +++ b/src/PIL/GimpGradientFile.py @@ -21,6 +21,7 @@ from __future__ import annotations from math import log, pi, sin, sqrt +from typing import IO, Callable from ._binary import o8 @@ -28,7 +29,7 @@ """""" # Enable auto-doc for data member -def linear(middle, pos): +def linear(middle: float, pos: float) -> float: if pos <= middle: if middle < EPSILON: return 0.0 @@ -43,19 +44,19 @@ def linear(middle, pos): return 0.5 + 0.5 * pos / middle -def curved(middle, pos): +def curved(middle: float, pos: float) -> float: return pos ** (log(0.5) / log(max(middle, EPSILON))) -def sine(middle, pos): +def sine(middle: float, pos: float) -> float: return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 -def sphere_increasing(middle, pos): +def sphere_increasing(middle: float, pos: float) -> float: return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) -def sphere_decreasing(middle, pos): +def sphere_decreasing(middle: float, pos: float) -> float: return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) @@ -64,9 +65,22 @@ def sphere_decreasing(middle, pos): class GradientFile: - gradient = None - - def getpalette(self, entries=256): + gradient: ( + list[ + tuple[ + float, + float, + float, + list[float], + list[float], + Callable[[float, float], float], + ] + ] + | None + ) = None + + def getpalette(self, entries: int = 256) -> tuple[bytes, str]: + assert self.gradient is not None palette = [] ix = 0 @@ -101,7 +115,7 @@ def getpalette(self, entries=256): class GimpGradientFile(GradientFile): """File handler for GIMP's gradient format.""" - def __init__(self, fp): + def __init__(self, fp: IO[bytes]) -> None: if fp.readline()[:13] != b"GIMP Gradient": msg = "not a GIMP gradient file" raise SyntaxError(msg) @@ -114,7 +128,16 @@ def __init__(self, fp): count = int(line) - gradient = [] + gradient: list[ + tuple[ + float, + float, + float, + list[float], + list[float], + Callable[[float, float], float], + ] + ] = [] for i in range(count): s = fp.readline().split() diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f61acc1d317..f6ffac8a77f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2470,7 +2470,7 @@ def save( save_all = params.pop("save_all", False) self.encoderinfo = params - self.encoderconfig = () + self.encoderconfig: tuple[Any, ...] = () preinit() diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index 35ee5834e34..b42f5d9eac1 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -30,7 +30,7 @@ class Pen: """Stores an outline color and width.""" - def __init__(self, color, width=1, opacity=255): + def __init__(self, color: str, width: int = 1, opacity: int = 255) -> None: self.color = ImageColor.getrgb(color) self.width = width @@ -38,7 +38,7 @@ def __init__(self, color, width=1, opacity=255): class Brush: """Stores a fill color""" - def __init__(self, color, opacity=255): + def __init__(self, color: str, opacity: int = 255) -> None: self.color = ImageColor.getrgb(color) @@ -63,7 +63,7 @@ def __init__(self, image, size=None, color=None): self.image = image self.transform = None - def flush(self): + def flush(self) -> Image.Image: return self.image def render(self, op, xy, pen, brush=None): diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 6e2e7db1e19..90defdbbc23 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -37,7 +37,7 @@ _pilbitmap_ok = None -def _pilbitmap_check(): +def _pilbitmap_check() -> int: global _pilbitmap_ok if _pilbitmap_ok is None: try: @@ -162,7 +162,7 @@ def height(self) -> int: """ return self.__size[1] - def paste(self, im): + def paste(self, im: Image.Image) -> None: """ Paste a PIL image into the photo image. Note that this can be very slow if the photo image is displayed. @@ -254,7 +254,7 @@ def __str__(self) -> str: return str(self.__photo) -def getimage(photo): +def getimage(photo: PhotoImage) -> Image.Image: """Copies the contents of a PhotoImage to a PIL image memory.""" im = Image.new("RGBA", (photo.width(), photo.height())) block = im.im diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index e6395b1cb96..5a0ef0d01a3 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -18,6 +18,7 @@ import io import os import struct +from typing import IO from . import Image, ImageFile, ImagePalette, _binary @@ -328,7 +329,7 @@ def _accept(prefix: bytes) -> bool: # Save support -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: # Get the keyword arguments info = im.encoderinfo diff --git a/src/PIL/PaletteFile.py b/src/PIL/PaletteFile.py index eaed5367c99..81652e5eec2 100644 --- a/src/PIL/PaletteFile.py +++ b/src/PIL/PaletteFile.py @@ -14,6 +14,8 @@ # from __future__ import annotations +from typing import IO + from ._binary import o8 @@ -22,8 +24,8 @@ class PaletteFile: rawmode = "RGB" - def __init__(self, fp): - self.palette = [(i, i, i) for i in range(256)] + def __init__(self, fp: IO[bytes]) -> None: + palette = [o8(i) * 3 for i in range(256)] while True: s = fp.readline() @@ -44,9 +46,9 @@ def __init__(self, fp): g = b = r if 0 <= i <= 255: - self.palette[i] = o8(r) + o8(g) + o8(b) + palette[i] = o8(r) + o8(g) + o8(b) - self.palette = b"".join(self.palette) + self.palette = b"".join(palette) def getpalette(self) -> tuple[bytes, str]: return self.palette, self.rawmode diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 04f36744bed..0b96017552a 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -50,7 +50,7 @@ from collections.abc import MutableMapping from fractions import Fraction from numbers import Number, Rational -from typing import TYPE_CHECKING, Any, Callable +from typing import IO, TYPE_CHECKING, Any, Callable from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags from ._binary import i16be as i16 @@ -2149,7 +2149,7 @@ def fixOffsets(self, count, isShort=False, isLong=False): self.rewriteLastLong(offset) -def _save_all(im, fp, filename): +def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: encoderinfo = im.encoderinfo.copy() encoderconfig = im.encoderconfig append_images = list(encoderinfo.get("append_images", [])) From 1a14957c1954b18368a65bff06d8a959731d9e55 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 9 Jun 2024 15:16:17 +1000 Subject: [PATCH 222/300] Added type hints --- Tests/test_decompression_bomb.py | 2 +- Tests/test_file_jpeg.py | 17 ++++++++++------- Tests/test_file_tiff.py | 2 +- Tests/test_image_mode.py | 6 +++++- Tests/test_image_quantize.py | 2 +- Tests/test_image_reduce.py | 12 ++++++++---- Tests/test_image_thumbnail.py | 2 +- Tests/test_imagedraw.py | 6 +++++- Tests/test_imagefont.py | 2 +- Tests/test_imageops.py | 2 +- Tests/test_imagewin_pointers.py | 2 +- Tests/test_main.py | 2 +- 12 files changed, 36 insertions(+), 21 deletions(-) diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index 9c21efa45f7..c140156f9ea 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -12,7 +12,7 @@ class TestDecompressionBomb: - def teardown_method(self, method) -> None: + def teardown_method(self) -> None: Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT def test_no_warning_small_file(self) -> None: diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 18dc752d8bd..8e4d694c1a9 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -443,7 +443,9 @@ def test_smooth(self) -> None: assert_image(im1, im2.mode, im2.size) def test_subsampling(self) -> None: - def getsampling(im: JpegImagePlugin.JpegImageFile): + def getsampling( + im: JpegImagePlugin.JpegImageFile, + ) -> tuple[int, int, int, int, int, int]: layer = im.layer return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] @@ -917,24 +919,25 @@ def test_icc_after_SOF(self) -> None: with Image.open("Tests/images/icc-after-SOF.jpg") as im: assert im.info["icc_profile"] == b"profile" - def test_jpeg_magic_number(self) -> None: + def test_jpeg_magic_number(self, monkeypatch: pytest.MonkeyPatch) -> None: size = 4097 buffer = BytesIO(b"\xFF" * size) # Many xFF bytes - buffer.max_pos = 0 + max_pos = 0 orig_read = buffer.read - def read(n=-1): + def read(n: int | None = -1) -> bytes: + nonlocal max_pos res = orig_read(n) - buffer.max_pos = max(buffer.max_pos, buffer.tell()) + max_pos = max(max_pos, buffer.tell()) return res - buffer.read = read + monkeypatch.setattr(buffer, "read", read) with pytest.raises(UnidentifiedImageError): with Image.open(buffer): pass # Assert the entire file has not been read - assert 0 < buffer.max_pos < size + assert 0 < max_pos < size def test_getxmp(self) -> None: with Image.open("Tests/images/xmp_test.jpg") as im: diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 8821fb46a84..06591a29a37 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -113,7 +113,7 @@ def test_bigtiff(self, tmp_path: Path) -> None: outfile = str(tmp_path / "temp.tif") im.save(outfile, save_all=True, append_images=[im], tiffinfo=im.tag_v2) - def test_seek_too_large(self): + def test_seek_too_large(self) -> None: with pytest.raises(ValueError, match="Unable to seek to frame"): Image.open("Tests/images/seek_too_large.tif") diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py index 8e94aafc598..20d3a160e9c 100644 --- a/Tests/test_image_mode.py +++ b/Tests/test_image_mode.py @@ -68,7 +68,11 @@ def test_sanity() -> None: ), ) def test_properties( - mode, expected_base, expected_type, expected_bands, expected_band_names + mode: str, + expected_base: str, + expected_type: str, + expected_bands: int, + expected_band_names: tuple[str, ...], ) -> None: assert Image.getmodebase(mode) == expected_base assert Image.getmodetype(mode) == expected_type diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 2daaf5c3cbb..2d461d985dd 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -98,7 +98,7 @@ def test_quantize_dither_diff() -> None: @pytest.mark.parametrize( "method", (Image.Quantize.MEDIANCUT, Image.Quantize.MAXCOVERAGE) ) -def test_quantize_kmeans(method) -> None: +def test_quantize_kmeans(method: Image.Quantize) -> None: im = hopper() no_kmeans = im.quantize(kmeans=0, method=method) kmeans = im.quantize(kmeans=1, method=method) diff --git a/Tests/test_image_reduce.py b/Tests/test_image_reduce.py index f6609a1a054..6771b46b051 100644 --- a/Tests/test_image_reduce.py +++ b/Tests/test_image_reduce.py @@ -56,10 +56,12 @@ def test_args_factor(size: int | tuple[int, int], expected: tuple[int, int]) -> @pytest.mark.parametrize( "size, expected_error", ((0, ValueError), (2.0, TypeError), ((0, 10), ValueError)) ) -def test_args_factor_error(size: float | tuple[int, int], expected_error) -> None: +def test_args_factor_error( + size: float | tuple[int, int], expected_error: type[Exception] +) -> None: im = Image.new("L", (10, 10)) with pytest.raises(expected_error): - im.reduce(size) + im.reduce(size) # type: ignore[arg-type] @pytest.mark.parametrize( @@ -86,10 +88,12 @@ def test_args_box(size: tuple[int, int, int, int], expected: tuple[int, int]) -> ((5, 0, 5, 10), ValueError), ), ) -def test_args_box_error(size: str | tuple[int, int, int, int], expected_error) -> None: +def test_args_box_error( + size: str | tuple[int, int, int, int], expected_error: type[Exception] +) -> None: im = Image.new("L", (10, 10)) with pytest.raises(expected_error): - im.reduce(2, size).size + im.reduce(2, size).size # type: ignore[arg-type] @pytest.mark.parametrize("mode", ("P", "1", "I;16")) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 1593eaaf7fa..1606d8939c0 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -16,7 +16,7 @@ def test_sanity() -> None: im = hopper() - assert im.thumbnail((100, 100)) is None + assert im.thumbnail((100, 100)) is None # type: ignore[func-returns-value] assert im.size == (100, 100) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index c221fe00829..51543d7851f 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1562,7 +1562,11 @@ def test_compute_regular_polygon_vertices( ], ) def test_compute_regular_polygon_vertices_input_error_handling( - n_sides, bounding_circle, rotation, expected_error, error_message + n_sides: int, + bounding_circle: int | tuple[int | tuple[int] | str, ...], + rotation: int | str, + expected_error: type[Exception], + error_message: str, ) -> None: with pytest.raises(expected_error) as e: ImageDraw._compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 4398f8a3055..73cad513e62 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -224,7 +224,7 @@ def test_render_multiline(font: ImageFont.FreeTypeFont) -> None: draw = ImageDraw.Draw(im) line_spacing = font.getbbox("A")[3] + 4 lines = TEST_TEXT.split("\n") - y = 0 + y: float = 0 for line in lines: draw.text((0, y), line, font=font) y += line_spacing diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index d6bdaf45073..27a6090c5fa 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -454,7 +454,7 @@ def test_autocontrast_cutoff() -> None: # Test the cutoff argument of autocontrast with Image.open("Tests/images/bw_gradient.png") as img: - def autocontrast(cutoff: int | tuple[int, int]): + def autocontrast(cutoff: int | tuple[int, int]) -> list[int]: return ImageOps.autocontrast(img, cutoff).histogram() assert autocontrast(10) == autocontrast((10, 10)) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index f59ee7284b8..e6c312a0c46 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -70,7 +70,7 @@ class BITMAPINFOHEADER(ctypes.Structure): ] CreateDIBSection.restype = ctypes.wintypes.HBITMAP - def serialize_dib(bi, pixels) -> bytearray: + def serialize_dib(bi: BITMAPINFOHEADER, pixels: ctypes.c_void_p) -> bytearray: bf = BITMAPFILEHEADER() bf.bfType = 0x4D42 bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize diff --git a/Tests/test_main.py b/Tests/test_main.py index e9e12b24a79..2582dbee3db 100644 --- a/Tests/test_main.py +++ b/Tests/test_main.py @@ -11,7 +11,7 @@ "args, report", ((["PIL"], False), (["PIL", "--report"], True), (["PIL.report"], True)), ) -def test_main(args, report) -> None: +def test_main(args: list[str], report: bool) -> None: args = [sys.executable, "-m"] + args out = subprocess.check_output(args).decode("utf-8") lines = out.splitlines() From de0779eee876ae92c48458d38778a3c557566312 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 9 Jun 2024 18:09:54 +1000 Subject: [PATCH 223/300] Removed return value assertion --- Tests/test_image_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 1606d8939c0..bdbf09c407e 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -16,7 +16,7 @@ def test_sanity() -> None: im = hopper() - assert im.thumbnail((100, 100)) is None # type: ignore[func-returns-value] + im.thumbnail((100, 100)) assert im.size == (100, 100) From 56c79b6f523d2bb7733b9193e47fab2f63f5b546 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 9 Jun 2024 22:13:01 +1000 Subject: [PATCH 224/300] Simplified code --- src/PIL/GimpGradientFile.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/PIL/GimpGradientFile.py b/src/PIL/GimpGradientFile.py index 92068b904e7..220eac57e38 100644 --- a/src/PIL/GimpGradientFile.py +++ b/src/PIL/GimpGradientFile.py @@ -128,16 +128,7 @@ def __init__(self, fp: IO[bytes]) -> None: count = int(line) - gradient: list[ - tuple[ - float, - float, - float, - list[float], - list[float], - Callable[[float, float], float], - ] - ] = [] + self.gradient = [] for i in range(count): s = fp.readline().split() @@ -155,6 +146,4 @@ def __init__(self, fp: IO[bytes]) -> None: msg = "cannot handle HSV colour space" raise OSError(msg) - gradient.append((x0, x1, xm, rgb0, rgb1, segment)) - - self.gradient = gradient + self.gradient.append((x0, x1, xm, rgb0, rgb1, segment)) From e225f9f589e07d46ad5d1f79e7c233addf9c3836 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jun 2024 11:50:13 +1000 Subject: [PATCH 225/300] Deprecate ImageDraw.getdraw hints argument --- Tests/test_imagedraw.py | 5 +++++ docs/deprecations.rst | 7 +++++++ docs/releasenotes/10.4.0.rst | 5 +++++ src/PIL/ImageDraw.py | 24 ++++++++---------------- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index c221fe00829..61d7b5c6a18 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1624,3 +1624,8 @@ def test_incorrectly_ordered_coordinates(xy: tuple[int, int, int, int]) -> None: draw.rectangle(xy) with pytest.raises(ValueError): draw.rounded_rectangle(xy) + + +def test_getdraw(): + with pytest.warns(DeprecationWarning): + ImageDraw.getdraw(None, []) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index b2cd968fee3..8a03d858c71 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -115,6 +115,13 @@ Support for LibTIFF earlier than 4 Support for LibTIFF earlier than version 4 has been deprecated. Upgrade to a newer version of LibTIFF instead. +ImageDraw.getdraw hints argument +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 10.4.0 + +The ``hints`` argument in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. + Removed features ---------------- diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index e0d695a8bba..8c49e0842c5 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -34,6 +34,11 @@ Support for LibTIFF earlier than 4 Support for LibTIFF earlier than version 4 has been deprecated. Upgrade to a newer version of LibTIFF instead. +ImageDraw.getdraw hints argument +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``hints`` argument in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. + API Changes =========== diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 73ed3d4a9a6..ec15b535fbe 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -37,6 +37,7 @@ from typing import TYPE_CHECKING, AnyStr, Sequence, cast from . import Image, ImageColor +from ._deprecate import deprecate from ._typing import Coords """ @@ -902,26 +903,17 @@ def Draw(im, mode: str | None = None) -> ImageDraw: def getdraw(im=None, hints=None): """ - (Experimental) A more advanced 2D drawing interface for PIL images, - based on the WCK interface. - :param im: The image to draw in. - :param hints: An optional list of hints. + :param hints: An optional list of hints. Deprecated. :returns: A (drawing context, drawing resource factory) tuple. """ - # FIXME: this needs more work! - # FIXME: come up with a better 'hints' scheme. - handler = None - if not hints or "nicest" in hints: - try: - from . import _imagingagg as handler - except ImportError: - pass - if handler is None: - from . import ImageDraw2 as handler + if hints is not None: + deprecate("'hints' argument", 12) + from . import ImageDraw2 + if im: - im = handler.Draw(im) - return im, handler + im = ImageDraw2.Draw(im) + return im, ImageDraw2 def floodfill( From 2d1fe7572f461e13ed70f4f6162d5266d8440df0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jun 2024 14:15:28 +1000 Subject: [PATCH 226/300] Added type hints --- src/PIL/BlpImagePlugin.py | 15 ++++++++++----- src/PIL/BmpImagePlugin.py | 4 ++-- src/PIL/BufrStubImagePlugin.py | 2 +- src/PIL/DdsImagePlugin.py | 2 +- src/PIL/GifImagePlugin.py | 6 +++--- src/PIL/GribStubImagePlugin.py | 2 +- src/PIL/Hdf5StubImagePlugin.py | 2 +- src/PIL/IcnsImagePlugin.py | 19 +++++++++---------- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/ImImagePlugin.py | 4 +++- src/PIL/Image.py | 26 +++++++++++++++++--------- src/PIL/ImageDraw.py | 16 ++++++++++------ src/PIL/ImageFile.py | 2 +- src/PIL/Jpeg2KImagePlugin.py | 6 ++++-- src/PIL/JpegImagePlugin.py | 6 +++--- src/PIL/MpoImagePlugin.py | 2 +- src/PIL/MspImagePlugin.py | 2 +- src/PIL/PcxImagePlugin.py | 2 +- src/PIL/PdfImagePlugin.py | 2 +- src/PIL/PdfParser.py | 13 ++++++------- src/PIL/PngImagePlugin.py | 2 +- src/PIL/PpmImagePlugin.py | 2 +- src/PIL/QoiImagePlugin.py | 16 ++++++++++------ src/PIL/SgiImagePlugin.py | 7 ++++--- src/PIL/SpiderImagePlugin.py | 7 ++++--- src/PIL/TgaImagePlugin.py | 2 +- src/PIL/TiffImagePlugin.py | 4 ++-- src/PIL/WebPImagePlugin.py | 8 ++++---- src/PIL/WmfImagePlugin.py | 2 +- src/PIL/XbmImagePlugin.py | 2 +- 30 files changed, 106 insertions(+), 81 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 2db115ccc4f..003fa9b2479 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -31,6 +31,7 @@ from __future__ import annotations +import abc import os import struct from enum import IntEnum @@ -276,7 +277,7 @@ def _open(self) -> None: class _BLPBaseDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: try: self._read_blp_header() self._load() @@ -285,6 +286,10 @@ def decode(self, buffer): raise OSError(msg) from e return -1, 0 + @abc.abstractmethod + def _load(self) -> None: + pass + def _read_blp_header(self) -> None: assert self.fd is not None self.fd.seek(4) @@ -318,7 +323,7 @@ def _read_palette(self) -> list[tuple[int, int, int, int]]: ret.append((b, g, r, a)) return ret - def _read_bgra(self, palette): + def _read_bgra(self, palette: list[tuple[int, int, int, int]]) -> bytearray: data = bytearray() _data = BytesIO(self._safe_read(self._blp_lengths[0])) while True: @@ -327,7 +332,7 @@ def _read_bgra(self, palette): except struct.error: break b, g, r, a = palette[offset] - d = (r, g, b) + d: tuple[int, ...] = (r, g, b) if self._blp_alpha_depth: d += (a,) data.extend(d) @@ -431,7 +436,7 @@ def _write_palette(self) -> bytes: data += b"\x00" * 4 return data - def encode(self, bufsize): + def encode(self, bufsize: int) -> tuple[int, int, bytes]: palette_data = self._write_palette() offset = 20 + 16 * 4 * 2 + len(palette_data) @@ -449,7 +454,7 @@ def encode(self, bufsize): return len(data), 0, data -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode != "P": msg = "Unsupported BLP image mode" raise ValueError(msg) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 2df1d8d33bd..45c1ea941e8 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -395,12 +395,12 @@ def _open(self) -> None: } -def _dib_save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _dib_save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: _save(im, fp, filename, False) def _save( - im: Image.Image, fp: IO[bytes], filename: str, bitmap_header: bool = True + im: Image.Image, fp: IO[bytes], filename: str | bytes, bitmap_header: bool = True ) -> None: try: rawmode, bits, colors = SAVE[im.mode] diff --git a/src/PIL/BufrStubImagePlugin.py b/src/PIL/BufrStubImagePlugin.py index 7388a2b8a3a..0ee2f653b2c 100644 --- a/src/PIL/BufrStubImagePlugin.py +++ b/src/PIL/BufrStubImagePlugin.py @@ -60,7 +60,7 @@ def _load(self) -> ImageFile.StubHandler | None: return _handler -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "BUFR save handler not installed" raise OSError(msg) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index a3efadb030d..861a1eca0cc 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -511,7 +511,7 @@ def decode(self, buffer): return -1, 0 -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode not in ("RGB", "RGBA", "L", "LA"): msg = f"cannot write mode {im.mode} as DDS" raise OSError(msg) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index f41bc2b321d..a540595b85b 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -715,12 +715,12 @@ def _write_multiple_frames(im, fp, palette): return True -def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: _save(im, fp, filename, save_all=True) def _save( - im: Image.Image, fp: IO[bytes], filename: str, save_all: bool = False + im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False ) -> None: # header if "palette" in im.encoderinfo or "palette" in im.info: @@ -796,7 +796,7 @@ def _write_local_header(fp, im, offset, flags): fp.write(o8(8)) # bits -def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: # Unused by default. # To use, uncomment the register_save call at the end of the file. # diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index d3655f4ddc0..e9aa084b281 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -60,7 +60,7 @@ def _load(self) -> ImageFile.StubHandler | None: return _handler -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "GRIB save handler not installed" raise OSError(msg) diff --git a/src/PIL/Hdf5StubImagePlugin.py b/src/PIL/Hdf5StubImagePlugin.py index b789c215fee..cc9e73deb80 100644 --- a/src/PIL/Hdf5StubImagePlugin.py +++ b/src/PIL/Hdf5StubImagePlugin.py @@ -60,7 +60,7 @@ def _load(self) -> ImageFile.StubHandler | None: return _handler -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "HDF5 save handler not installed" raise OSError(msg) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 0a86ba883f1..2a89d498cbf 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -22,6 +22,7 @@ import os import struct import sys +from typing import IO from . import Image, ImageFile, PngImagePlugin, features @@ -312,7 +313,7 @@ def load(self): return px -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: """ Saves the image as a series of PNG files, that are then combined into a .icns file. @@ -346,29 +347,27 @@ def _save(im, fp, filename): entries = [] for type, size in sizes.items(): stream = size_streams[size] - entries.append( - {"type": type, "size": HEADERSIZE + len(stream), "stream": stream} - ) + entries.append((type, HEADERSIZE + len(stream), stream)) # Header fp.write(MAGIC) file_length = HEADERSIZE # Header file_length += HEADERSIZE + 8 * len(entries) # TOC - file_length += sum(entry["size"] for entry in entries) + file_length += sum(entry[1] for entry in entries) fp.write(struct.pack(">i", file_length)) # TOC fp.write(b"TOC ") fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) for entry in entries: - fp.write(entry["type"]) - fp.write(struct.pack(">i", entry["size"])) + fp.write(entry[0]) + fp.write(struct.pack(">i", entry[1])) # Data for entry in entries: - fp.write(entry["type"]) - fp.write(struct.pack(">i", entry["size"])) - fp.write(entry["stream"]) + fp.write(entry[0]) + fp.write(struct.pack(">i", entry[1])) + fp.write(entry[2]) if hasattr(fp, "flush"): fp.flush() diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index af94e5a2e7f..227fcf35cbb 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -40,7 +40,7 @@ _MAGIC = b"\0\0\1\0" -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: fp.write(_MAGIC) # (2+2) bmp = im.encoderinfo.get("bitmap_format") == "bmp" sizes = im.encoderinfo.get( diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index c98cfb0984b..015c2febea8 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -326,7 +326,7 @@ def tell(self) -> int: } -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: try: image_type, rawmode = SAVE[im.mode] except KeyError as e: @@ -341,6 +341,8 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: # or: SyntaxError("not an IM file") # 8 characters are used for "Name: " and "\r\n" # Keep just the filename, ditch the potentially overlong path + if isinstance(filename, bytes): + filename = filename.decode("ascii") name, ext = os.path.splitext(os.path.basename(filename)) name = "".join([name[: 92 - len(ext)], ext]) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f6ffac8a77f..af174861019 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -626,7 +626,7 @@ def _ensure_mutable(self) -> None: self.load() def _dump( - self, file: str | None = None, format: str | None = None, **options + self, file: str | None = None, format: str | None = None, **options: Any ) -> str: suffix = "" if format: @@ -649,10 +649,12 @@ def _dump( return filename - def __eq__(self, other): + def __eq__(self, other: object) -> bool: + if self.__class__ is not other.__class__: + return False + assert isinstance(other, Image) return ( - self.__class__ is other.__class__ - and self.mode == other.mode + self.mode == other.mode and self.size == other.size and self.info == other.info and self.getpalette() == other.getpalette() @@ -2965,7 +2967,7 @@ def transform( # Debugging -def _wedge(): +def _wedge() -> Image: """Create grayscale wedge (for debugging only)""" return Image()._new(core.wedge("L")) @@ -3566,7 +3568,9 @@ def register_mime(id: str, mimetype: str) -> None: MIME[id.upper()] = mimetype -def register_save(id: str, driver) -> None: +def register_save( + id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] +) -> None: """ Registers an image save function. This function should not be used in application code. @@ -3577,7 +3581,9 @@ def register_save(id: str, driver) -> None: SAVE[id.upper()] = driver -def register_save_all(id: str, driver) -> None: +def register_save_all( + id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] +) -> None: """ Registers an image function to save all the frames of a multiframe format. This function should not be @@ -3651,7 +3657,7 @@ def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None: # Simple display support. -def _show(image, **options) -> None: +def _show(image: Image, **options: Any) -> None: from . import ImageShow ImageShow.show(image, **options) @@ -3661,7 +3667,9 @@ def _show(image, **options) -> None: # Effects -def effect_mandelbrot(size, extent, quality): +def effect_mandelbrot( + size: tuple[int, int], extent: tuple[int, int, int, int], quality: int +) -> Image: """ Generate a Mandelbrot set covering the given extent. diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 73ed3d4a9a6..01f99c11984 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -219,7 +219,9 @@ def line(self, xy: Coords, fill=None, width=0, joint=None) -> None: # This is a straight line, so no joint is required continue - def coord_at_angle(coord, angle): + def coord_at_angle( + coord: Sequence[float], angle: float + ) -> tuple[float, float]: x, y = coord angle -= 90 distance = width / 2 - 1 @@ -1109,11 +1111,13 @@ def _get_angles(n_sides: int, rotation: float) -> list[float]: return [_compute_polygon_vertex(angle) for angle in angles] -def _color_diff(color1, color2: float | tuple[int, ...]) -> float: +def _color_diff( + color1: float | tuple[int, ...], color2: float | tuple[int, ...] +) -> float: """ Uses 1-norm distance to calculate difference between two values. """ - if isinstance(color2, tuple): - return sum(abs(color1[i] - color2[i]) for i in range(0, len(color2))) - else: - return abs(color1 - color2) + first = color1 if isinstance(color1, tuple) else (color1,) + second = color2 if isinstance(color2, tuple) else (color2,) + + return sum(abs(first[i] - second[i]) for i in range(0, len(second))) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index f0e49238760..6bef681e979 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -763,7 +763,7 @@ class PyEncoder(PyCodec): def pushes_fd(self): return self._pushes_fd - def encode(self, bufsize): + def encode(self, bufsize: int) -> tuple[int, int, bytes]: """ Override to perform the encoding process. diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 5a0ef0d01a3..72c2cb85e3c 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -329,11 +329,13 @@ def _accept(prefix: bytes) -> bool: # Save support -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: # Get the keyword arguments info = im.encoderinfo - if filename.endswith(".j2k") or info.get("no_jp2", False): + if isinstance(filename, str): + filename = filename.encode() + if filename.endswith(b".j2k") or info.get("no_jp2", False): kind = "j2k" else: kind = "jp2" diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 4d0b75e77ed..0c8a678887e 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -42,7 +42,7 @@ import sys import tempfile import warnings -from typing import Any +from typing import IO, Any from . import Image, ImageFile from ._binary import i16be as i16 @@ -644,7 +644,7 @@ def get_sampling(im): return samplings.get(sampling, -1) -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.width == 0 or im.height == 0: msg = "cannot write empty image as JPEG" raise ValueError(msg) @@ -827,7 +827,7 @@ def validate_qtables(qtables): ImageFile._save(im, fp, [("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize) -def _save_cjpeg(im, fp, filename): +def _save_cjpeg(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: # ALTERNATIVE: handle JPEGs via the IJG command line utilities. tempfile = im._dump() subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 6716722f204..152e19e2365 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -33,7 +33,7 @@ from ._binary import o32le -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: JpegImagePlugin._save(im, fp, filename) diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 65cc70624b7..0a75c868b97 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -164,7 +164,7 @@ def decode(self, buffer: bytes) -> tuple[int, int]: # write MSP files (uncompressed only) -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode != "1": msg = f"cannot write mode {im.mode} as MSP" raise OSError(msg) diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index 026bfd9a01b..dd42003b5a3 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -144,7 +144,7 @@ def _open(self) -> None: } -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: try: version, bits, planes, rawmode = SAVE[im.mode] except KeyError as e: diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index ccd28f3434b..f0da1e04797 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -40,7 +40,7 @@ # 5. page contents -def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: _save(im, fp, filename, save_all=True) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index a6c24e67179..52e8358017a 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -76,7 +76,7 @@ class PdfFormatError(RuntimeError): pass -def check_format_condition(condition, error_message): +def check_format_condition(condition: bool, error_message: str) -> None: if not condition: raise PdfFormatError(error_message) @@ -93,12 +93,11 @@ def __str__(self) -> str: def __bytes__(self) -> bytes: return self.__str__().encode("us-ascii") - def __eq__(self, other): - return ( - other.__class__ is self.__class__ - and other.object_id == self.object_id - and other.generation == self.generation - ) + def __eq__(self, other: object) -> bool: + if self.__class__ is not other.__class__: + return False + assert isinstance(other, IndirectReference) + return other.object_id == self.object_id and other.generation == self.generation def __ne__(self, other): return not (self == other) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 76ffdef3f55..9aaadb47d5e 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1234,7 +1234,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) seq_num = fdat_chunks.seq_num -def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: _save(im, fp, filename, save_all=True) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 94bf430b825..16c9ccbba72 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -328,7 +328,7 @@ def decode(self, buffer: bytes) -> tuple[int, int]: # -------------------------------------------------------------------- -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode == "1": rawmode, head = "1;I", b"P4" elif im.mode == "L": diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py index f2cf06d0dd7..202ef52d09b 100644 --- a/src/PIL/QoiImagePlugin.py +++ b/src/PIL/QoiImagePlugin.py @@ -37,6 +37,8 @@ def _open(self) -> None: class QoiDecoder(ImageFile.PyDecoder): _pulls_fd = True + _previous_pixel: bytes | bytearray | None = None + _previously_seen_pixels: dict[int, bytes | bytearray] = {} def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: self._previous_pixel = value @@ -45,9 +47,10 @@ def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 self._previously_seen_pixels[hash_value] = value - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: + assert self.fd is not None + self._previously_seen_pixels = {} - self._previous_pixel = None self._add_to_previous_pixels(bytearray((0, 0, 0, 255))) data = bytearray() @@ -55,7 +58,8 @@ def decode(self, buffer): dest_length = self.state.xsize * self.state.ysize * bands while len(data) < dest_length: byte = self.fd.read(1)[0] - if byte == 0b11111110: # QOI_OP_RGB + value: bytes | bytearray + if byte == 0b11111110 and self._previous_pixel: # QOI_OP_RGB value = bytearray(self.fd.read(3)) + self._previous_pixel[3:] elif byte == 0b11111111: # QOI_OP_RGBA value = self.fd.read(4) @@ -66,7 +70,7 @@ def decode(self, buffer): value = self._previously_seen_pixels.get( op_index, bytearray((0, 0, 0, 0)) ) - elif op == 1: # QOI_OP_DIFF + elif op == 1 and self._previous_pixel: # QOI_OP_DIFF value = bytearray( ( (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2) @@ -77,7 +81,7 @@ def decode(self, buffer): self._previous_pixel[3], ) ) - elif op == 2: # QOI_OP_LUMA + elif op == 2 and self._previous_pixel: # QOI_OP_LUMA second_byte = self.fd.read(1)[0] diff_green = (byte & 0b00111111) - 32 diff_red = ((second_byte & 0b11110000) >> 4) - 8 @@ -90,7 +94,7 @@ def decode(self, buffer): ) ) value += self._previous_pixel[3:] - elif op == 3: # QOI_OP_RUN + elif op == 3 and self._previous_pixel: # QOI_OP_RUN run_length = (byte & 0b00111111) + 1 value = self._previous_pixel if bands == 3: diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 7bd84ebd491..50d97910932 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -125,7 +125,7 @@ def _open(self) -> None: ] -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode not in {"RGB", "RGBA", "L"}: msg = "Unsupported SGI image mode" raise ValueError(msg) @@ -171,8 +171,9 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: # Maximum Byte value (255 = 8bits per pixel) pinmax = 255 # Image name (79 characters max, truncated below in write) - filename = os.path.basename(filename) - img_name = os.path.splitext(filename)[0].encode("ascii", "ignore") + img_name = os.path.splitext(os.path.basename(filename))[0] + if isinstance(img_name, str): + img_name = img_name.encode("ascii", "ignore") # Standard representation of pixel in the file colormap = 0 fp.write(struct.pack(">h", magic_number)) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 98dd91c0e24..a6cc00019da 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -263,7 +263,7 @@ def makeSpiderHeader(im: Image.Image) -> list[bytes]: return [struct.pack("f", v) for v in hdr] -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode[0] != "F": im = im.convert("F") @@ -279,9 +279,10 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) -def _save_spider(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: # get the filename extension and register it with Image - ext = os.path.splitext(filename)[1] + filename_ext = os.path.splitext(filename)[1] + ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext Image.register_extension(SpiderImageFile.format, ext) _save(im, fp, filename) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 401a83f9fba..f16f075df05 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -178,7 +178,7 @@ def load_end(self) -> None: } -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: try: rawmode, bits, colormaptype, imagetype = SAVE[im.mode] except KeyError as e: diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 0b96017552a..08ee506b162 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -387,7 +387,7 @@ def __repr__(self) -> str: def __hash__(self): return self._val.__hash__() - def __eq__(self, other): + def __eq__(self, other: object) -> bool: val = self._val if isinstance(other, IFDRational): other = other._val @@ -2149,7 +2149,7 @@ def fixOffsets(self, count, isShort=False, isLong=False): self.rewriteLastLong(offset) -def _save_all(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: encoderinfo = im.encoderinfo.copy() encoderconfig = im.encoderconfig append_images = list(encoderinfo.get("append_images", [])) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 463d6a62398..97debc2edc9 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -1,7 +1,7 @@ from __future__ import annotations from io import BytesIO -from typing import Any +from typing import IO, Any from . import Image, ImageFile @@ -182,7 +182,7 @@ def tell(self) -> int: return self.__logical_frame -def _save_all(im, fp, filename): +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: encoderinfo = im.encoderinfo.copy() append_images = list(encoderinfo.get("append_images", [])) @@ -195,7 +195,7 @@ def _save_all(im, fp, filename): _save(im, fp, filename) return - background = (0, 0, 0, 0) + background: int | tuple[int, ...] = (0, 0, 0, 0) if "background" in encoderinfo: background = encoderinfo["background"] elif "background" in im.info: @@ -325,7 +325,7 @@ def _save_all(im, fp, filename): fp.write(data) -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: lossless = im.encoderinfo.get("lossless", False) quality = im.encoderinfo.get("quality", 80) alpha_quality = im.encoderinfo.get("alpha_quality", 100) diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index a68f705a03c..3d5cddcc8f5 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -163,7 +163,7 @@ def load(self, dpi=None): return super().load() -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if _handler is None or not hasattr(_handler, "save"): msg = "WMF save handler not installed" raise OSError(msg) diff --git a/src/PIL/XbmImagePlugin.py b/src/PIL/XbmImagePlugin.py index eee7274361c..6d11bbfcf6b 100644 --- a/src/PIL/XbmImagePlugin.py +++ b/src/PIL/XbmImagePlugin.py @@ -70,7 +70,7 @@ def _open(self) -> None: self.tile = [("xbm", (0, 0) + self.size, m.end(), None)] -def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None: +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode != "1": msg = f"cannot write mode {im.mode} as XBM" raise OSError(msg) From 9f831317fe98633214ad0266417d349b44e5d6bf Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:47:18 +1000 Subject: [PATCH 227/300] Updated text Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- docs/deprecations.rst | 6 +++--- docs/releasenotes/10.4.0.rst | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 8a03d858c71..627672e1f3b 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -115,12 +115,12 @@ Support for LibTIFF earlier than 4 Support for LibTIFF earlier than version 4 has been deprecated. Upgrade to a newer version of LibTIFF instead. -ImageDraw.getdraw hints argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ImageDraw.getdraw hints parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. deprecated:: 10.4.0 -The ``hints`` argument in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. +The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. Removed features ---------------- diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 8c49e0842c5..44727efd41f 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -34,10 +34,10 @@ Support for LibTIFF earlier than 4 Support for LibTIFF earlier than version 4 has been deprecated. Upgrade to a newer version of LibTIFF instead. -ImageDraw.getdraw hints argument -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ImageDraw.getdraw hints parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The ``hints`` argument in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. +The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. API Changes =========== From 4679e4bf9e542ffae8c81e68603d5c944108389f Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:47:52 +1000 Subject: [PATCH 228/300] Updated deprecation warning --- src/PIL/ImageDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index ec15b535fbe..23d7e6973b4 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -908,7 +908,7 @@ def getdraw(im=None, hints=None): :returns: A (drawing context, drawing resource factory) tuple. """ if hints is not None: - deprecate("'hints' argument", 12) + deprecate("'hints' parameter", 12) from . import ImageDraw2 if im: From 8e8ee1e4c4b8037c9b755e2ba26a7297dfa5d6ac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jun 2024 17:38:17 +1000 Subject: [PATCH 229/300] Accept 't' suffix for libtiff version --- Tests/test_features.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/test_features.py b/Tests/test_features.py index de418115ee0..b7eefa09ae1 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -38,7 +38,9 @@ def test(name: str, function: Callable[[str], str | None]) -> None: assert function(name) == version if name != "PIL": if name == "zlib" and version is not None: - version = version.replace(".zlib-ng", "") + version = re.sub(".zlib-ng$", "", version) + elif name == "libtiff" and version is not None: + version = re.sub("t$", "", version) assert version is None or re.search(r"\d+(\.\d+)*$", version) for module in features.modules: From b84c970fbf3f08825cabf9c5a132e20f868907f1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jun 2024 19:19:06 +1000 Subject: [PATCH 230/300] Wait until all markers are read to process EXIF --- Tests/images/multiple_exif.jpg | Bin 364 -> 391 bytes Tests/test_file_jpeg.py | 2 +- src/PIL/JpegImagePlugin.py | 68 +++++++++++++++++---------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Tests/images/multiple_exif.jpg b/Tests/images/multiple_exif.jpg index 32e0aa301a94dfa13dc47faeb3071c25a5d8b573..f9f343507e73c5fe2df844deaa5d025f5cd013ec 100644 GIT binary patch delta 66 zcmaFE)XqFX++5wYA~TJF!Pl2Ti-CcGgMpEekAVru;sIhQAZAF*EGjPf|B!(nqOdqM NIX^FjVWY7nBLK0l4od(4 delta 39 qcmZo?e#10DT!z=RA~TJFAuY40xa9vs20jS4I5jyxFJ+^jB_jX@Ck-qB diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 18dc752d8bd..342c9e47bd4 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -870,7 +870,7 @@ def test_ifd_offset_exif(self) -> None: def test_multiple_exif(self) -> None: with Image.open("Tests/images/multiple_exif.jpg") as im: - assert im.info["exif"] == b"Exif\x00\x00firstsecond" + assert im.getexif()[270] == "firstsecond" @mark_if_feature_version( pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing" diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 4d0b75e77ed..a9a89e58bd7 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -159,38 +159,6 @@ def APP(self, marker): # plus constant header size self.info["mpoffset"] = self.fp.tell() - n + 4 - # If DPI isn't in JPEG header, fetch from EXIF - if "dpi" not in self.info and "exif" in self.info: - try: - exif = self.getexif() - resolution_unit = exif[0x0128] - x_resolution = exif[0x011A] - try: - dpi = float(x_resolution[0]) / x_resolution[1] - except TypeError: - dpi = x_resolution - if math.isnan(dpi): - msg = "DPI is not a number" - raise ValueError(msg) - if resolution_unit == 3: # cm - # 1 dpcm = 2.54 dpi - dpi *= 2.54 - self.info["dpi"] = dpi, dpi - except ( - struct.error, - KeyError, - SyntaxError, - TypeError, - ValueError, - ZeroDivisionError, - ): - # struct.error for truncated EXIF - # KeyError for dpi not included - # SyntaxError for invalid/unreadable EXIF - # ValueError or TypeError for dpi being an invalid float - # ZeroDivisionError for invalid dpi rational value - self.info["dpi"] = 72, 72 - def COM(self: JpegImageFile, marker: int) -> None: # @@ -409,6 +377,8 @@ def _open(self): msg = "no marker found" raise SyntaxError(msg) + self._read_dpi_from_exif() + def load_read(self, read_bytes: int) -> bytes: """ internal: read more image data @@ -497,6 +467,40 @@ def load_djpeg(self) -> None: def _getexif(self) -> dict[str, Any] | None: return _getexif(self) + def _read_dpi_from_exif(self) -> None: + # If DPI isn't in JPEG header, fetch from EXIF + if "dpi" in self.info or "exif" not in self.info: + return + try: + exif = self.getexif() + resolution_unit = exif[0x0128] + x_resolution = exif[0x011A] + try: + dpi = float(x_resolution[0]) / x_resolution[1] + except TypeError: + dpi = x_resolution + if math.isnan(dpi): + msg = "DPI is not a number" + raise ValueError(msg) + if resolution_unit == 3: # cm + # 1 dpcm = 2.54 dpi + dpi *= 2.54 + self.info["dpi"] = dpi, dpi + except ( + struct.error, + KeyError, + SyntaxError, + TypeError, + ValueError, + ZeroDivisionError, + ): + # struct.error for truncated EXIF + # KeyError for dpi not included + # SyntaxError for invalid/unreadable EXIF + # ValueError or TypeError for dpi being an invalid float + # ZeroDivisionError for invalid dpi rational value + self.info["dpi"] = 72, 72 + def _getmp(self): return _getmp(self) From 9afe9d2769d9241385a4fd8f8e2376fbdca74981 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jun 2024 16:08:06 +1000 Subject: [PATCH 231/300] Added type hints --- Tests/test_file_gif.py | 9 +- src/PIL/GifImagePlugin.py | 206 +++++++++++++++++++++++--------------- src/PIL/Image.py | 20 ++-- src/PIL/ImageOps.py | 2 +- src/PIL/ImagePalette.py | 13 ++- 5 files changed, 154 insertions(+), 96 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 4e790926bfc..e19c88a47c4 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -53,6 +53,7 @@ def test_closed_file() -> None: def test_seek_after_close() -> None: im = Image.open("Tests/images/iss634.gif") + assert isinstance(im, GifImagePlugin.GifImageFile) im.load() im.close() @@ -377,7 +378,8 @@ def test_save_netpbm_bmp_mode(tmp_path: Path) -> None: img = img.convert("RGB") tempfile = str(tmp_path / "temp.gif") - GifImagePlugin._save_netpbm(img, 0, tempfile) + b = BytesIO() + GifImagePlugin._save_netpbm(img, b, tempfile) with Image.open(tempfile) as reloaded: assert_image_similar(img, reloaded.convert("RGB"), 0) @@ -388,7 +390,8 @@ def test_save_netpbm_l_mode(tmp_path: Path) -> None: img = img.convert("L") tempfile = str(tmp_path / "temp.gif") - GifImagePlugin._save_netpbm(img, 0, tempfile) + b = BytesIO() + GifImagePlugin._save_netpbm(img, b, tempfile) with Image.open(tempfile) as reloaded: assert_image_similar(img, reloaded.convert("L"), 0) @@ -648,7 +651,7 @@ def test_dispose2_palette(tmp_path: Path) -> None: assert rgb_img.getpixel((50, 50)) == circle # Check that frame transparency wasn't added unnecessarily - assert img._frame_transparency is None + assert getattr(img, "_frame_transparency") is None def test_dispose2_diff(tmp_path: Path) -> None: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index a540595b85b..a305e8de668 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -29,9 +29,10 @@ import math import os import subprocess +import sys from enum import IntEnum from functools import cached_property -from typing import IO +from typing import IO, TYPE_CHECKING, Any, List, Literal, NamedTuple, Union from . import ( Image, @@ -46,6 +47,9 @@ from ._binary import o8 from ._binary import o16le as o16 +if TYPE_CHECKING: + from . import _imaging + class LoadingStrategy(IntEnum): """.. versionadded:: 9.1.0""" @@ -118,7 +122,7 @@ def _open(self) -> None: self._seek(0) # get ready to read first frame @property - def n_frames(self): + def n_frames(self) -> int: if self._n_frames is None: current = self.tell() try: @@ -163,11 +167,11 @@ def seek(self, frame: int) -> None: msg = "no more images in GIF file" raise EOFError(msg) from e - def _seek(self, frame, update_image=True): + def _seek(self, frame: int, update_image: bool = True) -> None: if frame == 0: # rewind self.__offset = 0 - self.dispose = None + self.dispose: _imaging.ImagingCore | None = None self.__frame = -1 self._fp.seek(self.__rewind) self.disposal_method = 0 @@ -195,9 +199,9 @@ def _seek(self, frame, update_image=True): msg = "no more images in GIF file" raise EOFError(msg) - palette = None + palette: ImagePalette.ImagePalette | Literal[False] | None = None - info = {} + info: dict[str, Any] = {} frame_transparency = None interlace = None frame_dispose_extent = None @@ -213,7 +217,7 @@ def _seek(self, frame, update_image=True): # s = self.fp.read(1) block = self.data() - if s[0] == 249: + if s[0] == 249 and block is not None: # # graphic control extension # @@ -249,14 +253,14 @@ def _seek(self, frame, update_image=True): info["comment"] = comment s = None continue - elif s[0] == 255 and frame == 0: + elif s[0] == 255 and frame == 0 and block is not None: # # application extension # info["extension"] = block, self.fp.tell() if block[:11] == b"NETSCAPE2.0": block = self.data() - if len(block) >= 3 and block[0] == 1: + if block and len(block) >= 3 and block[0] == 1: self.info["loop"] = i16(block, 1) while self.data(): pass @@ -345,51 +349,52 @@ def _rgb(color: int) -> tuple[int, int, int]: else: return (color, color, color) + self.dispose = None self.dispose_extent = frame_dispose_extent - try: - if self.disposal_method < 2: - # do not dispose or none specified - self.dispose = None - elif self.disposal_method == 2: - # replace with background colour - - # only dispose the extent in this frame - x0, y0, x1, y1 = self.dispose_extent - dispose_size = (x1 - x0, y1 - y0) - - Image._decompression_bomb_check(dispose_size) - - # by convention, attempt to use transparency first - dispose_mode = "P" - color = self.info.get("transparency", frame_transparency) - if color is not None: - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGBA" - color = _rgb(color) + (0,) - else: - color = self.info.get("background", 0) - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGB" - color = _rgb(color) - self.dispose = Image.core.fill(dispose_mode, dispose_size, color) - else: - # replace with previous contents - if self.im is not None: + if self.dispose_extent and self.disposal_method >= 2: + try: + if self.disposal_method == 2: + # replace with background colour + # only dispose the extent in this frame - self.dispose = self._crop(self.im, self.dispose_extent) - elif frame_transparency is not None: x0, y0, x1, y1 = self.dispose_extent dispose_size = (x1 - x0, y1 - y0) Image._decompression_bomb_check(dispose_size) + + # by convention, attempt to use transparency first dispose_mode = "P" - color = frame_transparency - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGBA" - color = _rgb(frame_transparency) + (0,) + color = self.info.get("transparency", frame_transparency) + if color is not None: + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(color) + (0,) + else: + color = self.info.get("background", 0) + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGB" + color = _rgb(color) self.dispose = Image.core.fill(dispose_mode, dispose_size, color) - except AttributeError: - pass + else: + # replace with previous contents + if self.im is not None: + # only dispose the extent in this frame + self.dispose = self._crop(self.im, self.dispose_extent) + elif frame_transparency is not None: + x0, y0, x1, y1 = self.dispose_extent + dispose_size = (x1 - x0, y1 - y0) + + Image._decompression_bomb_check(dispose_size) + dispose_mode = "P" + color = frame_transparency + if self.mode in ("RGB", "RGBA"): + dispose_mode = "RGBA" + color = _rgb(frame_transparency) + (0,) + self.dispose = Image.core.fill( + dispose_mode, dispose_size, color + ) + except AttributeError: + pass if interlace is not None: transparency = -1 @@ -498,7 +503,12 @@ def _normalize_mode(im: Image.Image) -> Image.Image: return im.convert("L") -def _normalize_palette(im, palette, info): +_Palette = Union[bytes, bytearray, List[int], ImagePalette.ImagePalette] + + +def _normalize_palette( + im: Image.Image, palette: _Palette | None, info: dict[str, Any] +) -> Image.Image: """ Normalizes the palette for image. - Sets the palette to the incoming palette, if provided. @@ -526,8 +536,10 @@ def _normalize_palette(im, palette, info): source_palette = bytearray(i // 3 for i in range(768)) im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette) + used_palette_colors: list[int] | None if palette: used_palette_colors = [] + assert source_palette is not None for i in range(0, len(source_palette), 3): source_color = tuple(source_palette[i : i + 3]) index = im.palette.colors.get(source_color) @@ -561,7 +573,7 @@ def _normalize_palette(im, palette, info): def _write_single_frame( im: Image.Image, fp: IO[bytes], - palette: bytes | bytearray | list[int] | ImagePalette.ImagePalette, + palette: _Palette | None, ) -> None: im_out = _normalize_mode(im) for k, v in im_out.info.items(): @@ -585,7 +597,7 @@ def _write_single_frame( def _getbbox( base_im: Image.Image, im_frame: Image.Image -) -> tuple[Image.Image, tuple[int, int, int, int]]: +) -> tuple[Image.Image, tuple[int, int, int, int] | None]: if _get_palette_bytes(im_frame) != _get_palette_bytes(base_im): im_frame = im_frame.convert("RGBA") base_im = base_im.convert("RGBA") @@ -593,12 +605,20 @@ def _getbbox( return delta, delta.getbbox(alpha_only=False) -def _write_multiple_frames(im, fp, palette): +class _Frame(NamedTuple): + im: Image.Image + bbox: tuple[int, int, int, int] | None + encoderinfo: dict[str, Any] + + +def _write_multiple_frames( + im: Image.Image, fp: IO[bytes], palette: _Palette | None +) -> bool: duration = im.encoderinfo.get("duration") disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) - im_frames = [] - previous_im = None + im_frames: list[_Frame] = [] + previous_im: Image.Image | None = None frame_count = 0 background_im = None for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): @@ -624,24 +644,22 @@ def _write_multiple_frames(im, fp, palette): frame_count += 1 diff_frame = None - if im_frames: + if im_frames and previous_im: # delta frame delta, bbox = _getbbox(previous_im, im_frame) if not bbox: # This frame is identical to the previous frame if encoderinfo.get("duration"): - im_frames[-1]["encoderinfo"]["duration"] += encoderinfo[ - "duration" - ] + im_frames[-1].encoderinfo["duration"] += encoderinfo["duration"] continue - if im_frames[-1]["encoderinfo"].get("disposal") == 2: + if im_frames[-1].encoderinfo.get("disposal") == 2: if background_im is None: color = im.encoderinfo.get( "transparency", im.info.get("transparency", (0, 0, 0)) ) background = _get_background(im_frame, color) background_im = Image.new("P", im_frame.size, background) - background_im.putpalette(im_frames[0]["im"].palette) + background_im.putpalette(im_frames[0].im.palette) bbox = _getbbox(background_im, im_frame)[1] elif encoderinfo.get("optimize") and im_frame.mode != "1": if "transparency" not in encoderinfo: @@ -687,31 +705,29 @@ def _write_multiple_frames(im, fp, palette): else: bbox = None previous_im = im_frame - im_frames.append( - {"im": diff_frame or im_frame, "bbox": bbox, "encoderinfo": encoderinfo} - ) + im_frames.append(_Frame(diff_frame or im_frame, bbox, encoderinfo)) if len(im_frames) == 1: if "duration" in im.encoderinfo: # Since multiple frames will not be written, use the combined duration - im.encoderinfo["duration"] = im_frames[0]["encoderinfo"]["duration"] - return + im.encoderinfo["duration"] = im_frames[0].encoderinfo["duration"] + return False for frame_data in im_frames: - im_frame = frame_data["im"] - if not frame_data["bbox"]: + im_frame = frame_data.im + if not frame_data.bbox: # global header - for s in _get_global_header(im_frame, frame_data["encoderinfo"]): + for s in _get_global_header(im_frame, frame_data.encoderinfo): fp.write(s) offset = (0, 0) else: # compress difference if not palette: - frame_data["encoderinfo"]["include_color_table"] = True + frame_data.encoderinfo["include_color_table"] = True - im_frame = im_frame.crop(frame_data["bbox"]) - offset = frame_data["bbox"][:2] - _write_frame_data(fp, im_frame, offset, frame_data["encoderinfo"]) + im_frame = im_frame.crop(frame_data.bbox) + offset = frame_data.bbox[:2] + _write_frame_data(fp, im_frame, offset, frame_data.encoderinfo) return True @@ -748,7 +764,9 @@ def get_interlace(im: Image.Image) -> int: return interlace -def _write_local_header(fp, im, offset, flags): +def _write_local_header( + fp: IO[bytes], im: Image.Image, offset: tuple[int, int], flags: int +) -> None: try: transparency = im.encoderinfo["transparency"] except KeyError: @@ -849,7 +867,7 @@ def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: _FORCE_OPTIMIZE = False -def _get_optimize(im, info): +def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None: """ Palette optimization is a potentially expensive operation. @@ -893,6 +911,7 @@ def _get_optimize(im, info): and current_palette_size > 2 ): return used_palette_colors + return None def _get_color_table_size(palette_bytes: bytes) -> int: @@ -933,7 +952,10 @@ def _get_palette_bytes(im: Image.Image) -> bytes: return im.palette.palette if im.palette else b"" -def _get_background(im, info_background): +def _get_background( + im: Image.Image, + info_background: int | tuple[int, int, int] | tuple[int, int, int, int] | None, +) -> int: background = 0 if info_background: if isinstance(info_background, tuple): @@ -956,7 +978,7 @@ def _get_background(im, info_background): return background -def _get_global_header(im, info): +def _get_global_header(im: Image.Image, info: dict[str, Any]) -> list[bytes]: """Return a list of strings representing a GIF header""" # Header Block @@ -1018,7 +1040,12 @@ def _get_global_header(im, info): return header -def _write_frame_data(fp, im_frame, offset, params): +def _write_frame_data( + fp: IO[bytes], + im_frame: Image.Image, + offset: tuple[int, int], + params: dict[str, Any], +) -> None: try: im_frame.encoderinfo = params @@ -1038,7 +1065,9 @@ def _write_frame_data(fp, im_frame, offset, params): # Legacy GIF utilities -def getheader(im, palette=None, info=None): +def getheader( + im: Image.Image, palette: _Palette | None = None, info: dict[str, Any] | None = None +) -> tuple[list[bytes], list[int] | None]: """ Legacy Method to get Gif data from image. @@ -1050,11 +1079,11 @@ def getheader(im, palette=None, info=None): :returns: tuple of(list of header items, optimized palette) """ - used_palette_colors = _get_optimize(im, info) - if info is None: info = {} + used_palette_colors = _get_optimize(im, info) + if "background" not in info and "background" in im.info: info["background"] = im.info["background"] @@ -1066,7 +1095,9 @@ def getheader(im, palette=None, info=None): return header, used_palette_colors -def getdata(im, offset=(0, 0), **params): +def getdata( + im: Image.Image, offset: tuple[int, int] = (0, 0), **params: Any +) -> list[bytes]: """ Legacy Method @@ -1083,12 +1114,23 @@ def getdata(im, offset=(0, 0), **params): :returns: List of bytes containing GIF encoded frame data """ + from io import BytesIO - class Collector: + class Collector(BytesIO): data = [] - def write(self, data: bytes) -> None: - self.data.append(data) + if sys.version_info >= (3, 12): + from collections.abc import Buffer + + def write(self, data: Buffer) -> int: + self.data.append(data) + return len(data) + + else: + + def write(self, data: Any) -> int: + self.data.append(data) + return len(data) im.load() # make sure raster data is available diff --git a/src/PIL/Image.py b/src/PIL/Image.py index af174861019..bdd869ccc15 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -41,7 +41,7 @@ from collections.abc import Callable, MutableMapping from enum import IntEnum from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, cast +from typing import IO, TYPE_CHECKING, Any, Literal, Protocol, Sequence, Tuple, cast # VERSION was removed in Pillow 6.0.0. # PILLOW_VERSION was removed in Pillow 9.0.0. @@ -1367,7 +1367,7 @@ def getbands(self) -> tuple[str, ...]: """ return ImageMode.getmode(self.mode).bands - def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int]: + def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int] | None: """ Calculates the bounding box of the non-zero regions in the image. @@ -3029,12 +3029,18 @@ def new( color = ImageColor.getcolor(color, mode) im = Image() - if mode == "P" and isinstance(color, (list, tuple)) and len(color) in [3, 4]: - # RGB or RGBA value for a P image - from . import ImagePalette + if ( + mode == "P" + and isinstance(color, (list, tuple)) + and all(isinstance(i, int) for i in color) + ): + color_ints: tuple[int, ...] = cast(Tuple[int, ...], tuple(color)) + if len(color_ints) == 3 or len(color_ints) == 4: + # RGB or RGBA value for a P image + from . import ImagePalette - im.palette = ImagePalette.ImagePalette() - color = im.palette.getcolor(color) + im.palette = ImagePalette.ImagePalette() + color = im.palette.getcolor(color_ints) return im._new(core.fill(mode, size, color)) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 33db8fa50c7..cbe189cc926 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -497,7 +497,7 @@ def expand( color = _color(fill, image.mode) if image.palette: palette = ImagePalette.ImagePalette(palette=image.getpalette()) - if isinstance(color, tuple): + if isinstance(color, tuple) and (len(color) == 3 or len(color) == 4): color = palette.getcolor(color) else: palette = None diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 057ccd1d7c4..1ff05a3eff1 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -18,10 +18,13 @@ from __future__ import annotations import array -from typing import IO, Sequence +from typing import IO, TYPE_CHECKING, Sequence from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile +if TYPE_CHECKING: + from . import Image + class ImagePalette: """ @@ -128,7 +131,11 @@ def _new_color_index(self, image=None, e=None): raise ValueError(msg) from e return index - def getcolor(self, color, image=None) -> int: + def getcolor( + self, + color: tuple[int, int, int] | tuple[int, int, int, int], + image: Image.Image | None = None, + ) -> int: """Given an rgb tuple, allocate palette entry. .. warning:: This method is experimental. @@ -163,7 +170,7 @@ def getcolor(self, color, image=None) -> int: self.dirty = 1 return index else: - msg = f"unknown color specifier: {repr(color)}" + msg = f"unknown color specifier: {repr(color)}" # type: ignore[unreachable] raise ValueError(msg) def save(self, fp: str | IO[str]) -> None: From 84b284723259f2a7d1e079daea0670e0138c4980 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Jun 2024 07:15:47 +1000 Subject: [PATCH 232/300] Accept 't' suffix for libtiff version --- Tests/test_file_libtiff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 22bcd285622..fe9d017c086 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -54,7 +54,7 @@ class TestFileLibTiff(LibTiffTestCase): def test_version(self) -> None: version = features.version_codec("libtiff") assert version is not None - assert re.search(r"\d+\.\d+\.\d+$", version) + assert re.search(r"\d+\.\d+\.\d+t?$", version) def test_g4_tiff(self, tmp_path: Path) -> None: """Test the ordinary file path load path""" From 474ef6ff8d152effba530f364d7c3c5a0653358f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:01:02 +0000 Subject: [PATCH 233/300] Update dependency cibuildwheel to v2.19.0 --- .ci/requirements-cibw.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/requirements-cibw.txt b/.ci/requirements-cibw.txt index 7e257b75cf3..bf1d1315bfc 100644 --- a/.ci/requirements-cibw.txt +++ b/.ci/requirements-cibw.txt @@ -1 +1 @@ -cibuildwheel==2.18.1 +cibuildwheel==2.19.0 From 780d85b667f3201c02c1563ca7c09581f8871237 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Jun 2024 23:18:11 +1000 Subject: [PATCH 234/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index dc4016d76f6..d7231ebeabf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Accept 't' suffix for libtiff version #8126, #8129 + [radarhere] + +- Deprecate ImageDraw.getdraw hints parameter #8124 + [radarhere, hugovk] + - Added ImageDraw circle() #8085 [void4, hugovk, radarhere] From 1eb960f7e3e662b489c7cfc2b97b88e5317ffff5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Jun 2024 23:26:00 +1000 Subject: [PATCH 235/300] Added type hints --- src/PIL/BlpImagePlugin.py | 20 ++++++++-------- src/PIL/BmpImagePlugin.py | 3 ++- src/PIL/DdsImagePlugin.py | 3 ++- src/PIL/EpsImagePlugin.py | 45 ++++++++++++++++++------------------ src/PIL/FitsImagePlugin.py | 2 +- src/PIL/FpxImagePlugin.py | 2 +- src/PIL/ImageFile.py | 2 +- src/PIL/Jpeg2KImagePlugin.py | 15 ++++++------ src/PIL/MicImagePlugin.py | 2 +- src/PIL/MpoImagePlugin.py | 15 ++++-------- src/PIL/PSDraw.py | 2 +- src/PIL/PalmImagePlugin.py | 10 ++++---- src/PIL/PdfParser.py | 3 +-- src/PIL/PngImagePlugin.py | 2 +- src/PIL/TarIO.py | 8 +------ src/PIL/TiffImagePlugin.py | 29 +++++++++++------------ 16 files changed, 78 insertions(+), 85 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 003fa9b2479..59246c6e2e1 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -61,7 +61,9 @@ def unpack_565(i: int) -> tuple[int, int, int]: return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 -def decode_dxt1(data, alpha=False): +def decode_dxt1( + data: bytes, alpha: bool = False +) -> tuple[bytearray, bytearray, bytearray, bytearray]: """ input: one "row" of data (i.e. will produce 4*width pixels) """ @@ -69,9 +71,9 @@ def decode_dxt1(data, alpha=False): blocks = len(data) // 8 # number of blocks in row ret = (bytearray(), bytearray(), bytearray(), bytearray()) - for block in range(blocks): + for block_index in range(blocks): # Decode next 8-byte block. - idx = block * 8 + idx = block_index * 8 color0, color1, bits = struct.unpack_from(" tuple[bytearray, bytearray, bytearray, bytearray]: """ input: one "row" of data (i.e. will produce 4*width pixels) """ @@ -124,8 +126,8 @@ def decode_dxt3(data): blocks = len(data) // 16 # number of blocks in row ret = (bytearray(), bytearray(), bytearray(), bytearray()) - for block in range(blocks): - idx = block * 16 + for block_index in range(blocks): + idx = block_index * 16 block = data[idx : idx + 16] # Decode next 16-byte block. bits = struct.unpack_from("<8B", block) @@ -169,7 +171,7 @@ def decode_dxt3(data): return ret -def decode_dxt5(data): +def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: """ input: one "row" of data (i.e. will produce 4 * width pixels) """ @@ -177,8 +179,8 @@ def decode_dxt5(data): blocks = len(data) // 16 # number of blocks in row ret = (bytearray(), bytearray(), bytearray(), bytearray()) - for block in range(blocks): - idx = block * 16 + for block_index in range(blocks): + idx = block_index * 16 block = data[idx : idx + 16] # Decode next 16-byte block. a0, a1 = struct.unpack_from(" None: class BmpRleDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: + assert self.fd is not None rle4 = self.args[1] data = bytearray() x = 0 diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 861a1eca0cc..e7472700765 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -480,7 +480,8 @@ def load_seek(self, pos: int) -> None: class DdsRgbDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: + assert self.fd is not None bitcount, masks = self.args # Some masks will be padded with zeros, e.g. R 0b11 G 0b1100 diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index d24a2ba80f0..380b1cf0ec4 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -27,6 +27,7 @@ import subprocess import sys import tempfile +from typing import IO from . import Image, ImageFile from ._binary import i32le as i32 @@ -236,7 +237,7 @@ def check_required_header_comments() -> None: msg = 'EPS header missing "%%BoundingBox" comment' raise SyntaxError(msg) - def _read_comment(s): + def _read_comment(s: str) -> bool: nonlocal reading_trailer_comments try: m = split.match(s) @@ -244,27 +245,25 @@ def _read_comment(s): msg = "not an EPS file" raise SyntaxError(msg) from e - if m: - k, v = m.group(1, 2) - self.info[k] = v - if k == "BoundingBox": - if v == "(atend)": - reading_trailer_comments = True - elif not self._size or ( - trailer_reached and reading_trailer_comments - ): - try: - # Note: The DSC spec says that BoundingBox - # fields should be integers, but some drivers - # put floating point values there anyway. - box = [int(float(i)) for i in v.split()] - self._size = box[2] - box[0], box[3] - box[1] - self.tile = [ - ("eps", (0, 0) + self.size, offset, (length, box)) - ] - except Exception: - pass - return True + if not m: + return False + + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + if v == "(atend)": + reading_trailer_comments = True + elif not self._size or (trailer_reached and reading_trailer_comments): + try: + # Note: The DSC spec says that BoundingBox + # fields should be integers, but some drivers + # put floating point values there anyway. + box = [int(float(i)) for i in v.split()] + self._size = box[2] - box[0], box[3] - box[1] + self.tile = [("eps", (0, 0) + self.size, offset, (length, box))] + except Exception: + pass + return True while True: byte = self.fp.read(1) @@ -413,7 +412,7 @@ def load_seek(self, pos: int) -> None: # -------------------------------------------------------------------- -def _save(im, fp, filename, eps=1): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -> None: """EPS Writer for the Python Imaging Library.""" # make sure image data is available diff --git a/src/PIL/FitsImagePlugin.py b/src/PIL/FitsImagePlugin.py index 07191892506..a169b6083e9 100644 --- a/src/PIL/FitsImagePlugin.py +++ b/src/PIL/FitsImagePlugin.py @@ -122,7 +122,7 @@ def _parse_headers( class FitsGzipDecoder(ImageFile.PyDecoder): _pulls_fd = True - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: assert self.fd is not None value = gzip.decompress(self.fd.read()) diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index b3e6c6e362b..c1927bd26aa 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -241,7 +241,7 @@ def close(self) -> None: self.ole.close() super().close() - def __exit__(self, *args): + def __exit__(self, *args: object) -> None: self.ole.close() super().__exit__() diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 6bef681e979..5d67409ea45 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -487,7 +487,7 @@ def feed(self, data): def __enter__(self): return self - def __exit__(self, *args): + def __exit__(self, *args: object) -> None: self.close() def close(self): diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 72c2cb85e3c..60f3bff0acb 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -18,7 +18,7 @@ import io import os import struct -from typing import IO +from typing import IO, Tuple, cast from . import Image, ImageFile, ImagePalette, _binary @@ -59,7 +59,7 @@ def _read_bytes(self, num_bytes: int) -> bytes: self.remaining_in_box -= num_bytes return data - def read_fields(self, field_format): + def read_fields(self, field_format: str) -> tuple[int | bytes, ...]: size = struct.calcsize(field_format) data = self._read_bytes(size) return struct.unpack(field_format, data) @@ -82,9 +82,9 @@ def next_box_type(self) -> bytes: self.remaining_in_box = -1 # Read the length and type of the next box - lbox, tbox = self.read_fields(">I4s") + lbox, tbox = cast(Tuple[int, bytes], self.read_fields(">I4s")) if lbox == 1: - lbox = self.read_fields(">Q")[0] + lbox = cast(int, self.read_fields(">Q")[0]) hlen = 16 else: hlen = 8 @@ -127,12 +127,13 @@ def _parse_codestream(fp): return size, mode -def _res_to_dpi(num, denom, exp): +def _res_to_dpi(num: int, denom: int, exp: int) -> float | None: """Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution, calculated as (num / denom) * 10^exp and stored in dots per meter, to floating-point dots per inch.""" - if denom != 0: - return (254 * num * (10**exp)) / (10000 * denom) + if denom == 0: + return None + return (254 * num * (10**exp)) / (10000 * denom) def _parse_jp2_header(fp): diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 5aef94dfbff..ed2ea2849d0 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -93,7 +93,7 @@ def close(self) -> None: self.ole.close() super().close() - def __exit__(self, *args): + def __exit__(self, *args: object) -> None: self.__fp.close() self.ole.close() super().__exit__() diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 152e19e2365..f21570661f8 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -37,19 +37,14 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: JpegImagePlugin._save(im, fp, filename) -def _save_all(im, fp, filename): +def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: append_images = im.encoderinfo.get("append_images", []) - if not append_images: - try: - animated = im.is_animated - except AttributeError: - animated = False - if not animated: - _save(im, fp, filename) - return + if not append_images and not getattr(im, "is_animated", False): + _save(im, fp, filename) + return mpf_offset = 28 - offsets = [] + offsets: list[int] = [] for imSequence in itertools.chain([im], append_images): for im_frame in ImageSequence.Iterator(imSequence): if not offsets: diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index 4e2b9788ef3..673eae1d12c 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -138,7 +138,7 @@ def image( sx = x / im.size[0] sy = y / im.size[1] self.fp.write(b"%f %f scale\n" % (sx, sy)) - EpsImagePlugin._save(im, self.fp, None, 0) + EpsImagePlugin._save(im, self.fp, "", 0) self.fp.write(b"\ngrestore\n") diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index 85f9fe1bf61..fc83918b5b4 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -8,6 +8,8 @@ ## from __future__ import annotations +from typing import IO + from . import Image, ImageFile from ._binary import o8 from ._binary import o16be as o16b @@ -82,10 +84,10 @@ # so build a prototype image to be used for palette resampling -def build_prototype_image(): +def build_prototype_image() -> Image.Image: image = Image.new("L", (1, len(_Palm8BitColormapValues))) image.putdata(list(range(len(_Palm8BitColormapValues)))) - palettedata = () + palettedata: tuple[int, ...] = () for colormapValue in _Palm8BitColormapValues: palettedata += colormapValue palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) @@ -112,7 +114,7 @@ def build_prototype_image(): # (Internal) Image save plugin for the Palm format. -def _save(im, fp, filename): +def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: if im.mode == "P": # we assume this is a color Palm image with the standard colormap, # unless the "info" dict has a "custom-colormap" field @@ -141,7 +143,7 @@ def _save(im, fp, filename): raise OSError(msg) # we ignore the palette here - im.mode = "P" + im._mode = "P" rawmode = f"P;{bpp}" version = 1 diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 52e8358017a..9e22313475e 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -404,9 +404,8 @@ def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): def __enter__(self) -> PdfParser: return self - def __exit__(self, exc_type, exc_value, traceback): + def __exit__(self, *args: object) -> None: self.close() - return False # do not suppress exceptions def start_writing(self) -> None: self.close_buf() diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 9aaadb47d5e..ba95980653a 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -178,7 +178,7 @@ def read(self) -> tuple[bytes, int, int]: def __enter__(self) -> ChunkStream: return self - def __exit__(self, *args): + def __exit__(self, *args: object) -> None: self.close() def close(self) -> None: diff --git a/src/PIL/TarIO.py b/src/PIL/TarIO.py index 7470663b4a1..cba26d4b059 100644 --- a/src/PIL/TarIO.py +++ b/src/PIL/TarIO.py @@ -16,7 +16,6 @@ from __future__ import annotations import io -from types import TracebackType from . import ContainerIO @@ -61,12 +60,7 @@ def __init__(self, tarfile: str, file: str) -> None: def __enter__(self) -> TarIO: return self - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: + def __exit__(self, *args: object) -> None: self.close() def close(self) -> None: diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 08ee506b162..702d8f33b5b 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -717,7 +717,7 @@ def _setitem(self, tag, value, legacy_api): # Unspec'd, and length > 1 dest[tag] = values - def __delitem__(self, tag): + def __delitem__(self, tag: int) -> None: self._tags_v2.pop(tag, None) self._tags_v1.pop(tag, None) self._tagdata.pop(tag, None) @@ -1106,7 +1106,7 @@ def __init__(self, fp=None, filename=None): super().__init__(fp, filename) - def _open(self): + def _open(self) -> None: """Open the first image in a TIFF file""" # Header @@ -1123,8 +1123,8 @@ def _open(self): self.__first = self.__next = self.tag_v2.next self.__frame = -1 self._fp = self.fp - self._frame_pos = [] - self._n_frames = None + self._frame_pos: list[int] = [] + self._n_frames: int | None = None logger.debug("*** TiffImageFile._open ***") logger.debug("- __first: %s", self.__first) @@ -1998,10 +1998,9 @@ def newFrame(self) -> None: def __enter__(self) -> AppendingTiffWriter: return self - def __exit__(self, exc_type, exc_value, traceback): + def __exit__(self, *args: object) -> None: if self.close_fp: self.close() - return False def tell(self) -> int: return self.f.tell() - self.offsetOfNewPage @@ -2043,42 +2042,42 @@ def skipIFDs(self) -> None: def write(self, data): return self.f.write(data) - def readShort(self): + def readShort(self) -> int: (value,) = struct.unpack(self.shortFmt, self.f.read(2)) return value - def readLong(self): + def readLong(self) -> int: (value,) = struct.unpack(self.longFmt, self.f.read(4)) return value - def rewriteLastShortToLong(self, value): + def rewriteLastShortToLong(self, value: int) -> None: self.f.seek(-2, os.SEEK_CUR) bytes_written = self.f.write(struct.pack(self.longFmt, value)) if bytes_written is not None and bytes_written != 4: msg = f"wrote only {bytes_written} bytes but wanted 4" raise RuntimeError(msg) - def rewriteLastShort(self, value): + def rewriteLastShort(self, value: int) -> None: self.f.seek(-2, os.SEEK_CUR) bytes_written = self.f.write(struct.pack(self.shortFmt, value)) if bytes_written is not None and bytes_written != 2: msg = f"wrote only {bytes_written} bytes but wanted 2" raise RuntimeError(msg) - def rewriteLastLong(self, value): + def rewriteLastLong(self, value: int) -> None: self.f.seek(-4, os.SEEK_CUR) bytes_written = self.f.write(struct.pack(self.longFmt, value)) if bytes_written is not None and bytes_written != 4: msg = f"wrote only {bytes_written} bytes but wanted 4" raise RuntimeError(msg) - def writeShort(self, value): + def writeShort(self, value: int) -> None: bytes_written = self.f.write(struct.pack(self.shortFmt, value)) if bytes_written is not None and bytes_written != 2: msg = f"wrote only {bytes_written} bytes but wanted 2" raise RuntimeError(msg) - def writeLong(self, value): + def writeLong(self, value: int) -> None: bytes_written = self.f.write(struct.pack(self.longFmt, value)) if bytes_written is not None and bytes_written != 4: msg = f"wrote only {bytes_written} bytes but wanted 4" @@ -2097,9 +2096,9 @@ def fixIFD(self) -> None: field_size = self.fieldSizes[field_type] total_size = field_size * count is_local = total_size <= 4 + offset: int | None if not is_local: - offset = self.readLong() - offset += self.offsetOfNewPage + offset = self.readLong() + self.offsetOfNewPage self.rewriteLastLong(offset) if tag in self.Tags: From 2e15dc9f534be4411e4eddbf3d85b2034384b6a3 Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Tue, 11 Jun 2024 22:00:08 -0400 Subject: [PATCH 236/300] Improve xdg directory support in Linux --- src/PIL/ImageFont.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index fa5608e6cd8..163bb700678 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -837,12 +837,19 @@ def freetype(font: StrOrBytesPath | BinaryIO | None) -> FreeTypeFont: if windir: dirs.append(os.path.join(windir, "fonts")) elif sys.platform in ("linux", "linux2"): - lindirs = os.environ.get("XDG_DATA_DIRS") - if not lindirs: - # According to the freedesktop spec, XDG_DATA_DIRS should - # default to /usr/share - lindirs = "/usr/share" - dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] + data_home = os.environ.get("XDG_DATA_HOME") + if not data_home: + # The freedesktop spec defines the following default directory for + # when XDG_DATA_HOME is unset or empty. This user-level directory + # takes precedence over system-level directories. + data_home = os.path.expanduser("~/.local/share") + dirs.append(os.path.join(data_home, "fonts")) + + data_dirs = os.environ.get("XDG_DATA_DIRS") + if not data_dirs: + # Similarly, defaults are defined for the system-level directories + data_dirs = "/usr/local/share:/usr/share" + dirs += [os.path.join(ddir, "fonts") for ddir in data_dirs.split(":")] elif sys.platform == "darwin": dirs += [ "/Library/Fonts", From 1175e53d53ccd1fa86a94b2f8d2365dbc0a3336a Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Tue, 11 Jun 2024 22:08:53 -0400 Subject: [PATCH 237/300] Set XDG_DATA_HOME on font tests --- Tests/test_imagefont.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 4398f8a3055..3cba726d64e 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -564,6 +564,7 @@ def loadable_font( # catching syntax like errors monkeypatch.setattr(sys, "platform", platform) if platform == "linux": + monkeypatch.setenv("XDG_DATA_HOME", "/home/__pillow__/.local/share") monkeypatch.setenv("XDG_DATA_DIRS", "/usr/share/:/usr/local/share/") def fake_walker(path: str) -> list[tuple[str, list[str], list[str]]]: From be73b13ad33a88a17e8055e74993d02c5d3f68a0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 12 Jun 2024 21:15:55 +1000 Subject: [PATCH 238/300] Added type hints --- src/PIL/BdfFontFile.py | 2 +- src/PIL/ImageDraw.py | 20 ++++++------ src/PIL/ImagePalette.py | 12 +++++--- src/PIL/MicImagePlugin.py | 4 +-- src/PIL/PngImagePlugin.py | 63 +++++++++++++++++++++++++------------- src/PIL/TiffImagePlugin.py | 31 +++++++++++-------- src/PIL/_imaging.pyi | 2 +- src/PIL/features.py | 18 ++++++----- 8 files changed, 95 insertions(+), 57 deletions(-) diff --git a/src/PIL/BdfFontFile.py b/src/PIL/BdfFontFile.py index e3eda4fe98c..bc1416c74c6 100644 --- a/src/PIL/BdfFontFile.py +++ b/src/PIL/BdfFontFile.py @@ -103,7 +103,7 @@ def bdf_char( class BdfFontFile(FontFile.FontFile): """Font file plugin for the X11 BDF format.""" - def __init__(self, fp: BinaryIO): + def __init__(self, fp: BinaryIO) -> None: super().__init__() s = fp.readline() diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index e74fab9fb3d..41a3eb0cb46 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -34,12 +34,16 @@ import math import numbers import struct +from types import ModuleType from typing import TYPE_CHECKING, AnyStr, Sequence, cast from . import Image, ImageColor from ._deprecate import deprecate from ._typing import Coords +if TYPE_CHECKING: + from . import ImageDraw2, ImageFont + """ A simple 2D drawing interface for PIL images.

@@ -93,9 +97,6 @@ def __init__(self, im: Image.Image, mode: str | None = None) -> None: self.fontmode = "L" # aliasing is okay for other modes self.fill = False - if TYPE_CHECKING: - from . import ImageFont - def getfont( self, ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: @@ -879,7 +880,7 @@ def multiline_textbbox( return bbox -def Draw(im, mode: str | None = None) -> ImageDraw: +def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw: """ A simple 2D drawing interface for PIL images. @@ -891,7 +892,7 @@ def Draw(im, mode: str | None = None) -> ImageDraw: defaults to the mode of the image. """ try: - return im.getdraw(mode) + return getattr(im, "getdraw")(mode) except AttributeError: return ImageDraw(im, mode) @@ -903,7 +904,9 @@ def Draw(im, mode: str | None = None) -> ImageDraw: Outline = None -def getdraw(im=None, hints=None): +def getdraw( + im: Image.Image | None = None, hints: list[str] | None = None +) -> tuple[ImageDraw2.Draw | None, ModuleType]: """ :param im: The image to draw in. :param hints: An optional list of hints. Deprecated. @@ -913,9 +916,8 @@ def getdraw(im=None, hints=None): deprecate("'hints' parameter", 12) from . import ImageDraw2 - if im: - im = ImageDraw2.Draw(im) - return im, ImageDraw2 + draw = ImageDraw2.Draw(im) if im is not None else None + return draw, ImageDraw2 def floodfill( diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 1ff05a3eff1..6473c4577b0 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -54,7 +54,7 @@ def palette(self, palette): self._palette = palette @property - def colors(self): + def colors(self) -> dict[tuple[int, int, int] | tuple[int, int, int, int], int]: if self._colors is None: mode_len = len(self.mode) self._colors = {} @@ -66,7 +66,9 @@ def colors(self): return self._colors @colors.setter - def colors(self, colors): + def colors( + self, colors: dict[tuple[int, int, int] | tuple[int, int, int, int], int] + ) -> None: self._colors = colors def copy(self) -> ImagePalette: @@ -107,11 +109,13 @@ def tobytes(self) -> bytes: # Declare tostring as an alias for tobytes tostring = tobytes - def _new_color_index(self, image=None, e=None): + def _new_color_index( + self, image: Image.Image | None = None, e: Exception | None = None + ) -> int: if not isinstance(self.palette, bytearray): self._palette = bytearray(self.palette) index = len(self.palette) // 3 - special_colors = () + special_colors: tuple[int | tuple[int, ...] | None, ...] = () if image: special_colors = ( image.info.get("background"), diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index ed2ea2849d0..07239887f9f 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -63,7 +63,7 @@ def _open(self) -> None: msg = "not an MIC file; no image entries" raise SyntaxError(msg) - self.frame = None + self.frame = -1 self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 @@ -85,7 +85,7 @@ def seek(self, frame): self.frame = frame - def tell(self): + def tell(self) -> int: return self.frame def close(self) -> None: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index ba95980653a..927d6c0cfbd 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -39,7 +39,7 @@ import warnings import zlib from enum import IntEnum -from typing import IO, Any +from typing import IO, TYPE_CHECKING, Any, NoReturn from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence from ._binary import i16be as i16 @@ -48,6 +48,9 @@ from ._binary import o16be as o16 from ._binary import o32be as o32 +if TYPE_CHECKING: + from . import _imaging + logger = logging.getLogger(__name__) is_cid = re.compile(rb"\w\w\w\w").match @@ -249,6 +252,9 @@ class iTXt(str): """ + lang: str | bytes | None + tkey: str | bytes | None + @staticmethod def __new__(cls, text, lang=None, tkey=None): """ @@ -270,10 +276,10 @@ class PngInfo: """ - def __init__(self): - self.chunks = [] + def __init__(self) -> None: + self.chunks: list[tuple[bytes, bytes, bool]] = [] - def add(self, cid, data, after_idat=False): + def add(self, cid: bytes, data: bytes, after_idat: bool = False) -> None: """Appends an arbitrary chunk. Use with caution. :param cid: a byte string, 4 bytes long. @@ -283,12 +289,16 @@ def add(self, cid, data, after_idat=False): """ - chunk = [cid, data] - if after_idat: - chunk.append(True) - self.chunks.append(tuple(chunk)) + self.chunks.append((cid, data, after_idat)) - def add_itxt(self, key, value, lang="", tkey="", zip=False): + def add_itxt( + self, + key: str | bytes, + value: str | bytes, + lang: str | bytes = "", + tkey: str | bytes = "", + zip: bool = False, + ) -> None: """Appends an iTXt chunk. :param key: latin-1 encodable text key name @@ -316,7 +326,9 @@ def add_itxt(self, key, value, lang="", tkey="", zip=False): else: self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value) - def add_text(self, key, value, zip=False): + def add_text( + self, key: str | bytes, value: str | bytes | iTXt, zip: bool = False + ) -> None: """Appends a text chunk. :param key: latin-1 encodable text key name @@ -326,7 +338,13 @@ def add_text(self, key, value, zip=False): """ if isinstance(value, iTXt): - return self.add_itxt(key, value, value.lang, value.tkey, zip=zip) + return self.add_itxt( + key, + value, + value.lang if value.lang is not None else b"", + value.tkey if value.tkey is not None else b"", + zip=zip, + ) # The tEXt chunk stores latin-1 text if not isinstance(value, bytes): @@ -434,7 +452,7 @@ def chunk_IHDR(self, pos: int, length: int) -> bytes: raise SyntaxError(msg) return s - def chunk_IDAT(self, pos, length): + def chunk_IDAT(self, pos: int, length: int) -> NoReturn: # image data if "bbox" in self.im_info: tile = [("zip", self.im_info["bbox"], pos, self.im_rawmode)] @@ -447,7 +465,7 @@ def chunk_IDAT(self, pos, length): msg = "image data found" raise EOFError(msg) - def chunk_IEND(self, pos, length): + def chunk_IEND(self, pos: int, length: int) -> NoReturn: msg = "end of PNG image" raise EOFError(msg) @@ -821,7 +839,10 @@ def seek(self, frame: int) -> None: msg = "no more images in APNG file" raise EOFError(msg) from e - def _seek(self, frame, rewind=False): + def _seek(self, frame: int, rewind: bool = False) -> None: + assert self.png is not None + + self.dispose: _imaging.ImagingCore | None if frame == 0: if rewind: self._fp.seek(self.__rewind) @@ -906,14 +927,14 @@ def _seek(self, frame, rewind=False): if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: self.dispose_op = Disposal.OP_BACKGROUND + self.dispose = None if self.dispose_op == Disposal.OP_PREVIOUS: - self.dispose = self._prev_im.copy() - self.dispose = self._crop(self.dispose, self.dispose_extent) + if self._prev_im: + self.dispose = self._prev_im.copy() + self.dispose = self._crop(self.dispose, self.dispose_extent) elif self.dispose_op == Disposal.OP_BACKGROUND: self.dispose = Image.core.fill(self.mode, self.size) self.dispose = self._crop(self.dispose, self.dispose_extent) - else: - self.dispose = None def tell(self) -> int: return self.__frame @@ -1026,7 +1047,7 @@ def _getexif(self) -> dict[str, Any] | None: return None return self.getexif()._get_merged_dict() - def getexif(self): + def getexif(self) -> Image.Exif: if "exif" not in self.info: self.load() @@ -1346,7 +1367,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): chunk(fp, cid, data) elif cid[1:2].islower(): # Private chunk - after_idat = info_chunk[2:3] + after_idat = len(info_chunk) == 3 and info_chunk[2] if not after_idat: chunk(fp, cid, data) @@ -1425,7 +1446,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): cid, data = info_chunk[:2] if cid[1:2].islower(): # Private chunk - after_idat = info_chunk[2:3] + after_idat = len(info_chunk) == 3 and info_chunk[2] if after_idat: chunk(fp, cid, data) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 702d8f33b5b..833e12d2b37 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -50,7 +50,7 @@ from collections.abc import MutableMapping from fractions import Fraction from numbers import Number, Rational -from typing import IO, TYPE_CHECKING, Any, Callable +from typing import IO, TYPE_CHECKING, Any, Callable, NoReturn from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags from ._binary import i16be as i16 @@ -384,7 +384,7 @@ def limit_rational(self, max_denominator): def __repr__(self) -> str: return str(float(self._val)) - def __hash__(self): + def __hash__(self) -> int: return self._val.__hash__() def __eq__(self, other: object) -> bool: @@ -551,7 +551,12 @@ class ImageFileDirectory_v2(_IFDv2Base): _load_dispatch: dict[int, Callable[[ImageFileDirectory_v2, bytes, bool], Any]] = {} _write_dispatch: dict[int, Callable[..., Any]] = {} - def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None): + def __init__( + self, + ifh: bytes = b"II\052\0\0\0\0\0", + prefix: bytes | None = None, + group: int | None = None, + ) -> None: """Initialize an ImageFileDirectory. To construct an ImageFileDirectory from a real file, pass the 8-byte @@ -575,7 +580,7 @@ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None): raise SyntaxError(msg) self._bigtiff = ifh[2] == 43 self.group = group - self.tagtype = {} + self.tagtype: dict[int, int] = {} """ Dictionary of tag types """ self.reset() (self.next,) = ( @@ -587,18 +592,18 @@ def __init__(self, ifh=b"II\052\0\0\0\0\0", prefix=None, group=None): offset = property(lambda self: self._offset) @property - def legacy_api(self): + def legacy_api(self) -> bool: return self._legacy_api @legacy_api.setter - def legacy_api(self, value): + def legacy_api(self, value: bool) -> NoReturn: msg = "Not allowing setting of legacy api" raise Exception(msg) - def reset(self): - self._tags_v1 = {} # will remain empty if legacy_api is false - self._tags_v2 = {} # main tag storage - self._tagdata = {} + def reset(self) -> None: + self._tags_v1: dict[int, Any] = {} # will remain empty if legacy_api is false + self._tags_v2: dict[int, Any] = {} # main tag storage + self._tagdata: dict[int, bytes] = {} self.tagtype = {} # added 2008-06-05 by Florian Hoech self._next = None self._offset = None @@ -2039,7 +2044,7 @@ def skipIFDs(self) -> None: num_tags = self.readShort() self.f.seek(num_tags * 12, os.SEEK_CUR) - def write(self, data): + def write(self, data: bytes) -> int | None: return self.f.write(data) def readShort(self) -> int: @@ -2122,7 +2127,9 @@ def fixIFD(self) -> None: # skip the locally stored value that is not an offset self.f.seek(4, os.SEEK_CUR) - def fixOffsets(self, count, isShort=False, isLong=False): + def fixOffsets( + self, count: int, isShort: bool = False, isLong: bool = False + ) -> None: if not isShort and not isLong: msg = "offset is neither short nor long" raise RuntimeError(msg) diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index 1fe95441715..b233eb34d8b 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -12,5 +12,5 @@ class ImagingDraw: class PixelAccess: def __getattr__(self, name: str) -> Any: ... -def font(image, glyphdata: bytes) -> ImagingFont: ... +def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ... def __getattr__(name: str) -> Any: ... diff --git a/src/PIL/features.py b/src/PIL/features.py index 16c749f148b..13908c4eb78 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -4,6 +4,7 @@ import os import sys import warnings +from typing import IO import PIL @@ -223,7 +224,7 @@ def get_supported() -> list[str]: return ret -def pilinfo(out=None, supported_formats=True): +def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None: """ Prints information about this installation of Pillow. This function can be called with ``python3 -m PIL``. @@ -244,9 +245,9 @@ def pilinfo(out=None, supported_formats=True): print("-" * 68, file=out) print(f"Pillow {PIL.__version__}", file=out) - py_version = sys.version.splitlines() - print(f"Python {py_version[0].strip()}", file=out) - for py_version in py_version[1:]: + py_version_lines = sys.version.splitlines() + print(f"Python {py_version_lines[0].strip()}", file=out) + for py_version in py_version_lines[1:]: print(f" {py_version.strip()}", file=out) print("-" * 68, file=out) print(f"Python executable is {sys.executable or 'unknown'}", file=out) @@ -282,9 +283,12 @@ def pilinfo(out=None, supported_formats=True): ("xcb", "XCB (X protocol)"), ]: if check(name): - if name == "jpg" and check_feature("libjpeg_turbo"): - v = "libjpeg-turbo " + version_feature("libjpeg_turbo") - else: + v: str | None = None + if name == "jpg": + libjpeg_turbo_version = version_feature("libjpeg_turbo") + if libjpeg_turbo_version is not None: + v = "libjpeg-turbo " + libjpeg_turbo_version + if v is None: v = version(name) if v is not None: version_static = name in ("pil", "jpg") From eea3ac765c2793f1931ca5696555f497011e0b0e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 12 Jun 2024 22:44:03 +1000 Subject: [PATCH 239/300] Deprecate non-image and unsupported modes --- Tests/test_imagecms.py | 9 ++++++++- docs/deprecations.rst | 9 +++++++++ docs/releasenotes/10.4.0.rst | 7 +++++++ src/PIL/ImageCms.py | 25 +++++++++++++++++++++++++ src/_imagingcms.c | 10 ++++++---- 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 55f72c3b95c..dbbd46dcf76 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -678,7 +678,8 @@ def test_auxiliary_channels_isolated() -> None: def test_long_modes() -> None: p = ImageCms.getOpenProfile("Tests/icc/sGrey-v2-nano.icc") - ImageCms.buildTransform(p, p, "ABCDEFGHI", "ABCDEFGHI") + with pytest.warns(DeprecationWarning): + ImageCms.buildTransform(p, p, "ABCDEFGHI", "ABCDEFGHI") @pytest.mark.parametrize("mode", ("RGB", "RGBA", "RGBX")) @@ -699,3 +700,9 @@ def test_deprecation() -> None: assert ImageCms.VERSION == "1.0.0 pil" with pytest.warns(DeprecationWarning): assert isinstance(ImageCms.FLAGS, dict) + + profile = ImageCmsProfile(ImageCms.createProfile("sRGB")) + with pytest.warns(DeprecationWarning): + ImageCms.ImageCmsTransform(profile, profile, "RGBA;16B", "RGB") + with pytest.warns(DeprecationWarning): + ImageCms.ImageCmsTransform(profile, profile, "RGB", "RGBA;16B") diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 627672e1f3b..e6566eae297 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -107,6 +107,15 @@ BGR;15, BGR 16 and BGR;24 The experimental BGR;15, BGR;16 and BGR;24 modes have been deprecated. +Non-image modes in ImageCms +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 10.4.0 + +The use in :py:mod:`.ImageCms` of input modes and output modes that are not Pillow +image modes has been deprecated. Defaulting to "L" or "1" if the mode cannot be mapped +is also deprecated. + Support for LibTIFF earlier than 4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 44727efd41f..96300c008e8 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -28,6 +28,13 @@ BGR;15, BGR 16 and BGR;24 The experimental BGR;15, BGR;16 and BGR;24 modes have been deprecated. +Non-image modes in ImageCms +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The use in :py:mod:`.ImageCms` of input modes and output modes that are not Pillow +image modes has been deprecated. Defaulting to "L" or "1" if the mode cannot be mapped +is also deprecated. + Support for LibTIFF earlier than 4 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 19a79facc24..ec10230f12b 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -299,6 +299,31 @@ def __init__( proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC, flags: Flags = Flags.NONE, ): + supported_modes = ( + "RGB", + "RGBA", + "RGBX", + "CMYK", + "I;16", + "I;16L", + "I;16B", + "YCbCr", + "LAB", + "L", + "1", + ) + for mode in (input_mode, output_mode): + if mode not in supported_modes: + deprecate( + mode, + 12, + { + "L;16": "I;16 or I;16L", + "L:16B": "I;16B", + "YCCA": "YCbCr", + "YCC": "YCbCr", + }.get(mode), + ) if proof is None: self.transform = core.buildTransform( input.profile, output.profile, input_mode, output_mode, intent, flags diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 2b9612db73d..590e1b983a9 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -223,20 +223,22 @@ findLCMStype(char *PILmode) { if (strcmp(PILmode, "CMYK") == 0) { return TYPE_CMYK_8; } - if (strcmp(PILmode, "L;16") == 0) { + if (strcmp(PILmode, "I;16") == 0 || strcmp(PILmode, "I;16L") == 0 || + strcmp(PILmode, "L;16") == 0) { return TYPE_GRAY_16; } - if (strcmp(PILmode, "L;16B") == 0) { + if (strcmp(PILmode, "I;16B") == 0 || strcmp(PILmode, "L;16B") == 0) { return TYPE_GRAY_16_SE; } - if (strcmp(PILmode, "YCCA") == 0 || strcmp(PILmode, "YCC") == 0) { + if (strcmp(PILmode, "YCbCr") == 0 || strcmp(PILmode, "YCCA") == 0 || + strcmp(PILmode, "YCC") == 0) { return TYPE_YCbCr_8; } if (strcmp(PILmode, "LAB") == 0) { // LabX equivalent like ALab, but not reversed -- no #define in lcms2 return (COLORSPACE_SH(PT_LabV2) | CHANNELS_SH(3) | BYTES_SH(1) | EXTRA_SH(1)); } - /* presume "L" by default */ + /* presume "1" or "L" by default */ return TYPE_GRAY_8; } From c9a9e81749c12fdc4c7187284fe661810c9bc5c4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 13 Jun 2024 00:03:16 +1000 Subject: [PATCH 240/300] Use latest Ubuntu --- .readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index b83ba05b128..def6282dd56 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,7 +3,7 @@ version: 2 formats: [pdf] build: - os: ubuntu-22.04 + os: ubuntu-lts-latest tools: python: "3" jobs: From 00161099c7df3aa76bcbd7106aa144072b45a592 Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:36:14 -0400 Subject: [PATCH 241/300] Update docs for ImageFont.truetype [ci skip] --- src/PIL/ImageFont.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 163bb700678..235a76325c1 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -775,10 +775,15 @@ def truetype( :param font: A filename or file-like object containing a TrueType font. If the file is not found in this filename, the loader may also - search in other directories, such as the :file:`fonts/` - directory on Windows or :file:`/Library/Fonts/`, - :file:`/System/Library/Fonts/` and :file:`~/Library/Fonts/` on - macOS. + search in other directories, such as: + + * The :file:`fonts/` directory on Windows, + * :file:`/Library/Fonts/`, :file:`/System/Library/Fonts/` + and :file:`~/Library/Fonts/` on macOS. + * :file:`~/.local/share/fonts`, :file:`/usr/local/share/fonts`, + and :file:`/usr/share/fonts` on Linux; or those specified by + the ``XDG_DATA_HOME`` and ``XDG_DATA_DIRS`` environment variables + for user-installed and system-wide fonts, respectively. :param size: The requested size, in pixels. :param index: Which font face to load (default is first available face). From ed828d23df58f8e59f69933b74db12d7fba87c8c Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:08:48 -0400 Subject: [PATCH 242/300] Update changes in release notes [ci skip] --- docs/releasenotes/10.4.0.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 44727efd41f..0a54cc7a39a 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -42,6 +42,14 @@ The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecat API Changes =========== +ImageFont.truetype +^^^^^^^^^^^^^^^^^^ + +:py:function:`~PIL.ImageFont.truetype` now searches for fonts in the directory +indicated by the ``XDG_DATA_HOME`` in addition to those specified in ``XDG_DATA_DIRS``. +Additionally, the default for ``XDG_DATA_DIRS`` has been updated to match the +freedesktop spec. + TODO ^^^^ From 7e14364cee9e5f78d0bb658dbf470a4e7e580e34 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 1 May 2024 22:03:33 +1000 Subject: [PATCH 243/300] putalpha does not allow other color values --- src/PIL/Image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index da61ef4dd13..5416524c015 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1933,8 +1933,7 @@ def putalpha(self, alpha: Image | int) -> None: The new layer must be either "L" or "1". :param alpha: The new alpha layer. This can either be an "L" or "1" - image having the same size as this image, or an integer or - other color value. + image having the same size as this image, or an integer. """ self._ensure_mutable() From eb56f3ed56c9f72e5579943f9c87a2fcc9846587 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 May 2024 23:20:13 +1000 Subject: [PATCH 244/300] Removed ignores --- src/PIL/Image.py | 5 ++--- src/PIL/ImageGrab.py | 5 +++-- src/PIL/PyAccess.py | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 5416524c015..faf70b718a9 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2074,9 +2074,8 @@ def putpixel(self, xy: tuple[int, int], value: float | tuple[int, ...]) -> None: if self.mode == "PA": alpha = value[3] if len(value) == 4 else 255 value = value[:3] - value = self.palette.getcolor(value, self) - if self.mode == "PA": - value = (value, alpha) # type: ignore[assignment] + palette_index = self.palette.getcolor(value, self) + value = (palette_index, alpha) if self.mode == "PA" else palette_index return self.im.putpixel(xy, value) def remap_palette(self, dest_map, source_palette=None): diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index f16e9554433..96a28bb3570 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -33,6 +33,7 @@ def grab( all_screens: bool = False, xdisplay: str | None = None, ) -> Image.Image: + im: Image.Image if xdisplay is None: if sys.platform == "darwin": fh, filepath = tempfile.mkstemp(".png") @@ -42,7 +43,7 @@ def grab( left, top, right, bottom = bbox args += ["-R", f"{left},{top},{right-left},{bottom-top}"] subprocess.call(args + ["-x", filepath]) - im: Image.Image = Image.open(filepath) + im = Image.open(filepath) im.load() os.unlink(filepath) if bbox: @@ -84,7 +85,7 @@ def grab( fh, filepath = tempfile.mkstemp(".png") os.close(fh) subprocess.call(["gnome-screenshot", "-f", filepath]) - im: Image.Image = Image.open(filepath) # type: ignore[no-redef, unused-ignore] + im = Image.open(filepath) im.load() os.unlink(filepath) if bbox: diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index cc8e2e35958..5663a7e48ae 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -107,9 +107,8 @@ def __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> No if self._im.mode == "PA": alpha = color[3] if len(color) == 4 else 255 color = color[:3] - color = self._palette.getcolor(color, self._img) - if self._im.mode == "PA": - color = (color, alpha) # type: ignore[assignment] + palette_index = self._palette.getcolor(color, self._img) + color = (palette_index, alpha) if self._im.mode == "PA" else palette_index return self.set_pixel(x, y, color) From 2a2033eea1bd36637645f211239033bb7fad482d Mon Sep 17 00:00:00 2001 From: Nulano Date: Wed, 12 Jun 2024 21:35:22 +0200 Subject: [PATCH 245/300] mypy fixes after merge --- src/PIL/Image.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 76ea48510a3..6dee3fe2397 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1128,7 +1128,10 @@ def convert_transparency(m, v): del new_im.info["transparency"] if trns is not None: try: - new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im) + new_im.info["transparency"] = new_im.palette.getcolor( + cast(Tuple[int, int, int], trns), # trns was converted to RGB + new_im, + ) except Exception: # if we can't make a transparent color, don't leave the old # transparency hanging around to mess us up. @@ -1178,7 +1181,10 @@ def convert_transparency(m, v): if trns is not None: if new_im.mode == "P": try: - new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im) + new_im.info["transparency"] = new_im.palette.getcolor( + cast(Tuple[int, int, int], trns), # trns was converted to RGB + new_im, + ) except ValueError as e: del new_im.info["transparency"] if str(e) != "cannot allocate more than 256 colors": From 238110303c7bf829c0e56e5f721bcd80ad715699 Mon Sep 17 00:00:00 2001 From: Nulano Date: Wed, 12 Jun 2024 22:17:10 +0200 Subject: [PATCH 246/300] getpixel and putpixel also support a list argument --- src/PIL/Image.py | 6 ++++-- src/PIL/PyAccess.py | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6dee3fe2397..9c60bbb7a35 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1676,7 +1676,7 @@ def apply_transparency(self) -> None: del self.info["transparency"] def getpixel( - self, xy: tuple[SupportsInt, SupportsInt] + self, xy: tuple[int, int] | list[int] ) -> float | tuple[int, ...] | None: """ Returns the pixel value at a given position. @@ -2058,7 +2058,9 @@ def putpalette(self, data, rawmode="RGB") -> None: self.palette.mode = "RGB" self.load() # install new palette - def putpixel(self, xy: tuple[int, int], value: float | tuple[int, ...]) -> None: + def putpixel( + self, xy: tuple[int, int], value: float | tuple[int, ...] | list[int] + ) -> None: """ Modifies the pixel at the given position. The color is given as a single numerical value for single-band images, and a tuple for diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 5663a7e48ae..d41f00aea49 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -77,7 +77,11 @@ def __init__(self, img: Image.Image, readonly: bool = False) -> None: def _post_init(self) -> None: pass - def __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> None: + def __setitem__( + self, + xy: tuple[int, int] | list[int], + color: float | tuple[int, ...] | list[int], + ) -> None: """ Modifies the pixel at x,y. The color is given as a single numerical value for single band images, and a tuple for @@ -112,7 +116,7 @@ def __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> No return self.set_pixel(x, y, color) - def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: + def __getitem__(self, xy: tuple[int, int] | list[int]) -> float | tuple[int, ...]: """ Returns the pixel at x,y. The pixel is returned as a single value for single band images or a tuple for multiple band From 0a2baab6c1744a42422713858bc2bc2872805794 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 20:19:17 +0000 Subject: [PATCH 247/300] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/Image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9c60bbb7a35..5619915ea78 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -48,7 +48,6 @@ Literal, Protocol, Sequence, - SupportsInt, Tuple, cast, ) From 6cf08afb1587bfb148334907f91ae7dbe4d8a41b Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Thu, 13 Jun 2024 00:03:03 -0400 Subject: [PATCH 248/300] Fix broken reference in release notes [ci skip] Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/releasenotes/10.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 0a54cc7a39a..36c2758f9bb 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -45,7 +45,7 @@ API Changes ImageFont.truetype ^^^^^^^^^^^^^^^^^^ -:py:function:`~PIL.ImageFont.truetype` now searches for fonts in the directory +:py:func:`~PIL.ImageFont.truetype` now searches for fonts in the directory indicated by the ``XDG_DATA_HOME`` in addition to those specified in ``XDG_DATA_DIRS``. Additionally, the default for ``XDG_DATA_DIRS`` has been updated to match the freedesktop spec. From 48606afeb6e69483c329535106383c3dd79a323c Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Thu, 13 Jun 2024 00:12:07 -0400 Subject: [PATCH 249/300] Add missing wording for envvar in release notes [ci skip] --- docs/releasenotes/10.4.0.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 36c2758f9bb..00506a094ae 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -45,10 +45,10 @@ API Changes ImageFont.truetype ^^^^^^^^^^^^^^^^^^ -:py:func:`~PIL.ImageFont.truetype` now searches for fonts in the directory -indicated by the ``XDG_DATA_HOME`` in addition to those specified in ``XDG_DATA_DIRS``. -Additionally, the default for ``XDG_DATA_DIRS`` has been updated to match the -freedesktop spec. +:py:func:`~PIL.ImageFont.truetype` now searches for fonts in the directory indicated +by the ``XDG_DATA_HOME`` envirnorment variable in addition to those specified in +``XDG_DATA_DIRS``. Additionally, the default for ``XDG_DATA_DIRS`` has been updated +to match the freedesktop spec. TODO ^^^^ From d56ffebf2a18ffd94043300418bcb98819a6a776 Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:43:40 -0400 Subject: [PATCH 250/300] Fix typo in release notes [ci skip] Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/releasenotes/10.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 00506a094ae..1678b460c37 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -46,7 +46,7 @@ ImageFont.truetype ^^^^^^^^^^^^^^^^^^ :py:func:`~PIL.ImageFont.truetype` now searches for fonts in the directory indicated -by the ``XDG_DATA_HOME`` envirnorment variable in addition to those specified in +by the ``XDG_DATA_HOME`` environment variable in addition to those specified in ``XDG_DATA_DIRS``. Additionally, the default for ``XDG_DATA_DIRS`` has been updated to match the freedesktop spec. From 20ce7ad9f8c23bf6b02fd0a68cd39b20f12b58b7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 13 Jun 2024 19:36:32 +1000 Subject: [PATCH 251/300] Updated type hint --- src/PIL/PyAccess.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index d41f00aea49..3be1ccace0c 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -148,7 +148,9 @@ def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]: def get_pixel(self, x: int, y: int) -> float | tuple[int, ...]: raise NotImplementedError() - def set_pixel(self, x: int, y: int, color: float | tuple[int, ...]) -> None: + def set_pixel( + self, x: int, y: int, color: float | tuple[int, ...] | list[int] + ) -> None: raise NotImplementedError() From 05a70e7861f37d2eb65be0186345ea8ea62fc3fe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 14 Jun 2024 20:59:12 +1000 Subject: [PATCH 252/300] Corrected Ghostscript path --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 92a04a02154..6ce5200b6ea 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -35,7 +35,7 @@ install: - curl -fsSL -o nasm-win64.zip https://raw.githubusercontent.com/python-pillow/pillow-depends/main/nasm-2.16.03-win64.zip - 7z x nasm-win64.zip -oc:\ - choco install ghostscript --version=10.3.1 -- path c:\nasm-2.16.03;C:\Program Files\gs\gs10.00.0\bin;%PATH% +- path c:\nasm-2.16.03;C:\Program Files\gs\gs10.03.1\bin;%PATH% - cd c:\pillow\winbuild\ - ps: | c:\python38\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\ From dfd53564ff6a3fc7d35a5884bc0ef03939bcec0a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jun 2024 11:51:02 +1000 Subject: [PATCH 253/300] Ignore brew dependencies for libraqm on macOS 13 --- .github/workflows/macos-install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index 28124d7f759..f8f191d387a 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -7,11 +7,15 @@ brew install \ ghostscript \ libimagequant \ libjpeg \ - libraqm \ libtiff \ little-cms2 \ openjpeg \ webp +if [[ "$ImageOS" == "macos13" ]]; then + brew install --ignore-dependencies libraqm +else + brew install libraqm +fi export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig" # TODO Update condition when cffi supports 3.13 From b3e3784b8ebf4fc8c29d9a28d1987fb21ec57c65 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jun 2024 16:05:58 +1000 Subject: [PATCH 254/300] Added byte support to FreeTypeFont --- Tests/test_imagefont.py | 17 +++++++++++ src/PIL/ImageFont.py | 8 +++--- src/PIL/_imagingft.pyi | 4 +-- src/_imagingft.c | 64 +++++++++++++++++++---------------------- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 4398f8a3055..4ca882aab03 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1096,6 +1096,23 @@ def test_too_many_characters(font: ImageFont.FreeTypeFont) -> None: imagefont.getmask("A" * 1_000_001) +def test_bytes(font: ImageFont.FreeTypeFont) -> None: + assert font.getlength(b"test") == font.getlength("test") + + assert font.getbbox(b"test") == font.getbbox("test") + + assert_image_equal( + Image.Image()._new(font.getmask(b"test")), + Image.Image()._new(font.getmask("test")), + ) + + assert_image_equal( + Image.Image()._new(font.getmask2(b"test")[0]), + Image.Image()._new(font.getmask2("test")[0]), + ) + assert font.getmask2(b"test")[1] == font.getmask2("test")[1] + + @pytest.mark.parametrize( "test_file", [ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index fa5608e6cd8..ac85f48bedf 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -279,7 +279,7 @@ def getmetrics(self) -> tuple[int, int]: return self.font.ascent, self.font.descent def getlength( - self, text: str, mode="", direction=None, features=None, language=None + self, text: str | bytes, mode="", direction=None, features=None, language=None ) -> float: """ Returns length (in pixels with 1/64 precision) of given text when rendered @@ -354,7 +354,7 @@ def getlength( def getbbox( self, - text: str, + text: str | bytes, mode: str = "", direction: str | None = None, features: list[str] | None = None, @@ -511,7 +511,7 @@ def getmask( def getmask2( self, - text: str, + text: str | bytes, mode="", direction=None, features=None, @@ -730,7 +730,7 @@ def getbbox(self, text, *args, **kwargs): return 0, 0, height, width return 0, 0, width, height - def getlength(self, text: str, *args, **kwargs) -> float: + def getlength(self, text: str | bytes, *args, **kwargs) -> float: if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): msg = "text length is undefined for text rotated by 90 or 270 degrees" raise ValueError(msg) diff --git a/src/PIL/_imagingft.pyi b/src/PIL/_imagingft.pyi index 6e0ddd2f165..5e97b40b2e0 100644 --- a/src/PIL/_imagingft.pyi +++ b/src/PIL/_imagingft.pyi @@ -27,7 +27,7 @@ class Font: def glyphs(self) -> int: ... def render( self, - string: str, + string: str | bytes, fill, mode=..., dir=..., @@ -51,7 +51,7 @@ class Font: /, ) -> tuple[tuple[int, int], tuple[int, int]]: ... def getlength( - self, string: str, mode=..., dir=..., features=..., lang=..., / + self, string: str | bytes, mode=..., dir=..., features=..., lang=..., / ) -> float: ... def getvarnames(self) -> list[bytes]: ... def getvaraxes(self) -> list[_Axis] | None: ... diff --git a/src/_imagingft.c b/src/_imagingft.c index e83ddfec122..ba36cc72c23 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -233,18 +233,6 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) { return (PyObject *)self; } -static int -font_getchar(PyObject *string, int index, FT_ULong *char_out) { - if (PyUnicode_Check(string)) { - if (index >= PyUnicode_GET_LENGTH(string)) { - return 0; - } - *char_out = PyUnicode_READ_CHAR(string, index); - return 1; - } - return 0; -} - #ifdef HAVE_RAQM static size_t @@ -266,28 +254,34 @@ text_layout_raqm( goto failed; } + Py_ssize_t size; + int set_text; if (PyUnicode_Check(string)) { Py_UCS4 *text = PyUnicode_AsUCS4Copy(string); - Py_ssize_t size = PyUnicode_GET_LENGTH(string); + size = PyUnicode_GET_LENGTH(string); if (!text || !size) { /* return 0 and clean up, no glyphs==no size, and raqm fails with empty strings */ goto failed; } - int set_text = raqm_set_text(rq, text, size); + set_text = raqm_set_text(rq, text, size); PyMem_Free(text); - if (!set_text) { - PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed"); + } else { + char *buffer; + PyBytes_AsStringAndSize(string, &buffer, &size); + if (!buffer || !size) { + /* return 0 and clean up, no glyphs==no size, + and raqm fails with empty strings */ goto failed; } - if (lang) { - if (!raqm_set_language(rq, lang, start, size)) { - PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed"); - goto failed; - } - } - } else { - PyErr_SetString(PyExc_TypeError, "expected string"); + set_text = raqm_set_text_utf8(rq, buffer, size); + } + if (!set_text) { + PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed"); + goto failed; + } + if (lang && !raqm_set_language(rq, lang, start, size)) { + PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed"); goto failed; } @@ -405,13 +399,13 @@ text_layout_fallback( GlyphInfo **glyph_info, int mask, int color) { - int error, load_flags; + int error, load_flags, i; + char *buffer = NULL; FT_ULong ch; Py_ssize_t count; FT_GlyphSlot glyph; FT_Bool kerning = FT_HAS_KERNING(self->face); FT_UInt last_index = 0; - int i; if (features != Py_None || dir != NULL || lang != NULL) { PyErr_SetString( @@ -419,14 +413,11 @@ text_layout_fallback( "setting text direction, language or font features is not supported " "without libraqm"); } - if (!PyUnicode_Check(string)) { - PyErr_SetString(PyExc_TypeError, "expected string"); - return 0; - } - count = 0; - while (font_getchar(string, count, &ch)) { - count++; + if (PyUnicode_Check(string)) { + count = PyUnicode_GET_LENGTH(string); + } else { + PyBytes_AsStringAndSize(string, &buffer, &count); } if (count == 0) { return 0; @@ -445,7 +436,12 @@ text_layout_fallback( if (color) { load_flags |= FT_LOAD_COLOR; } - for (i = 0; font_getchar(string, i, &ch); i++) { + for (i = 0; i < count; i++) { + if (buffer) { + ch = buffer[i]; + } else { + ch = PyUnicode_READ_CHAR(string, i); + } (*glyph_info)[i].index = FT_Get_Char_Index(self->face, ch); error = FT_Load_Glyph(self->face, (*glyph_info)[i].index, load_flags); if (error) { From ed5e8f91c57a402ba67ee23530084b8e99c0c41e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jun 2024 19:11:11 +1000 Subject: [PATCH 255/300] Use pkg-config to help find libwebp and raqm --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index abdd87ea252..0abfaaddc5b 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,9 @@ def get_version(): JPEG2K_ROOT = None JPEG_ROOT = None LCMS_ROOT = None +RAQM_ROOT = None TIFF_ROOT = None +WEBP_ROOT = None ZLIB_ROOT = None FUZZING_BUILD = "LIB_FUZZING_ENGINE" in os.environ @@ -459,6 +461,8 @@ def build_extensions(self): "FREETYPE_ROOT": "freetype2", "HARFBUZZ_ROOT": "harfbuzz", "FRIBIDI_ROOT": "fribidi", + "RAQM_ROOT": "raqm", + "WEBP_ROOT": "libwebp", "LCMS_ROOT": "lcms2", "IMAGEQUANT_ROOT": "libimagequant", }.items(): From f62796dadc35ed21ab5b06be1e655f283046c7cb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jun 2024 19:35:46 +1000 Subject: [PATCH 256/300] Rearranged code --- src/PIL/ImageFont.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 235a76325c1..cc00cdfb6a1 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -848,13 +848,15 @@ def freetype(font: StrOrBytesPath | BinaryIO | None) -> FreeTypeFont: # when XDG_DATA_HOME is unset or empty. This user-level directory # takes precedence over system-level directories. data_home = os.path.expanduser("~/.local/share") - dirs.append(os.path.join(data_home, "fonts")) + xdg_dirs = [data_home] data_dirs = os.environ.get("XDG_DATA_DIRS") if not data_dirs: # Similarly, defaults are defined for the system-level directories data_dirs = "/usr/local/share:/usr/share" - dirs += [os.path.join(ddir, "fonts") for ddir in data_dirs.split(":")] + xdg_dirs += data_dirs.split(":") + + dirs += [os.path.join(xdg_dir, "fonts") for xdg_dir in xdg_dirs] elif sys.platform == "darwin": dirs += [ "/Library/Fonts", From e4887610e9ac48699d386b399dbcbf104a470e0f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 15 Jun 2024 22:40:46 +0000 Subject: [PATCH 257/300] Update dependency cibuildwheel to v2.19.1 --- .ci/requirements-cibw.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/requirements-cibw.txt b/.ci/requirements-cibw.txt index bf1d1315bfc..0d0f81fbfad 100644 --- a/.ci/requirements-cibw.txt +++ b/.ci/requirements-cibw.txt @@ -1 +1 @@ -cibuildwheel==2.19.0 +cibuildwheel==2.19.1 From 8d14a452df0a6ab1264294b0b28b35032db4f791 Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Sat, 15 Jun 2024 19:50:10 -0400 Subject: [PATCH 258/300] Update test environment variable for ImageFont Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_imagefont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 3cba726d64e..4e6ec86d9d7 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -564,7 +564,7 @@ def loadable_font( # catching syntax like errors monkeypatch.setattr(sys, "platform", platform) if platform == "linux": - monkeypatch.setenv("XDG_DATA_HOME", "/home/__pillow__/.local/share") + monkeypatch.setenv("XDG_DATA_HOME", os.path.expanduser("~/.local/share")) monkeypatch.setenv("XDG_DATA_DIRS", "/usr/share/:/usr/local/share/") def fake_walker(path: str) -> list[tuple[str, list[str], list[str]]]: From b2c4539cd99da978d523dcaf2d8577b6ee5a7934 Mon Sep 17 00:00:00 2001 From: mamg22 <45301823+mamg22@users.noreply.github.com> Date: Sat, 15 Jun 2024 19:51:59 -0400 Subject: [PATCH 259/300] Remove releate note for truetype() changes The proposed changes don't match match with previous release notes' meaning of "API Changes". --- docs/releasenotes/10.4.0.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 1678b460c37..44727efd41f 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -42,14 +42,6 @@ The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecat API Changes =========== -ImageFont.truetype -^^^^^^^^^^^^^^^^^^ - -:py:func:`~PIL.ImageFont.truetype` now searches for fonts in the directory indicated -by the ``XDG_DATA_HOME`` environment variable in addition to those specified in -``XDG_DATA_DIRS``. Additionally, the default for ``XDG_DATA_DIRS`` has been updated -to match the freedesktop spec. - TODO ^^^^ From 6b5b2f6e58ed77aad8b2950319a5c0178b00285a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jun 2024 22:44:17 +1000 Subject: [PATCH 260/300] Added type hints to Image --- src/PIL/FitsImagePlugin.py | 6 +- src/PIL/GifImagePlugin.py | 2 + src/PIL/Image.py | 173 +++++++++++++++++++++-------------- src/PIL/Jpeg2KImagePlugin.py | 4 +- src/PIL/PalmImagePlugin.py | 9 +- src/PIL/_imaging.pyi | 6 ++ 6 files changed, 124 insertions(+), 76 deletions(-) diff --git a/src/PIL/FitsImagePlugin.py b/src/PIL/FitsImagePlugin.py index a169b6083e9..4846054b1e4 100644 --- a/src/PIL/FitsImagePlugin.py +++ b/src/PIL/FitsImagePlugin.py @@ -115,7 +115,11 @@ def _parse_headers( elif number_of_bits in (-32, -64): self._mode = "F" - args = (self.mode, 0, -1) if decoder_name == "raw" else (number_of_bits,) + args: tuple[str | int, ...] + if decoder_name == "raw": + args = (self.mode, 0, -1) + else: + args = (number_of_bits,) return decoder_name, offset, args diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index a305e8de668..541d97f8c3b 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -458,6 +458,8 @@ def load_end(self) -> None: frame_im = self.im.convert("RGBA") else: frame_im = self.im.convert("RGB") + + assert self.dispose_extent is not None frame_im = self._crop(frame_im, self.dispose_extent) self.im = self._prev_im diff --git a/src/PIL/Image.py b/src/PIL/Image.py index bdd869ccc15..4725811274e 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -410,7 +410,9 @@ def init() -> bool: # Codec factories (used by tobytes/frombytes and ImageFile.load) -def _getdecoder(mode, decoder_name, args, extra=()): +def _getdecoder( + mode: str, decoder_name: str, args: Any, extra: tuple[Any, ...] = () +) -> core.ImagingDecoder | ImageFile.PyDecoder: # tweak arguments if args is None: args = () @@ -433,7 +435,9 @@ def _getdecoder(mode, decoder_name, args, extra=()): return decoder(mode, *args + extra) -def _getencoder(mode, encoder_name, args, extra=()): +def _getencoder( + mode: str, encoder_name: str, args: Any, extra: tuple[Any, ...] = () +) -> core.ImagingEncoder | ImageFile.PyEncoder: # tweak arguments if args is None: args = () @@ -550,10 +554,10 @@ def size(self) -> tuple[int, int]: return self._size @property - def mode(self): + def mode(self) -> str: return self._mode - def _new(self, im) -> Image: + def _new(self, im: core.ImagingCore) -> Image: new = Image() new.im = im new._mode = im.mode @@ -687,7 +691,7 @@ def _repr_pretty_(self, p, cycle) -> None: ) ) - def _repr_image(self, image_format, **kwargs): + def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None: """Helper function for iPython display hook. :param image_format: Image format. @@ -700,14 +704,14 @@ def _repr_image(self, image_format, **kwargs): return None return b.getvalue() - def _repr_png_(self): + def _repr_png_(self) -> bytes | None: """iPython display hook support for PNG format. :returns: PNG version of the image as bytes """ return self._repr_image("PNG", compress_level=1) - def _repr_jpeg_(self): + def _repr_jpeg_(self) -> bytes | None: """iPython display hook support for JPEG format. :returns: JPEG version of the image as bytes @@ -754,7 +758,7 @@ def __setstate__(self, state) -> None: self.putpalette(palette) self.frombytes(data) - def tobytes(self, encoder_name: str = "raw", *args) -> bytes: + def tobytes(self, encoder_name: str = "raw", *args: Any) -> bytes: """ Return image as a bytes object. @@ -776,12 +780,13 @@ def tobytes(self, encoder_name: str = "raw", *args) -> bytes: :returns: A :py:class:`bytes` object. """ - # may pass tuple instead of argument list - if len(args) == 1 and isinstance(args[0], tuple): - args = args[0] + encoder_args: Any = args + if len(encoder_args) == 1 and isinstance(encoder_args[0], tuple): + # may pass tuple instead of argument list + encoder_args = encoder_args[0] - if encoder_name == "raw" and args == (): - args = self.mode + if encoder_name == "raw" and encoder_args == (): + encoder_args = self.mode self.load() @@ -789,7 +794,7 @@ def tobytes(self, encoder_name: str = "raw", *args) -> bytes: return b"" # unpack data - e = _getencoder(self.mode, encoder_name, args) + e = _getencoder(self.mode, encoder_name, encoder_args) e.setimage(self.im) bufsize = max(65536, self.size[0] * 4) # see RawEncode.c @@ -832,7 +837,9 @@ def tobitmap(self, name: str = "image") -> bytes: ] ) - def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None: + def frombytes( + self, data: bytes | bytearray, decoder_name: str = "raw", *args: Any + ) -> None: """ Loads this image with pixel data from a bytes object. @@ -843,16 +850,17 @@ def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None: if self.width == 0 or self.height == 0: return - # may pass tuple instead of argument list - if len(args) == 1 and isinstance(args[0], tuple): - args = args[0] + decoder_args: Any = args + if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): + # may pass tuple instead of argument list + decoder_args = decoder_args[0] # default format - if decoder_name == "raw" and args == (): - args = self.mode + if decoder_name == "raw" and decoder_args == (): + decoder_args = self.mode # unpack data - d = _getdecoder(self.mode, decoder_name, args) + d = _getdecoder(self.mode, decoder_name, decoder_args) d.setimage(self.im) s = d.decode(data) @@ -996,9 +1004,11 @@ def convert( if has_transparency and self.im.bands == 3: transparency = new_im.info["transparency"] - def convert_transparency(m, v): - v = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 - return max(0, min(255, int(v))) + def convert_transparency( + m: tuple[float, ...], v: tuple[int, int, int] + ) -> int: + value = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 + return max(0, min(255, int(value))) if mode == "L": transparency = convert_transparency(matrix, transparency) @@ -1250,7 +1260,7 @@ def copy(self) -> Image: __copy__ = copy - def crop(self, box: tuple[int, int, int, int] | None = None) -> Image: + def crop(self, box: tuple[float, float, float, float] | None = None) -> Image: """ Returns a rectangular region from this image. The box is a 4-tuple defining the left, upper, right, and lower pixel @@ -1276,7 +1286,9 @@ def crop(self, box: tuple[int, int, int, int] | None = None) -> Image: self.load() return self._new(self._crop(self.im, box)) - def _crop(self, im, box): + def _crop( + self, im: core.ImagingCore, box: tuple[float, float, float, float] + ) -> core.ImagingCore: """ Returns a rectangular region from the core image object im. @@ -1448,7 +1460,7 @@ def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]: return self.im.getextrema() def _getxmp(self, xmp_tags): - def get_name(tag): + def get_name(tag: str) -> str: return re.sub("^{[^}]+}", "", tag) def get_value(element): @@ -1549,7 +1561,11 @@ def get_child_images(self) -> list[ImageFile.ImageFile]: fp = io.BytesIO(data) with open(fp) as im: - if thumbnail_offset is None: + from . import TiffImagePlugin + + if thumbnail_offset is None and isinstance( + im, TiffImagePlugin.TiffImageFile + ): im._frame_pos = [ifd_offset] im._seek(0) im.load() @@ -1803,7 +1819,9 @@ def paste( else: self.im.paste(im, box) - def alpha_composite(self, im, dest=(0, 0), source=(0, 0)): + def alpha_composite( + self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0) + ) -> None: """'In-place' analog of Image.alpha_composite. Composites an image onto this image. @@ -1818,32 +1836,35 @@ def alpha_composite(self, im, dest=(0, 0), source=(0, 0)): """ if not isinstance(source, (list, tuple)): - msg = "Source must be a tuple" + msg = "Source must be a list or tuple" raise ValueError(msg) if not isinstance(dest, (list, tuple)): - msg = "Destination must be a tuple" + msg = "Destination must be a list or tuple" raise ValueError(msg) - if len(source) not in (2, 4): - msg = "Source must be a 2 or 4-tuple" + + if len(source) == 4: + overlay_crop_box = tuple(source) + elif len(source) == 2: + overlay_crop_box = tuple(source) + im.size + else: + msg = "Source must be a sequence of length 2 or 4" raise ValueError(msg) + if not len(dest) == 2: - msg = "Destination must be a 2-tuple" + msg = "Destination must be a sequence of length 2" raise ValueError(msg) if min(source) < 0: msg = "Source must be non-negative" raise ValueError(msg) - if len(source) == 2: - source = source + im.size - - # over image, crop if it's not the whole thing. - if source == (0, 0) + im.size: + # over image, crop if it's not the whole image. + if overlay_crop_box == (0, 0) + im.size: overlay = im else: - overlay = im.crop(source) + overlay = im.crop(overlay_crop_box) # target for the paste - box = dest + (dest[0] + overlay.width, dest[1] + overlay.height) + box = tuple(dest) + (dest[0] + overlay.width, dest[1] + overlay.height) # destination image. don't copy if we're using the whole image. if box == (0, 0) + self.size: @@ -1854,7 +1875,11 @@ def alpha_composite(self, im, dest=(0, 0), source=(0, 0)): result = alpha_composite(background, overlay) self.paste(result, box) - def point(self, lut, mode: str | None = None) -> Image: + def point( + self, + lut: Sequence[float] | Callable[[int], float] | ImagePointHandler, + mode: str | None = None, + ) -> Image: """ Maps this image through a lookup table or function. @@ -1891,7 +1916,9 @@ def point(self, data): scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table - lut = [lut(i) for i in range(256)] * self.im.bands + flatLut = [lut(i) for i in range(256)] * self.im.bands + else: + flatLut = lut if self.mode == "F": # FIXME: _imaging returns a confusing error message for this case @@ -1899,8 +1926,8 @@ def point(self, data): raise ValueError(msg) if mode != "F": - lut = [round(i) for i in lut] - return self._new(self.im.point(lut, mode)) + flatLut = [round(i) for i in flatLut] + return self._new(self.im.point(flatLut, mode)) def putalpha(self, alpha): """ @@ -2973,29 +3000,29 @@ def _wedge() -> Image: return Image()._new(core.wedge("L")) -def _check_size(size): +def _check_size(size: Any) -> None: """ Common check to enforce type and sanity check on size tuples :param size: Should be a 2 tuple of (width, height) - :returns: True, or raises a ValueError + :returns: None, or raises a ValueError """ if not isinstance(size, (list, tuple)): - msg = "Size must be a tuple" + msg = "Size must be a list or tuple" raise ValueError(msg) if len(size) != 2: - msg = "Size must be a tuple of length 2" + msg = "Size must be a sequence of length 2" raise ValueError(msg) if size[0] < 0 or size[1] < 0: msg = "Width and height must be >= 0" raise ValueError(msg) - return True - def new( - mode: str, size: tuple[int, int], color: float | tuple[float, ...] | str | None = 0 + mode: str, + size: tuple[int, int] | list[int], + color: float | tuple[float, ...] | str | None = 0, ) -> Image: """ Creates a new image with the given mode and size. @@ -3044,7 +3071,13 @@ def new( return im._new(core.fill(mode, size, color)) -def frombytes(mode, size, data, decoder_name: str = "raw", *args) -> Image: +def frombytes( + mode: str, + size: tuple[int, int], + data: bytes | bytearray, + decoder_name: str = "raw", + *args: Any, +) -> Image: """ Creates a copy of an image memory from pixel data in a buffer. @@ -3072,18 +3105,21 @@ def frombytes(mode, size, data, decoder_name: str = "raw", *args) -> Image: im = new(mode, size) if im.width != 0 and im.height != 0: - # may pass tuple instead of argument list - if len(args) == 1 and isinstance(args[0], tuple): - args = args[0] + decoder_args: Any = args + if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): + # may pass tuple instead of argument list + decoder_args = decoder_args[0] - if decoder_name == "raw" and args == (): - args = mode + if decoder_name == "raw" and decoder_args == (): + decoder_args = mode - im.frombytes(data, decoder_name, args) + im.frombytes(data, decoder_name, decoder_args) return im -def frombuffer(mode: str, size, data, decoder_name: str = "raw", *args) -> Image: +def frombuffer( + mode: str, size: tuple[int, int], data, decoder_name: str = "raw", *args: Any +) -> Image: """ Creates an image memory referencing pixel data in a byte buffer. @@ -3540,7 +3576,7 @@ def merge(mode: str, bands: Sequence[Image]) -> Image: def register_open( - id, + id: str, factory: Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], accept: Callable[[bytes], bool | str] | None = None, ) -> None: @@ -3674,7 +3710,7 @@ def _show(image: Image, **options: Any) -> None: def effect_mandelbrot( - size: tuple[int, int], extent: tuple[int, int, int, int], quality: int + size: tuple[int, int], extent: tuple[float, float, float, float], quality: int ) -> Image: """ Generate a Mandelbrot set covering the given extent. @@ -3721,19 +3757,18 @@ def radial_gradient(mode: str) -> Image: # Resources -def _apply_env_variables(env=None) -> None: - if env is None: - env = os.environ +def _apply_env_variables(env: dict[str, str] | None = None) -> None: + env_dict = env if env is not None else os.environ for var_name, setter in [ ("PILLOW_ALIGNMENT", core.set_alignment), ("PILLOW_BLOCK_SIZE", core.set_block_size), ("PILLOW_BLOCKS_MAX", core.set_blocks_max), ]: - if var_name not in env: + if var_name not in env_dict: continue - var = env[var_name].lower() + var = env_dict[var_name].lower() units = 1 for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: @@ -3742,13 +3777,13 @@ def _apply_env_variables(env=None) -> None: var = var[: -len(postfix)] try: - var = int(var) * units + var_int = int(var) * units except ValueError: warnings.warn(f"{var_name} is not int") continue try: - setter(var) + setter(var_int) except ValueError as e: warnings.warn(f"{var_name}: {e}") diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 60f3bff0acb..39eb1c20314 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -122,7 +122,7 @@ def _parse_codestream(fp): elif csiz == 4: mode = "RGBA" else: - mode = None + mode = "" return size, mode @@ -237,7 +237,7 @@ def _open(self) -> None: msg = "not a JPEG 2000 file" raise SyntaxError(msg) - if self.size is None or self.mode is None: + if self.size is None or not self.mode: msg = "unable to determine size/mode" raise SyntaxError(msg) diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index fc83918b5b4..1735070f81b 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -129,15 +129,16 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: # and invert it because # Palm does grayscale from white (0) to black (1) bpp = im.encoderinfo["bpp"] - im = im.point( - lambda x, shift=8 - bpp, maxval=(1 << bpp) - 1: maxval - (x >> shift) - ) + maxval = (1 << bpp) - 1 + shift = 8 - bpp + im = im.point(lambda x: maxval - (x >> shift)) elif im.info.get("bpp") in (1, 2, 4): # here we assume that even though the inherent mode is 8-bit grayscale, # only the lower bpp bits are significant. # We invert them to match the Palm. bpp = im.info["bpp"] - im = im.point(lambda x, maxval=(1 << bpp) - 1: maxval - (x & maxval)) + maxval = (1 << bpp) - 1 + im = im.point(lambda x: maxval - (x & maxval)) else: msg = f"cannot write mode {im.mode} as Palm" raise OSError(msg) diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index 1fe95441715..3467eeb4549 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -12,5 +12,11 @@ class ImagingDraw: class PixelAccess: def __getattr__(self, name: str) -> Any: ... +class ImagingDecoder: + def __getattr__(self, name: str) -> Any: ... + +class ImagingEncoder: + def __getattr__(self, name: str) -> Any: ... + def font(image, glyphdata: bytes) -> ImagingFont: ... def __getattr__(name: str) -> Any: ... From 291ee352047c845e2e6e5062c2c219923689da8d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jun 2024 23:03:03 +1000 Subject: [PATCH 261/300] Added type hints --- Tests/test_file_jpeg.py | 2 +- Tests/test_file_jpeg2k.py | 2 +- Tests/test_image.py | 2 +- Tests/test_image_putdata.py | 8 ++++---- Tests/test_numpy.py | 25 +++++++++++++++++-------- Tests/test_shell_injection.py | 7 ++++--- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 8e4d694c1a9..1459a87eb13 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -701,7 +701,7 @@ def test_load_djpeg(self) -> None: def test_save_cjpeg(self, tmp_path: Path) -> None: with Image.open(TEST_FILE) as img: tempfile = str(tmp_path / "temp.jpg") - JpegImagePlugin._save_cjpeg(img, 0, tempfile) + JpegImagePlugin._save_cjpeg(img, BytesIO(), tempfile) # Default save quality is 75%, so a tiny bit of difference is alright assert_image_similar_tofile(img, tempfile, 17) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 5a208739f70..ed7ea4fcfbc 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -460,7 +460,7 @@ def test_plt_marker() -> None: out.seek(length - 2, os.SEEK_CUR) -def test_9bit(): +def test_9bit() -> None: with Image.open("Tests/images/9bit.j2k") as im: assert im.mode == "I;16" assert im.size == (128, 128) diff --git a/Tests/test_image.py b/Tests/test_image.py index d6a739c79df..0d7d034806c 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -152,7 +152,7 @@ def test_bad_mode(self) -> None: def test_stringio(self) -> None: with pytest.raises(ValueError): - with Image.open(io.StringIO()): + with Image.open(io.StringIO()): # type: ignore[arg-type] pass def test_pathlib(self, tmp_path: Path) -> None: diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index dad26ef144c..5e57e4c4c8f 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -113,13 +113,13 @@ def test_array_F() -> None: def test_not_flattened() -> None: im = Image.new("L", (1, 1)) with pytest.raises(TypeError): - im.putdata([[0]]) + im.putdata([[0]]) # type: ignore[list-item] with pytest.raises(TypeError): - im.putdata([[0]], 2) + im.putdata([[0]], 2) # type: ignore[list-item] with pytest.raises(TypeError): im = Image.new("I", (1, 1)) - im.putdata([[0]]) + im.putdata([[0]]) # type: ignore[list-item] with pytest.raises(TypeError): im = Image.new("F", (1, 1)) - im.putdata([[0]]) + im.putdata([[0]]) # type: ignore[list-item] diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 9f4e6534e8a..36cdb368267 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,6 +1,7 @@ from __future__ import annotations import warnings +from typing import TYPE_CHECKING, Any import pytest @@ -8,13 +9,19 @@ from .helper import assert_deep_equal, assert_image, hopper, skip_unless_feature -numpy = pytest.importorskip("numpy", reason="NumPy not installed") +if TYPE_CHECKING: + import numpy + import numpy.typing +else: + numpy = pytest.importorskip("numpy", reason="NumPy not installed") TEST_IMAGE_SIZE = (10, 10) def test_numpy_to_image() -> None: - def to_image(dtype, bands: int = 1, boolean: int = 0) -> Image.Image: + def to_image( + dtype: numpy.typing.DTypeLike, bands: int = 1, boolean: int = 0 + ) -> Image.Image: if bands == 1: if boolean: data = [0, 255] * 50 @@ -99,14 +106,16 @@ def test_1d_array() -> None: assert_image(Image.fromarray(a), "L", (1, 5)) -def _test_img_equals_nparray(img: Image.Image, np) -> None: - assert len(np.shape) >= 2 - np_size = np.shape[1], np.shape[0] +def _test_img_equals_nparray( + img: Image.Image, np_img: numpy.typing.NDArray[Any] +) -> None: + assert len(np_img.shape) >= 2 + np_size = np_img.shape[1], np_img.shape[0] assert img.size == np_size px = img.load() for x in range(0, img.size[0], int(img.size[0] / 10)): for y in range(0, img.size[1], int(img.size[1] / 10)): - assert_deep_equal(px[x, y], np[y, x]) + assert_deep_equal(px[x, y], np_img[y, x]) def test_16bit() -> None: @@ -157,7 +166,7 @@ def test_save_tiff_uint16() -> None: ("HSV", numpy.uint8), ), ) -def test_to_array(mode: str, dtype) -> None: +def test_to_array(mode: str, dtype: numpy.typing.DTypeLike) -> None: img = hopper(mode) # Resize to non-square @@ -207,7 +216,7 @@ def test_putdata() -> None: numpy.float64, ), ) -def test_roundtrip_eye(dtype) -> None: +def test_roundtrip_eye(dtype: numpy.typing.DTypeLike) -> None: arr = numpy.eye(10, dtype=dtype) numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) diff --git a/Tests/test_shell_injection.py b/Tests/test_shell_injection.py index 2a072fd44c5..dd4fc46c374 100644 --- a/Tests/test_shell_injection.py +++ b/Tests/test_shell_injection.py @@ -1,8 +1,9 @@ from __future__ import annotations import shutil +from io import BytesIO from pathlib import Path -from typing import Callable +from typing import IO, Callable import pytest @@ -22,11 +23,11 @@ def assert_save_filename_check( self, tmp_path: Path, src_img: Image.Image, - save_func: Callable[[Image.Image, int, str], None], + save_func: Callable[[Image.Image, IO[bytes], str | bytes], None], ) -> None: for filename in test_filenames: dest_file = str(tmp_path / filename) - save_func(src_img, 0, dest_file) + save_func(src_img, BytesIO(), dest_file) # If file can't be opened, shell injection probably occurred with Image.open(dest_file) as im: im.load() From 9f79e5d7680a0eba9b625ad24595fc2ece6e523c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 19 Jun 2024 08:43:23 +1000 Subject: [PATCH 262/300] Added type hints to ImageDraw shape methods --- Tests/test_imagedraw.py | 20 +++- src/PIL/ImageDraw.py | 248 +++++++++++++++++++++++++++------------- src/PIL/_imaging.pyi | 5 + 3 files changed, 187 insertions(+), 86 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index d9593c60f60..8b55df992e1 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -448,6 +448,7 @@ def test_shape1() -> None: x3, y3 = 95, 5 # Act + assert ImageDraw.Outline is not None s = ImageDraw.Outline() s.move(x0, y0) s.curve(x1, y1, x2, y2, x3, y3) @@ -469,6 +470,7 @@ def test_shape2() -> None: x3, y3 = 5, 95 # Act + assert ImageDraw.Outline is not None s = ImageDraw.Outline() s.move(x0, y0) s.curve(x1, y1, x2, y2, x3, y3) @@ -487,6 +489,7 @@ def test_transform() -> None: draw = ImageDraw.Draw(im) # Act + assert ImageDraw.Outline is not None s = ImageDraw.Outline() s.line(0, 0) s.transform((0, 0, 0, 0, 0, 0)) @@ -913,7 +916,12 @@ def test_rounded_rectangle_translucent( def test_floodfill(bbox: Coords) -> None: red = ImageColor.getrgb("red") - for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]: + mode_values: list[tuple[str, int | tuple[int, ...]]] = [ + ("L", 1), + ("RGBA", (255, 0, 0, 0)), + ("RGB", red), + ] + for mode, value in mode_values: # Arrange im = Image.new(mode, (W, H)) draw = ImageDraw.Draw(im) @@ -1429,6 +1437,7 @@ def test_same_color_outline(bbox: Coords) -> None: x2, y2 = 95, 50 x3, y3 = 95, 5 + assert ImageDraw.Outline is not None s = ImageDraw.Outline() s.move(x0, y0) s.curve(x1, y1, x2, y2, x3, y3) @@ -1467,7 +1476,7 @@ def test_same_color_outline(bbox: Coords) -> None: (4, "square", {}), (8, "regular_octagon", {}), (4, "square_rotate_45", {"rotation": 45}), - (3, "triangle_width", {"width": 5, "outline": "yellow"}), + (3, "triangle_width", {"outline": "yellow", "width": 5}), ], ) def test_draw_regular_polygon( @@ -1477,7 +1486,10 @@ def test_draw_regular_polygon( filename = f"Tests/images/imagedraw_{polygon_name}.png" draw = ImageDraw.Draw(im) bounding_circle = ((W // 2, H // 2), 25) - draw.regular_polygon(bounding_circle, n_sides, fill="red", **args) + rotation = int(args.get("rotation", 0)) + outline = args.get("outline") + width = int(args.get("width", 1)) + draw.regular_polygon(bounding_circle, n_sides, rotation, "red", outline, width) assert_image_equal_tofile(im, filename) @@ -1630,6 +1642,6 @@ def test_incorrectly_ordered_coordinates(xy: tuple[int, int, int, int]) -> None: draw.rounded_rectangle(xy) -def test_getdraw(): +def test_getdraw() -> None: with pytest.warns(DeprecationWarning): ImageDraw.getdraw(None, []) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 41a3eb0cb46..91f8988387d 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -35,15 +35,24 @@ import numbers import struct from types import ModuleType -from typing import TYPE_CHECKING, AnyStr, Sequence, cast +from typing import TYPE_CHECKING, AnyStr, Callable, List, Sequence, Tuple, Union, cast from . import Image, ImageColor from ._deprecate import deprecate from ._typing import Coords +# experimental access to the outline API +Outline: Callable[[], Image.core._Outline] | None +try: + Outline = Image.core.outline +except AttributeError: + Outline = None + if TYPE_CHECKING: from . import ImageDraw2, ImageFont +_Ink = Union[float, Tuple[int, ...], str] + """ A simple 2D drawing interface for PIL images.

@@ -134,34 +143,47 @@ def _getfont( else: return self.getfont() - def _getink(self, ink, fill=None) -> tuple[int | None, int | None]: + def _getink( + self, ink: _Ink | None, fill: _Ink | None = None + ) -> tuple[int | None, int | None]: + result_ink = None + result_fill = None if ink is None and fill is None: if self.fill: - fill = self.ink + result_fill = self.ink else: - ink = self.ink + result_ink = self.ink else: if ink is not None: if isinstance(ink, str): ink = ImageColor.getcolor(ink, self.mode) if self.palette and not isinstance(ink, numbers.Number): ink = self.palette.getcolor(ink, self._image) - ink = self.draw.draw_ink(ink) + result_ink = self.draw.draw_ink(ink) if fill is not None: if isinstance(fill, str): fill = ImageColor.getcolor(fill, self.mode) if self.palette and not isinstance(fill, numbers.Number): fill = self.palette.getcolor(fill, self._image) - fill = self.draw.draw_ink(fill) - return ink, fill + result_fill = self.draw.draw_ink(fill) + return result_ink, result_fill - def arc(self, xy: Coords, start, end, fill=None, width=1) -> None: + def arc( + self, + xy: Coords, + start: float, + end: float, + fill: _Ink | None = None, + width: int = 1, + ) -> None: """Draw an arc.""" ink, fill = self._getink(fill) if ink is not None: self.draw.draw_arc(xy, start, end, ink, width) - def bitmap(self, xy: Sequence[int], bitmap, fill=None) -> None: + def bitmap( + self, xy: Sequence[int], bitmap: Image.Image, fill: _Ink | None = None + ) -> None: """Draw a bitmap.""" bitmap.load() ink, fill = self._getink(fill) @@ -170,30 +192,55 @@ def bitmap(self, xy: Sequence[int], bitmap, fill=None) -> None: if ink is not None: self.draw.draw_bitmap(xy, bitmap.im, ink) - def chord(self, xy: Coords, start, end, fill=None, outline=None, width=1) -> None: + def chord( + self, + xy: Coords, + start: float, + end: float, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: """Draw a chord.""" - ink, fill = self._getink(outline, fill) - if fill is not None: - self.draw.draw_chord(xy, start, end, fill, 1) - if ink is not None and ink != fill and width != 0: + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_chord(xy, start, end, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: self.draw.draw_chord(xy, start, end, ink, 0, width) - def ellipse(self, xy: Coords, fill=None, outline=None, width=1) -> None: + def ellipse( + self, + xy: Coords, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: """Draw an ellipse.""" - ink, fill = self._getink(outline, fill) - if fill is not None: - self.draw.draw_ellipse(xy, fill, 1) - if ink is not None and ink != fill and width != 0: + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_ellipse(xy, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: self.draw.draw_ellipse(xy, ink, 0, width) def circle( - self, xy: Sequence[float], radius: float, fill=None, outline=None, width=1 + self, + xy: Sequence[float], + radius: float, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, ) -> None: """Draw a circle given center coordinates and a radius.""" ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) self.ellipse(ellipse_xy, fill, outline, width) - def line(self, xy: Coords, fill=None, width=0, joint=None) -> None: + def line( + self, + xy: Coords, + fill: _Ink | None = None, + width: int = 0, + joint: str | None = None, + ) -> None: """Draw a line, or a connected sequence of line segments.""" ink = self._getink(fill)[0] if ink is not None: @@ -223,7 +270,7 @@ def line(self, xy: Coords, fill=None, width=0, joint=None) -> None: def coord_at_angle( coord: Sequence[float], angle: float - ) -> tuple[float, float]: + ) -> tuple[float, ...]: x, y = coord angle -= 90 distance = width / 2 - 1 @@ -264,37 +311,54 @@ def coord_at_angle( ] self.line(gap_coords, fill, width=3) - def shape(self, shape, fill=None, outline=None) -> None: + def shape( + self, + shape: Image.core._Outline, + fill: _Ink | None = None, + outline: _Ink | None = None, + ) -> None: """(Experimental) Draw a shape.""" shape.close() - ink, fill = self._getink(outline, fill) - if fill is not None: - self.draw.draw_outline(shape, fill, 1) - if ink is not None and ink != fill: + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_outline(shape, fill_ink, 1) + if ink is not None and ink != fill_ink: self.draw.draw_outline(shape, ink, 0) def pieslice( - self, xy: Coords, start, end, fill=None, outline=None, width=1 + self, + xy: Coords, + start: float, + end: float, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, ) -> None: """Draw a pieslice.""" - ink, fill = self._getink(outline, fill) - if fill is not None: - self.draw.draw_pieslice(xy, start, end, fill, 1) - if ink is not None and ink != fill and width != 0: + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_pieslice(xy, start, end, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: self.draw.draw_pieslice(xy, start, end, ink, 0, width) - def point(self, xy: Coords, fill=None) -> None: + def point(self, xy: Coords, fill: _Ink | None = None) -> None: """Draw one or more individual pixels.""" ink, fill = self._getink(fill) if ink is not None: self.draw.draw_points(xy, ink) - def polygon(self, xy: Coords, fill=None, outline=None, width=1) -> None: + def polygon( + self, + xy: Coords, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: """Draw a polygon.""" - ink, fill = self._getink(outline, fill) - if fill is not None: - self.draw.draw_polygon(xy, fill, 1) - if ink is not None and ink != fill and width != 0: + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_polygon(xy, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: if width == 1: self.draw.draw_polygon(xy, ink, 0, width) elif self.im is not None: @@ -320,22 +384,41 @@ def polygon(self, xy: Coords, fill=None, outline=None, width=1) -> None: self.im.paste(im.im, (0, 0) + im.size, mask.im) def regular_polygon( - self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1 + self, + bounding_circle: Sequence[Sequence[float] | float], + n_sides: int, + rotation: float = 0, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, ) -> None: """Draw a regular polygon.""" xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) self.polygon(xy, fill, outline, width) - def rectangle(self, xy: Coords, fill=None, outline=None, width=1) -> None: + def rectangle( + self, + xy: Coords, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + ) -> None: """Draw a rectangle.""" - ink, fill = self._getink(outline, fill) - if fill is not None: - self.draw.draw_rectangle(xy, fill, 1) - if ink is not None and ink != fill and width != 0: + ink, fill_ink = self._getink(outline, fill) + if fill_ink is not None: + self.draw.draw_rectangle(xy, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: self.draw.draw_rectangle(xy, ink, 0, width) def rounded_rectangle( - self, xy: Coords, radius=0, fill=None, outline=None, width=1, *, corners=None + self, + xy: Coords, + radius: float = 0, + fill: _Ink | None = None, + outline: _Ink | None = None, + width: int = 1, + *, + corners: tuple[bool, bool, bool, bool] | None = None, ) -> None: """Draw a rounded rectangle.""" if isinstance(xy[0], (list, tuple)): @@ -377,10 +460,10 @@ def rounded_rectangle( # that is a rectangle return self.rectangle(xy, fill, outline, width) - r = d // 2 - ink, fill = self._getink(outline, fill) + r = int(d // 2) + ink, fill_ink = self._getink(outline, fill) - def draw_corners(pieslice) -> None: + def draw_corners(pieslice: bool) -> None: parts: tuple[tuple[tuple[float, float, float, float], int, int], ...] if full_x: # Draw top and bottom halves @@ -410,32 +493,32 @@ def draw_corners(pieslice) -> None: ) for part in parts: if pieslice: - self.draw.draw_pieslice(*(part + (fill, 1))) + self.draw.draw_pieslice(*(part + (fill_ink, 1))) else: self.draw.draw_arc(*(part + (ink, width))) - if fill is not None: + if fill_ink is not None: draw_corners(True) if full_x: - self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill, 1) + self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill_ink, 1) else: - self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill, 1) + self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill_ink, 1) if not full_x and not full_y: left = [x0, y0, x0 + r, y1] if corners[0]: left[1] += r + 1 if corners[3]: left[3] -= r + 1 - self.draw.draw_rectangle(left, fill, 1) + self.draw.draw_rectangle(left, fill_ink, 1) right = [x1 - r, y0, x1, y1] if corners[1]: right[1] += r + 1 if corners[2]: right[3] -= r + 1 - self.draw.draw_rectangle(right, fill, 1) - if ink is not None and ink != fill and width != 0: + self.draw.draw_rectangle(right, fill_ink, 1) + if ink is not None and ink != fill_ink and width != 0: draw_corners(False) if not full_x: @@ -530,10 +613,11 @@ def text( embedded_color, ) - def getink(fill): - ink, fill = self._getink(fill) + def getink(fill: _Ink | None) -> int: + ink, fill_ink = self._getink(fill) if ink is None: - return fill + assert fill_ink is not None + return fill_ink return ink def draw_text(ink, stroke_width=0, stroke_offset=None) -> None: @@ -897,13 +981,6 @@ def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw: return ImageDraw(im, mode) -# experimental access to the outline API -try: - Outline = Image.core.outline -except AttributeError: - Outline = None - - def getdraw( im: Image.Image | None = None, hints: list[str] | None = None ) -> tuple[ImageDraw2.Draw | None, ModuleType]: @@ -983,12 +1060,12 @@ def floodfill( def _compute_regular_polygon_vertices( - bounding_circle, n_sides, rotation + bounding_circle: Sequence[Sequence[float] | float], n_sides: int, rotation: float ) -> list[tuple[float, float]]: """ Generate a list of vertices for a 2D regular polygon. - :param bounding_circle: The bounding circle is a tuple defined + :param bounding_circle: The bounding circle is a sequence defined by a point and radius. The polygon is inscribed in this circle. (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``) :param n_sides: Number of sides @@ -1026,7 +1103,7 @@ def _compute_regular_polygon_vertices( # 1. Error Handling # 1.1 Check `n_sides` has an appropriate value if not isinstance(n_sides, int): - msg = "n_sides should be an int" + msg = "n_sides should be an int" # type: ignore[unreachable] raise TypeError(msg) if n_sides < 3: msg = "n_sides should be an int > 2" @@ -1038,9 +1115,24 @@ def _compute_regular_polygon_vertices( raise TypeError(msg) if len(bounding_circle) == 3: - *centroid, polygon_radius = bounding_circle - elif len(bounding_circle) == 2: - centroid, polygon_radius = bounding_circle + if not all(isinstance(i, (int, float)) for i in bounding_circle): + msg = "bounding_circle should only contain numeric data" + raise ValueError(msg) + + *centroid, polygon_radius = cast(List[float], list(bounding_circle)) + elif len(bounding_circle) == 2 and isinstance(bounding_circle[0], (list, tuple)): + if not all( + isinstance(i, (int, float)) for i in bounding_circle[0] + ) or not isinstance(bounding_circle[1], (int, float)): + msg = "bounding_circle should only contain numeric data" + raise ValueError(msg) + + if len(bounding_circle[0]) != 2: + msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" + raise ValueError(msg) + + centroid = cast(List[float], list(bounding_circle[0])) + polygon_radius = cast(float, bounding_circle[1]) else: msg = ( "bounding_circle should contain 2D coordinates " @@ -1048,25 +1140,17 @@ def _compute_regular_polygon_vertices( ) raise ValueError(msg) - if not all(isinstance(i, (int, float)) for i in (*centroid, polygon_radius)): - msg = "bounding_circle should only contain numeric data" - raise ValueError(msg) - - if not len(centroid) == 2: - msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" - raise ValueError(msg) - if polygon_radius <= 0: msg = "bounding_circle radius should be > 0" raise ValueError(msg) # 1.3 Check `rotation` has an appropriate value if not isinstance(rotation, (int, float)): - msg = "rotation should be an int or float" + msg = "rotation should be an int or float" # type: ignore[unreachable] raise ValueError(msg) # 2. Define Helper Functions - def _apply_rotation(point: list[float], degrees: float) -> tuple[int, int]: + def _apply_rotation(point: list[float], degrees: float) -> tuple[float, float]: return ( round( point[0] * math.cos(math.radians(360 - degrees)) @@ -1082,7 +1166,7 @@ def _apply_rotation(point: list[float], degrees: float) -> tuple[int, int]: ), ) - def _compute_polygon_vertex(angle: float) -> tuple[int, int]: + def _compute_polygon_vertex(angle: float) -> tuple[float, float]: start_point = [polygon_radius, 0] return _apply_rotation(start_point, angle) diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index dda06b5a1af..2bfe421e6e5 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -18,5 +18,10 @@ class ImagingDecoder: class ImagingEncoder: def __getattr__(self, name: str) -> Any: ... +class _Outline: + def close(self) -> None: ... + def __getattr__(self, name: str) -> Any: ... + def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ... +def outline() -> _Outline: ... def __getattr__(name: str) -> Any: ... From b64847e07fd32f8865f45600f663655116d46840 Mon Sep 17 00:00:00 2001 From: Nulano Date: Wed, 19 Jun 2024 21:48:48 +0200 Subject: [PATCH 263/300] Do not use a protocol for PixelAccess object --- docs/reference/PixelAccess.rst | 22 +++++++++++++++++++--- src/PIL/Image.py | 29 ++--------------------------- src/PIL/_imaging.pyi | 5 ++++- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/docs/reference/PixelAccess.rst b/docs/reference/PixelAccess.rst index 026f488d8e1..1ac3d034b49 100644 --- a/docs/reference/PixelAccess.rst +++ b/docs/reference/PixelAccess.rst @@ -44,7 +44,23 @@ Access using negative indexes is also possible. :: ----------------------------- .. class:: PixelAccess - :canonical: PIL.Image.PixelAccess + :canonical: PIL.Image.core.PixelAccess - .. automethod:: PIL.Image.PixelAccess.__getitem__ - .. automethod:: PIL.Image.PixelAccess.__setitem__ + .. method:: __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...] + + Returns the pixel at x,y. The pixel is returned as a single + value for single band images or a tuple for multi-band images. + + :param xy: The pixel coordinate, given as (x, y). + :returns: a pixel value for single band images, a tuple of + pixel values for multiband images. + + .. method:: __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> None + + Modifies the pixel at x,y. The color is given as a single + numerical value for single band images, and a tuple for + multi-band images. + + :param xy: The pixel coordinate, given as (x, y). + :param color: The pixel value according to its mode, + e.g. tuple (r, g, b) for RGB mode. diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 5619915ea78..26849707ebc 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -227,7 +227,7 @@ class Quantize(IntEnum): # Registries if TYPE_CHECKING: - from . import ImageFile + from . import ImageFile, PyAccess ID: list[str] = [] OPEN: dict[ str, @@ -512,31 +512,6 @@ def _getscaleoffset(expr): # Implementation wrapper -class PixelAccess(Protocol): - def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: - """ - Returns the pixel at x,y. The pixel is returned as a single - value for single band images or a tuple for multi-band images. - - :param xy: The pixel coordinate, given as (x, y). - :returns: a pixel value for single band images, a tuple of - pixel values for multiband images. - """ - raise NotImplementedError() - - def __setitem__(self, xy: tuple[int, int], color: float | tuple[int, ...]) -> None: - """ - Modifies the pixel at x,y. The color is given as a single - numerical value for single band images, and a tuple for - multi-band images. - - :param xy: The pixel coordinate, given as (x, y). - :param color: The pixel value according to its mode, - e.g. tuple (r, g, b) for RGB mode. - """ - raise NotImplementedError() - - class SupportsGetData(Protocol): def getdata( self, @@ -897,7 +872,7 @@ def frombytes(self, data: bytes, decoder_name: str = "raw", *args) -> None: msg = "cannot decode image data" raise ValueError(msg) - def load(self) -> PixelAccess | None: + def load(self) -> core.PixelAccess | PyAccess.PyAccess | None: """ Allocates storage for the image and loads the pixel data. In normal cases, you don't need to call this method, since the diff --git a/src/PIL/_imaging.pyi b/src/PIL/_imaging.pyi index 1fe95441715..1a0184069fc 100644 --- a/src/PIL/_imaging.pyi +++ b/src/PIL/_imaging.pyi @@ -10,7 +10,10 @@ class ImagingDraw: def __getattr__(self, name: str) -> Any: ... class PixelAccess: - def __getattr__(self, name: str) -> Any: ... + def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: ... + def __setitem__( + self, xy: tuple[int, int], color: float | tuple[int, ...] + ) -> None: ... def font(image, glyphdata: bytes) -> ImagingFont: ... def __getattr__(name: str) -> Any: ... From 324e548e525f2e7a3e31bb9dde10086fc202e8b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 21 Jun 2024 20:41:22 +1000 Subject: [PATCH 264/300] Added type hints to ImageFilter --- Tests/test_color_lut.py | 4 +- Tests/test_image_filter.py | 2 +- Tests/test_numpy.py | 18 +++----- docs/reference/internal_modules.rst | 4 ++ src/PIL/ImageFilter.py | 72 ++++++++++++++++++++--------- src/PIL/_typing.py | 10 +++- 6 files changed, 71 insertions(+), 39 deletions(-) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index c8886a7796d..00c8995b08d 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -354,10 +354,10 @@ def test_overflow(self) -> None: class TestColorLut3DFilter: def test_wrong_args(self) -> None: with pytest.raises(ValueError, match="should be either an integer"): - ImageFilter.Color3DLUT("small", [1]) + ImageFilter.Color3DLUT("small", [1]) # type: ignore[arg-type] with pytest.raises(ValueError, match="should be either an integer"): - ImageFilter.Color3DLUT((11, 11), [1]) + ImageFilter.Color3DLUT((11, 11), [1]) # type: ignore[arg-type] with pytest.raises(ValueError, match=r"in \[2, 65\] range"): ImageFilter.Color3DLUT((11, 11, 1), [1]) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 1f0644471ee..412ab44c3c1 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -137,7 +137,7 @@ def test_builtinfilter_p() -> None: builtin_filter = ImageFilter.BuiltinFilter() with pytest.raises(ValueError): - builtin_filter.filter(hopper("P")) + builtin_filter.filter(hopper("P").im) def test_kernel_not_enough_coefficients() -> None: diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 36cdb368267..32d2cf9853a 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -1,17 +1,17 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING import pytest -from PIL import Image +from PIL import Image, _typing from .helper import assert_deep_equal, assert_image, hopper, skip_unless_feature if TYPE_CHECKING: import numpy - import numpy.typing + import numpy.typing as npt else: numpy = pytest.importorskip("numpy", reason="NumPy not installed") @@ -19,9 +19,7 @@ def test_numpy_to_image() -> None: - def to_image( - dtype: numpy.typing.DTypeLike, bands: int = 1, boolean: int = 0 - ) -> Image.Image: + def to_image(dtype: npt.DTypeLike, bands: int = 1, boolean: int = 0) -> Image.Image: if bands == 1: if boolean: data = [0, 255] * 50 @@ -106,9 +104,7 @@ def test_1d_array() -> None: assert_image(Image.fromarray(a), "L", (1, 5)) -def _test_img_equals_nparray( - img: Image.Image, np_img: numpy.typing.NDArray[Any] -) -> None: +def _test_img_equals_nparray(img: Image.Image, np_img: _typing.NumpyArray) -> None: assert len(np_img.shape) >= 2 np_size = np_img.shape[1], np_img.shape[0] assert img.size == np_size @@ -166,7 +162,7 @@ def test_save_tiff_uint16() -> None: ("HSV", numpy.uint8), ), ) -def test_to_array(mode: str, dtype: numpy.typing.DTypeLike) -> None: +def test_to_array(mode: str, dtype: npt.DTypeLike) -> None: img = hopper(mode) # Resize to non-square @@ -216,7 +212,7 @@ def test_putdata() -> None: numpy.float64, ), ) -def test_roundtrip_eye(dtype: numpy.typing.DTypeLike) -> None: +def test_roundtrip_eye(dtype: npt.DTypeLike) -> None: arr = numpy.eye(10, dtype=dtype) numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) diff --git a/docs/reference/internal_modules.rst b/docs/reference/internal_modules.rst index 899e4966ff2..e4cb17c4d8f 100644 --- a/docs/reference/internal_modules.rst +++ b/docs/reference/internal_modules.rst @@ -33,6 +33,10 @@ Internal Modules Provides a convenient way to import type hints that are not available on some Python versions. +.. py:class:: NumpyArray + + Typing alias. + .. py:class:: StrOrBytesPath Typing alias. diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 02288e13576..e18b4a4466a 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -19,12 +19,16 @@ import abc import functools from types import ModuleType -from typing import Any, Sequence +from typing import TYPE_CHECKING, Any, Callable, Sequence, cast + +if TYPE_CHECKING: + from . import _imaging + from ._typing import NumpyArray class Filter: @abc.abstractmethod - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: pass @@ -33,7 +37,9 @@ class MultibandFilter(Filter): class BuiltinFilter(MultibandFilter): - def filter(self, image): + filterargs: tuple[Any, ...] + + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: if image.mode == "P": msg = "cannot filter palette images" raise ValueError(msg) @@ -91,7 +97,7 @@ def __init__(self, size: int, rank: int) -> None: self.size = size self.rank = rank - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: if image.mode == "P": msg = "cannot filter palette images" raise ValueError(msg) @@ -158,7 +164,7 @@ class ModeFilter(Filter): def __init__(self, size: int = 3) -> None: self.size = size - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: return image.modefilter(self.size) @@ -176,9 +182,9 @@ class GaussianBlur(MultibandFilter): def __init__(self, radius: float | Sequence[float] = 2) -> None: self.radius = radius - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: xy = self.radius - if not isinstance(xy, (tuple, list)): + if isinstance(xy, (int, float)): xy = (xy, xy) if xy == (0, 0): return image.copy() @@ -208,9 +214,9 @@ def __init__(self, radius: float | Sequence[float]) -> None: raise ValueError(msg) self.radius = radius - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: xy = self.radius - if not isinstance(xy, (tuple, list)): + if isinstance(xy, (int, float)): xy = (xy, xy) if xy == (0, 0): return image.copy() @@ -241,7 +247,7 @@ def __init__( self.percent = percent self.threshold = threshold - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: return image.unsharp_mask(self.radius, self.percent, self.threshold) @@ -387,8 +393,13 @@ class Color3DLUT(MultibandFilter): name = "Color 3D LUT" def __init__( - self, size, table, channels: int = 3, target_mode: str | None = None, **kwargs - ): + self, + size: int | tuple[int, int, int], + table: Sequence[float] | Sequence[Sequence[int]] | NumpyArray, + channels: int = 3, + target_mode: str | None = None, + **kwargs: bool, + ) -> None: if channels not in (3, 4): msg = "Only 3 or 4 output channels are supported" raise ValueError(msg) @@ -410,15 +421,16 @@ def __init__( pass if numpy and isinstance(table, numpy.ndarray): + numpy_table: NumpyArray = table if copy_table: - table = table.copy() + numpy_table = numpy_table.copy() - if table.shape in [ + if numpy_table.shape in [ (items * channels,), (items, channels), (size[2], size[1], size[0], channels), ]: - table = table.reshape(items * channels) + table = numpy_table.reshape(items * channels) else: wrong_size = True @@ -428,7 +440,8 @@ def __init__( # Convert to a flat list if table and isinstance(table[0], (list, tuple)): - table, raw_table = [], table + raw_table = cast(Sequence[Sequence[int]], table) + flat_table: list[int] = [] for pixel in raw_table: if len(pixel) != channels: msg = ( @@ -436,7 +449,8 @@ def __init__( f"have a length of {channels}." ) raise ValueError(msg) - table.extend(pixel) + flat_table.extend(pixel) + table = flat_table if wrong_size or len(table) != items * channels: msg = ( @@ -449,7 +463,7 @@ def __init__( self.table = table @staticmethod - def _check_size(size: Any) -> list[int]: + def _check_size(size: Any) -> tuple[int, int, int]: try: _, _, _ = size except ValueError as e: @@ -457,7 +471,7 @@ def _check_size(size: Any) -> list[int]: raise ValueError(msg) from e except TypeError: size = (size, size, size) - size = [int(x) for x in size] + size = tuple(int(x) for x in size) for size_1d in size: if not 2 <= size_1d <= 65: msg = "Size should be in [2, 65] range." @@ -465,7 +479,13 @@ def _check_size(size: Any) -> list[int]: return size @classmethod - def generate(cls, size, callback, channels=3, target_mode=None): + def generate( + cls, + size: int | tuple[int, int, int], + callback: Callable[[float, float, float], tuple[float, ...]], + channels: int = 3, + target_mode: str | None = None, + ) -> Color3DLUT: """Generates new LUT using provided callback. :param size: Size of the table. Passed to the constructor. @@ -482,7 +502,7 @@ def generate(cls, size, callback, channels=3, target_mode=None): msg = "Only 3 or 4 output channels are supported" raise ValueError(msg) - table = [0] * (size_1d * size_2d * size_3d * channels) + table: list[float] = [0] * (size_1d * size_2d * size_3d * channels) idx_out = 0 for b in range(size_3d): for g in range(size_2d): @@ -500,7 +520,13 @@ def generate(cls, size, callback, channels=3, target_mode=None): _copy_table=False, ) - def transform(self, callback, with_normals=False, channels=None, target_mode=None): + def transform( + self, + callback: Callable[..., tuple[float, ...]], + with_normals: bool = False, + channels: int | None = None, + target_mode: str | None = None, + ) -> Color3DLUT: """Transforms the table values using provided callback and returns a new LUT with altered values. @@ -564,7 +590,7 @@ def __repr__(self) -> str: r.append(f"target_mode={self.mode}") return "<{}>".format(" ".join(r)) - def filter(self, image): + def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: from . import Image return image.color_lut_3d( diff --git a/src/PIL/_typing.py b/src/PIL/_typing.py index 7075e86726a..09ece18fa60 100644 --- a/src/PIL/_typing.py +++ b/src/PIL/_typing.py @@ -2,7 +2,14 @@ import os import sys -from typing import Protocol, Sequence, TypeVar, Union +from typing import Any, Protocol, Sequence, TypeVar, Union + +try: + import numpy.typing as npt + + NumpyArray = npt.NDArray[Any] +except ImportError: + pass if sys.version_info >= (3, 10): from typing import TypeGuard @@ -10,7 +17,6 @@ try: from typing_extensions import TypeGuard except ImportError: - from typing import Any class TypeGuard: # type: ignore[no-redef] def __class_getitem__(cls, item: Any) -> type[bool]: From c155677c4febece9a280208be11f2dd844fe22ad Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jun 2024 00:39:37 +1000 Subject: [PATCH 265/300] Removed support for Qt 5 --- Tests/test_imageqt.py | 19 +++++++------------ src/PIL/ImageQt.py | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index 88ad1f9eee4..22cd674ce28 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -41,18 +41,13 @@ def checkrgb(r: int, g: int, b: int) -> None: checkrgb(0, 0, 255) -def test_image() -> None: - modes = ["1", "RGB", "RGBA", "L", "P"] - qt_format = ImageQt.QImage.Format if ImageQt.qt_version == "6" else ImageQt.QImage - if hasattr(qt_format, "Format_Grayscale16"): # Qt 5.13+ - modes.append("I;16") - - for mode in modes: - im = hopper(mode) - roundtripped_im = ImageQt.fromqimage(ImageQt.ImageQt(im)) - if mode not in ("RGB", "RGBA"): - im = im.convert("RGB") - assert_image_similar(roundtripped_im, im, 1) +@pytest.mark.parametrize("mode", ("1", "RGB", "RGBA", "L", "P", "I;16")) +def test_image(mode: str) -> None: + im = hopper(mode) + roundtripped_im = ImageQt.fromqimage(ImageQt.ImageQt(im)) + if mode not in ("RGB", "RGBA"): + im = im.convert("RGB") + assert_image_similar(roundtripped_im, im, 1) def test_closed_file() -> None: diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index 293ba4941e3..7819ae9c1f5 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -152,7 +152,7 @@ def _toqclass_helper(im): elif im.mode == "RGBA": data = im.tobytes("raw", "BGRA") format = qt_format.Format_ARGB32 - elif im.mode == "I;16" and hasattr(qt_format, "Format_Grayscale16"): # Qt 5.13+ + elif im.mode == "I;16": im = im.point(lambda i: i * 256) format = qt_format.Format_Grayscale16 From cc83cc8ec866ac0308fc8fc16ff16893540f77e2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jun 2024 10:09:11 +1000 Subject: [PATCH 266/300] Updated type hints --- Tests/helper.py | 4 ++-- Tests/test_file_jpeg.py | 4 +++- Tests/test_file_png.py | 16 +++++++--------- Tests/test_file_ppm.py | 12 ++++-------- Tests/test_file_tiff.py | 2 +- Tests/test_font_leaks.py | 2 +- Tests/test_image.py | 8 ++++---- Tests/test_image_draft.py | 4 +++- Tests/test_image_paste.py | 5 +++++ Tests/test_image_point.py | 2 +- Tests/test_image_putdata.py | 8 ++++---- Tests/test_image_resample.py | 2 +- Tests/test_imagecms.py | 11 ++++++----- Tests/test_imagedraw.py | 2 +- Tests/test_imagedraw2.py | 5 +++-- Tests/test_imagefile.py | 2 +- Tests/test_imagefont.py | 6 +++--- Tests/test_imagefontpil.py | 7 ++++++- Tests/test_imagepalette.py | 2 +- Tests/test_psdraw.py | 12 ++++-------- src/PIL/Image.py | 30 +++++++++++++++++++----------- src/PIL/ImageDraw.py | 4 +++- src/PIL/ImageFont.py | 12 ++++++++---- src/PIL/ImageQt.py | 2 +- src/PIL/ImageTransform.py | 2 +- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/TiffTags.py | 6 ++++-- 27 files changed, 98 insertions(+), 76 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index fe337c09f5c..605a214d400 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -18,7 +18,7 @@ import pytest from packaging.version import parse as parse_version -from PIL import Image, ImageMath, features +from PIL import Image, ImageFile, ImageMath, features logger = logging.getLogger(__name__) @@ -240,7 +240,7 @@ def _test_leak(self, core: Callable[[], None]) -> None: # helpers -def fromstring(data: bytes) -> Image.Image: +def fromstring(data: bytes) -> ImageFile.ImageFile: return Image.open(BytesIO(data)) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 02d0b29d8c8..dbd91751712 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -1033,8 +1033,10 @@ def test_separate_tables(self) -> None: def test_repr_jpeg(self) -> None: im = hopper() + b = im._repr_jpeg_() + assert b is not None - with Image.open(BytesIO(im._repr_jpeg_())) as repr_jpeg: + with Image.open(BytesIO(b)) as repr_jpeg: assert repr_jpeg.format == "JPEG" assert_image_similar(im, repr_jpeg, 17) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 46714a0f00b..0f09768028e 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -535,8 +535,10 @@ def test_roundtrip_no_icc_profile(self) -> None: def test_repr_png(self) -> None: im = hopper() + b = im._repr_png_() + assert b is not None - with Image.open(BytesIO(im._repr_png_())) as repr_png: + with Image.open(BytesIO(b)) as repr_png: assert repr_png.format == "PNG" assert_image_equal(im, repr_png) @@ -768,14 +770,10 @@ def test_seek(self) -> None: def test_save_stdout(self, buffer: bool) -> None: old_stdout = sys.stdout - if buffer: + class MyStdOut: + buffer = BytesIO() - class MyStdOut: - buffer = BytesIO() - - mystdout = MyStdOut() - else: - mystdout = BytesIO() + mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO() sys.stdout = mystdout @@ -785,7 +783,7 @@ class MyStdOut: # Reset stdout sys.stdout = old_stdout - if buffer: + if isinstance(mystdout, MyStdOut): mystdout = mystdout.buffer with Image.open(mystdout) as reloaded: assert_image_equal_tofile(reloaded, TEST_PNG_FILE) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 1bfd0434e96..0a61830a40a 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -368,14 +368,10 @@ def test_mimetypes(tmp_path: Path) -> None: def test_save_stdout(buffer: bool) -> None: old_stdout = sys.stdout - if buffer: + class MyStdOut: + buffer = BytesIO() - class MyStdOut: - buffer = BytesIO() - - mystdout = MyStdOut() - else: - mystdout = BytesIO() + mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO() sys.stdout = mystdout @@ -385,7 +381,7 @@ class MyStdOut: # Reset stdout sys.stdout = old_stdout - if buffer: + if isinstance(mystdout, MyStdOut): mystdout = mystdout.buffer with Image.open(mystdout) as reloaded: assert_image_equal_tofile(reloaded, TEST_FILE) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 17dab25e91b..08ff334ac45 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -120,7 +120,7 @@ def test_seek_too_large(self) -> None: def test_set_legacy_api(self) -> None: ifd = TiffImagePlugin.ImageFileDirectory_v2() with pytest.raises(Exception) as e: - ifd.legacy_api = None + ifd.legacy_api = False assert str(e.value) == "Not allowing setting of legacy api" def test_xyres_tiff(self) -> None: diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 3fb92a62edb..ab8a7f9ecca 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -34,7 +34,7 @@ class TestDefaultFontLeak(TestTTypeFontLeak): def test_leak(self) -> None: if features.check_module("freetype2"): - ImageFont.core = _util.DeferredError(ImportError) + ImageFont.core = _util.DeferredError(ImportError("Disabled for testing")) try: default_font = ImageFont.load_default() finally: diff --git a/Tests/test_image.py b/Tests/test_image.py index 35a7a49a876..a8f61d67700 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -393,13 +393,13 @@ def test_alpha_inplace(self) -> None: # errors with pytest.raises(ValueError): - source.alpha_composite(over, "invalid source") + source.alpha_composite(over, "invalid destination") # type: ignore[arg-type] with pytest.raises(ValueError): - source.alpha_composite(over, (0, 0), "invalid destination") + source.alpha_composite(over, (0, 0), "invalid source") # type: ignore[arg-type] with pytest.raises(ValueError): - source.alpha_composite(over, 0) + source.alpha_composite(over, 0) # type: ignore[arg-type] with pytest.raises(ValueError): - source.alpha_composite(over, (0, 0), 0) + source.alpha_composite(over, (0, 0), 0) # type: ignore[arg-type] with pytest.raises(ValueError): source.alpha_composite(over, (0, 0), (0, -1)) diff --git a/Tests/test_image_draft.py b/Tests/test_image_draft.py index 1ce1a7cd8f6..c9d8c93f314 100644 --- a/Tests/test_image_draft.py +++ b/Tests/test_image_draft.py @@ -16,7 +16,9 @@ def draft_roundtrip( im = Image.new(in_mode, in_size) data = tostring(im, "JPEG") im = fromstring(data) - mode, box = im.draft(req_mode, req_size) + result = im.draft(req_mode, req_size) + assert result is not None + box = result[1] scale, _ = im.decoderconfig assert box[:2] == (0, 0) assert (im.width - scale) < box[2] <= im.width diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index d8f6b65e0d8..01cb880cb31 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -338,3 +338,8 @@ def test_different_sizes(self) -> None: im.copy().paste(im2) im.copy().paste(im2, (0, 0)) + + def test_incorrect_abbreviated_form(self) -> None: + im = Image.new("L", (1, 1)) + with pytest.raises(ValueError): + im.paste(im, im, im) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 05f209351d2..a5d5a15dbab 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -61,4 +61,4 @@ def test_f_lut() -> None: def test_f_mode() -> None: im = hopper("F") with pytest.raises(ValueError): - im.point(None) + im.point([]) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 5e57e4c4c8f..dad26ef144c 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -113,13 +113,13 @@ def test_array_F() -> None: def test_not_flattened() -> None: im = Image.new("L", (1, 1)) with pytest.raises(TypeError): - im.putdata([[0]]) # type: ignore[list-item] + im.putdata([[0]]) with pytest.raises(TypeError): - im.putdata([[0]], 2) # type: ignore[list-item] + im.putdata([[0]], 2) with pytest.raises(TypeError): im = Image.new("I", (1, 1)) - im.putdata([[0]]) # type: ignore[list-item] + im.putdata([[0]]) with pytest.raises(TypeError): im = Image.new("F", (1, 1)) - im.putdata([[0]]) # type: ignore[list-item] + im.putdata([[0]]) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 9b3bdf3306f..d6055b577e3 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -445,7 +445,7 @@ def test_wrong_arguments(self, resample: Image.Resampling) -> None: im.resize((32, 32), resample, (20, 20, 100, 20)) with pytest.raises(TypeError, match="must be sequence of length 4"): - im.resize((32, 32), resample, (im.width, im.height)) + im.resize((32, 32), resample, (im.width, im.height)) # type: ignore[arg-type] with pytest.raises(ValueError, match="can't be negative"): im.resize((32, 32), resample, (-20, 20, 100, 100)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 55f72c3b95c..8a25460d36a 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -103,7 +103,7 @@ def test_sanity() -> None: def test_flags() -> None: - assert ImageCms.Flags.NONE == 0 + assert ImageCms.Flags.NONE.value == 0 assert ImageCms.Flags.GRIDPOINTS(0) == ImageCms.Flags.NONE assert ImageCms.Flags.GRIDPOINTS(256) == ImageCms.Flags.NONE @@ -569,9 +569,9 @@ def create_test_image() -> Image.Image: for delta in nine_grid_deltas: channel_data.paste( channel_pattern, - tuple( - paste_offset[c] + delta[c] * channel_pattern.size[c] - for c in range(2) + ( + paste_offset[0] + delta[0] * channel_pattern.size[0], + paste_offset[1] + delta[1] * channel_pattern.size[1], ), ) chans.append(channel_data) @@ -642,7 +642,8 @@ def test_auxiliary_channels_isolated() -> None: # convert with and without AUX data, test colors are equal src_colorSpace = cast(Literal["LAB", "XYZ", "sRGB"], src_format[1]) source_profile = ImageCms.createProfile(src_colorSpace) - destination_profile = ImageCms.createProfile(dst_format[1]) + dst_colorSpace = cast(Literal["LAB", "XYZ", "sRGB"], dst_format[1]) + destination_profile = ImageCms.createProfile(dst_colorSpace) source_image = src_format[3] test_transform = ImageCms.buildTransform( source_profile, diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 8b55df992e1..a24aa16caba 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1581,7 +1581,7 @@ def test_compute_regular_polygon_vertices_input_error_handling( error_message: str, ) -> None: with pytest.raises(expected_error) as e: - ImageDraw._compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) + ImageDraw._compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) # type: ignore[arg-type] assert str(e.value) == error_message diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 3171eb9aec1..35fe0cf40dd 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -51,9 +51,10 @@ def test_sanity() -> None: pen = ImageDraw2.Pen("blue", width=7) draw.line(list(range(10)), pen) - draw, handler = ImageDraw.getdraw(im) + draw2, handler = ImageDraw.getdraw(im) + assert draw2 is not None pen = ImageDraw2.Pen("blue", width=7) - draw.line(list(range(10)), pen) + draw2.line(list(range(10)), pen) @pytest.mark.parametrize("bbox", BBOX) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index c9dba294359..00cdec95255 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -381,7 +381,7 @@ def test_oversize(self) -> None: def test_encode(self) -> None: encoder = ImageFile.PyEncoder(None) with pytest.raises(NotImplementedError): - encoder.encode(None) + encoder.encode(0) bytes_consumed, errcode = encoder.encode_to_pyfd() assert bytes_consumed == 0 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 73cad513e62..9f5c3984bad 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -209,7 +209,7 @@ def test_getlength( assert length == length_raqm -def test_float_size() -> None: +def test_float_size(layout_engine: ImageFont.Layout) -> None: lengths = [] for size in (48, 48.5, 49): f = ImageFont.truetype( @@ -494,8 +494,8 @@ def test_default_font() -> None: assert_image_equal_tofile(im, "Tests/images/default_font_freetype.png") -@pytest.mark.parametrize("mode", (None, "1", "RGBA")) -def test_getbbox(font: ImageFont.FreeTypeFont, mode: str | None) -> None: +@pytest.mark.parametrize("mode", ("", "1", "RGBA")) +def test_getbbox(font: ImageFont.FreeTypeFont, mode: str) -> None: assert (0, 4, 12, 16) == font.getbbox("A", mode) diff --git a/Tests/test_imagefontpil.py b/Tests/test_imagefontpil.py index 3b1c14b4e18..c4a39d1faf2 100644 --- a/Tests/test_imagefontpil.py +++ b/Tests/test_imagefontpil.py @@ -14,7 +14,7 @@ def setup_module() -> None: if features.check_module("freetype2"): - ImageFont.core = _util.DeferredError(ImportError) + ImageFont.core = _util.DeferredError(ImportError("Disabled for testing")) def teardown_module() -> None: @@ -76,3 +76,8 @@ def test_oom() -> None: font = ImageFont.ImageFont() font._load_pilfont_data(fp, Image.new("L", (1, 1))) font.getmask("A" * 1_000_000) + + +def test_freetypefont_without_freetype() -> None: + with pytest.raises(ImportError): + ImageFont.truetype("Tests/fonts/FreeMono.ttf") diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 8e2db15aab7..17f68710a48 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -45,7 +45,7 @@ def test_getcolor() -> None: # Test unknown color specifier with pytest.raises(ValueError): - palette.getcolor("unknown") + palette.getcolor("unknown") # type: ignore[arg-type] def test_getcolor_rgba_color_rgb_palette() -> None: diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py index 64dfb2c95fb..c3afa908937 100644 --- a/Tests/test_psdraw.py +++ b/Tests/test_psdraw.py @@ -54,14 +54,10 @@ def test_stdout(buffer: bool) -> None: # Temporarily redirect stdout old_stdout = sys.stdout - if buffer: + class MyStdOut: + buffer = BytesIO() - class MyStdOut: - buffer = BytesIO() - - mystdout = MyStdOut() - else: - mystdout = BytesIO() + mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO() sys.stdout = mystdout @@ -71,6 +67,6 @@ class MyStdOut: # Reset stdout sys.stdout = old_stdout - if buffer: + if isinstance(mystdout, MyStdOut): mystdout = mystdout.buffer assert mystdout.getvalue() != b"" diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 57da58522e7..6e2f3865039 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1168,7 +1168,7 @@ def convert_transparency( def quantize( self, colors: int = 256, - method: Quantize | None = None, + method: int | None = None, kmeans: int = 0, palette=None, dither: Dither = Dither.FLOYDSTEINBERG, @@ -1309,7 +1309,7 @@ def _crop( return im.crop((x0, y0, x1, y1)) def draft( - self, mode: str | None, size: tuple[int, int] + self, mode: str | None, size: tuple[int, int] | None ) -> tuple[str, tuple[int, int, float, float]] | None: """ Configures the image file loader so it returns a version of the @@ -1744,7 +1744,7 @@ def entropy(self, mask=None, extrema=None): def paste( self, im: Image | str | float | tuple[float, ...], - box: tuple[int, int, int, int] | tuple[int, int] | None = None, + box: Image | tuple[int, int, int, int] | tuple[int, int] | None = None, mask: Image | None = None, ) -> None: """ @@ -1786,10 +1786,14 @@ def paste( :param mask: An optional mask image. """ - if isImageType(box) and mask is None: + if isImageType(box): + if mask is not None: + msg = "If using second argument as mask, third argument must be None" + raise ValueError(msg) # abbreviated paste(im, mask) syntax mask = box box = None + assert not isinstance(box, Image) if box is None: box = (0, 0) @@ -1995,7 +1999,10 @@ def putalpha(self, alpha): self.im.putband(alpha.im, band) def putdata( - self, data: Sequence[float], scale: float = 1.0, offset: float = 0.0 + self, + data: Sequence[float] | Sequence[Sequence[int]], + scale: float = 1.0, + offset: float = 0.0, ) -> None: """ Copies pixel data from a flattened sequence object into the image. The @@ -2656,7 +2663,7 @@ def thumbnail( self, size: tuple[float, float], resample: Resampling = Resampling.BICUBIC, - reducing_gap: float = 2.0, + reducing_gap: float | None = 2.0, ) -> None: """ Make this image into a thumbnail. This method modifies the @@ -2717,11 +2724,12 @@ def round_aspect(number, key): return x, y box = None + final_size: tuple[int, int] if reducing_gap is not None: preserved_size = preserve_aspect_ratio() if preserved_size is None: return - size = preserved_size + final_size = preserved_size res = self.draft( None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) @@ -2735,13 +2743,13 @@ def round_aspect(number, key): preserved_size = preserve_aspect_ratio() if preserved_size is None: return - size = preserved_size + final_size = preserved_size - if self.size != size: - im = self.resize(size, resample, box=box, reducing_gap=reducing_gap) + if self.size != final_size: + im = self.resize(final_size, resample, box=box, reducing_gap=reducing_gap) self.im = im.im - self._size = size + self._size = final_size self._mode = self.im.mode self.readonly = 0 diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 91f8988387d..a471a7ffde9 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -62,7 +62,9 @@ class ImageDraw: - font = None + font: ( + ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None + ) = None def __init__(self, im: Image.Image, mode: str | None = None) -> None: """ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index fa5608e6cd8..61e7a7abec4 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -33,11 +33,12 @@ import warnings from enum import IntEnum from io import BytesIO +from types import ModuleType from typing import IO, TYPE_CHECKING, Any, BinaryIO from . import Image from ._typing import StrOrBytesPath -from ._util import is_path +from ._util import DeferredError, is_path if TYPE_CHECKING: from . import ImageFile @@ -53,11 +54,10 @@ class Layout(IntEnum): MAX_STRING_LENGTH = 1_000_000 +core: ModuleType | DeferredError try: from . import _imagingft as core except ImportError as ex: - from ._util import DeferredError - core = DeferredError.new(ex) @@ -199,6 +199,7 @@ class FreeTypeFont: """FreeType font wrapper (requires _imagingft service)""" font: Font + font_bytes: bytes def __init__( self, @@ -210,6 +211,9 @@ def __init__( ) -> None: # FIXME: use service provider instead + if isinstance(core, DeferredError): + raise core.ex + if size <= 0: msg = "font size must be greater than 0" raise ValueError(msg) @@ -903,7 +907,7 @@ def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: :return: A font object. """ f: FreeTypeFont | ImageFont - if core.__class__.__name__ == "module" or size is not None: + if isinstance(core, ModuleType) or size is not None: f = truetype( BytesIO( base64.b64decode( diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index 293ba4941e3..a0c16b7656b 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -196,7 +196,7 @@ def __init__(self, im): self.setColorTable(im_data["colortable"]) -def toqimage(im): +def toqimage(im) -> ImageQt: return ImageQt(im) diff --git a/src/PIL/ImageTransform.py b/src/PIL/ImageTransform.py index 80a6116b7cf..ffd7916745c 100644 --- a/src/PIL/ImageTransform.py +++ b/src/PIL/ImageTransform.py @@ -24,7 +24,7 @@ class Transform(Image.ImageTransformHandler): method: Image.Transform - def __init__(self, data: Sequence[int]) -> None: + def __init__(self, data: Sequence[Any]) -> None: self.data = data def getdata(self) -> tuple[Image.Transform, Sequence[int]]: diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 7a32f1cdec0..9d6811cb759 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -428,7 +428,7 @@ def load_read(self, read_bytes: int) -> bytes: return s def draft( - self, mode: str | None, size: tuple[int, int] + self, mode: str | None, size: tuple[int, int] | None ) -> tuple[str, tuple[int, int, float, float]] | None: if len(self.tile) != 1: return None diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 89fad703343..e318c87398c 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -89,7 +89,7 @@ def lookup(tag, group=None): IFD = 13 LONG8 = 16 -TAGS_V2 = { +_tags_v2 = { 254: ("NewSubfileType", LONG, 1), 255: ("SubfileType", SHORT, 1), 256: ("ImageWidth", LONG, 1), @@ -425,9 +425,11 @@ def lookup(tag, group=None): 50784: "Alias Layer Metadata", } +TAGS_V2: dict[int, TagInfo] = {} + def _populate(): - for k, v in TAGS_V2.items(): + for k, v in _tags_v2.items(): # Populate legacy structure. TAGS[k] = v[0] if len(v) == 4: From a2c0a90c44db86c78ccd2cb7ed1a08829e5389bb Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 17 Jun 2024 01:23:36 -0500 Subject: [PATCH 267/300] check required EPS header comments at end of file, not always --- src/PIL/EpsImagePlugin.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 380b1cf0ec4..c7f9625c962 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -230,6 +230,11 @@ def _open(self) -> None: trailer_reached = False def check_required_header_comments() -> None: + """ + The EPS spec requires that some headers exist. + This should be checked after all headers have been read, + or at the end of the file if that comes first. + """ if "PS-Adobe" not in self.info: msg = 'EPS header missing "%!PS-Adobe" comment' raise SyntaxError(msg) @@ -270,6 +275,8 @@ def _read_comment(s: str) -> bool: if byte == b"": # if we didn't read a byte we must be at the end of the file if bytes_read == 0: + if reading_header_comments: + check_required_header_comments() break elif byte in b"\r\n": # if we read a line ending character, ignore it and parse what @@ -365,8 +372,6 @@ def _read_comment(s: str) -> bool: trailer_reached = True bytes_read = 0 - check_required_header_comments() - if not self._size: msg = "cannot determine EPS bounding box" raise OSError(msg) From 00c9448cb0610dd957789de402c331ae3000dc5a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 23 Jun 2024 21:37:41 +1000 Subject: [PATCH 268/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d7231ebeabf..ec3073446bf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,21 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Support unpacking more rawmodes to RGBA palettes #7966 + [radarhere] + +- Removed support for Qt 5 #8159 + [radarhere] + +- Improve ``ImageFont.freetype`` support for XDG directories on Linux #8135 + [mamg22, radarhere] + +- Improved consistency of XMP handling #8069 + [radarhere] + +- Use pkg-config to help find libwebp and raqm #8142 + [radarhere] + - Accept 't' suffix for libtiff version #8126, #8129 [radarhere] From 42381aa1e65735f67f4abbae170d97082f212de5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Jun 2024 06:59:00 +1000 Subject: [PATCH 269/300] Added type hints --- Tests/test_file_bufrstub.py | 7 +++++-- Tests/test_file_gribstub.py | 7 +++++-- Tests/test_file_hdf5stub.py | 7 +++++-- Tests/test_file_png.py | 2 +- Tests/test_file_ppm.py | 2 +- Tests/test_file_psd.py | 2 +- Tests/test_file_tiff_metadata.py | 10 +++++++--- Tests/test_file_webp.py | 5 ++++- Tests/test_image.py | 16 ++++++++++++---- Tests/test_image_array.py | 7 +++++-- Tests/test_imagefile.py | 4 +++- Tests/test_imagefont.py | 2 +- Tests/test_imagewin_pointers.py | 11 ++++++----- Tests/test_psdraw.py | 2 +- 14 files changed, 57 insertions(+), 27 deletions(-) diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py index 939e82e7717..77ee5b0ea12 100644 --- a/Tests/test_file_bufrstub.py +++ b/Tests/test_file_bufrstub.py @@ -64,6 +64,9 @@ def load(self, im: ImageFile.StubImageFile) -> Image.Image: im.fp.close() return Image.new("RGB", (1, 1)) + def is_loaded(self) -> bool: + return self.loaded + def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: self.saved = True @@ -71,10 +74,10 @@ def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: BufrStubImagePlugin.register_handler(handler) with Image.open(TEST_FILE) as im: assert handler.opened - assert not handler.loaded + assert not handler.is_loaded() im.load() - assert handler.loaded + assert handler.is_loaded() temp_file = str(tmp_path / "temp.bufr") im.save(temp_file) diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index 86a9064fc46..aba473d24d0 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -64,6 +64,9 @@ def load(self, im: Image.Image) -> Image.Image: im.fp.close() return Image.new("RGB", (1, 1)) + def is_loaded(self) -> bool: + return self.loaded + def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: self.saved = True @@ -71,10 +74,10 @@ def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: GribStubImagePlugin.register_handler(handler) with Image.open(TEST_FILE) as im: assert handler.opened - assert not handler.loaded + assert not handler.is_loaded() im.load() - assert handler.loaded + assert handler.is_loaded() temp_file = str(tmp_path / "temp.grib") im.save(temp_file) diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index ee1544c51fa..8275bd0d890 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -66,6 +66,9 @@ def load(self, im: Image.Image) -> Image.Image: im.fp.close() return Image.new("RGB", (1, 1)) + def is_loaded(self) -> bool: + return self.loaded + def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: self.saved = True @@ -73,10 +76,10 @@ def save(self, im: Image.Image, fp: IO[bytes], filename: str) -> None: Hdf5StubImagePlugin.register_handler(handler) with Image.open(TEST_FILE) as im: assert handler.opened - assert not handler.loaded + assert not handler.is_loaded() im.load() - assert handler.loaded + assert handler.is_loaded() temp_file = str(tmp_path / "temp.h5") im.save(temp_file) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 0f09768028e..a8b50a6d4cd 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -775,7 +775,7 @@ class MyStdOut: mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO() - sys.stdout = mystdout + sys.stdout = mystdout # type: ignore[assignment] with Image.open(TEST_PNG_FILE) as im: im.save(sys.stdout, "PNG") diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 0a61830a40a..d6451ec18b5 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -373,7 +373,7 @@ class MyStdOut: mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO() - sys.stdout = mystdout + sys.stdout = mystdout # type: ignore[assignment] with Image.open(TEST_FILE) as im: im.save(sys.stdout, "PPM") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 484a1be8f83..ddae8f23577 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -162,7 +162,7 @@ def test_combined_larger_than_size() -> None: ("Tests/images/timeout-dedc7a4ebd856d79b4359bbcc79e8ef231ce38f6.psd", OSError), ], ) -def test_crashes(test_file: str, raises) -> None: +def test_crashes(test_file: str, raises: type[Exception]) -> None: with open(test_file, "rb") as f: with pytest.raises(raises): with Image.open(f): diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 8b816aa4fb5..1e0310001a5 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -11,7 +11,11 @@ from .helper import assert_deep_equal, hopper -TAG_IDS = {info.name: info.value for info in TiffTags.TAGS_V2.values()} +TAG_IDS: dict[str, int] = { + info.name: info.value + for info in TiffTags.TAGS_V2.values() + if info.value is not None +} def test_rt_metadata(tmp_path: Path) -> None: @@ -411,8 +415,8 @@ def test_empty_values() -> None: info = TiffImagePlugin.ImageFileDirectory_v2(head) info.load(data) # Should not raise ValueError. - info = dict(info) - assert 33432 in info + info_dict = dict(info) + assert 33432 in info_dict def test_photoshop_info(tmp_path: Path) -> None: diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 1caf032f6e1..cbc905de4e7 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -5,6 +5,7 @@ import sys import warnings from pathlib import Path +from typing import Any import pytest @@ -70,7 +71,9 @@ def test_read_rgb(self) -> None: # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0) - def _roundtrip(self, tmp_path: Path, mode, epsilon, args={}) -> None: + def _roundtrip( + self, tmp_path: Path, mode: str, epsilon: float, args: dict[str, Any] = {} + ) -> None: temp_file = str(tmp_path / "temp.webp") hopper(mode).save(temp_file, **args) diff --git a/Tests/test_image.py b/Tests/test_image.py index a8f61d67700..5b1ba6289f4 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -8,7 +8,7 @@ import tempfile import warnings from pathlib import Path -from typing import IO +from typing import IO, Any import pytest @@ -175,11 +175,19 @@ def test_pathlib(self, tmp_path: Path) -> None: def test_fp_name(self, tmp_path: Path) -> None: temp_file = str(tmp_path / "temp.jpg") - class FP: + class FP(io.BytesIO): name: str - def write(self, b: bytes) -> None: - pass + if sys.version_info >= (3, 12): + from collections.abc import Buffer + + def write(self, data: Buffer) -> int: + return len(data) + + else: + + def write(self, data: Any) -> int: + return len(data) fp = FP() fp.name = temp_file diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index d7e6c562c39..bb606488286 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any import pytest from packaging.version import parse as parse_version @@ -13,13 +13,16 @@ im = hopper().resize((128, 100)) +if TYPE_CHECKING: + import numpy.typing as npt + def test_toarray() -> None: def test(mode: str) -> tuple[tuple[int, ...], str, int]: ai = numpy.array(im.convert(mode)) return ai.shape, ai.dtype.str, ai.nbytes - def test_with_dtype(dtype) -> None: + def test_with_dtype(dtype: npt.DTypeLike) -> None: ai = numpy.array(im, dtype=dtype) assert ai.dtype == dtype diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 00cdec95255..a269b846a25 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -351,7 +351,9 @@ def test_negsize(self) -> None: ImageFile._save( im, fp, [("MOCK", (xoff, yoff, -10, yoff + ysize), 0, "RGB")] ) - assert MockPyEncoder.last.cleanup_called + last: MockPyEncoder | None = MockPyEncoder.last + assert last + assert last.cleanup_called with pytest.raises(ValueError): ImageFile._save( diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index c3906de2430..e50198087be 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -548,7 +548,7 @@ def _test_fake_loading_font(path_to_fake: str, fontname: str) -> None: def loadable_font( filepath: str, size: int, index: int, encoding: str, *args: Any - ): + ) -> ImageFont.FreeTypeFont: _freeTypeFont = getattr(ImageFont, "_FreeTypeFont") if filepath == path_to_fake: return _freeTypeFont(FONT_PATH, size, index, encoding, *args) diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index e6c312a0c46..c23a5c690a2 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -45,21 +45,22 @@ class BITMAPINFOHEADER(ctypes.Structure): memcpy = ctypes.cdll.msvcrt.memcpy memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t] - CreateCompatibleDC = ctypes.windll.gdi32.CreateCompatibleDC + windll = getattr(ctypes, "windll") + CreateCompatibleDC = windll.gdi32.CreateCompatibleDC CreateCompatibleDC.argtypes = [ctypes.wintypes.HDC] CreateCompatibleDC.restype = ctypes.wintypes.HDC - DeleteDC = ctypes.windll.gdi32.DeleteDC + DeleteDC = windll.gdi32.DeleteDC DeleteDC.argtypes = [ctypes.wintypes.HDC] - SelectObject = ctypes.windll.gdi32.SelectObject + SelectObject = windll.gdi32.SelectObject SelectObject.argtypes = [ctypes.wintypes.HDC, ctypes.wintypes.HGDIOBJ] SelectObject.restype = ctypes.wintypes.HGDIOBJ - DeleteObject = ctypes.windll.gdi32.DeleteObject + DeleteObject = windll.gdi32.DeleteObject DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ] - CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection + CreateDIBSection = windll.gdi32.CreateDIBSection CreateDIBSection.argtypes = [ ctypes.wintypes.HDC, ctypes.c_void_p, diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py index c3afa908937..130ffa863a5 100644 --- a/Tests/test_psdraw.py +++ b/Tests/test_psdraw.py @@ -59,7 +59,7 @@ class MyStdOut: mystdout: MyStdOut | BytesIO = MyStdOut() if buffer else BytesIO() - sys.stdout = mystdout + sys.stdout = mystdout # type: ignore[assignment] ps = PSDraw.PSDraw() _create_document(ps) From e5c4d56a9e62ab5834350ef1e39da542ea1559d5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Jun 2024 15:08:36 +1000 Subject: [PATCH 270/300] Clarify error message when size is missing --- Tests/test_imagedraw2.py | 8 ++++++++ src/PIL/ImageDraw2.py | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 35fe0cf40dd..c80aa739cdf 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -57,6 +57,14 @@ def test_sanity() -> None: draw2.line(list(range(10)), pen) +def test_mode() -> None: + draw = ImageDraw2.Draw("L", (1, 1)) + assert draw.image.mode == "L" + + with pytest.raises(ValueError): + ImageDraw2.Draw("L") + + @pytest.mark.parametrize("bbox", BBOX) def test_ellipse(bbox: Coords) -> None: # Arrange diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index b42f5d9eac1..600e140e1f6 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -56,8 +56,16 @@ class Draw: (Experimental) WCK-style drawing interface """ - def __init__(self, image, size=None, color=None): - if not hasattr(image, "im"): + def __init__( + self, + image: Image.Image | str, + size: tuple[int, int] | list[int] | None = None, + color: float | tuple[float, ...] | str | None = None, + ) -> None: + if isinstance(image, str): + if size is None: + msg = "If image argument is mode string, size must be a list or tuple" + raise ValueError(msg) image = Image.new(image, size, color) self.draw = ImageDraw.Draw(image) self.image = image From f2302ab716e9ee4e45bca3dee2c83ce5f56283b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Jun 2024 21:04:33 +1000 Subject: [PATCH 271/300] Added type hints --- Tests/test_file_spider.py | 1 + Tests/test_imagefile.py | 4 +-- Tests/test_imageops_usm.py | 2 +- Tests/test_imagepalette.py | 8 +++--- src/PIL/BlpImagePlugin.py | 2 ++ src/PIL/ImageDraw2.py | 7 +++++- src/PIL/ImageFile.py | 25 ++++++++++--------- src/PIL/ImagePalette.py | 48 ++++++++++++++++++++++-------------- src/PIL/ImageWin.py | 13 ++++++---- src/PIL/SpiderImagePlugin.py | 22 +++++++++-------- 10 files changed, 79 insertions(+), 53 deletions(-) diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 9b82a962aee..66c88e9d8eb 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -105,6 +105,7 @@ def test_load_image_series() -> None: img_list = SpiderImagePlugin.loadImageSeries(file_list) # Assert + assert img_list is not None assert len(img_list) == 1 assert isinstance(img_list[0], Image.Image) assert img_list[0].size == (128, 128) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index a269b846a25..68b28ef07e2 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -209,7 +209,7 @@ def __init__(self, mode: str, *args: Any) -> None: super().__init__(mode, *args) - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: # eof return -1, 0 @@ -222,7 +222,7 @@ def __init__(self, mode: str, *args: Any) -> None: super().__init__(mode, *args) - def encode(self, buffer): + def encode(self, bufsize: int) -> tuple[int, int, bytes]: return 1, 1, b"" def cleanup(self) -> None: diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index dbdd5b317d5..c8e2c0467de 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -101,7 +101,7 @@ def test_blur_accuracy(test_images: dict[str, ImageFile.ImageFile]) -> None: assert i.im.getpixel((x, y))[c] >= 250 # Fuzzy match. - def gp(x, y): + def gp(x: int, y: int) -> tuple[int, ...]: return i.im.getpixel((x, y)) assert 236 <= gp(7, 4)[0] <= 239 diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 17f68710a48..6cf0079dda7 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -88,13 +88,13 @@ def test_file(tmp_path: Path) -> None: palette.save(f) - p = ImagePalette.load(f) + lut = ImagePalette.load(f) # load returns raw palette information - assert len(p[0]) == 768 - assert p[1] == "RGB" + assert len(lut[0]) == 768 + assert lut[1] == "RGB" - p = ImagePalette.raw(p[1], p[0]) + p = ImagePalette.raw(lut[1], lut[0]) assert isinstance(p, ImagePalette.ImagePalette) assert p.palette == palette.tobytes() diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 59246c6e2e1..b9cefafdd07 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -430,6 +430,7 @@ class BLPEncoder(ImageFile.PyEncoder): def _write_palette(self) -> bytes: data = b"" + assert self.im is not None palette = self.im.getpalette("RGBA", "RGBA") for i in range(len(palette) // 4): r, g, b, a = palette[i * 4 : (i + 1) * 4] @@ -444,6 +445,7 @@ def encode(self, bufsize: int) -> tuple[int, int, bytes]: offset = 20 + 16 * 4 * 2 + len(palette_data) data = struct.pack("<16I", offset, *((0,) * 15)) + assert self.im is not None w, h = self.im.size data += struct.pack("<16I", w * h, *((0,) * 15)) diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index 600e140e1f6..e89a78be4b5 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -24,7 +24,10 @@ """ from __future__ import annotations +from typing import BinaryIO + from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath +from ._typing import StrOrBytesPath class Pen: @@ -45,7 +48,9 @@ def __init__(self, color: str, opacity: int = 255) -> None: class Font: """Stores a TrueType font and color""" - def __init__(self, color, file, size=12): + def __init__( + self, color: str, file: StrOrBytesPath | BinaryIO, size: float = 12 + ) -> None: # FIXME: add support for bitmap fonts self.color = ImageColor.getrgb(color) self.font = ImageFont.truetype(file, size) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 5d67409ea45..69e7ee54811 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -65,7 +65,7 @@ # Helpers -def _get_oserror(error, *, encoder): +def _get_oserror(error: int, *, encoder: bool) -> OSError: try: msg = Image.core.getcodecstatus(error) except AttributeError: @@ -76,7 +76,7 @@ def _get_oserror(error, *, encoder): return OSError(msg) -def raise_oserror(error): +def raise_oserror(error: int) -> OSError: deprecate( "raise_oserror", 12, @@ -154,11 +154,12 @@ def __init__(self, fp=None, filename=None): self.fp.close() raise - def get_format_mimetype(self): + def get_format_mimetype(self) -> str | None: if self.custom_mimetype: return self.custom_mimetype if self.format is not None: return Image.MIME.get(self.format.upper()) + return None def __setstate__(self, state): self.tile = [] @@ -365,7 +366,7 @@ class StubImageFile(ImageFile): certain format, but relies on external code to load the file. """ - def _open(self): + def _open(self) -> None: msg = "StubImageFile subclass must implement _open" raise NotImplementedError(msg) @@ -381,7 +382,7 @@ def load(self): self.__dict__ = image.__dict__ return image.load() - def _load(self): + def _load(self) -> StubHandler | None: """(Hook) Find actual image loader.""" msg = "StubImageFile subclass must implement _load" raise NotImplementedError(msg) @@ -621,7 +622,7 @@ def __init__(self) -> None: self.xoff = 0 self.yoff = 0 - def extents(self): + def extents(self) -> tuple[int, int, int, int]: return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize @@ -661,7 +662,7 @@ def setfd(self, fd): """ self.fd = fd - def setimage(self, im, extents=None): + def setimage(self, im, extents: tuple[int, int, int, int] | None = None) -> None: """ Called from ImageFile to set the core output image for the codec @@ -710,10 +711,10 @@ class PyDecoder(PyCodec): _pulls_fd = False @property - def pulls_fd(self): + def pulls_fd(self) -> bool: return self._pulls_fd - def decode(self, buffer): + def decode(self, buffer: bytes) -> tuple[int, int]: """ Override to perform the decoding process. @@ -738,6 +739,7 @@ def set_as_raw(self, data: bytes, rawmode=None) -> None: if not rawmode: rawmode = self.mode d = Image._getdecoder(self.mode, "raw", rawmode) + assert self.im is not None d.setimage(self.im, self.state.extents()) s = d.decode(data) @@ -760,7 +762,7 @@ class PyEncoder(PyCodec): _pushes_fd = False @property - def pushes_fd(self): + def pushes_fd(self) -> bool: return self._pushes_fd def encode(self, bufsize: int) -> tuple[int, int, bytes]: @@ -775,7 +777,7 @@ def encode(self, bufsize: int) -> tuple[int, int, bytes]: msg = "unavailable in base encoder" raise NotImplementedError(msg) - def encode_to_pyfd(self): + def encode_to_pyfd(self) -> tuple[int, int]: """ If ``pushes_fd`` is ``True``, then this method will be used, and ``encode()`` will only be called once. @@ -787,6 +789,7 @@ def encode_to_pyfd(self): return 0, -8 # bad configuration bytes_consumed, errcode, data = self.encode(0) if data: + assert self.fd is not None self.fd.write(data) return bytes_consumed, errcode diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 6473c4577b0..ed38285dc28 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -38,23 +38,27 @@ class ImagePalette: Defaults to an empty palette. """ - def __init__(self, mode: str = "RGB", palette: Sequence[int] | None = None) -> None: + def __init__( + self, + mode: str = "RGB", + palette: Sequence[int] | bytes | bytearray | None = None, + ) -> None: self.mode = mode - self.rawmode = None # if set, palette contains raw data + self.rawmode: str | None = None # if set, palette contains raw data self.palette = palette or bytearray() self.dirty: int | None = None @property - def palette(self): + def palette(self) -> Sequence[int] | bytes | bytearray: return self._palette @palette.setter - def palette(self, palette): - self._colors = None + def palette(self, palette: Sequence[int] | bytes | bytearray) -> None: + self._colors: dict[tuple[int, ...], int] | None = None self._palette = palette @property - def colors(self) -> dict[tuple[int, int, int] | tuple[int, int, int, int], int]: + def colors(self) -> dict[tuple[int, ...], int]: if self._colors is None: mode_len = len(self.mode) self._colors = {} @@ -66,9 +70,7 @@ def colors(self) -> dict[tuple[int, int, int] | tuple[int, int, int, int], int]: return self._colors @colors.setter - def colors( - self, colors: dict[tuple[int, int, int] | tuple[int, int, int, int], int] - ) -> None: + def colors(self, colors: dict[tuple[int, ...], int]) -> None: self._colors = colors def copy(self) -> ImagePalette: @@ -82,7 +84,7 @@ def copy(self) -> ImagePalette: return new - def getdata(self) -> tuple[str, bytes]: + def getdata(self) -> tuple[str, Sequence[int] | bytes | bytearray]: """ Get palette contents in format suitable for the low-level ``im.putpalette`` primitive. @@ -137,7 +139,7 @@ def _new_color_index( def getcolor( self, - color: tuple[int, int, int] | tuple[int, int, int, int], + color: tuple[int, ...], image: Image.Image | None = None, ) -> int: """Given an rgb tuple, allocate palette entry. @@ -162,12 +164,13 @@ def getcolor( except KeyError as e: # allocate new color slot index = self._new_color_index(image, e) + assert isinstance(self._palette, bytearray) self.colors[color] = index if index * 3 < len(self.palette): self._palette = ( - self.palette[: index * 3] + self._palette[: index * 3] + bytes(color) - + self.palette[index * 3 + 3 :] + + self._palette[index * 3 + 3 :] ) else: self._palette += bytes(color) @@ -204,7 +207,7 @@ def save(self, fp: str | IO[str]) -> None: # Internal -def raw(rawmode, data) -> ImagePalette: +def raw(rawmode, data: Sequence[int] | bytes | bytearray) -> ImagePalette: palette = ImagePalette() palette.rawmode = rawmode palette.palette = data @@ -216,9 +219,9 @@ def raw(rawmode, data) -> ImagePalette: # Factories -def make_linear_lut(black, white): +def make_linear_lut(black: int, white: float) -> list[int]: if black == 0: - return [white * i // 255 for i in range(256)] + return [int(white * i // 255) for i in range(256)] msg = "unavailable when black is non-zero" raise NotImplementedError(msg) # FIXME @@ -251,15 +254,22 @@ def wedge(mode: str = "RGB") -> ImagePalette: return ImagePalette(mode, [i // len(mode) for i in palette]) -def load(filename): +def load(filename: str) -> tuple[bytes, str]: # FIXME: supports GIMP gradients only with open(filename, "rb") as fp: - for paletteHandler in [ + paletteHandlers: list[ + type[ + GimpPaletteFile.GimpPaletteFile + | GimpGradientFile.GimpGradientFile + | PaletteFile.PaletteFile + ] + ] = [ GimpPaletteFile.GimpPaletteFile, GimpGradientFile.GimpGradientFile, PaletteFile.PaletteFile, - ]: + ] + for paletteHandler in paletteHandlers: try: fp.seek(0) lut = paletteHandler(fp).getpalette() diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 6c29e2590cf..978c5a9d176 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -69,19 +69,22 @@ class Dib: defines the size of the image. """ - def __init__(self, image, size=None): - if hasattr(image, "mode") and hasattr(image, "size"): + def __init__( + self, image: Image.Image | str, size: tuple[int, int] | list[int] | None = None + ) -> None: + if isinstance(image, str): + mode = image + image = "" + else: mode = image.mode size = image.size - else: - mode = image - image = None if mode not in ["1", "L", "P", "RGB"]: mode = Image.getmodebase(mode) self.image = Image.core.display(mode, size) self.mode = mode self.size = size if image: + assert not isinstance(image, str) self.paste(image) def expose(self, handle): diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index a6cc00019da..f5a09c3ef61 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -37,12 +37,12 @@ import os import struct import sys -from typing import IO, TYPE_CHECKING +from typing import IO, TYPE_CHECKING, Any, Tuple, cast from . import Image, ImageFile -def isInt(f): +def isInt(f: Any) -> int: try: i = int(f) if f - i == 0: @@ -62,7 +62,7 @@ def isInt(f): # otherwise returns 0 -def isSpiderHeader(t): +def isSpiderHeader(t: tuple[float, ...]) -> int: h = (99,) + t # add 1 value so can use spider header index start=1 # header values 1,2,5,12,13,22,23 should be integers for i in [1, 2, 5, 12, 13, 22, 23]: @@ -82,7 +82,7 @@ def isSpiderHeader(t): return labbyt -def isSpiderImage(filename): +def isSpiderImage(filename: str) -> int: with open(filename, "rb") as fp: f = fp.read(92) # read 23 * 4 bytes t = struct.unpack(">23f", f) # try big-endian first @@ -184,13 +184,15 @@ def seek(self, frame: int) -> None: self._open() # returns a byte image after rescaling to 0..255 - def convert2byte(self, depth=255): - (minimum, maximum) = self.getextrema() - m = 1 + def convert2byte(self, depth: int = 255) -> Image.Image: + extrema = self.getextrema() + assert isinstance(extrema[0], float) + minimum, maximum = cast(Tuple[float, float], extrema) + m: float = 1 if maximum != minimum: m = depth / (maximum - minimum) b = -m * minimum - return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + return self.point(lambda i: i * m + b).convert("L") if TYPE_CHECKING: from . import ImageTk @@ -207,10 +209,10 @@ def tkPhotoImage(self) -> ImageTk.PhotoImage: # given a list of filenames, return a list of images -def loadImageSeries(filelist=None): +def loadImageSeries(filelist: list[str] | None = None) -> list[SpiderImageFile] | None: """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" if filelist is None or len(filelist) < 1: - return + return None imglist = [] for img in filelist: From 065aeaea472b38896d43363095cccc05220b3c9c Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 24 Jun 2024 07:28:47 -0500 Subject: [PATCH 272/300] improve check_required_header_comments() description Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/EpsImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index c7f9625c962..f31b1c1a2d0 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -231,9 +231,9 @@ def _open(self) -> None: def check_required_header_comments() -> None: """ - The EPS spec requires that some headers exist. - This should be checked after all headers have been read, - or at the end of the file if that comes first. + The EPS specification requires that some headers exist. + This should be checked when the header comments formally end, + when image data starts, or when the file ends, whichever comes first. """ if "PS-Adobe" not in self.info: msg = 'EPS header missing "%!PS-Adobe" comment' From a941f096aa06df7d71dc451151076925dc8dc9f9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Jun 2024 18:49:40 +1000 Subject: [PATCH 273/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ec3073446bf..9c731fe9463 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Clarify ImageDraw2 error message when size is missing #8165 + [radarhere] + - Support unpacking more rawmodes to RGBA palettes #7966 [radarhere] From ded404507bf8e8ae33ee194e9e1dc275e25edc82 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jun 2024 17:02:19 +1000 Subject: [PATCH 274/300] Removed ignores --- src/PIL/ImageGrab.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 96a28bb3570..aa53abd2084 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -22,7 +22,6 @@ import subprocess import sys import tempfile -from typing import Union, cast from . import Image @@ -70,15 +69,15 @@ def grab( left, top, right, bottom = bbox im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) return im - xdisplay = cast(Union[str, None], xdisplay) # type: ignore[redundant-cast, unused-ignore] + display_name: str | None = xdisplay try: if not Image.core.HAVE_XCB: msg = "Pillow was built without XCB support" raise OSError(msg) - size, data = Image.core.grabscreen_x11(xdisplay) + size, data = Image.core.grabscreen_x11(display_name) except OSError: if ( - xdisplay is None + display_name is None and sys.platform not in ("darwin", "win32") and shutil.which("gnome-screenshot") ): From d0d53d4bac853acaad51af7d6aa9582cad59945a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jun 2024 17:02:28 +1000 Subject: [PATCH 275/300] Added type hints to tests --- Tests/test_imagegrab.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index e23adeb7083..5dfa51697c2 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -89,6 +89,7 @@ def test_grabclipboard_file(self) -> None: p.communicate() im = ImageGrab.grabclipboard() + assert isinstance(im, list) assert len(im) == 1 assert os.path.samefile(im[0], "Tests/images/hopper.gif") @@ -105,6 +106,7 @@ def test_grabclipboard_png(self) -> None: p.communicate() im = ImageGrab.grabclipboard() + assert isinstance(im, Image.Image) assert_image_equal_tofile(im, "Tests/images/hopper.png") @pytest.mark.skipif( @@ -120,6 +122,7 @@ def test_grabclipboard_wl_clipboard(self, ext: str) -> None: with open(image_path, "rb") as fp: subprocess.call(["wl-copy"], stdin=fp) im = ImageGrab.grabclipboard() + assert isinstance(im, Image.Image) assert_image_equal_tofile(im, image_path) @pytest.mark.skipif( From ab183958188e38b6501410b4f34a561fbea0ba56 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 25 Jun 2024 07:13:17 +1000 Subject: [PATCH 276/300] Added comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondrej Baranovič --- src/PIL/ImageGrab.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index aa53abd2084..e27ca7e5033 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -69,6 +69,7 @@ def grab( left, top, right, bottom = bbox im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) return im + # Cast to Optional[str] needed for Windows and macOS. display_name: str | None = xdisplay try: if not Image.core.HAVE_XCB: From 44b82e4513529fd20479169eba22366815f7821c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Jun 2024 21:40:30 +1000 Subject: [PATCH 277/300] Use more specific error --- Tests/test_file_jpeg2k.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index ed7ea4fcfbc..c6f1e0f52f5 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -337,7 +337,7 @@ def test_issue_6194() -> None: def test_unbound_local() -> None: # prepatch, a malformed jp2 file could cause an UnboundLocalError exception. - with pytest.raises(OSError): + with pytest.raises(UnidentifiedImageError): with Image.open("Tests/images/unbound_variable.jp2"): pass From e7c1da3cc8a26b821e0a4ec2d4651d62271f00c5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Jun 2024 21:42:25 +1000 Subject: [PATCH 278/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9c731fe9463..6375f5c6397 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Read IM and TIFF images as RGB, rather than RGBX #7997 + [radarhere] + +- Only preserve TIFF IPTC_NAA_CHUNK tag if type is BYTE or UNDEFINED #7948 + [radarhere] + - Clarify ImageDraw2 error message when size is missing #8165 [radarhere] From 99666dac291da395dc03743fb540277c9da0b7b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Jun 2024 22:00:51 +1000 Subject: [PATCH 279/300] Simplified casts --- src/PIL/Image.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c2eac31c730..e9cf7cebcc4 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1113,7 +1113,7 @@ def convert_transparency( if trns is not None: try: new_im.info["transparency"] = new_im.palette.getcolor( - cast(Tuple[int, int, int], trns), # trns was converted to RGB + cast(Tuple[int, ...], trns), # trns was converted to RGB new_im, ) except Exception: @@ -1163,12 +1163,9 @@ def convert_transparency( # crash fail if we leave a bytes transparency in an rgb/l mode. del new_im.info["transparency"] if trns is not None: - if new_im.mode == "P": + if new_im.mode == "P" and new_im.palette: try: - new_im.info["transparency"] = new_im.palette.getcolor( - cast(Tuple[int, int, int], trns), # trns was converted to RGB - new_im, - ) + new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im) except ValueError as e: del new_im.info["transparency"] if str(e) != "cannot allocate more than 256 colors": From 6f28a0c009affcfb8f67c4bde921aa7d6684a7ac Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 11 Apr 2024 00:40:11 -0500 Subject: [PATCH 280/300] Fix TGA rawmode BGR;15 --- src/PIL/TgaImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index f16f075df05..ebf9a1b80cd 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -36,7 +36,7 @@ (3, 1): "1", (3, 8): "L", (3, 16): "LA", - (2, 16): "BGR;5", + (2, 16): "BGR;15", (2, 24): "BGR", (2, 32): "BGRA", } From 872ff125fcc9182fcc4cbcbf3d1bc5632a21860b Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 11 Apr 2024 00:53:06 -0500 Subject: [PATCH 281/300] "BGR;15" -> "BGRA;15" --- src/PIL/TgaImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index ebf9a1b80cd..6a6bceacaba 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -36,7 +36,7 @@ (3, 1): "1", (3, 8): "L", (3, 16): "LA", - (2, 16): "BGR;15", + (2, 16): "BGRA;15", (2, 24): "BGR", (2, 32): "BGRA", } @@ -118,7 +118,7 @@ def _open(self) -> None: start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] if mapdepth == 16: self.palette = ImagePalette.raw( - "BGR;15", b"\0" * 2 * start + self.fp.read(2 * size) + "BGRA;15", b"\0" * 2 * start + self.fp.read(2 * size) ) elif mapdepth == 24: self.palette = ImagePalette.raw( From e5c6d883d44aab0ba27ec8faf384f9a3e3b5aa01 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Thu, 11 Apr 2024 07:56:29 -0500 Subject: [PATCH 282/300] set palette mode for BGRA;15 Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/TgaImagePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 6a6bceacaba..9bc483fc5b7 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -120,6 +120,7 @@ def _open(self) -> None: self.palette = ImagePalette.raw( "BGRA;15", b"\0" * 2 * start + self.fp.read(2 * size) ) + self.palette.mode = "RGBA" elif mapdepth == 24: self.palette = ImagePalette.raw( "BGR", b"\0" * 3 * start + self.fp.read(3 * size) From 38402554869bdb2af6ac81bcb90070e939a41c38 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Jun 2024 20:50:17 +1000 Subject: [PATCH 283/300] Invert alpha bit for map depth 16 --- Tests/images/p_16.png | Bin 378 -> 414 bytes Tests/test_file_tga.py | 5 +++-- src/PIL/TgaImagePlugin.py | 8 +++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Tests/images/p_16.png b/Tests/images/p_16.png index e3588641277b804c0100707146a46a506f33d0d6..458f7138e8026159cb9e52a18088db59358e4c44 100644 GIT binary patch literal 414 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU`+LNaSW-L^Y)S>UxNY}4gE|G$2o{oCumKWmr&|6dn*{vnHidV>Yy5l#g^ ohC-$eVTU;kk4A;CheS1lgtNdOv)R!Vz;I{qboFyt=akR{0D$9pJpcdz literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRh7^V083!aSW-L^Y-FF&I1Yp3 None: def test_palette_depth_16(tmp_path: Path) -> None: with Image.open("Tests/images/p_16.tga") as im: - assert_image_equal_tofile(im.convert("RGB"), "Tests/images/p_16.png") + assert im.palette.mode == "RGBA" + assert_image_equal_tofile(im.convert("RGBA"), "Tests/images/p_16.png") out = str(tmp_path / "temp.png") im.save(out) with Image.open(out) as reloaded: - assert_image_equal_tofile(reloaded.convert("RGB"), "Tests/images/p_16.png") + assert_image_equal_tofile(reloaded.convert("RGBA"), "Tests/images/p_16.png") def test_id_field() -> None: diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index 9bc483fc5b7..d158fe49203 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -117,9 +117,11 @@ def _open(self) -> None: # read palette start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] if mapdepth == 16: - self.palette = ImagePalette.raw( - "BGRA;15", b"\0" * 2 * start + self.fp.read(2 * size) - ) + colormap = self.fp.read(2 * size) + palette_data = bytearray(b"\0" * 2 * start) + for a, b in zip(colormap[::2], colormap[1::2]): + palette_data += bytearray((a, b ^ 128)) + self.palette = ImagePalette.raw("BGRA;15", bytes(palette_data)) self.palette.mode = "RGBA" elif mapdepth == 24: self.palette = ImagePalette.raw( From 4cadf5c99f690b82cf281b601f96bf5711e52c58 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Tue, 25 Jun 2024 09:17:40 -0500 Subject: [PATCH 284/300] use bytes/bytearray int constructor instead of string multiplication --- src/PIL/TgaImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index d158fe49203..c41b31d6852 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -118,18 +118,18 @@ def _open(self) -> None: start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] if mapdepth == 16: colormap = self.fp.read(2 * size) - palette_data = bytearray(b"\0" * 2 * start) + palette_data = bytearray(2 * start) for a, b in zip(colormap[::2], colormap[1::2]): palette_data += bytearray((a, b ^ 128)) self.palette = ImagePalette.raw("BGRA;15", bytes(palette_data)) self.palette.mode = "RGBA" elif mapdepth == 24: self.palette = ImagePalette.raw( - "BGR", b"\0" * 3 * start + self.fp.read(3 * size) + "BGR", bytes(3 * start) + self.fp.read(3 * size) ) elif mapdepth == 32: self.palette = ImagePalette.raw( - "BGRA", b"\0" * 4 * start + self.fp.read(4 * size) + "BGRA", bytes(4 * start) + self.fp.read(4 * size) ) else: msg = "unknown TGA map depth" From 58d5a73fac7b4084fe4ba66a1a2c4ed097d0d557 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 17 Jun 2024 09:29:14 -0500 Subject: [PATCH 285/300] raise SyntaxError when parsing codestream rather than returning an empty string Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/Jpeg2KImagePlugin.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 39eb1c20314..e50cd7799a3 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -97,7 +97,7 @@ def next_box_type(self) -> bytes: return tbox -def _parse_codestream(fp): +def _parse_codestream(fp) -> tuple[tuple[int, int], str]: """Parse the JPEG 2000 codestream to extract the size and component count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" @@ -122,7 +122,8 @@ def _parse_codestream(fp): elif csiz == 4: mode = "RGBA" else: - mode = "" + msg = "unable to determine J2K image mode" + raise SyntaxError(msg) return size, mode @@ -237,10 +238,6 @@ def _open(self) -> None: msg = "not a JPEG 2000 file" raise SyntaxError(msg) - if self.size is None or not self.mode: - msg = "unable to determine size/mode" - raise SyntaxError(msg) - self._reduce = 0 self.layers = 0 From 88b21e725432d418e56bdf0721f8581d14a24e40 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 25 Jun 2024 21:38:47 +1000 Subject: [PATCH 286/300] Added test --- Tests/images/unknown_mode.j2k | Bin 0 -> 318 bytes Tests/test_file_jpeg2k.py | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 Tests/images/unknown_mode.j2k diff --git a/Tests/images/unknown_mode.j2k b/Tests/images/unknown_mode.j2k new file mode 100644 index 0000000000000000000000000000000000000000..38719fe2561bb1341b69ed96e63f23bfb2c45d58 GIT binary patch literal 318 zcmezG|38pHlK})cpcu>n5-d;*;j{8HGX4)@-~sX&8JJjD7#RP@FmO3EbaYGrqW>uj zstktDsYS(^`FRRPdM0`X|0ghTfsHc&YM*@mj{y%C z&*TFPKN$`%2pnLLWnpJ#2J#pWFjxuje6;1!2CHWT>M%yAuLr4Lz#z||z{q}pp+?Dp z!AgXIp@Yf&e?50US3Ot@6HtpOiWZ>pKx8N2z|717u|m*+fun)v&wOrsUWg`Upe8ef jCVQaa3P6|eFfc!4VPpo{^^*x=R||JSd_8wE None: assert im.getpixel((5, 5)) == 31 +def test_unknown_j2k_mode() -> None: + with pytest.raises(UnidentifiedImageError): + with Image.open("Tests/images/unknown_mode.j2k"): + pass + + def test_unbound_local() -> None: # prepatch, a malformed jp2 file could cause an UnboundLocalError exception. with pytest.raises(OSError): From d2d03a1da24503f547fce334ff02b6f7183d2bc6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jun 2024 06:35:15 +1000 Subject: [PATCH 287/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6375f5c6397..20bc34152ed 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,27 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Do not use first frame duration for other frames when saving APNG images #8104 + [radarhere] + +- Consider I;16 pixel size when using a 1 mode mask #8112 + [radarhere] + +- When saving multiple PNG frames, convert to mode rather than raw mode #8087 + [radarhere] + +- Added byte support to FreeTypeFont #8141 + [radarhere] + +- Allow float center for rotate operations #8114 + [radarhere] + +- Do not read layers immediately when opening PSD images #8039 + [radarhere] + +- Restore original thread state #8065 + [radarhere] + - Read IM and TIFF images as RGB, rather than RGBX #7997 [radarhere] From 50d18bf547727d397b192555b2fa5b9a50e5aa63 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jun 2024 07:38:53 +1000 Subject: [PATCH 288/300] Truncated image to reduce file size --- Tests/images/ultrahdr.jpg | Bin 520842 -> 48197 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/images/ultrahdr.jpg b/Tests/images/ultrahdr.jpg index 1c5cc4adfe07ff4bbf80e79a784caf287c3b867f..34f615b61aac021eff116bec24ae1facefb83d0a 100644 GIT binary patch delta 9 QcmeBrDu46`(+1Z)02#Oh{{R30 delta 475939 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�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 z%0D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 #bbzLjl!^@>VKyP+O_S; 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%0DvG;*BG+6k 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 From 6ee41897e277a6e05f3673255b21333d7279d9fa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jun 2024 07:19:44 +1000 Subject: [PATCH 289/300] Added dedicated unpacker for inverted alpha --- src/PIL/TgaImagePlugin.py | 14 +++++--------- src/libImaging/Unpack.c | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index c41b31d6852..39104aeced9 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -36,7 +36,7 @@ (3, 1): "1", (3, 8): "L", (3, 16): "LA", - (2, 16): "BGRA;15", + (2, 16): "BGRA;15Z", (2, 24): "BGR", (2, 32): "BGRA", } @@ -87,9 +87,7 @@ def _open(self) -> None: elif imagetype in (1, 9): self._mode = "P" if colormaptype else "L" elif imagetype in (2, 10): - self._mode = "RGB" - if depth == 32: - self._mode = "RGBA" + self._mode = "RGB" if depth == 24 else "RGBA" else: msg = "unknown TGA mode" raise SyntaxError(msg) @@ -117,11 +115,9 @@ def _open(self) -> None: # read palette start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] if mapdepth == 16: - colormap = self.fp.read(2 * size) - palette_data = bytearray(2 * start) - for a, b in zip(colormap[::2], colormap[1::2]): - palette_data += bytearray((a, b ^ 128)) - self.palette = ImagePalette.raw("BGRA;15", bytes(palette_data)) + self.palette = ImagePalette.raw( + "BGRA;15Z", bytes(2 * start) + self.fp.read(2 * size) + ) self.palette.mode = "RGBA" elif mapdepth == 24: self.palette = ImagePalette.raw( diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 063bcbbcc48..eaa4374e3e9 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -718,6 +718,21 @@ ImagingUnpackBGRA15(UINT8 *out, const UINT8 *in, int pixels) { } } +void +ImagingUnpackBGRA15Z(UINT8 *out, const UINT8 *in, int pixels) { + int i, pixel; + /* RGB, rearranged channels, 5/5/5/1 bits per pixel, inverted alpha */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel >> 5) & 31) * 255 / 31; + out[R] = ((pixel >> 10) & 31) * 255 / 31; + out[A] = ~((pixel >> 15) * 255); + out += 4; + in += 2; + } +} + void ImagingUnpackRGB16(UINT8 *out, const UINT8 *in, int pixels) { int i, pixel; @@ -1538,7 +1553,7 @@ static struct { /* flags: "I" inverted data; "R" reversed bit order; "B" big endian byte order (default is little endian); "L" line - interleave, "S" signed, "F" floating point */ + interleave, "S" signed, "F" floating point, "Z" inverted alpha */ /* exception: rawmodes "I" and "F" are always native endian byte order */ @@ -1646,6 +1661,7 @@ static struct { {"RGBA", "RGBA;L", 32, unpackRGBAL}, {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15}, {"RGBA", "BGRA;15", 16, ImagingUnpackBGRA15}, + {"RGBA", "BGRA;15Z", 16, ImagingUnpackBGRA15Z}, {"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B}, {"RGBA", "RGBA;16L", 64, unpackRGBA16L}, {"RGBA", "RGBA;16B", 64, unpackRGBA16B}, From a90a9d5ea5fce7257cb22f904b6f013ec53d7eb3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jun 2024 19:14:58 +1000 Subject: [PATCH 290/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 20bc34152ed..cfb987bebb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Do not detect Ultra HDR images as MPO #8056 + [radarhere] + +- Raise SyntaxError specific to JP2 #8146 + [Yay295, radarhere] + - Do not use first frame duration for other frames when saving APNG images #8104 [radarhere] From fd3f6c1a929e52332ac43fdbd328d767b9301839 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jun 2024 19:31:40 +1000 Subject: [PATCH 291/300] Remove zero-byte end padding when parsing any XMP data --- Tests/test_image.py | 22 ++++++++++++++++++++++ src/PIL/Image.py | 2 +- src/PIL/JpegImagePlugin.py | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 5b1ba6289f4..c5cf5b38331 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -8,6 +8,7 @@ import tempfile import warnings from pathlib import Path +from types import ModuleType from typing import IO, Any import pytest @@ -35,6 +36,12 @@ skip_unless_feature, ) +ElementTree: ModuleType | None +try: + from defusedxml import ElementTree +except ImportError: + ElementTree = None + # Deprecation helper def helper_image_new(mode: str, size: tuple[int, int]) -> Image.Image: @@ -921,6 +928,21 @@ def test_empty_xmp(self) -> None: with Image.open("Tests/images/hopper.gif") as im: assert im.getxmp() == {} + def test_getxmp_padded(self) -> None: + im = Image.new("RGB", (1, 1)) + im.info["xmp"] = ( + b'\n' + b'\n\x00\x00' + ) + if ElementTree is None: + with pytest.warns( + UserWarning, + match="XMP data cannot be read without defusedxml dependency", + ): + assert im.getxmp() == {} + else: + assert im.getxmp() == {"xmpmeta": None} + @pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0))) def test_zero_tobytes(self, size: tuple[int, int]) -> None: im = Image.new("RGB", size) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index e9fb55d6b7a..cbeefa189a5 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1511,7 +1511,7 @@ def get_value(element): return {} if "xmp" not in self.info: return {} - root = ElementTree.fromstring(self.info["xmp"]) + root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00")) return {get_name(root.tag): get_value(root)} def getexif(self) -> Exif: diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index b7d0d24f242..ada52915373 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -96,7 +96,7 @@ def APP(self, marker): self.info["exif"] = s self._exif_offset = self.fp.tell() - n + 6 elif marker == 0xFFE1 and s[:29] == b"http://ns.adobe.com/xap/1.0/\x00": - self.info["xmp"] = s.split(b"\x00")[1] + self.info["xmp"] = s.split(b"\x00", 1)[1] elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change From 6863c87c0151c9330c4ef1db1bb9058b8d34f703 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jun 2024 18:06:47 +1000 Subject: [PATCH 292/300] Added test for non-colormap 16-bit image --- Tests/images/rgba16.tga | Bin 0 -> 48 bytes Tests/test_file_tga.py | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 Tests/images/rgba16.tga diff --git a/Tests/images/rgba16.tga b/Tests/images/rgba16.tga new file mode 100644 index 0000000000000000000000000000000000000000..3918647a23cf31628e0beb8a14084ef7383582a1 GIT binary patch literal 48 qcmZQzU}As)CI&_Z0S@_)SIsbykf2c4FwbC5e?Q#_H%}i|Jq7@4vIgk@ literal 0 HcmV?d00001 diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 15c7bbdb89e..a03a6a6e10a 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -81,6 +81,14 @@ def test_palette_depth_16(tmp_path: Path) -> None: assert_image_equal_tofile(reloaded.convert("RGBA"), "Tests/images/p_16.png") +def test_rgba_16() -> None: + with Image.open("Tests/images/rgba16.tga") as im: + assert im.mode == "RGBA" + + assert im.getpixel((0, 0)) == (172, 0, 255, 255) + assert im.getpixel((1, 0)) == (0, 255, 82, 0) + + def test_id_field() -> None: # tga file with id field test_file = "Tests/images/tga_id_field.tga" From 304cf484aaba5285cfdf854c31cfe51505de3f9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 Jun 2024 13:29:22 +1000 Subject: [PATCH 293/300] Do not presume "xmp" info simply because "XML:com.adobe.xmp" info exists --- Tests/test_imageops.py | 10 ++++++++++ src/PIL/ImageOps.py | 23 ++++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 27a6090c5fa..64ef929c48d 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -432,6 +432,16 @@ def check(orientation_im: Image.Image) -> None: assert 0x0112 not in transposed_im.getexif() +def test_exif_transpose_xml_without_xmp() -> None: + with Image.open("Tests/images/xmp_tags_orientation.png") as im: + assert im.getexif()[0x0112] == 3 + assert "XML:com.adobe.xmp" in im.info + + del im.info["xmp"] + transposed_im = ImageOps.exif_transpose(im) + assert 0x0112 not in transposed_im.getexif() + + def test_exif_transpose_in_place() -> None: with Image.open("Tests/images/orientation_rectangle.jpg") as im: assert im.size == (2, 1) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 6d2814345a3..a84c0834561 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -709,17 +709,18 @@ def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image exif_image.info["exif"] = exif.tobytes() elif "Raw profile type exif" in exif_image.info: exif_image.info["Raw profile type exif"] = exif.tobytes().hex() - elif "XML:com.adobe.xmp" in exif_image.info: - for pattern in ( - r'tiff:Orientation="([0-9])"', - r"([0-9])", - ): - exif_image.info["XML:com.adobe.xmp"] = re.sub( - pattern, "", exif_image.info["XML:com.adobe.xmp"] - ) - exif_image.info["xmp"] = re.sub( - pattern.encode(), b"", exif_image.info["xmp"] - ) + for key in ("XML:com.adobe.xmp", "xmp"): + if key in exif_image.info: + for pattern in ( + r'tiff:Orientation="([0-9])"', + r"([0-9])", + ): + value = exif_image.info[key] + exif_image.info[key] = ( + re.sub(pattern, "", value) + if isinstance(value, str) + else re.sub(pattern.encode(), b"", value) + ) if not in_place: return transposed_image elif not in_place: From 7b636a8e6c40b1d0802cbfc4394b5cae1233642a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 27 Jun 2024 20:28:36 +1000 Subject: [PATCH 294/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cfb987bebb9..3de9ef65bf2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Remove zero-byte end padding when parsing any XMP data #8171 + [radarhere] + - Do not detect Ultra HDR images as MPO #8056 [radarhere] From d18192cecfa239f2ce66991777199e32eedaef82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 04:20:09 +0000 Subject: [PATCH 295/300] Update dependency mypy to v1.10.1 --- .ci/requirements-mypy.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/requirements-mypy.txt b/.ci/requirements-mypy.txt index a0dcb92d22d..6dd43248837 100644 --- a/.ci/requirements-mypy.txt +++ b/.ci/requirements-mypy.txt @@ -1 +1 @@ -mypy==1.10.0 +mypy==1.10.1 From 88cd6d41eff702ac83300807b097f6044b665025 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:28:42 +1000 Subject: [PATCH 296/300] Rearranged comments Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- src/PIL/JpegImagePlugin.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index a9a89e58bd7..59259b7194c 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -487,18 +487,13 @@ def _read_dpi_from_exif(self) -> None: dpi *= 2.54 self.info["dpi"] = dpi, dpi except ( - struct.error, - KeyError, - SyntaxError, - TypeError, - ValueError, - ZeroDivisionError, + struct.error, # truncated EXIF + KeyError, # dpi not included + SyntaxError, # invalid/unreadable EXIF + TypeError, # dpi is an invalid float + ValueError, # dpi is an invalid float + ZeroDivisionError, # invalid dpi rational value ): - # struct.error for truncated EXIF - # KeyError for dpi not included - # SyntaxError for invalid/unreadable EXIF - # ValueError or TypeError for dpi being an invalid float - # ZeroDivisionError for invalid dpi rational value self.info["dpi"] = 72, 72 def _getmp(self): From b48d17565652d9537bacb910101ad1ef9f56811a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Jun 2024 21:13:41 +1000 Subject: [PATCH 297/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 3de9ef65bf2..a6f2bc12c1d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,21 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Deprecate non-image ImageCms modes #8031 + [radarhere] + +- Fixed processing multiple JPEG EXIF markers #8127 + [radarhere] + +- Do not preserve EXIFIFD tag by default when saving TIFF images #8110 + [radarhere] + +- Added ImageFont.load_default_imagefont() #8086 + [radarhere] + +- Added Image.WARN_POSSIBLE_FORMATS #8063 + [radarhere] + - Remove zero-byte end padding when parsing any XMP data #8171 [radarhere] From c9ec76aa0db65b52c96dd3973a755367a9c41755 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Jun 2024 21:27:44 +1000 Subject: [PATCH 298/300] Raise FileNotFoundError if show_file() path does not exist --- Tests/test_imageshow.py | 22 ++++++++++++++++++++++ docs/releasenotes/10.4.0.rst | 34 ++++++++-------------------------- src/PIL/ImageShow.py | 16 ++++++++++++++++ 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 4e9291fbbfb..0bff4389607 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from typing import Any import pytest @@ -65,6 +66,27 @@ def test_show_without_viewers() -> None: ImageShow._viewers = viewers +@pytest.mark.parametrize( + "viewer", + ( + ImageShow.Viewer(), + ImageShow.WindowsViewer(), + ImageShow.MacViewer(), + ImageShow.XDGViewer(), + ImageShow.DisplayViewer(), + ImageShow.GmDisplayViewer(), + ImageShow.EogViewer(), + ImageShow.XVViewer(), + ImageShow.IPythonViewer(), + ), +) +def test_show_file(viewer: ImageShow.Viewer) -> None: + assert not os.path.exists("missing.png") + + with pytest.raises(FileNotFoundError): + viewer.show_file("missing.png") + + def test_viewer() -> None: viewer = ImageShow.Viewer() diff --git a/docs/releasenotes/10.4.0.rst b/docs/releasenotes/10.4.0.rst index 96300c008e8..8d3706be617 100644 --- a/docs/releasenotes/10.4.0.rst +++ b/docs/releasenotes/10.4.0.rst @@ -4,21 +4,16 @@ Security ======== -TODO -^^^^ - -TODO - -:cve:`YYYY-XXXXX`: TODO -^^^^^^^^^^^^^^^^^^^^^^^ - -TODO +ImageShow.WindowsViewer.show_file +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Backwards Incompatible Changes -============================== +If an attacker has control over the ``path`` passed to +``ImageShow.WindowsViewer.show_file()``, they may be able to +execute arbitrary shell commands. -TODO -^^^^ +To prevent this, a :py:exc:`FileNotFoundError` will be raised if the ``path`` +does not exist as a file. To provide a consistent experience, the error has +been added to all :py:class:`~PIL.ImageShow` viewers. Deprecations ============ @@ -46,14 +41,6 @@ ImageDraw.getdraw hints parameter The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated. -API Changes -=========== - -TODO -^^^^ - -TODO - API Additions ============= @@ -64,11 +51,6 @@ Added :py:meth:`~PIL.ImageDraw.ImageDraw.circle`. It provides the same functiona :py:meth:`~PIL.ImageDraw.ImageDraw.ellipse`, but instead of taking a bounding box, it takes a center point and radius. -TODO -^^^^ - -TODO - Other Changes ============= diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f60b1e11e18..037d6f492ce 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -118,6 +118,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError os.system(self.get_command(path, **options)) # nosec return 1 @@ -142,6 +144,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError subprocess.Popen( self.get_command(path, **options), shell=True, @@ -171,6 +175,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError subprocess.call(["open", "-a", "Preview.app", path]) executable = sys.executable or shutil.which("python3") if executable: @@ -215,6 +221,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError subprocess.Popen(["xdg-open", path]) return 1 @@ -237,6 +245,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError args = ["display"] title = options.get("title") if title: @@ -259,6 +269,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError subprocess.Popen(["gm", "display", path]) return 1 @@ -275,6 +287,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError subprocess.Popen(["eog", "-n", path]) return 1 @@ -299,6 +313,8 @@ def show_file(self, path: str, **options: Any) -> int: """ Display given file. """ + if not os.path.exists(path): + raise FileNotFoundError args = ["xv"] title = options.get("title") if title: From b55d74bcfe4b4e69a64d09c785552af8dc9f013d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 29 Jun 2024 07:47:26 +1000 Subject: [PATCH 299/300] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a6f2bc12c1d..641aa8803f7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 10.4.0 (unreleased) ------------------- +- Raise FileNotFoundError if show_file() path does not exist #8178 + [radarhere] + +- Improved reading 16-bit TGA images with colour #7965 + [Yay295, radarhere] + - Deprecate non-image ImageCms modes #8031 [radarhere] From 9b4fae77178e827ab17118fbc89c739ffd6a0fab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Jul 2024 15:42:12 +1000 Subject: [PATCH 300/300] 10.4.0 version bump --- CHANGES.rst | 2 +- src/PIL/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 641aa8803f7..d071f3214bc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,7 @@ Changelog (Pillow) ================== -10.4.0 (unreleased) +10.4.0 (2024-07-01) ------------------- - Raise FileNotFoundError if show_file() path does not exist #8178 diff --git a/src/PIL/_version.py b/src/PIL/_version.py index 12d7412eaa1..cebfd868cfd 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,4 +1,4 @@ # Master version for Pillow from __future__ import annotations -__version__ = "10.4.0.dev0" +__version__ = "10.4.0"