mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-15 03:46:28 +03:00
Merge branch 'main' into context_manager
This commit is contained in:
commit
15e0f1a180
47
.github/workflows/wheels-dependencies.sh
vendored
47
.github/workflows/wheels-dependencies.sh
vendored
|
@ -16,7 +16,11 @@ ARCHIVE_SDIR=pillow-depends-main
|
||||||
|
|
||||||
# Package versions for fresh source builds
|
# Package versions for fresh source builds
|
||||||
FREETYPE_VERSION=2.13.2
|
FREETYPE_VERSION=2.13.2
|
||||||
HARFBUZZ_VERSION=8.5.0
|
if [[ "$MB_ML_VER" != 2014 ]]; then
|
||||||
|
HARFBUZZ_VERSION=9.0.0
|
||||||
|
else
|
||||||
|
HARFBUZZ_VERSION=8.5.0
|
||||||
|
fi
|
||||||
LIBPNG_VERSION=1.6.43
|
LIBPNG_VERSION=1.6.43
|
||||||
JPEGTURBO_VERSION=3.0.3
|
JPEGTURBO_VERSION=3.0.3
|
||||||
OPENJPEG_VERSION=2.5.2
|
OPENJPEG_VERSION=2.5.2
|
||||||
|
@ -40,7 +44,7 @@ BROTLI_VERSION=1.1.0
|
||||||
|
|
||||||
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then
|
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then
|
||||||
function build_openjpeg {
|
function build_openjpeg {
|
||||||
local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v${OPENJPEG_VERSION}.tar.gz openjpeg-${OPENJPEG_VERSION}.tar.gz)
|
local out_dir=$(fetch_unpack https://github.com/uclouvain/openjpeg/archive/v$OPENJPEG_VERSION.tar.gz openjpeg-$OPENJPEG_VERSION.tar.gz)
|
||||||
(cd $out_dir \
|
(cd $out_dir \
|
||||||
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
&& cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||||
&& make install)
|
&& make install)
|
||||||
|
@ -50,7 +54,7 @@ fi
|
||||||
|
|
||||||
function build_brotli {
|
function build_brotli {
|
||||||
local cmake=$(get_modern_cmake)
|
local cmake=$(get_modern_cmake)
|
||||||
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-1.1.0.tar.gz)
|
local out_dir=$(fetch_unpack https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz brotli-$BROTLI_VERSION.tar.gz)
|
||||||
(cd $out_dir \
|
(cd $out_dir \
|
||||||
&& $cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
&& $cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib . \
|
||||||
&& make install)
|
&& make install)
|
||||||
|
@ -60,6 +64,25 @@ function build_brotli {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function build_harfbuzz {
|
||||||
|
if [[ "$HARFBUZZ_VERSION" == 8.5.0 ]]; then
|
||||||
|
export FREETYPE_LIBS=-lfreetype
|
||||||
|
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/
|
||||||
|
build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no
|
||||||
|
export FREETYPE_LIBS=""
|
||||||
|
export FREETYPE_CFLAGS=""
|
||||||
|
else
|
||||||
|
local out_dir=$(fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION/$HARFBUZZ_VERSION.tar.xz harfbuzz-$HARFBUZZ_VERSION.tar.xz)
|
||||||
|
(cd $out_dir \
|
||||||
|
&& meson setup build --buildtype=release -Dfreetype=enabled -Dglib=disabled)
|
||||||
|
(cd $out_dir/build \
|
||||||
|
&& meson install)
|
||||||
|
if [[ "$MB_ML_LIBC" == "manylinux" ]]; then
|
||||||
|
cp /usr/local/lib64/libharfbuzz* /usr/local/lib
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
function build {
|
function build {
|
||||||
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
|
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
|
||||||
sudo chown -R runner /usr/local
|
sudo chown -R runner /usr/local
|
||||||
|
@ -109,15 +132,7 @@ function build {
|
||||||
build_freetype
|
build_freetype
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$IS_MACOS" ]; then
|
build_harfbuzz
|
||||||
export FREETYPE_LIBS=-lfreetype
|
|
||||||
export FREETYPE_CFLAGS=-I/usr/local/include/freetype2/
|
|
||||||
fi
|
|
||||||
build_simple harfbuzz $HARFBUZZ_VERSION https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION tar.xz --with-freetype=yes --with-glib=no
|
|
||||||
if [ -z "$IS_MACOS" ]; then
|
|
||||||
export FREETYPE_LIBS=""
|
|
||||||
export FREETYPE_CFLAGS=""
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Any stuff that you need to do before you start building the wheels
|
# Any stuff that you need to do before you start building the wheels
|
||||||
|
@ -140,7 +155,13 @@ if [[ -n "$IS_MACOS" ]]; then
|
||||||
brew remove --ignore-dependencies webp
|
brew remove --ignore-dependencies webp
|
||||||
fi
|
fi
|
||||||
|
|
||||||
brew install pkg-config
|
brew install meson pkg-config
|
||||||
|
elif [[ "$MB_ML_LIBC" == "manylinux" ]]; then
|
||||||
|
if [[ "$HARFBUZZ_VERSION" != 8.5.0 ]]; then
|
||||||
|
yum install -y meson
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
apk add meson
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wrap_wheel_builder build
|
wrap_wheel_builder build
|
||||||
|
|
|
@ -5,6 +5,9 @@ Changelog (Pillow)
|
||||||
11.0.0 (unreleased)
|
11.0.0 (unreleased)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
- Deprecate isImageType #8364
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Support converting more modes to LAB by converting to RGBA first #8358
|
- Support converting more modes to LAB by converting to RGBA first #8358
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
|
|
@ -1481,3 +1481,22 @@ def test_saving_rgba(tmp_path: Path) -> None:
|
||||||
value = px[0, 0]
|
value = px[0, 0]
|
||||||
assert isinstance(value, tuple)
|
assert isinstance(value, tuple)
|
||||||
assert value[3] == 0
|
assert value[3] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_optimizing_p_rgba(tmp_path: Path) -> None:
|
||||||
|
out = str(tmp_path / "temp.gif")
|
||||||
|
|
||||||
|
im1 = Image.new("P", (100, 100))
|
||||||
|
d = ImageDraw.Draw(im1)
|
||||||
|
d.ellipse([(40, 40), (60, 60)], fill=1)
|
||||||
|
data = [0, 0, 0, 0, 0, 0, 0, 255] + [0, 0, 0, 0] * 254
|
||||||
|
im1.putpalette(data, "RGBA")
|
||||||
|
|
||||||
|
im2 = Image.new("P", (100, 100))
|
||||||
|
im2.putpalette(data, "RGBA")
|
||||||
|
|
||||||
|
im1.save(out, save_all=True, append_images=[im2])
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
assert isinstance(reloaded, GifImagePlugin.GifImageFile)
|
||||||
|
assert reloaded.n_frames == 2
|
||||||
|
|
|
@ -68,8 +68,8 @@ def test_save_append_images(tmp_path: Path) -> None:
|
||||||
|
|
||||||
with Image.open(temp_file) as reread:
|
with Image.open(temp_file) as reread:
|
||||||
assert isinstance(reread, IcnsImagePlugin.IcnsImageFile)
|
assert isinstance(reread, IcnsImagePlugin.IcnsImageFile)
|
||||||
reread.size = (16, 16, 2)
|
reread.size = (16, 16)
|
||||||
reread.load()
|
reread.load(2)
|
||||||
assert_image_equal(reread, provided_im)
|
assert_image_equal(reread, provided_im)
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,14 +93,21 @@ def test_sizes() -> None:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im.size = (w, h, r)
|
with pytest.warns(DeprecationWarning):
|
||||||
|
im.size = (w, h, r)
|
||||||
im.load()
|
im.load()
|
||||||
assert im.mode == "RGBA"
|
assert im.mode == "RGBA"
|
||||||
assert im.size == (wr, hr)
|
assert im.size == (wr, hr)
|
||||||
|
|
||||||
|
# Test using load() with scale
|
||||||
|
im.size = (w, h)
|
||||||
|
im.load(scale=r)
|
||||||
|
assert im.mode == "RGBA"
|
||||||
|
assert im.size == (wr, hr)
|
||||||
|
|
||||||
# Check that we cannot load an incorrect size
|
# Check that we cannot load an incorrect size
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 2)
|
||||||
|
|
||||||
|
|
||||||
def test_older_icon() -> None:
|
def test_older_icon() -> None:
|
||||||
|
@ -112,8 +119,8 @@ def test_older_icon() -> None:
|
||||||
hr = h * r
|
hr = h * r
|
||||||
with Image.open("Tests/images/pillow2.icns") as im2:
|
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||||
assert isinstance(im2, IcnsImagePlugin.IcnsImageFile)
|
assert isinstance(im2, IcnsImagePlugin.IcnsImageFile)
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h)
|
||||||
im2.load()
|
im2.load(r)
|
||||||
assert im2.mode == "RGBA"
|
assert im2.mode == "RGBA"
|
||||||
assert im2.size == (wr, hr)
|
assert im2.size == (wr, hr)
|
||||||
|
|
||||||
|
@ -130,8 +137,8 @@ def test_jp2_icon() -> None:
|
||||||
hr = h * r
|
hr = h * r
|
||||||
with Image.open("Tests/images/pillow3.icns") as im2:
|
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||||
assert isinstance(im2, IcnsImagePlugin.IcnsImageFile)
|
assert isinstance(im2, IcnsImagePlugin.IcnsImageFile)
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h)
|
||||||
im2.load()
|
im2.load(r)
|
||||||
assert im2.mode == "RGBA"
|
assert im2.mode == "RGBA"
|
||||||
assert im2.size == (wr, hr)
|
assert im2.size == (wr, hr)
|
||||||
|
|
||||||
|
|
|
@ -1119,6 +1119,10 @@ class TestImage:
|
||||||
assert len(caplog.records) == 0
|
assert len(caplog.records) == 0
|
||||||
assert im.fp is None
|
assert im.fp is None
|
||||||
|
|
||||||
|
def test_deprecation(self) -> None:
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert not Image.isImageType(None)
|
||||||
|
|
||||||
|
|
||||||
class TestImageBytes:
|
class TestImageBytes:
|
||||||
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
|
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
|
||||||
|
|
|
@ -122,6 +122,22 @@ vulnerability introduced in FreeType 2.6 (:cve:`2020-15999`).
|
||||||
|
|
||||||
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
|
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
|
||||||
|
|
||||||
|
ICNS (width, height, scale) sizes
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. deprecated:: 11.0.0
|
||||||
|
|
||||||
|
Setting an ICNS image size to ``(width, height, scale)`` before loading has been
|
||||||
|
deprecated. Instead, ``load(scale)`` can be used.
|
||||||
|
|
||||||
|
Image isImageType()
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. deprecated:: 11.0.0
|
||||||
|
|
||||||
|
``Image.isImageType(im)`` has been deprecated. Use ``isinstance(im, Image.Image)``
|
||||||
|
instead.
|
||||||
|
|
||||||
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
|
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -324,12 +324,19 @@ sets the following :py:attr:`~PIL.Image.Image.info` property:
|
||||||
**sizes**
|
**sizes**
|
||||||
A list of supported sizes found in this icon file; these are a
|
A list of supported sizes found in this icon file; these are a
|
||||||
3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina
|
3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina
|
||||||
icon and 1 for a standard icon. You *are* permitted to use this 3-tuple
|
icon and 1 for a standard icon.
|
||||||
format for the :py:attr:`~PIL.Image.Image.size` property if you set it
|
|
||||||
before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size
|
.. _icns-loading:
|
||||||
will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you
|
|
||||||
ask for ``(512, 512, 2)``, the final value of
|
Loading
|
||||||
:py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``).
|
~~~~~~~
|
||||||
|
|
||||||
|
You can call the :py:meth:`~PIL.Image.Image.load` method with the following parameter.
|
||||||
|
|
||||||
|
**scale**
|
||||||
|
Affects the scale of the resultant image. If the size is set to ``(512, 512)``,
|
||||||
|
after loading at scale 2, the final value of :py:attr:`~PIL.Image.Image.size` will
|
||||||
|
be ``(1024, 1024)``.
|
||||||
|
|
||||||
.. _icns-saving:
|
.. _icns-saving:
|
||||||
|
|
||||||
|
|
|
@ -61,9 +61,27 @@ vulnerability introduced in FreeType 2.6 (:cve:`2020-15999`).
|
||||||
|
|
||||||
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
|
.. _2.10.4: https://sourceforge.net/projects/freetype/files/freetype2/2.10.4/
|
||||||
|
|
||||||
|
ICNS (width, height, scale) sizes
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. deprecated:: 11.0.0
|
||||||
|
|
||||||
|
Setting an ICNS image size to ``(width, height, scale)`` before loading has been
|
||||||
|
deprecated. Instead, ``load(scale)`` can be used.
|
||||||
|
|
||||||
|
Image isImageType()
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. deprecated:: 11.0.0
|
||||||
|
|
||||||
|
``Image.isImageType(im)`` has been deprecated. Use ``isinstance(im, Image.Image)``
|
||||||
|
instead.
|
||||||
|
|
||||||
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
|
ImageMath.lambda_eval and ImageMath.unsafe_eval options parameter
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. deprecated:: 11.0.0
|
||||||
|
|
||||||
The ``options`` parameter in :py:meth:`~PIL.ImageMath.lambda_eval()` and
|
The ``options`` parameter in :py:meth:`~PIL.ImageMath.lambda_eval()` and
|
||||||
:py:meth:`~PIL.ImageMath.unsafe_eval()` has been deprecated. One or more
|
:py:meth:`~PIL.ImageMath.unsafe_eval()` has been deprecated. One or more
|
||||||
keyword arguments can be used instead.
|
keyword arguments can be used instead.
|
||||||
|
@ -79,6 +97,8 @@ have been deprecated, and will be removed in Pillow 12 (2025-10-15).
|
||||||
Specific WebP Feature Checks
|
Specific WebP Feature Checks
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. deprecated:: 11.0.0
|
||||||
|
|
||||||
``features.check("transp_webp")``, ``features.check("webp_mux")`` and
|
``features.check("transp_webp")``, ``features.check("webp_mux")`` and
|
||||||
``features.check("webp_anim")`` are now deprecated. They will always return
|
``features.check("webp_anim")`` are now deprecated. They will always return
|
||||||
``True`` if the WebP module is installed, until they are removed in Pillow
|
``True`` if the WebP module is installed, until they are removed in Pillow
|
||||||
|
|
|
@ -561,7 +561,9 @@ def _normalize_palette(
|
||||||
|
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
if not source_palette:
|
if not source_palette:
|
||||||
source_palette = im.im.getpalette("RGB")[:768]
|
im_palette = im.getpalette(None)
|
||||||
|
assert im_palette is not None
|
||||||
|
source_palette = bytearray(im_palette)
|
||||||
else: # L-mode
|
else: # L-mode
|
||||||
if not source_palette:
|
if not source_palette:
|
||||||
source_palette = bytearray(i // 3 for i in range(768))
|
source_palette = bytearray(i // 3 for i in range(768))
|
||||||
|
@ -637,7 +639,10 @@ def _write_single_frame(
|
||||||
def _getbbox(
|
def _getbbox(
|
||||||
base_im: Image.Image, im_frame: Image.Image
|
base_im: Image.Image, im_frame: Image.Image
|
||||||
) -> tuple[Image.Image, tuple[int, int, int, int] | None]:
|
) -> tuple[Image.Image, tuple[int, int, int, int] | None]:
|
||||||
if _get_palette_bytes(im_frame) != _get_palette_bytes(base_im):
|
palette_bytes = [
|
||||||
|
bytes(im.palette.palette) if im.palette else b"" for im in (base_im, im_frame)
|
||||||
|
]
|
||||||
|
if palette_bytes[0] != palette_bytes[1]:
|
||||||
im_frame = im_frame.convert("RGBA")
|
im_frame = im_frame.convert("RGBA")
|
||||||
base_im = base_im.convert("RGBA")
|
base_im = base_im.convert("RGBA")
|
||||||
delta = ImageChops.subtract_modulo(im_frame, base_im)
|
delta = ImageChops.subtract_modulo(im_frame, base_im)
|
||||||
|
@ -992,7 +997,13 @@ def _get_palette_bytes(im: Image.Image) -> bytes:
|
||||||
:param im: Image object
|
:param im: Image object
|
||||||
:returns: Bytes, len<=768 suitable for inclusion in gif header
|
:returns: Bytes, len<=768 suitable for inclusion in gif header
|
||||||
"""
|
"""
|
||||||
return bytes(im.palette.palette) if im.palette else b""
|
if not im.palette:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
palette = bytes(im.palette.palette)
|
||||||
|
if im.palette.mode == "RGBA":
|
||||||
|
palette = b"".join(palette[i * 4 : i * 4 + 3] for i in range(len(palette) // 3))
|
||||||
|
return palette
|
||||||
|
|
||||||
|
|
||||||
def _get_background(
|
def _get_background(
|
||||||
|
|
|
@ -25,6 +25,7 @@ import sys
|
||||||
from typing import IO
|
from typing import IO
|
||||||
|
|
||||||
from . import Image, ImageFile, PngImagePlugin, features
|
from . import Image, ImageFile, PngImagePlugin, features
|
||||||
|
from ._deprecate import deprecate
|
||||||
|
|
||||||
enable_jpeg2k = features.check_codec("jpg_2000")
|
enable_jpeg2k = features.check_codec("jpg_2000")
|
||||||
if enable_jpeg2k:
|
if enable_jpeg2k:
|
||||||
|
@ -276,37 +277,37 @@ class IcnsImageFile(ImageFile.ImageFile):
|
||||||
self.best_size[1] * self.best_size[2],
|
self.best_size[1] * self.best_size[2],
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property # type: ignore[override]
|
||||||
def size(self):
|
def size(self) -> tuple[int, int] | tuple[int, int, int]:
|
||||||
return self._size
|
return self._size
|
||||||
|
|
||||||
@size.setter
|
@size.setter
|
||||||
def size(self, value) -> None:
|
def size(self, value: tuple[int, int] | tuple[int, int, int]) -> None:
|
||||||
info_size = value
|
if len(value) == 3:
|
||||||
if info_size not in self.info["sizes"] and len(info_size) == 2:
|
deprecate("Setting size to (width, height, scale)", 12, "load(scale)")
|
||||||
info_size = (info_size[0], info_size[1], 1)
|
if value in self.info["sizes"]:
|
||||||
if (
|
self._size = value # type: ignore[assignment]
|
||||||
info_size not in self.info["sizes"]
|
return
|
||||||
and len(info_size) == 3
|
else:
|
||||||
and info_size[2] == 1
|
# Check that a matching size exists,
|
||||||
):
|
# or that there is a scale that would create a size that matches
|
||||||
simple_sizes = [
|
for size in self.info["sizes"]:
|
||||||
(size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"]
|
simple_size = size[0] * size[2], size[1] * size[2]
|
||||||
]
|
scale = simple_size[0] // value[0]
|
||||||
if value in simple_sizes:
|
if simple_size[1] / value[1] == scale:
|
||||||
info_size = self.info["sizes"][simple_sizes.index(value)]
|
self._size = value
|
||||||
if info_size not in self.info["sizes"]:
|
return
|
||||||
msg = "This is not one of the allowed sizes of this image"
|
msg = "This is not one of the allowed sizes of this image"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
self._size = value
|
|
||||||
|
|
||||||
def load(self) -> Image.core.PixelAccess | None:
|
def load(self, scale: int | None = None) -> Image.core.PixelAccess | None:
|
||||||
if len(self.size) == 3:
|
if scale is not None or len(self.size) == 3:
|
||||||
self.best_size = self.size
|
if scale is None and len(self.size) == 3:
|
||||||
self.size = (
|
scale = self.size[2]
|
||||||
self.best_size[0] * self.best_size[2],
|
assert scale is not None
|
||||||
self.best_size[1] * self.best_size[2],
|
width, height = self.size[:2]
|
||||||
)
|
self.size = width * scale, height * scale
|
||||||
|
self.best_size = width, height, scale
|
||||||
|
|
||||||
px = Image.Image.load(self)
|
px = Image.Image.load(self)
|
||||||
if self._im is not None and self.im.size == self.size:
|
if self._im is not None and self.im.size == self.size:
|
||||||
|
|
|
@ -133,6 +133,7 @@ def isImageType(t: Any) -> TypeGuard[Image]:
|
||||||
:param t: object to check if it's an image
|
:param t: object to check if it's an image
|
||||||
:returns: True if the object is an image
|
:returns: True if the object is an image
|
||||||
"""
|
"""
|
||||||
|
deprecate("Image.isImageType(im)", 12, "isinstance(im, Image.Image)")
|
||||||
return hasattr(t, "im")
|
return hasattr(t, "im")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1048,7 +1049,7 @@ class Image:
|
||||||
trns_im = new(self.mode, (1, 1))
|
trns_im = new(self.mode, (1, 1))
|
||||||
if self.mode == "P":
|
if self.mode == "P":
|
||||||
assert self.palette is not None
|
assert self.palette is not None
|
||||||
trns_im.putpalette(self.palette)
|
trns_im.putpalette(self.palette, self.palette.mode)
|
||||||
if isinstance(t, tuple):
|
if isinstance(t, tuple):
|
||||||
err = "Couldn't allocate a palette color for transparency"
|
err = "Couldn't allocate a palette color for transparency"
|
||||||
assert trns_im.palette is not None
|
assert trns_im.palette is not None
|
||||||
|
@ -1763,23 +1764,22 @@ class Image:
|
||||||
:param mask: An optional mask image.
|
:param mask: An optional mask image.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isImageType(box):
|
if isinstance(box, Image):
|
||||||
if mask is not None:
|
if mask is not None:
|
||||||
msg = "If using second argument as mask, third argument must be None"
|
msg = "If using second argument as mask, third argument must be None"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
# abbreviated paste(im, mask) syntax
|
# abbreviated paste(im, mask) syntax
|
||||||
mask = box
|
mask = box
|
||||||
box = None
|
box = None
|
||||||
assert not isinstance(box, Image)
|
|
||||||
|
|
||||||
if box is None:
|
if box is None:
|
||||||
box = (0, 0)
|
box = (0, 0)
|
||||||
|
|
||||||
if len(box) == 2:
|
if len(box) == 2:
|
||||||
# upper left corner given; get size from image or mask
|
# upper left corner given; get size from image or mask
|
||||||
if isImageType(im):
|
if isinstance(im, Image):
|
||||||
size = im.size
|
size = im.size
|
||||||
elif isImageType(mask):
|
elif isinstance(mask, Image):
|
||||||
size = mask.size
|
size = mask.size
|
||||||
else:
|
else:
|
||||||
# FIXME: use self.size here?
|
# FIXME: use self.size here?
|
||||||
|
@ -1792,17 +1792,15 @@ class Image:
|
||||||
from . import ImageColor
|
from . import ImageColor
|
||||||
|
|
||||||
source = ImageColor.getcolor(im, self.mode)
|
source = ImageColor.getcolor(im, self.mode)
|
||||||
elif isImageType(im):
|
elif isinstance(im, Image):
|
||||||
im.load()
|
im.load()
|
||||||
if self.mode != im.mode:
|
if self.mode != im.mode:
|
||||||
if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"):
|
if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"):
|
||||||
# should use an adapter for this!
|
# should use an adapter for this!
|
||||||
im = im.convert(self.mode)
|
im = im.convert(self.mode)
|
||||||
source = im.im
|
source = im.im
|
||||||
elif isinstance(im, tuple):
|
|
||||||
source = im
|
|
||||||
else:
|
else:
|
||||||
source = cast(float, im)
|
source = im
|
||||||
|
|
||||||
self._ensure_mutable()
|
self._ensure_mutable()
|
||||||
|
|
||||||
|
@ -1963,7 +1961,7 @@ class Image:
|
||||||
else:
|
else:
|
||||||
band = 3
|
band = 3
|
||||||
|
|
||||||
if isImageType(alpha):
|
if isinstance(alpha, Image):
|
||||||
# alpha layer
|
# alpha layer
|
||||||
if alpha.mode not in ("1", "L"):
|
if alpha.mode not in ("1", "L"):
|
||||||
msg = "illegal image mode"
|
msg = "illegal image mode"
|
||||||
|
@ -1973,7 +1971,6 @@ class Image:
|
||||||
alpha = alpha.convert("L")
|
alpha = alpha.convert("L")
|
||||||
else:
|
else:
|
||||||
# constant alpha
|
# constant alpha
|
||||||
alpha = cast(int, alpha) # see python/typing#1013
|
|
||||||
try:
|
try:
|
||||||
self.im.fillband(band, alpha)
|
self.im.fillband(band, alpha)
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
|
@ -2122,6 +2119,9 @@ class Image:
|
||||||
source_palette = self.im.getpalette(palette_mode, palette_mode)
|
source_palette = self.im.getpalette(palette_mode, palette_mode)
|
||||||
else: # L-mode
|
else: # L-mode
|
||||||
source_palette = bytearray(i // 3 for i in range(768))
|
source_palette = bytearray(i // 3 for i in range(768))
|
||||||
|
elif len(source_palette) > 768:
|
||||||
|
bands = 4
|
||||||
|
palette_mode = "RGBA"
|
||||||
|
|
||||||
palette_bytes = b""
|
palette_bytes = b""
|
||||||
new_positions = [0] * 256
|
new_positions = [0] * 256
|
||||||
|
|
|
@ -268,7 +268,7 @@ def lambda_eval(
|
||||||
args.update(options)
|
args.update(options)
|
||||||
args.update(kw)
|
args.update(kw)
|
||||||
for k, v in args.items():
|
for k, v in args.items():
|
||||||
if hasattr(v, "im"):
|
if isinstance(v, Image.Image):
|
||||||
args[k] = _Operand(v)
|
args[k] = _Operand(v)
|
||||||
|
|
||||||
out = expression(args)
|
out = expression(args)
|
||||||
|
@ -319,7 +319,7 @@ def unsafe_eval(
|
||||||
args.update(options)
|
args.update(options)
|
||||||
args.update(kw)
|
args.update(kw)
|
||||||
for k, v in args.items():
|
for k, v in args.items():
|
||||||
if hasattr(v, "im"):
|
if isinstance(v, Image.Image):
|
||||||
args[k] = _Operand(v)
|
args[k] = _Operand(v)
|
||||||
|
|
||||||
compiled_code = compile(expression, "<string>", "eval")
|
compiled_code = compile(expression, "<string>", "eval")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user