mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-27 08:30:05 +03:00
Merge branch 'main' into imagecms-typing
This commit is contained in:
commit
5cdaa1e46c
|
@ -1,10 +1,11 @@
|
||||||
mypy==1.16.1
|
mypy==1.17.0
|
||||||
IceSpringPySideStubs-PyQt6
|
IceSpringPySideStubs-PyQt6
|
||||||
IceSpringPySideStubs-PySide6
|
IceSpringPySideStubs-PySide6
|
||||||
ipython
|
ipython
|
||||||
numpy
|
numpy
|
||||||
packaging
|
packaging
|
||||||
pyarrow-stubs
|
pyarrow-stubs
|
||||||
|
pybind11
|
||||||
pytest
|
pytest
|
||||||
sphinx
|
sphinx
|
||||||
types-atheris
|
types-atheris
|
||||||
|
|
19
.github/workflows/wheels-dependencies.sh
vendored
19
.github/workflows/wheels-dependencies.sh
vendored
|
@ -60,7 +60,7 @@ if [[ "$CIBW_PLATFORM" == "ios" ]]; then
|
||||||
# on using the Xcode builder, which isn't very helpful for most of Pillow's
|
# on using the Xcode builder, which isn't very helpful for most of Pillow's
|
||||||
# dependencies. Therefore, we lean on the OSX configurations, plus CC, CFLAGS
|
# dependencies. Therefore, we lean on the OSX configurations, plus CC, CFLAGS
|
||||||
# etc. to ensure the right sysroot is selected.
|
# etc. to ensure the right sysroot is selected.
|
||||||
HOST_CMAKE_FLAGS="-DCMAKE_SYSTEM_NAME=$CMAKE_SYSTEM_NAME -DCMAKE_SYSTEM_PROCESSOR=$GNU_ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET -DCMAKE_OSX_SYSROOT=$IOS_SDK_PATH -DBUILD_SHARED_LIBS=NO"
|
HOST_CMAKE_FLAGS="-DCMAKE_SYSTEM_NAME=$CMAKE_SYSTEM_NAME -DCMAKE_SYSTEM_PROCESSOR=$GNU_ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$IPHONEOS_DEPLOYMENT_TARGET -DCMAKE_OSX_SYSROOT=$IOS_SDK_PATH -DBUILD_SHARED_LIBS=NO -DENABLE_SHARED=NO"
|
||||||
|
|
||||||
# Meson needs to be pointed at a cross-platform configuration file
|
# Meson needs to be pointed at a cross-platform configuration file
|
||||||
# This will be generated once CC etc. have been evaluated.
|
# This will be generated once CC etc. have been evaluated.
|
||||||
|
@ -103,7 +103,7 @@ TIFF_VERSION=4.7.0
|
||||||
LCMS2_VERSION=2.17
|
LCMS2_VERSION=2.17
|
||||||
ZLIB_VERSION=1.3.1
|
ZLIB_VERSION=1.3.1
|
||||||
ZLIB_NG_VERSION=2.2.4
|
ZLIB_NG_VERSION=2.2.4
|
||||||
LIBWEBP_VERSION=1.5.0 # Patched; next release won't need patching. See patch file.
|
LIBWEBP_VERSION=1.6.0
|
||||||
BZIP2_VERSION=1.0.8
|
BZIP2_VERSION=1.0.8
|
||||||
LIBXCB_VERSION=1.17.0
|
LIBXCB_VERSION=1.17.0
|
||||||
BROTLI_VERSION=1.1.0 # Patched; next release won't need patching. See patch file.
|
BROTLI_VERSION=1.1.0 # Patched; next release won't need patching. See patch file.
|
||||||
|
@ -280,7 +280,11 @@ function build {
|
||||||
if [[ -n "$IS_MACOS" ]]; then
|
if [[ -n "$IS_MACOS" ]]; then
|
||||||
webp_cflags="$webp_cflags -Wl,-headerpad_max_install_names"
|
webp_cflags="$webp_cflags -Wl,-headerpad_max_install_names"
|
||||||
fi
|
fi
|
||||||
CFLAGS="$CFLAGS $webp_cflags" build_simple libwebp $LIBWEBP_VERSION \
|
webp_ldflags=""
|
||||||
|
if [[ -n "$IOS_SDK" ]]; then
|
||||||
|
webp_ldflags="$webp_ldflags -llzma -lz"
|
||||||
|
fi
|
||||||
|
CFLAGS="$CFLAGS $webp_cflags" LDFLAGS="$LDFLAGS $webp_ldflags" build_simple libwebp $LIBWEBP_VERSION \
|
||||||
https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \
|
https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \
|
||||||
--enable-libwebpmux --enable-libwebpdemux
|
--enable-libwebpmux --enable-libwebpdemux
|
||||||
|
|
||||||
|
@ -380,6 +384,15 @@ fi
|
||||||
|
|
||||||
wrap_wheel_builder build
|
wrap_wheel_builder build
|
||||||
|
|
||||||
|
# A safety catch for iOS. iOS can't use dynamic libraries, but clang will prefer
|
||||||
|
# to link dynamic libraries to static libraries. The only way to reliably
|
||||||
|
# prevent this is to not have dynamic libraries available in the first place.
|
||||||
|
# The build process *shouldn't* generate any dylibs... but just in case, purge
|
||||||
|
# any dylibs that *have* been installed into the build prefix directory.
|
||||||
|
if [[ -n "$IOS_SDK" ]]; then
|
||||||
|
find "$BUILD_PREFIX" -name "*.dylib" -exec rm -rf {} \;
|
||||||
|
fi
|
||||||
|
|
||||||
# Return to the project root to finish the build
|
# Return to the project root to finish the build
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
|
|
||||||
|
|
12
MANIFEST.in
12
MANIFEST.in
|
@ -13,6 +13,7 @@ include LICENSE
|
||||||
include Makefile
|
include Makefile
|
||||||
include tox.ini
|
include tox.ini
|
||||||
graft Tests
|
graft Tests
|
||||||
|
graft Tests/images
|
||||||
graft checks
|
graft checks
|
||||||
graft patches
|
graft patches
|
||||||
graft src
|
graft src
|
||||||
|
@ -28,8 +29,19 @@ exclude .editorconfig
|
||||||
exclude .readthedocs.yml
|
exclude .readthedocs.yml
|
||||||
exclude codecov.yml
|
exclude codecov.yml
|
||||||
exclude renovate.json
|
exclude renovate.json
|
||||||
|
exclude Tests/images/README.md
|
||||||
|
exclude Tests/images/crash*.tif
|
||||||
|
exclude Tests/images/string_dimension.tiff
|
||||||
global-exclude .git*
|
global-exclude .git*
|
||||||
global-exclude *.pyc
|
global-exclude *.pyc
|
||||||
global-exclude *.so
|
global-exclude *.so
|
||||||
prune .ci
|
prune .ci
|
||||||
prune wheels
|
prune wheels
|
||||||
|
prune winbuild/build
|
||||||
|
prune winbuild/depends
|
||||||
|
prune Tests/errors
|
||||||
|
prune Tests/images/jpeg2000
|
||||||
|
prune Tests/images/msp
|
||||||
|
prune Tests/images/picins
|
||||||
|
prune Tests/images/sunraster
|
||||||
|
prune Tests/test-images
|
||||||
|
|
|
@ -291,16 +291,6 @@ def djpeg_available() -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def cjpeg_available() -> bool:
|
|
||||||
if shutil.which("cjpeg"):
|
|
||||||
try:
|
|
||||||
subprocess.check_call(["cjpeg", "-version"])
|
|
||||||
return True
|
|
||||||
except subprocess.CalledProcessError: # pragma: no cover
|
|
||||||
return False
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def netpbm_available() -> bool:
|
def netpbm_available() -> bool:
|
||||||
return bool(shutil.which("ppmquant") and shutil.which("ppmtogif"))
|
return bool(shutil.which("ppmquant") and shutil.which("ppmtogif"))
|
||||||
|
|
||||||
|
|
BIN
Tests/images/unimplemented_pixel_format.dds
Normal file
BIN
Tests/images/unimplemented_pixel_format.dds
Normal file
Binary file not shown.
|
@ -380,21 +380,28 @@ def test_palette() -> None:
|
||||||
assert_image_equal_tofile(im, "Tests/images/transparent.gif")
|
assert_image_equal_tofile(im, "Tests/images/transparent.gif")
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_header_size() -> None:
|
||||||
|
with pytest.raises(OSError, match="Unsupported header size 0"):
|
||||||
|
with Image.open(BytesIO(b"DDS " + b"\x00" * 4)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_unsupported_bitcount() -> None:
|
def test_unsupported_bitcount() -> None:
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError, match="Unsupported bitcount 24 for 131072"):
|
||||||
with Image.open("Tests/images/unsupported_bitcount.dds"):
|
with Image.open("Tests/images/unsupported_bitcount.dds"):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_file",
|
"test_file, message",
|
||||||
(
|
(
|
||||||
"Tests/images/unimplemented_dxgi_format.dds",
|
("Tests/images/unimplemented_dxgi_format.dds", "Unimplemented DXGI format 93"),
|
||||||
"Tests/images/unimplemented_pfflags.dds",
|
("Tests/images/unimplemented_pixel_format.dds", "Unimplemented pixel format 0"),
|
||||||
|
("Tests/images/unimplemented_pfflags.dds", "Unknown pixel format flags 8"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_not_implemented(test_file: str) -> None:
|
def test_not_implemented(test_file: str, message: str) -> None:
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError, match=message):
|
||||||
with Image.open(test_file):
|
with Image.open(test_file):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ from .helper import (
|
||||||
assert_image_equal_tofile,
|
assert_image_equal_tofile,
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
assert_image_similar_tofile,
|
assert_image_similar_tofile,
|
||||||
cjpeg_available,
|
|
||||||
djpeg_available,
|
djpeg_available,
|
||||||
hopper,
|
hopper,
|
||||||
is_win32,
|
is_win32,
|
||||||
|
@ -731,14 +730,6 @@ class TestFileJpeg:
|
||||||
img.load_djpeg()
|
img.load_djpeg()
|
||||||
assert_image_similar_tofile(img, TEST_FILE, 5)
|
assert_image_similar_tofile(img, TEST_FILE, 5)
|
||||||
|
|
||||||
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
|
|
||||||
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, BytesIO(), tempfile)
|
|
||||||
# Default save quality is 75%, so a tiny bit of difference is alright
|
|
||||||
assert_image_similar_tofile(img, tempfile, 17)
|
|
||||||
|
|
||||||
def test_no_duplicate_0x1001_tag(self) -> None:
|
def test_no_duplicate_0x1001_tag(self) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
tag_ids = {v: k for k, v in ExifTags.TAGS.items()}
|
tag_ids = {v: k for k, v in ExifTags.TAGS.items()}
|
||||||
|
|
|
@ -873,8 +873,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert im.mode == "RGB"
|
assert im.mode == "RGB"
|
||||||
assert im.size == (128, 128)
|
assert im.size == (128, 128)
|
||||||
assert im.format == "TIFF"
|
assert im.format == "TIFF"
|
||||||
im2 = hopper()
|
with hopper() as im2:
|
||||||
assert_image_similar(im, im2, 5)
|
assert_image_similar(im, im2, 5)
|
||||||
except OSError:
|
except OSError:
|
||||||
captured = capfd.readouterr()
|
captured = capfd.readouterr()
|
||||||
if "LZMA compression support is not configured" in captured.err:
|
if "LZMA compression support is not configured" in captured.err:
|
||||||
|
|
|
@ -44,6 +44,18 @@ def test_load_zero_inch() -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_unsupported_wmf() -> None:
|
||||||
|
b = BytesIO(b"\xd7\xcd\xc6\x9a\x00\x00" + b"\x01" * 10)
|
||||||
|
with pytest.raises(SyntaxError, match="Unsupported WMF file format"):
|
||||||
|
WmfImagePlugin.WmfStubImageFile(b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_unsupported() -> None:
|
||||||
|
b = BytesIO(b"\x01\x00\x00\x00")
|
||||||
|
with pytest.raises(SyntaxError, match="Unsupported file format"):
|
||||||
|
WmfImagePlugin.WmfStubImageFile(b)
|
||||||
|
|
||||||
|
|
||||||
def test_render() -> None:
|
def test_render() -> None:
|
||||||
with open("Tests/images/drawing.emf", "rb") as fp:
|
with open("Tests/images/drawing.emf", "rb") as fp:
|
||||||
data = fp.read()
|
data = fp.read()
|
||||||
|
|
|
@ -315,3 +315,6 @@ int main(int argc, char* argv[])
|
||||||
process = subprocess.Popen(["embed_pil.exe"], env=env)
|
process = subprocess.Popen(["embed_pil.exe"], env=env)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
assert process.returncode == 0
|
assert process.returncode == 0
|
||||||
|
|
||||||
|
def teardown_method(self) -> None:
|
||||||
|
os.remove("embed_pil.c")
|
||||||
|
|
|
@ -2,7 +2,9 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from PIL import Image, ImageMath
|
import pytest
|
||||||
|
|
||||||
|
from PIL import Image, ImageMath, _imagingmath
|
||||||
|
|
||||||
|
|
||||||
def pixel(im: Image.Image | int) -> str | int:
|
def pixel(im: Image.Image | int) -> str | int:
|
||||||
|
@ -498,3 +500,31 @@ def test_logical_not_equal() -> None:
|
||||||
)
|
)
|
||||||
== "I 1"
|
== "I 1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_reflected_operands() -> None:
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 + args["A"], **images)) == "I 2"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 - args["A"], **images)) == "I 0"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 * args["A"], **images)) == "I 1"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 / args["A"], **images)) == "I 1"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 % args["A"], **images)) == "I 0"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 ** args["A"], **images)) == "I 1"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 & args["A"], **images)) == "I 1"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 | args["A"], **images)) == "I 1"
|
||||||
|
assert pixel(ImageMath.lambda_eval(lambda args: 1 ^ args["A"], **images)) == "I 0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_mode() -> None:
|
||||||
|
im = Image.new("RGB", (1, 1))
|
||||||
|
with pytest.raises(ValueError, match="unsupported mode: RGB"):
|
||||||
|
ImageMath.lambda_eval(lambda args: args["im"] + 1, im=im)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bad_operand_type(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
monkeypatch.delattr(_imagingmath, "abs_I")
|
||||||
|
with pytest.raises(TypeError, match="bad operand type for 'abs'"):
|
||||||
|
ImageMath.lambda_eval(lambda args: abs(args["I"]), I=I)
|
||||||
|
|
||||||
|
monkeypatch.delattr(_imagingmath, "max_F")
|
||||||
|
with pytest.raises(TypeError, match="bad operand type for 'max'"):
|
||||||
|
ImageMath.lambda_eval(lambda args: args["max"](args["I"], args["F"]), I=I, F=F)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from importlib.metadata import metadata
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import __version__
|
from PIL import __version__
|
||||||
|
@ -11,7 +9,7 @@ pyroma = pytest.importorskip("pyroma", reason="Pyroma not installed")
|
||||||
|
|
||||||
def test_pyroma() -> None:
|
def test_pyroma() -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
data = pyroma.projectdata.map_metadata_keys(metadata("Pillow"))
|
data = pyroma.projectdata.get_data(".")
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
rating = pyroma.ratings.rate(data)
|
rating = pyroma.ratings.rate(data)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import GifImagePlugin, Image, JpegImagePlugin
|
from PIL import GifImagePlugin, Image, JpegImagePlugin
|
||||||
|
|
||||||
from .helper import cjpeg_available, djpeg_available, is_win32, netpbm_available
|
from .helper import djpeg_available, is_win32, netpbm_available
|
||||||
|
|
||||||
TEST_JPG = "Tests/images/hopper.jpg"
|
TEST_JPG = "Tests/images/hopper.jpg"
|
||||||
TEST_GIF = "Tests/images/hopper.gif"
|
TEST_GIF = "Tests/images/hopper.gif"
|
||||||
|
@ -42,11 +42,6 @@ class TestShellInjection:
|
||||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
im.load_djpeg()
|
im.load_djpeg()
|
||||||
|
|
||||||
@pytest.mark.skipif(not cjpeg_available(), reason="cjpeg not available")
|
|
||||||
def test_save_cjpeg_filename(self, tmp_path: Path) -> None:
|
|
||||||
with Image.open(TEST_JPG) as im:
|
|
||||||
self.assert_save_filename_check(tmp_path, im, JpegImagePlugin._save_cjpeg)
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||||
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
|
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install webp
|
# install webp
|
||||||
|
|
||||||
archive=libwebp-1.5.0
|
archive=libwebp-1.6.0
|
||||||
|
|
||||||
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
|
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,6 @@ Deprecated features
|
||||||
Below are features which are considered deprecated. Where appropriate,
|
Below are features which are considered deprecated. Where appropriate,
|
||||||
a :py:exc:`DeprecationWarning` is issued.
|
a :py:exc:`DeprecationWarning` is issued.
|
||||||
|
|
||||||
ImageDraw.getdraw hints parameter
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. deprecated:: 10.4.0
|
|
||||||
|
|
||||||
The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecated.
|
|
||||||
|
|
||||||
ExifTags.IFD.Makernote
|
ExifTags.IFD.Makernote
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -195,6 +188,7 @@ ICNS (width, height, scale) sizes
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. deprecated:: 11.0.0
|
.. deprecated:: 11.0.0
|
||||||
|
.. versionremoved:: 12.0.0
|
||||||
|
|
||||||
Setting an ICNS image size to ``(width, height, scale)`` before loading has been
|
Setting an ICNS image size to ``(width, height, scale)`` before loading has been
|
||||||
removed. Instead, ``load(scale)`` can be used.
|
removed. Instead, ``load(scale)`` can be used.
|
||||||
|
|
|
@ -101,6 +101,28 @@ Palette
|
||||||
The palette mode (``P``) uses a color palette to define the actual color for
|
The palette mode (``P``) uses a color palette to define the actual color for
|
||||||
each pixel.
|
each pixel.
|
||||||
|
|
||||||
|
.. _colors:
|
||||||
|
|
||||||
|
Colors
|
||||||
|
------
|
||||||
|
|
||||||
|
To specify colors, you can use tuples with a value for each channel in the image, e.g.
|
||||||
|
``Image.new("RGB", (1, 1), (255, 0, 0))``.
|
||||||
|
|
||||||
|
If an image has a single channel, you can use a single number instead, e.g.
|
||||||
|
``Image.new("L", (1, 1), 255)``. For "F" mode images, floating point values are also
|
||||||
|
accepted. In the case of "P" mode images, these will be indexes for the color palette.
|
||||||
|
|
||||||
|
If a single value is used for an image with more than one channel, it will still be
|
||||||
|
parsed::
|
||||||
|
|
||||||
|
>>> from PIL import Image
|
||||||
|
>>> im = Image.new("RGBA", (1, 1), 0x04030201)
|
||||||
|
>>> im.getpixel((0, 0))
|
||||||
|
(1, 2, 3, 4)
|
||||||
|
|
||||||
|
Some methods accept other forms, such as color names. See :ref:`color-names`.
|
||||||
|
|
||||||
Info
|
Info
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ Many of Pillow's features require external libraries:
|
||||||
|
|
||||||
* **libtiff** provides compressed TIFF functionality
|
* **libtiff** provides compressed TIFF functionality
|
||||||
|
|
||||||
* Pillow has been tested with libtiff versions **3.x** and **4.0-4.7.0**
|
* Pillow has been tested with libtiff versions **4.0-4.7.0**
|
||||||
|
|
||||||
* **libfreetype** provides type related services
|
* **libfreetype** provides type related services
|
||||||
|
|
||||||
|
@ -276,10 +276,9 @@ Build options
|
||||||
|
|
||||||
* Config setting: ``-C parallel=n``. Can also be given
|
* Config setting: ``-C parallel=n``. Can also be given
|
||||||
with environment variable: ``MAX_CONCURRENCY=n``. Pillow can use
|
with environment variable: ``MAX_CONCURRENCY=n``. Pillow can use
|
||||||
multiprocessing to build the extension. Setting ``-C parallel=n``
|
multiprocessing to build the extensions. Setting ``-C parallel=n``
|
||||||
sets the number of CPUs to use to ``n``, or can disable parallel building by
|
sets the number of CPUs to use to ``n``, or can disable parallel building by
|
||||||
using a setting of 1. By default, it uses 4 CPUs, or if 4 are not
|
using a setting of 1. By default, it uses as many CPUs as are present.
|
||||||
available, as many as are present.
|
|
||||||
|
|
||||||
* Config settings: ``-C zlib=disable``, ``-C jpeg=disable``,
|
* Config settings: ``-C zlib=disable``, ``-C jpeg=disable``,
|
||||||
``-C tiff=disable``, ``-C freetype=disable``, ``-C raqm=disable``,
|
``-C tiff=disable``, ``-C freetype=disable``, ``-C raqm=disable``,
|
||||||
|
|
|
@ -45,9 +45,7 @@ Colors
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
To specify colors, you can use numbers or tuples just as you would use with
|
To specify colors, you can use numbers or tuples just as you would use with
|
||||||
:py:meth:`PIL.Image.new` or :py:meth:`PIL.Image.Image.putpixel`. For “1”,
|
:py:meth:`PIL.Image.new`. See :ref:`colors` for more information.
|
||||||
“L”, and “I” images, use integers. For “RGB” images, use a 3-tuple containing
|
|
||||||
integer values. For “F” images, use integer or floating point values.
|
|
||||||
|
|
||||||
For palette images (mode “P”), use integers as color indexes. In 1.1.4 and
|
For palette images (mode “P”), use integers as color indexes. In 1.1.4 and
|
||||||
later, you can also use RGB 3-tuples or color names (see below). The drawing
|
later, you can also use RGB 3-tuples or color names (see below). The drawing
|
||||||
|
|
|
@ -59,7 +59,7 @@ Access using negative indexes is also possible. ::
|
||||||
|
|
||||||
Modifies the pixel at x,y. The color is given as a single
|
Modifies the pixel at x,y. The color is given as a single
|
||||||
numerical value for single band images, and a tuple for
|
numerical value for single band images, and a tuple for
|
||||||
multi-band images.
|
multi-band images. See :ref:`colors` for more information.
|
||||||
|
|
||||||
:param xy: The pixel coordinate, given as (x, y).
|
:param xy: The pixel coordinate, given as (x, y).
|
||||||
:param color: The pixel value according to its mode,
|
:param color: The pixel value according to its mode,
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
# libwebp example binaries require dependencies that aren't available for iOS builds.
|
|
||||||
# There's also no easy way to invoke the build to *exclude* the example builds.
|
|
||||||
# Since we don't need the examples anyway, remove them from the Makefile.
|
|
||||||
#
|
|
||||||
# As a point of reference, libwebp provides an XCFramework build script that involves
|
|
||||||
# 7 separate invocations of make to avoid building the examples. Patching the Makefile
|
|
||||||
# to remove the examples is a simpler approach, and one that is more compatible with
|
|
||||||
# the existing multibuild infrastructure.
|
|
||||||
#
|
|
||||||
# In the next release, it should be possible to pass --disable-libwebpexamples
|
|
||||||
# instead of applying this patch.
|
|
||||||
#
|
|
||||||
diff -ur libwebp-1.5.0-orig/Makefile.am libwebp-1.5.0/Makefile.am
|
|
||||||
--- libwebp-1.5.0-orig/Makefile.am 2024-12-20 09:17:50
|
|
||||||
+++ libwebp-1.5.0/Makefile.am 2025-01-09 11:24:17
|
|
||||||
@@ -5,5 +5,3 @@
|
|
||||||
if BUILD_EXTRAS
|
|
||||||
SUBDIRS += extras
|
|
||||||
endif
|
|
||||||
-
|
|
||||||
-SUBDIRS += examples
|
|
||||||
diff -ur libwebp-1.5.0-orig/Makefile.in libwebp-1.5.0/Makefile.in
|
|
||||||
--- libwebp-1.5.0-orig/Makefile.in 2024-12-20 09:52:53
|
|
||||||
+++ libwebp-1.5.0/Makefile.in 2025-01-09 11:24:17
|
|
||||||
@@ -156,7 +156,7 @@
|
|
||||||
unique=`for i in $$list; do \
|
|
||||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
|
||||||
done | $(am__uniquify_input)`
|
|
||||||
-DIST_SUBDIRS = sharpyuv src imageio man extras examples
|
|
||||||
+DIST_SUBDIRS = sharpyuv src imageio man extras
|
|
||||||
am__DIST_COMMON = $(srcdir)/Makefile.in \
|
|
||||||
$(top_srcdir)/src/webp/config.h.in AUTHORS COPYING ChangeLog \
|
|
||||||
NEWS README.md ar-lib compile config.guess config.sub \
|
|
||||||
@@ -351,7 +351,7 @@
|
|
||||||
top_srcdir = @top_srcdir@
|
|
||||||
webp_libname_prefix = @webp_libname_prefix@
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
|
||||||
-SUBDIRS = sharpyuv src imageio man $(am__append_1) examples
|
|
||||||
+SUBDIRS = sharpyuv src imageio man $(am__append_1)
|
|
||||||
EXTRA_DIST = COPYING autogen.sh
|
|
||||||
all: all-recursive
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[build-system]
|
[build-system]
|
||||||
build-backend = "backend"
|
build-backend = "backend"
|
||||||
requires = [
|
requires = [
|
||||||
|
"pybind11",
|
||||||
"setuptools>=77",
|
"setuptools>=77",
|
||||||
]
|
]
|
||||||
backend-path = [
|
backend-path = [
|
||||||
|
|
23
setup.py
23
setup.py
|
@ -17,9 +17,20 @@ import sys
|
||||||
import warnings
|
import warnings
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
|
|
||||||
|
from pybind11.setup_helpers import ParallelCompile
|
||||||
from setuptools import Extension, setup
|
from setuptools import Extension, setup
|
||||||
from setuptools.command.build_ext import build_ext
|
from setuptools.command.build_ext import build_ext
|
||||||
|
|
||||||
|
configuration: dict[str, list[str]] = {}
|
||||||
|
|
||||||
|
# parse configuration from _custom_build/backend.py
|
||||||
|
while sys.argv[-1].startswith("--pillow-configuration="):
|
||||||
|
_, key, value = sys.argv.pop().split("=", 2)
|
||||||
|
configuration.setdefault(key, []).append(value)
|
||||||
|
|
||||||
|
default = int(configuration.get("parallel", ["0"])[-1])
|
||||||
|
ParallelCompile("MAX_CONCURRENCY", default).install()
|
||||||
|
|
||||||
|
|
||||||
def get_version() -> str:
|
def get_version() -> str:
|
||||||
version_file = "src/PIL/_version.py"
|
version_file = "src/PIL/_version.py"
|
||||||
|
@ -27,9 +38,6 @@ def get_version() -> str:
|
||||||
return f.read().split('"')[1]
|
return f.read().split('"')[1]
|
||||||
|
|
||||||
|
|
||||||
configuration: dict[str, list[str]] = {}
|
|
||||||
|
|
||||||
|
|
||||||
PILLOW_VERSION = get_version()
|
PILLOW_VERSION = get_version()
|
||||||
AVIF_ROOT = None
|
AVIF_ROOT = None
|
||||||
FREETYPE_ROOT = None
|
FREETYPE_ROOT = None
|
||||||
|
@ -386,9 +394,7 @@ class pil_build_ext(build_ext):
|
||||||
cpu_count = os.cpu_count()
|
cpu_count = os.cpu_count()
|
||||||
if cpu_count is not None:
|
if cpu_count is not None:
|
||||||
try:
|
try:
|
||||||
self.parallel = int(
|
self.parallel = int(os.environ.get("MAX_CONCURRENCY", cpu_count))
|
||||||
os.environ.get("MAX_CONCURRENCY", min(4, cpu_count))
|
|
||||||
)
|
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
for x in self.feature:
|
for x in self.feature:
|
||||||
|
@ -1083,11 +1089,6 @@ ext_modules = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# parse configuration from _custom_build/backend.py
|
|
||||||
while sys.argv[-1].startswith("--pillow-configuration="):
|
|
||||||
_, key, value = sys.argv.pop().split("=", 2)
|
|
||||||
configuration.setdefault(key, []).append(value)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
setup(
|
setup(
|
||||||
cmdclass={"build_ext": pil_build_ext},
|
cmdclass={"build_ext": pil_build_ext},
|
||||||
|
|
|
@ -1730,9 +1730,10 @@ class Image:
|
||||||
details).
|
details).
|
||||||
|
|
||||||
Instead of an image, the source can be a integer or tuple
|
Instead of an image, the source can be a integer or tuple
|
||||||
containing pixel values. The method then fills the region
|
containing pixel values. The method then fills the region
|
||||||
with the given color. When creating RGB images, you can
|
with the given color. When creating RGB images, you can
|
||||||
also use color strings as supported by the ImageColor module.
|
also use color strings as supported by the ImageColor module. See
|
||||||
|
:ref:`colors` for more information.
|
||||||
|
|
||||||
If a mask is given, this method updates only the regions
|
If a mask is given, this method updates only the regions
|
||||||
indicated by the mask. You can use either "1", "L", "LA", "RGBA"
|
indicated by the mask. You can use either "1", "L", "LA", "RGBA"
|
||||||
|
@ -1988,7 +1989,8 @@ class Image:
|
||||||
sequence ends. The scale and offset values are used to adjust the
|
sequence ends. The scale and offset values are used to adjust the
|
||||||
sequence values: **pixel = value*scale + offset**.
|
sequence values: **pixel = value*scale + offset**.
|
||||||
|
|
||||||
:param data: A flattened sequence object.
|
:param data: A flattened sequence object. See :ref:`colors` for more
|
||||||
|
information about values.
|
||||||
:param scale: An optional scale value. The default is 1.0.
|
:param scale: An optional scale value. The default is 1.0.
|
||||||
:param offset: An optional offset value. The default is 0.0.
|
:param offset: An optional offset value. The default is 0.0.
|
||||||
"""
|
"""
|
||||||
|
@ -2047,7 +2049,7 @@ class Image:
|
||||||
Modifies the pixel at the given position. The color is given as
|
Modifies the pixel at the given position. The color is given as
|
||||||
a single numerical value for single-band images, and a tuple for
|
a single numerical value for single-band images, and a tuple for
|
||||||
multi-band images. In addition to this, RGB and RGBA tuples are
|
multi-band images. In addition to this, RGB and RGBA tuples are
|
||||||
accepted for P and PA images.
|
accepted for P and PA images. See :ref:`colors` for more information.
|
||||||
|
|
||||||
Note that this method is relatively slow. For more extensive changes,
|
Note that this method is relatively slow. For more extensive changes,
|
||||||
use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw`
|
use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw`
|
||||||
|
@ -3055,12 +3057,12 @@ def new(
|
||||||
:param mode: The mode to use for the new image. See:
|
:param mode: The mode to use for the new image. See:
|
||||||
:ref:`concept-modes`.
|
:ref:`concept-modes`.
|
||||||
:param size: A 2-tuple, containing (width, height) in pixels.
|
:param size: A 2-tuple, containing (width, height) in pixels.
|
||||||
:param color: What color to use for the image. Default is black.
|
:param color: What color to use for the image. Default is black. If given,
|
||||||
If given, this should be a single integer or floating point value
|
this should be a single integer or floating point value for single-band
|
||||||
for single-band modes, and a tuple for multi-band modes (one value
|
modes, and a tuple for multi-band modes (one value per band). When
|
||||||
per band). When creating RGB or HSV images, you can also use color
|
creating RGB or HSV images, you can also use color strings as supported
|
||||||
strings as supported by the ImageColor module. If the color is
|
by the ImageColor module. See :ref:`colors` for more information. If the
|
||||||
None, the image is not initialised.
|
color is None, the image is not initialised.
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -845,16 +845,6 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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])
|
|
||||||
try:
|
|
||||||
os.unlink(tempfile)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Factory for making JPEG and MPO instances
|
# Factory for making JPEG and MPO instances
|
||||||
def jpeg_factory(
|
def jpeg_factory(
|
||||||
|
|
|
@ -9,7 +9,6 @@ from typing import IO
|
||||||
import PIL
|
import PIL
|
||||||
|
|
||||||
from . import Image
|
from . import Image
|
||||||
from ._deprecate import deprecate
|
|
||||||
|
|
||||||
modules = {
|
modules = {
|
||||||
"pil": ("PIL._imaging", "PILLOW_VERSION"),
|
"pil": ("PIL._imaging", "PILLOW_VERSION"),
|
||||||
|
@ -120,7 +119,7 @@ def get_supported_codecs() -> list[str]:
|
||||||
return [f for f in codecs if check_codec(f)]
|
return [f for f in codecs if check_codec(f)]
|
||||||
|
|
||||||
|
|
||||||
features: dict[str, tuple[str, str | bool, str | None]] = {
|
features: dict[str, tuple[str, str, str | None]] = {
|
||||||
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
||||||
"fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
|
"fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
|
||||||
"harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
|
"harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
|
||||||
|
@ -146,12 +145,8 @@ def check_feature(feature: str) -> bool | None:
|
||||||
|
|
||||||
module, flag, ver = features[feature]
|
module, flag, ver = features[feature]
|
||||||
|
|
||||||
if isinstance(flag, bool):
|
|
||||||
deprecate(f'check_feature("{feature}")', 12)
|
|
||||||
try:
|
try:
|
||||||
imported_module = __import__(module, fromlist=["PIL"])
|
imported_module = __import__(module, fromlist=["PIL"])
|
||||||
if isinstance(flag, bool):
|
|
||||||
return flag
|
|
||||||
return getattr(imported_module, flag)
|
return getattr(imported_module, flag)
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
return None
|
return None
|
||||||
|
@ -181,17 +176,7 @@ def get_supported_features() -> list[str]:
|
||||||
"""
|
"""
|
||||||
:returns: A list of all supported features.
|
:returns: A list of all supported features.
|
||||||
"""
|
"""
|
||||||
supported_features = []
|
return [f for f in features if check_feature(f)]
|
||||||
for f, (module, flag, _) in features.items():
|
|
||||||
if flag is True:
|
|
||||||
for feature, (feature_module, _) in modules.items():
|
|
||||||
if feature_module == module:
|
|
||||||
if check_module(feature):
|
|
||||||
supported_features.append(f)
|
|
||||||
break
|
|
||||||
elif check_feature(f):
|
|
||||||
supported_features.append(f)
|
|
||||||
return supported_features
|
|
||||||
|
|
||||||
|
|
||||||
def check(feature: str) -> bool | None:
|
def check(feature: str) -> bool | None:
|
||||||
|
|
|
@ -122,7 +122,7 @@ V = {
|
||||||
"LIBAVIF": "1.3.0",
|
"LIBAVIF": "1.3.0",
|
||||||
"LIBIMAGEQUANT": "4.3.4",
|
"LIBIMAGEQUANT": "4.3.4",
|
||||||
"LIBPNG": "1.6.49",
|
"LIBPNG": "1.6.49",
|
||||||
"LIBWEBP": "1.5.0",
|
"LIBWEBP": "1.6.0",
|
||||||
"OPENJPEG": "2.5.3",
|
"OPENJPEG": "2.5.3",
|
||||||
"TIFF": "4.7.0",
|
"TIFF": "4.7.0",
|
||||||
"XZ": "5.8.1",
|
"XZ": "5.8.1",
|
||||||
|
@ -149,18 +149,17 @@ DEPS: dict[str, dict[str, Any]] = {
|
||||||
},
|
},
|
||||||
"build": [
|
"build": [
|
||||||
*cmds_cmake(
|
*cmds_cmake(
|
||||||
("jpeg-static", "cjpeg-static", "djpeg-static"),
|
("jpeg-static", "djpeg-static"),
|
||||||
"-DENABLE_SHARED:BOOL=FALSE",
|
"-DENABLE_SHARED:BOOL=FALSE",
|
||||||
"-DWITH_JPEG8:BOOL=TRUE",
|
"-DWITH_JPEG8:BOOL=TRUE",
|
||||||
"-DWITH_CRT_DLL:BOOL=TRUE",
|
"-DWITH_CRT_DLL:BOOL=TRUE",
|
||||||
),
|
),
|
||||||
cmd_copy("jpeg-static.lib", "libjpeg.lib"),
|
cmd_copy("jpeg-static.lib", "libjpeg.lib"),
|
||||||
cmd_copy("cjpeg-static.exe", "cjpeg.exe"),
|
|
||||||
cmd_copy("djpeg-static.exe", "djpeg.exe"),
|
cmd_copy("djpeg-static.exe", "djpeg.exe"),
|
||||||
],
|
],
|
||||||
"headers": ["jconfig.h", r"src\j*.h"],
|
"headers": ["jconfig.h", r"src\j*.h"],
|
||||||
"libs": ["libjpeg.lib"],
|
"libs": ["libjpeg.lib"],
|
||||||
"bins": ["cjpeg.exe", "djpeg.exe"],
|
"bins": ["djpeg.exe"],
|
||||||
},
|
},
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"url": f"https://github.com/zlib-ng/zlib-ng/archive/refs/tags/{V['ZLIBNG']}.tar.gz",
|
"url": f"https://github.com/zlib-ng/zlib-ng/archive/refs/tags/{V['ZLIBNG']}.tar.gz",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user