mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +03:00 
			
		
		
		
	Merge branch 'main' into types-cms2
This commit is contained in:
		
						commit
						46b0b0e57d
					
				
							
								
								
									
										15
									
								
								.github/ISSUE_TEMPLATE/ISSUE_REPORT.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/ISSUE_TEMPLATE/ISSUE_REPORT.md
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -48,6 +48,21 @@ Thank you.
 | 
				
			||||||
* Python: 
 | 
					* Python: 
 | 
				
			||||||
* Pillow: 
 | 
					* Pillow: 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```text
 | 
				
			||||||
 | 
					Please paste here the output of running:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					python3 -m PIL.report
 | 
				
			||||||
 | 
					or
 | 
				
			||||||
 | 
					python3 -m PIL --report
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Or the output of the following Python code:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from PIL import report
 | 
				
			||||||
 | 
					# or
 | 
				
			||||||
 | 
					from PIL import features
 | 
				
			||||||
 | 
					features.pilinfo(supported_formats=False)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!--
 | 
					<!--
 | 
				
			||||||
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
 | 
					Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								.github/workflows/wheels-dependencies.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/wheels-dependencies.sh
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -16,7 +16,7 @@ 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.3.1
 | 
					HARFBUZZ_VERSION=8.4.0
 | 
				
			||||||
LIBPNG_VERSION=1.6.43
 | 
					LIBPNG_VERSION=1.6.43
 | 
				
			||||||
JPEGTURBO_VERSION=3.0.2
 | 
					JPEGTURBO_VERSION=3.0.2
 | 
				
			||||||
OPENJPEG_VERSION=2.5.2
 | 
					OPENJPEG_VERSION=2.5.2
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								.github/workflows/wheels.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/wheels.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -5,6 +5,7 @@ on:
 | 
				
			||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
      - ".ci/requirements-cibw.txt"
 | 
					      - ".ci/requirements-cibw.txt"
 | 
				
			||||||
      - ".github/workflows/wheel*"
 | 
					      - ".github/workflows/wheel*"
 | 
				
			||||||
 | 
					      - "setup.py"
 | 
				
			||||||
      - "wheels/*"
 | 
					      - "wheels/*"
 | 
				
			||||||
      - "winbuild/build_prepare.py"
 | 
					      - "winbuild/build_prepare.py"
 | 
				
			||||||
      - "winbuild/fribidi.cmake"
 | 
					      - "winbuild/fribidi.cmake"
 | 
				
			||||||
| 
						 | 
					@ -14,6 +15,7 @@ on:
 | 
				
			||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
      - ".ci/requirements-cibw.txt"
 | 
					      - ".ci/requirements-cibw.txt"
 | 
				
			||||||
      - ".github/workflows/wheel*"
 | 
					      - ".github/workflows/wheel*"
 | 
				
			||||||
 | 
					      - "setup.py"
 | 
				
			||||||
      - "wheels/*"
 | 
					      - "wheels/*"
 | 
				
			||||||
      - "winbuild/build_prepare.py"
 | 
					      - "winbuild/build_prepare.py"
 | 
				
			||||||
      - "winbuild/fribidi.cmake"
 | 
					      - "winbuild/fribidi.cmake"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,15 @@ Changelog (Pillow)
 | 
				
			||||||
10.3.0 (unreleased)
 | 
					10.3.0 (unreleased)
 | 
				
			||||||
-------------------
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Determine MPO size from markers, not EXIF data #7884
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Improved conversion from RGB to RGBa, LA and La #7888
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Support FITS images with GZIP_1 compression #7894
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Use I;16 mode for 9-bit JPEG 2000 images #7900
 | 
					- Use I;16 mode for 9-bit JPEG 2000 images #7900
 | 
				
			||||||
  [scaramallion, radarhere]
 | 
					  [scaramallion, radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -263,8 +263,6 @@ def hopper(mode: str | None = None, cache: dict[str, Image.Image] = {}) -> Image
 | 
				
			||||||
    if im is None:
 | 
					    if im is None:
 | 
				
			||||||
        if mode == "F":
 | 
					        if mode == "F":
 | 
				
			||||||
            im = hopper("L").convert(mode)
 | 
					            im = hopper("L").convert(mode)
 | 
				
			||||||
        elif mode[:4] == "I;16":
 | 
					 | 
				
			||||||
            im = hopper("I").convert(mode)
 | 
					 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            im = hopper().convert(mode)
 | 
					            im = hopper().convert(mode)
 | 
				
			||||||
        cache[mode] = im
 | 
					        cache[mode] = im
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,9 +117,10 @@ def test_unsupported_module() -> None:
 | 
				
			||||||
        features.version_module(module)
 | 
					        features.version_module(module)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_pilinfo() -> None:
 | 
					@pytest.mark.parametrize("supported_formats", (True, False))
 | 
				
			||||||
 | 
					def test_pilinfo(supported_formats) -> None:
 | 
				
			||||||
    buf = io.StringIO()
 | 
					    buf = io.StringIO()
 | 
				
			||||||
    features.pilinfo(buf)
 | 
					    features.pilinfo(buf, supported_formats=supported_formats)
 | 
				
			||||||
    out = buf.getvalue()
 | 
					    out = buf.getvalue()
 | 
				
			||||||
    lines = out.splitlines()
 | 
					    lines = out.splitlines()
 | 
				
			||||||
    assert lines[0] == "-" * 68
 | 
					    assert lines[0] == "-" * 68
 | 
				
			||||||
| 
						 | 
					@ -129,9 +130,15 @@ def test_pilinfo() -> None:
 | 
				
			||||||
    while lines[0].startswith("    "):
 | 
					    while lines[0].startswith("    "):
 | 
				
			||||||
        lines = lines[1:]
 | 
					        lines = lines[1:]
 | 
				
			||||||
    assert lines[0] == "-" * 68
 | 
					    assert lines[0] == "-" * 68
 | 
				
			||||||
    assert lines[1].startswith("Python modules loaded from ")
 | 
					    assert lines[1].startswith("Python executable is")
 | 
				
			||||||
    assert lines[2].startswith("Binary modules loaded from ")
 | 
					    lines = lines[2:]
 | 
				
			||||||
    assert lines[3] == "-" * 68
 | 
					    if lines[0].startswith("Environment Python files loaded from"):
 | 
				
			||||||
 | 
					        lines = lines[1:]
 | 
				
			||||||
 | 
					    assert lines[0].startswith("System Python files loaded from")
 | 
				
			||||||
 | 
					    assert lines[1] == "-" * 68
 | 
				
			||||||
 | 
					    assert lines[2].startswith("Python Pillow modules loaded from ")
 | 
				
			||||||
 | 
					    assert lines[3].startswith("Binary Pillow modules loaded from ")
 | 
				
			||||||
 | 
					    assert lines[4] == "-" * 68
 | 
				
			||||||
    jpeg = (
 | 
					    jpeg = (
 | 
				
			||||||
        "\n"
 | 
					        "\n"
 | 
				
			||||||
        + "-" * 68
 | 
					        + "-" * 68
 | 
				
			||||||
| 
						 | 
					@ -142,4 +149,4 @@ def test_pilinfo() -> None:
 | 
				
			||||||
        + "-" * 68
 | 
					        + "-" * 68
 | 
				
			||||||
        + "\n"
 | 
					        + "\n"
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    assert jpeg in out
 | 
					    assert supported_formats == (jpeg in out)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -825,7 +825,7 @@ class TestFileJpeg:
 | 
				
			||||||
        with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
 | 
					        with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
 | 
				
			||||||
            # Act / Assert
 | 
					            # Act / Assert
 | 
				
			||||||
            # "When the image resolution is unknown, 72 [dpi] is designated."
 | 
					            # "When the image resolution is unknown, 72 [dpi] is designated."
 | 
				
			||||||
            # https://exiv2.org/tags.html
 | 
					            # https://web.archive.org/web/20240227115053/https://exiv2.org/tags.html
 | 
				
			||||||
            assert im.info.get("dpi") == (72, 72)
 | 
					            assert im.info.get("dpi") == (72, 72)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_invalid_exif(self) -> None:
 | 
					    def test_invalid_exif(self) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,36 +33,38 @@ from .helper import (
 | 
				
			||||||
    skip_unless_feature,
 | 
					    skip_unless_feature,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# name, pixel size
 | 
				
			||||||
 | 
					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),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					image_mode_names = [name for name, _ in image_modes]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestImage:
 | 
					class TestImage:
 | 
				
			||||||
    @pytest.mark.parametrize(
 | 
					    @pytest.mark.parametrize("mode", image_mode_names)
 | 
				
			||||||
        "mode",
 | 
					 | 
				
			||||||
        (
 | 
					 | 
				
			||||||
            "1",
 | 
					 | 
				
			||||||
            "P",
 | 
					 | 
				
			||||||
            "PA",
 | 
					 | 
				
			||||||
            "L",
 | 
					 | 
				
			||||||
            "LA",
 | 
					 | 
				
			||||||
            "La",
 | 
					 | 
				
			||||||
            "F",
 | 
					 | 
				
			||||||
            "I",
 | 
					 | 
				
			||||||
            "I;16",
 | 
					 | 
				
			||||||
            "I;16L",
 | 
					 | 
				
			||||||
            "I;16B",
 | 
					 | 
				
			||||||
            "I;16N",
 | 
					 | 
				
			||||||
            "RGB",
 | 
					 | 
				
			||||||
            "RGBX",
 | 
					 | 
				
			||||||
            "RGBA",
 | 
					 | 
				
			||||||
            "RGBa",
 | 
					 | 
				
			||||||
            "BGR;15",
 | 
					 | 
				
			||||||
            "BGR;16",
 | 
					 | 
				
			||||||
            "BGR;24",
 | 
					 | 
				
			||||||
            "CMYK",
 | 
					 | 
				
			||||||
            "YCbCr",
 | 
					 | 
				
			||||||
            "LAB",
 | 
					 | 
				
			||||||
            "HSV",
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    def test_image_modes_success(self, mode: str) -> None:
 | 
					    def test_image_modes_success(self, mode: str) -> None:
 | 
				
			||||||
        Image.new(mode, (1, 1))
 | 
					        Image.new(mode, (1, 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1042,6 +1044,35 @@ class TestImage:
 | 
				
			||||||
            assert im.fp is None
 | 
					            assert im.fp is None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestImageBytes:
 | 
				
			||||||
 | 
					    @pytest.mark.parametrize("mode", image_mode_names)
 | 
				
			||||||
 | 
					    def test_roundtrip_bytes_constructor(self, mode: str) -> None:
 | 
				
			||||||
 | 
					        im = hopper(mode)
 | 
				
			||||||
 | 
					        source_bytes = im.tobytes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reloaded = Image.frombytes(mode, im.size, source_bytes)
 | 
				
			||||||
 | 
					        assert reloaded.tobytes() == source_bytes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @pytest.mark.parametrize("mode", image_mode_names)
 | 
				
			||||||
 | 
					    def test_roundtrip_bytes_method(self, mode: str) -> None:
 | 
				
			||||||
 | 
					        im = hopper(mode)
 | 
				
			||||||
 | 
					        source_bytes = im.tobytes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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))
 | 
				
			||||||
 | 
					        source_bytes = bytes(range(im.width * im.height * pixelsize))
 | 
				
			||||||
 | 
					        im.frombytes(source_bytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reloaded = Image.new(mode, im.size)
 | 
				
			||||||
 | 
					        reloaded.putdata(im.getdata())
 | 
				
			||||||
 | 
					        assert_image_equal(im, reloaded)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MockEncoder(ImageFile.PyEncoder):
 | 
					class MockEncoder(ImageFile.PyEncoder):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,16 @@ import os
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_main() -> None:
 | 
					
 | 
				
			||||||
    out = subprocess.check_output([sys.executable, "-m", "PIL"]).decode("utf-8")
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
 | 
					    "args, report",
 | 
				
			||||||
 | 
					    ((["PIL"], False), (["PIL", "--report"], True), (["PIL.report"], True)),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					def test_main(args, report) -> None:
 | 
				
			||||||
 | 
					    args = [sys.executable, "-m"] + args
 | 
				
			||||||
 | 
					    out = subprocess.check_output(args).decode("utf-8")
 | 
				
			||||||
    lines = out.splitlines()
 | 
					    lines = out.splitlines()
 | 
				
			||||||
    assert lines[0] == "-" * 68
 | 
					    assert lines[0] == "-" * 68
 | 
				
			||||||
    assert lines[1].startswith("Pillow ")
 | 
					    assert lines[1].startswith("Pillow ")
 | 
				
			||||||
| 
						 | 
					@ -15,9 +22,15 @@ def test_main() -> None:
 | 
				
			||||||
    while lines[0].startswith("    "):
 | 
					    while lines[0].startswith("    "):
 | 
				
			||||||
        lines = lines[1:]
 | 
					        lines = lines[1:]
 | 
				
			||||||
    assert lines[0] == "-" * 68
 | 
					    assert lines[0] == "-" * 68
 | 
				
			||||||
    assert lines[1].startswith("Python modules loaded from ")
 | 
					    assert lines[1].startswith("Python executable is")
 | 
				
			||||||
    assert lines[2].startswith("Binary modules loaded from ")
 | 
					    lines = lines[2:]
 | 
				
			||||||
    assert lines[3] == "-" * 68
 | 
					    if lines[0].startswith("Environment Python files loaded from"):
 | 
				
			||||||
 | 
					        lines = lines[1:]
 | 
				
			||||||
 | 
					    assert lines[0].startswith("System Python files loaded from")
 | 
				
			||||||
 | 
					    assert lines[1] == "-" * 68
 | 
				
			||||||
 | 
					    assert lines[2].startswith("Python Pillow modules loaded from ")
 | 
				
			||||||
 | 
					    assert lines[3].startswith("Binary Pillow modules loaded from ")
 | 
				
			||||||
 | 
					    assert lines[4] == "-" * 68
 | 
				
			||||||
    jpeg = (
 | 
					    jpeg = (
 | 
				
			||||||
        os.linesep
 | 
					        os.linesep
 | 
				
			||||||
        + "-" * 68
 | 
					        + "-" * 68
 | 
				
			||||||
| 
						 | 
					@ -31,4 +44,4 @@ def test_main() -> None:
 | 
				
			||||||
        + "-" * 68
 | 
					        + "-" * 68
 | 
				
			||||||
        + os.linesep
 | 
					        + os.linesep
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    assert jpeg in out
 | 
					    assert report == (jpeg not in out)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,41 +11,12 @@ backend_class = build_wheel.__self__.__class__
 | 
				
			||||||
class _CustomBuildMetaBackend(backend_class):
 | 
					class _CustomBuildMetaBackend(backend_class):
 | 
				
			||||||
    def run_setup(self, setup_script="setup.py"):
 | 
					    def run_setup(self, setup_script="setup.py"):
 | 
				
			||||||
        if self.config_settings:
 | 
					        if self.config_settings:
 | 
				
			||||||
 | 
					            for key, values in self.config_settings.items():
 | 
				
			||||||
 | 
					                if not isinstance(values, list):
 | 
				
			||||||
 | 
					                    values = [values]
 | 
				
			||||||
 | 
					                for value in values:
 | 
				
			||||||
 | 
					                    sys.argv.append(f"--pillow-configuration={key}={value}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            def config_has(key, value):
 | 
					 | 
				
			||||||
                settings = self.config_settings.get(key)
 | 
					 | 
				
			||||||
                if settings:
 | 
					 | 
				
			||||||
                    if not isinstance(settings, list):
 | 
					 | 
				
			||||||
                        settings = [settings]
 | 
					 | 
				
			||||||
                    return value in settings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            flags = []
 | 
					 | 
				
			||||||
            for dependency in (
 | 
					 | 
				
			||||||
                "zlib",
 | 
					 | 
				
			||||||
                "jpeg",
 | 
					 | 
				
			||||||
                "tiff",
 | 
					 | 
				
			||||||
                "freetype",
 | 
					 | 
				
			||||||
                "raqm",
 | 
					 | 
				
			||||||
                "lcms",
 | 
					 | 
				
			||||||
                "webp",
 | 
					 | 
				
			||||||
                "webpmux",
 | 
					 | 
				
			||||||
                "jpeg2000",
 | 
					 | 
				
			||||||
                "imagequant",
 | 
					 | 
				
			||||||
                "xcb",
 | 
					 | 
				
			||||||
            ):
 | 
					 | 
				
			||||||
                if config_has(dependency, "enable"):
 | 
					 | 
				
			||||||
                    flags.append("--enable-" + dependency)
 | 
					 | 
				
			||||||
                elif config_has(dependency, "disable"):
 | 
					 | 
				
			||||||
                    flags.append("--disable-" + dependency)
 | 
					 | 
				
			||||||
            for dependency in ("raqm", "fribidi"):
 | 
					 | 
				
			||||||
                if config_has(dependency, "vendor"):
 | 
					 | 
				
			||||||
                    flags.append("--vendor-" + dependency)
 | 
					 | 
				
			||||||
            if self.config_settings.get("platform-guessing") == "disable":
 | 
					 | 
				
			||||||
                flags.append("--disable-platform-guessing")
 | 
					 | 
				
			||||||
            if self.config_settings.get("debug") == "true":
 | 
					 | 
				
			||||||
                flags.append("--debug")
 | 
					 | 
				
			||||||
            if flags:
 | 
					 | 
				
			||||||
                sys.argv = sys.argv[:1] + ["build_ext"] + flags + sys.argv[1:]
 | 
					 | 
				
			||||||
        return super().run_setup(setup_script)
 | 
					        return super().run_setup(setup_script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def build_wheel(
 | 
					    def build_wheel(
 | 
				
			||||||
| 
						 | 
					@ -54,5 +25,15 @@ class _CustomBuildMetaBackend(backend_class):
 | 
				
			||||||
        self.config_settings = config_settings
 | 
					        self.config_settings = config_settings
 | 
				
			||||||
        return super().build_wheel(wheel_directory, config_settings, metadata_directory)
 | 
					        return super().build_wheel(wheel_directory, config_settings, metadata_directory)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def build_editable(
 | 
				
			||||||
 | 
					        self, wheel_directory, config_settings=None, metadata_directory=None
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        self.config_settings = config_settings
 | 
				
			||||||
 | 
					        return super().build_editable(
 | 
				
			||||||
 | 
					            wheel_directory, config_settings, metadata_directory
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
build_wheel = _CustomBuildMetaBackend().build_wheel
 | 
					
 | 
				
			||||||
 | 
					_backend = _CustomBuildMetaBackend()
 | 
				
			||||||
 | 
					build_wheel = _backend.build_wheel
 | 
				
			||||||
 | 
					build_editable = _backend.build_editable
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,9 +266,10 @@ After navigating to the Pillow directory, run::
 | 
				
			||||||
Build Options
 | 
					Build Options
 | 
				
			||||||
^^^^^^^^^^^^^
 | 
					^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Environment variable: ``MAX_CONCURRENCY=n``. Pillow can use
 | 
					* Config setting: ``-C parallel=n``. Can also be given
 | 
				
			||||||
  multiprocessing to build the extension. Setting ``MAX_CONCURRENCY``
 | 
					  with environment variable: ``MAX_CONCURRENCY=n``. Pillow can use
 | 
				
			||||||
  sets the number of CPUs to use, or can disable parallel building by
 | 
					  multiprocessing to build the extension. Setting ``-C parallel=n``
 | 
				
			||||||
 | 
					  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 4 CPUs, or if 4 are not
 | 
				
			||||||
  available, as many as are present.
 | 
					  available, as many as are present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -293,14 +294,13 @@ Build Options
 | 
				
			||||||
  used to compile the standard Pillow wheels. Compiling libraqm requires
 | 
					  used to compile the standard Pillow wheels. Compiling libraqm requires
 | 
				
			||||||
  a C99-compliant compiler.
 | 
					  a C99-compliant compiler.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Build flag: ``-C platform-guessing=disable``. Skips all of the
 | 
					* Config setting: ``-C platform-guessing=disable``. Skips all of the
 | 
				
			||||||
  platform dependent guessing of include and library directories for
 | 
					  platform dependent guessing of include and library directories for
 | 
				
			||||||
  automated build systems that configure the proper paths in the
 | 
					  automated build systems that configure the proper paths in the
 | 
				
			||||||
  environment variables (e.g. Buildroot).
 | 
					  environment variables (e.g. Buildroot).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Build flag: ``-C debug=true``. Adds a debugging flag to the include and
 | 
					* Config setting: ``-C debug=true``. Adds a debugging flag to the include and
 | 
				
			||||||
  library search process to dump all paths searched for and found to
 | 
					  library search process to dump all paths searched for and found to stdout.
 | 
				
			||||||
  stdout.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Sample usage::
 | 
					Sample usage::
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										26
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								setup.py
									
									
									
									
									
								
							| 
						 | 
					@ -27,6 +27,9 @@ def get_version():
 | 
				
			||||||
    return locals()["__version__"]
 | 
					    return locals()["__version__"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					configuration = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PILLOW_VERSION = get_version()
 | 
					PILLOW_VERSION = get_version()
 | 
				
			||||||
FREETYPE_ROOT = None
 | 
					FREETYPE_ROOT = None
 | 
				
			||||||
HARFBUZZ_ROOT = None
 | 
					HARFBUZZ_ROOT = None
 | 
				
			||||||
| 
						 | 
					@ -333,15 +336,24 @@ class pil_build_ext(build_ext):
 | 
				
			||||||
        + [("add-imaging-libs=", None, "Add libs to _imaging build")]
 | 
					        + [("add-imaging-libs=", None, "Add libs to _imaging build")]
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def check_configuration(option, value):
 | 
				
			||||||
 | 
					        return True if value in configuration.get(option, []) else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def initialize_options(self):
 | 
					    def initialize_options(self):
 | 
				
			||||||
        self.disable_platform_guessing = None
 | 
					        self.disable_platform_guessing = self.check_configuration(
 | 
				
			||||||
 | 
					            "platform-guessing", "disable"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        self.add_imaging_libs = ""
 | 
					        self.add_imaging_libs = ""
 | 
				
			||||||
        build_ext.initialize_options(self)
 | 
					        build_ext.initialize_options(self)
 | 
				
			||||||
        for x in self.feature:
 | 
					        for x in self.feature:
 | 
				
			||||||
            setattr(self, f"disable_{x}", None)
 | 
					            setattr(self, f"disable_{x}", self.check_configuration(x, "disable"))
 | 
				
			||||||
            setattr(self, f"enable_{x}", None)
 | 
					            setattr(self, f"enable_{x}", self.check_configuration(x, "enable"))
 | 
				
			||||||
        for x in ("raqm", "fribidi"):
 | 
					        for x in ("raqm", "fribidi"):
 | 
				
			||||||
            setattr(self, f"vendor_{x}", None)
 | 
					            setattr(self, f"vendor_{x}", self.check_configuration(x, "vendor"))
 | 
				
			||||||
 | 
					        if self.check_configuration("debug", "true"):
 | 
				
			||||||
 | 
					            self.debug = True
 | 
				
			||||||
 | 
					        self.parallel = configuration.get("parallel", [None])[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def finalize_options(self):
 | 
					    def finalize_options(self):
 | 
				
			||||||
        build_ext.finalize_options(self)
 | 
					        build_ext.finalize_options(self)
 | 
				
			||||||
| 
						 | 
					@ -987,6 +999,12 @@ ext_modules = [
 | 
				
			||||||
    Extension("PIL._imagingmorph", ["src/_imagingmorph.c"]),
 | 
					    Extension("PIL._imagingmorph", ["src/_imagingmorph.c"]),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@ You can get the subsampling of a JPEG with the
 | 
				
			||||||
:func:`.JpegImagePlugin.get_sampling` function.
 | 
					:func:`.JpegImagePlugin.get_sampling` function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
In JPEG compressed data a JPEG marker is used instead of an EXIF tag.
 | 
					In JPEG compressed data a JPEG marker is used instead of an EXIF tag.
 | 
				
			||||||
(ref.: https://exiv2.org/tags.html)
 | 
					(ref.: https://web.archive.org/web/20240227115053/https://exiv2.org/tags.html)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Quantization tables
 | 
					Quantization tables
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .features import pilinfo
 | 
					from .features import pilinfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pilinfo()
 | 
					pilinfo(supported_formats="--report" not in sys.argv)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -230,6 +230,9 @@ def pilinfo(out=None, supported_formats=True):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Prints information about this installation of Pillow.
 | 
					    Prints information about this installation of Pillow.
 | 
				
			||||||
    This function can be called with ``python3 -m PIL``.
 | 
					    This function can be called with ``python3 -m PIL``.
 | 
				
			||||||
 | 
					    It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report``
 | 
				
			||||||
 | 
					    to have "supported_formats" set to ``False``, omitting the list of all supported
 | 
				
			||||||
 | 
					    image file formats.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param out:
 | 
					    :param out:
 | 
				
			||||||
        The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
 | 
					        The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
 | 
				
			||||||
| 
						 | 
					@ -249,12 +252,17 @@ def pilinfo(out=None, supported_formats=True):
 | 
				
			||||||
    for py_version in py_version[1:]:
 | 
					    for py_version in py_version[1:]:
 | 
				
			||||||
        print(f"       {py_version.strip()}", file=out)
 | 
					        print(f"       {py_version.strip()}", file=out)
 | 
				
			||||||
    print("-" * 68, file=out)
 | 
					    print("-" * 68, file=out)
 | 
				
			||||||
 | 
					    print(f"Python executable is {sys.executable or 'unknown'}", file=out)
 | 
				
			||||||
 | 
					    if sys.prefix != sys.base_prefix:
 | 
				
			||||||
 | 
					        print(f"Environment Python files loaded from {sys.prefix}", file=out)
 | 
				
			||||||
 | 
					    print(f"System Python files loaded from {sys.base_prefix}", file=out)
 | 
				
			||||||
 | 
					    print("-" * 68, file=out)
 | 
				
			||||||
    print(
 | 
					    print(
 | 
				
			||||||
        f"Python modules loaded from {os.path.dirname(Image.__file__)}",
 | 
					        f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}",
 | 
				
			||||||
        file=out,
 | 
					        file=out,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    print(
 | 
					    print(
 | 
				
			||||||
        f"Binary modules loaded from {os.path.dirname(Image.core.__file__)}",
 | 
					        f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}",
 | 
				
			||||||
        file=out,
 | 
					        file=out,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    print("-" * 68, file=out)
 | 
					    print("-" * 68, file=out)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										5
									
								
								src/PIL/report.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/PIL/report.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .features import pilinfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pilinfo(supported_formats=False)
 | 
				
			||||||
| 
						 | 
					@ -1578,7 +1578,17 @@ if (PySequence_Check(op)) { \
 | 
				
			||||||
                int bigendian = 0;
 | 
					                int bigendian = 0;
 | 
				
			||||||
                if (image->type == IMAGING_TYPE_SPECIAL) {
 | 
					                if (image->type == IMAGING_TYPE_SPECIAL) {
 | 
				
			||||||
                    // I;16*
 | 
					                    // I;16*
 | 
				
			||||||
                    bigendian = strcmp(image->mode, "I;16B") == 0;
 | 
					                    if (strcmp(image->mode, "I;16N") == 0) {
 | 
				
			||||||
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
 | 
					                        bigendian = 1;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					                        bigendian = 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					                    } else if (strcmp(image->mode, "I;16B") == 0) {
 | 
				
			||||||
 | 
					                        bigendian = 1;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        bigendian = 0;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                for (i = x = y = 0; i < n; i++) {
 | 
					                for (i = x = y = 0; i < n; i++) {
 | 
				
			||||||
                    set_value_to_item(seq, i);
 | 
					                    set_value_to_item(seq, i);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -250,6 +250,26 @@ rgb2i(UINT8 *out_, const UINT8 *in, int xsize) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
rgb2f(UINT8 *out_, const UINT8 *in, int xsize) {
 | 
					rgb2f(UINT8 *out_, const UINT8 *in, int xsize) {
 | 
				
			||||||
    int x;
 | 
					    int x;
 | 
				
			||||||
| 
						 | 
					@ -944,6 +964,14 @@ static struct {
 | 
				
			||||||
    {"RGB", "LA", rgb2la},
 | 
					    {"RGB", "LA", rgb2la},
 | 
				
			||||||
    {"RGB", "La", rgb2la},
 | 
					    {"RGB", "La", rgb2la},
 | 
				
			||||||
    {"RGB", "I", rgb2i},
 | 
					    {"RGB", "I", rgb2i},
 | 
				
			||||||
 | 
					    {"RGB", "I;16", rgb2i16l},
 | 
				
			||||||
 | 
					    {"RGB", "I;16L", rgb2i16l},
 | 
				
			||||||
 | 
					    {"RGB", "I;16B", rgb2i16b},
 | 
				
			||||||
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
 | 
					    {"RGB", "I;16N", rgb2i16b},
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    {"RGB", "I;16N", rgb2i16l},
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    {"RGB", "F", rgb2f},
 | 
					    {"RGB", "F", rgb2f},
 | 
				
			||||||
    {"RGB", "BGR;15", rgb2bgr15},
 | 
					    {"RGB", "BGR;15", rgb2bgr15},
 | 
				
			||||||
    {"RGB", "BGR;16", rgb2bgr16},
 | 
					    {"RGB", "BGR;16", rgb2bgr16},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,11 +87,18 @@ are set by running ``winbuild\build\build_env.cmd`` and install Pillow with pip:
 | 
				
			||||||
    winbuild\build\build_env.cmd
 | 
					    winbuild\build\build_env.cmd
 | 
				
			||||||
    python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor .
 | 
					    python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To build a wheel instead, run::
 | 
					You can also install Pillow in `editable mode`_::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    winbuild\build\build_env.cmd
 | 
				
			||||||
 | 
					    python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor -e .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To build a binary wheel instead, run::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    winbuild\build\build_env.cmd
 | 
					    winbuild\build\build_env.cmd
 | 
				
			||||||
    python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor .
 | 
					    python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _editable mode: https://setuptools.pypa.io/en/stable/userguide/development_mode.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Testing Pillow
 | 
					Testing Pillow
 | 
				
			||||||
--------------
 | 
					--------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,7 +113,7 @@ V = {
 | 
				
			||||||
    "BROTLI": "1.1.0",
 | 
					    "BROTLI": "1.1.0",
 | 
				
			||||||
    "FREETYPE": "2.13.2",
 | 
					    "FREETYPE": "2.13.2",
 | 
				
			||||||
    "FRIBIDI": "1.0.13",
 | 
					    "FRIBIDI": "1.0.13",
 | 
				
			||||||
    "HARFBUZZ": "8.3.1",
 | 
					    "HARFBUZZ": "8.4.0",
 | 
				
			||||||
    "JPEGTURBO": "3.0.2",
 | 
					    "JPEGTURBO": "3.0.2",
 | 
				
			||||||
    "LCMS2": "2.16",
 | 
					    "LCMS2": "2.16",
 | 
				
			||||||
    "LIBPNG": "1.6.43",
 | 
					    "LIBPNG": "1.6.43",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user