mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Merge branch 'main' into pybind11
This commit is contained in:
		
						commit
						a549c5528a
					
				| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
cibuildwheel==2.23.3
 | 
					cibuildwheel==3.0.0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
mypy==1.15.0
 | 
					mypy==1.16.1
 | 
				
			||||||
IceSpringPySideStubs-PyQt6
 | 
					IceSpringPySideStubs-PyQt6
 | 
				
			||||||
IceSpringPySideStubs-PySide6
 | 
					IceSpringPySideStubs-PySide6
 | 
				
			||||||
ipython
 | 
					ipython
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								.github/workflows/test-windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test-windows.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -35,7 +35,7 @@ jobs:
 | 
				
			||||||
    strategy:
 | 
					    strategy:
 | 
				
			||||||
      fail-fast: false
 | 
					      fail-fast: false
 | 
				
			||||||
      matrix:
 | 
					      matrix:
 | 
				
			||||||
        python-version: ["pypy3.11", "pypy3.10", "3.10", "3.11", "3.12", "3.13", "3.14"]
 | 
					        python-version: ["pypy3.11", "pypy3.10", "3.10", "3.11", "3.12", ">=3.13.5", "3.14"]
 | 
				
			||||||
        architecture: ["x64"]
 | 
					        architecture: ["x64"]
 | 
				
			||||||
        include:
 | 
					        include:
 | 
				
			||||||
            # Test the oldest Python on 32-bit
 | 
					            # Test the oldest Python on 32-bit
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -43,6 +43,7 @@ jobs:
 | 
				
			||||||
        python-version: [
 | 
					        python-version: [
 | 
				
			||||||
          "pypy3.11",
 | 
					          "pypy3.11",
 | 
				
			||||||
          "pypy3.10",
 | 
					          "pypy3.10",
 | 
				
			||||||
 | 
					          "3.14t",
 | 
				
			||||||
          "3.14",
 | 
					          "3.14",
 | 
				
			||||||
          "3.13t",
 | 
					          "3.13t",
 | 
				
			||||||
          "3.13",
 | 
					          "3.13",
 | 
				
			||||||
| 
						 | 
					@ -55,6 +56,7 @@ jobs:
 | 
				
			||||||
        - { python-version: "3.11", PYTHONOPTIMIZE: 1, REVERSE: "--reverse" }
 | 
					        - { python-version: "3.11", PYTHONOPTIMIZE: 1, REVERSE: "--reverse" }
 | 
				
			||||||
        - { python-version: "3.10", PYTHONOPTIMIZE: 2 }
 | 
					        - { python-version: "3.10", PYTHONOPTIMIZE: 2 }
 | 
				
			||||||
        # Free-threaded
 | 
					        # Free-threaded
 | 
				
			||||||
 | 
					        - { python-version: "3.14t", disable-gil: true }
 | 
				
			||||||
        - { python-version: "3.13t", disable-gil: true }
 | 
					        - { python-version: "3.13t", disable-gil: true }
 | 
				
			||||||
        # M1 only available for 3.10+
 | 
					        # M1 only available for 3.10+
 | 
				
			||||||
        - { os: "macos-13", python-version: "3.9" }
 | 
					        - { os: "macos-13", python-version: "3.9" }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								.github/workflows/wheels-dependencies.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/wheels-dependencies.sh
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -39,8 +39,8 @@ ARCHIVE_SDIR=pillow-depends-main
 | 
				
			||||||
# Package versions for fresh source builds
 | 
					# Package versions for fresh source builds
 | 
				
			||||||
FREETYPE_VERSION=2.13.3
 | 
					FREETYPE_VERSION=2.13.3
 | 
				
			||||||
HARFBUZZ_VERSION=11.2.1
 | 
					HARFBUZZ_VERSION=11.2.1
 | 
				
			||||||
LIBPNG_VERSION=1.6.48
 | 
					LIBPNG_VERSION=1.6.49
 | 
				
			||||||
JPEGTURBO_VERSION=3.1.0
 | 
					JPEGTURBO_VERSION=3.1.1
 | 
				
			||||||
OPENJPEG_VERSION=2.5.3
 | 
					OPENJPEG_VERSION=2.5.3
 | 
				
			||||||
XZ_VERSION=5.8.1
 | 
					XZ_VERSION=5.8.1
 | 
				
			||||||
TIFF_VERSION=4.7.0
 | 
					TIFF_VERSION=4.7.0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								.github/workflows/wheels-test.ps1
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/wheels-test.ps1
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -9,17 +9,21 @@ if ("$venv" -like "*\cibw-run-*\pp*-win_amd64\*") {
 | 
				
			||||||
    C:\vc_redist.x64.exe /install /quiet /norestart | Out-Null
 | 
					    C:\vc_redist.x64.exe /install /quiet /norestart | Out-Null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$env:path += ";$pillow\winbuild\build\bin\"
 | 
					$env:path += ";$pillow\winbuild\build\bin\"
 | 
				
			||||||
& "$venv\Scripts\activate.ps1"
 | 
					if (Test-Path $venv\Scripts\pypy.exe) {
 | 
				
			||||||
 | 
					  $python = "pypy.exe"
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					  $python = "python.exe"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
& reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\python.exe" /v "GlobalFlag" /t REG_SZ /d "0x02000000" /f
 | 
					& reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\python.exe" /v "GlobalFlag" /t REG_SZ /d "0x02000000" /f
 | 
				
			||||||
if ("$venv" -like "*\cibw-run-*-win_amd64\*") {
 | 
					if ("$venv" -like "*\cibw-run-*-win_amd64\*") {
 | 
				
			||||||
  & python -m pip install numpy
 | 
					  & $venv\Scripts\$python -m pip install numpy
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
cd $pillow
 | 
					cd $pillow
 | 
				
			||||||
& python -VV
 | 
					& $venv\Scripts\$python -VV
 | 
				
			||||||
if (!$?) { exit $LASTEXITCODE }
 | 
					if (!$?) { exit $LASTEXITCODE }
 | 
				
			||||||
& python selftest.py
 | 
					& $venv\Scripts\$python selftest.py
 | 
				
			||||||
if (!$?) { exit $LASTEXITCODE }
 | 
					if (!$?) { exit $LASTEXITCODE }
 | 
				
			||||||
& python -m pytest -vx Tests\check_wheel.py
 | 
					& $venv\Scripts\$python -m pytest -vx Tests\check_wheel.py
 | 
				
			||||||
if (!$?) { exit $LASTEXITCODE }
 | 
					if (!$?) { exit $LASTEXITCODE }
 | 
				
			||||||
& python -m pytest -vx Tests
 | 
					& $venv\Scripts\$python -m pytest -vx Tests
 | 
				
			||||||
if (!$?) { exit $LASTEXITCODE }
 | 
					if (!$?) { exit $LASTEXITCODE }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								.github/workflows/wheels.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/wheels.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -58,7 +58,7 @@ jobs:
 | 
				
			||||||
          - name: "macOS 10.13 x86_64"
 | 
					          - name: "macOS 10.13 x86_64"
 | 
				
			||||||
            os: macos-13
 | 
					            os: macos-13
 | 
				
			||||||
            cibw_arch: x86_64
 | 
					            cibw_arch: x86_64
 | 
				
			||||||
            build: "cp3{12,13}*"
 | 
					            build: "cp3{12,13,14}*"
 | 
				
			||||||
            macosx_deployment_target: "10.13"
 | 
					            macosx_deployment_target: "10.13"
 | 
				
			||||||
          - name: "macOS 10.15 x86_64"
 | 
					          - name: "macOS 10.15 x86_64"
 | 
				
			||||||
            os: macos-13
 | 
					            os: macos-13
 | 
				
			||||||
| 
						 | 
					@ -110,7 +110,6 @@ jobs:
 | 
				
			||||||
          CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: ${{ matrix.manylinux }}
 | 
					          CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: ${{ matrix.manylinux }}
 | 
				
			||||||
          CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
 | 
					          CIBW_MANYLINUX_PYPY_X86_64_IMAGE: ${{ matrix.manylinux }}
 | 
				
			||||||
          CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
 | 
					          CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }}
 | 
				
			||||||
          CIBW_SKIP: pp39-*
 | 
					 | 
				
			||||||
          MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
 | 
					          MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - uses: actions/upload-artifact@v4
 | 
					      - uses: actions/upload-artifact@v4
 | 
				
			||||||
| 
						 | 
					@ -188,7 +187,6 @@ jobs:
 | 
				
			||||||
          CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
 | 
					          CIBW_BEFORE_ALL: "{package}\\winbuild\\build\\build_dep_all.cmd"
 | 
				
			||||||
          CIBW_CACHE_PATH: "C:\\cibw"
 | 
					          CIBW_CACHE_PATH: "C:\\cibw"
 | 
				
			||||||
          CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy
 | 
					          CIBW_ENABLE: cpython-prerelease cpython-freethreading pypy
 | 
				
			||||||
          CIBW_SKIP: pp39-*
 | 
					 | 
				
			||||||
          CIBW_TEST_SKIP: "*-win_arm64"
 | 
					          CIBW_TEST_SKIP: "*-win_arm64"
 | 
				
			||||||
          CIBW_TEST_COMMAND: 'docker run --rm
 | 
					          CIBW_TEST_COMMAND: 'docker run --rm
 | 
				
			||||||
            -v {project}:C:\pillow
 | 
					            -v {project}:C:\pillow
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
repos:
 | 
					repos:
 | 
				
			||||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
					  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
				
			||||||
    rev: v0.11.8
 | 
					    rev: v0.11.12
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: ruff
 | 
					      - id: ruff
 | 
				
			||||||
        args: [--exit-non-zero-on-fix]
 | 
					        args: [--exit-non-zero-on-fix]
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,7 @@ repos:
 | 
				
			||||||
        exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
 | 
					        exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.gd$|\.opt$)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - repo: https://github.com/pre-commit/mirrors-clang-format
 | 
					  - repo: https://github.com/pre-commit/mirrors-clang-format
 | 
				
			||||||
    rev: v20.1.3
 | 
					    rev: v20.1.5
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: clang-format
 | 
					      - id: clang-format
 | 
				
			||||||
        types: [c]
 | 
					        types: [c]
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ repos:
 | 
				
			||||||
      - id: check-renovate
 | 
					      - id: check-renovate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - repo: https://github.com/woodruffw/zizmor-pre-commit
 | 
					  - repo: https://github.com/woodruffw/zizmor-pre-commit
 | 
				
			||||||
    rev: v1.6.0
 | 
					    rev: v1.9.0
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: zizmor
 | 
					      - id: zizmor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,7 +68,7 @@ repos:
 | 
				
			||||||
      - id: sphinx-lint
 | 
					      - id: sphinx-lint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - repo: https://github.com/tox-dev/pyproject-fmt
 | 
					  - repo: https://github.com/tox-dev/pyproject-fmt
 | 
				
			||||||
    rev: v2.5.1
 | 
					    rev: v2.6.0
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: pyproject-fmt
 | 
					      - id: pyproject-fmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ from .helper import is_pypy
 | 
				
			||||||
def test_wheel_modules() -> None:
 | 
					def test_wheel_modules() -> None:
 | 
				
			||||||
    expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp"}
 | 
					    expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.platform == "win32":
 | 
				
			||||||
        # tkinter is not available in cibuildwheel installed CPython on Windows
 | 
					        # tkinter is not available in cibuildwheel installed CPython on Windows
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            import tkinter
 | 
					            import tkinter
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/op_index.qoi
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/op_index.qoi
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/p_4_planes.pcx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/p_4_planes.pcx
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -47,7 +47,6 @@ def test_unknown_version() -> None:
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def test_old_version(deprecated: str, plural: bool, expected: str) -> None:
 | 
					def test_old_version(deprecated: str, plural: bool, expected: str) -> None:
 | 
				
			||||||
    expected = r""
 | 
					 | 
				
			||||||
    with pytest.raises(RuntimeError, match=expected):
 | 
					    with pytest.raises(RuntimeError, match=expected):
 | 
				
			||||||
        _deprecate.deprecate(deprecated, 1, plural=plural)
 | 
					        _deprecate.deprecate(deprecated, 1, plural=plural)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,8 @@ import pytest
 | 
				
			||||||
from PIL import BlpImagePlugin, Image
 | 
					from PIL import BlpImagePlugin, Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import (
 | 
					from .helper import (
 | 
				
			||||||
    assert_image_equal,
 | 
					 | 
				
			||||||
    assert_image_equal_tofile,
 | 
					    assert_image_equal_tofile,
 | 
				
			||||||
    assert_image_similar,
 | 
					    assert_image_similar_tofile,
 | 
				
			||||||
    hopper,
 | 
					    hopper,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,18 +51,16 @@ def test_save(tmp_path: Path) -> None:
 | 
				
			||||||
        im = hopper("P")
 | 
					        im = hopper("P")
 | 
				
			||||||
        im.save(f, blp_version=version)
 | 
					        im.save(f, blp_version=version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with Image.open(f) as reloaded:
 | 
					        assert_image_equal_tofile(im.convert("RGB"), f)
 | 
				
			||||||
            assert_image_equal(im.convert("RGB"), reloaded)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with Image.open("Tests/images/transparent.png") as im:
 | 
					        with Image.open("Tests/images/transparent.png") as im:
 | 
				
			||||||
            f = tmp_path / "temp.blp"
 | 
					            f = tmp_path / "temp.blp"
 | 
				
			||||||
            im.convert("P").save(f, blp_version=version)
 | 
					            im.convert("P").save(f, blp_version=version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            with Image.open(f) as reloaded:
 | 
					            assert_image_similar_tofile(im, f, 8)
 | 
				
			||||||
                assert_image_similar(im, reloaded, 8)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    im = hopper()
 | 
					    im = hopper()
 | 
				
			||||||
    with pytest.raises(ValueError):
 | 
					    with pytest.raises(ValueError, match="Unsupported BLP image mode"):
 | 
				
			||||||
        im.save(f)
 | 
					        im.save(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -145,14 +145,16 @@ class TestFileJpeg:
 | 
				
			||||||
            assert k > 0.9
 | 
					            assert k > 0.9
 | 
				
			||||||
            # roundtrip, and check again
 | 
					            # roundtrip, and check again
 | 
				
			||||||
            im = self.roundtrip(im)
 | 
					            im = self.roundtrip(im)
 | 
				
			||||||
            c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
 | 
					            cmyk = im.getpixel((0, 0))
 | 
				
			||||||
 | 
					            assert isinstance(cmyk, tuple)
 | 
				
			||||||
 | 
					            c, m, y, k = (x / 255.0 for x in cmyk)
 | 
				
			||||||
            assert c == 0.0
 | 
					            assert c == 0.0
 | 
				
			||||||
            assert m > 0.8
 | 
					            assert m > 0.8
 | 
				
			||||||
            assert y > 0.8
 | 
					            assert y > 0.8
 | 
				
			||||||
            assert k == 0.0
 | 
					            assert k == 0.0
 | 
				
			||||||
            c, m, y, k = (
 | 
					            cmyk = im.getpixel((im.size[0] - 1, im.size[1] - 1))
 | 
				
			||||||
                x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
 | 
					            assert isinstance(cmyk, tuple)
 | 
				
			||||||
            )
 | 
					            k = cmyk[3] / 255.0
 | 
				
			||||||
            assert k > 0.9
 | 
					            assert k > 0.9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_rgb(self) -> None:
 | 
					    def test_rgb(self) -> None:
 | 
				
			||||||
| 
						 | 
					@ -1065,10 +1067,16 @@ class TestFileJpeg:
 | 
				
			||||||
        for marker in b"\xff\xd8", b"\xff\xd9":
 | 
					        for marker in b"\xff\xd8", b"\xff\xd9":
 | 
				
			||||||
            assert marker in data[1]
 | 
					            assert marker in data[1]
 | 
				
			||||||
            assert marker in data[2]
 | 
					            assert marker in data[2]
 | 
				
			||||||
        # DHT, DQT
 | 
					
 | 
				
			||||||
        for marker in b"\xff\xc4", b"\xff\xdb":
 | 
					        # DQT
 | 
				
			||||||
 | 
					        markers = [b"\xff\xdb"]
 | 
				
			||||||
 | 
					        if features.check_feature("libjpeg_turbo"):
 | 
				
			||||||
 | 
					            # DHT
 | 
				
			||||||
 | 
					            markers.append(b"\xff\xc4")
 | 
				
			||||||
 | 
					        for marker in markers:
 | 
				
			||||||
            assert marker in data[1]
 | 
					            assert marker in data[1]
 | 
				
			||||||
            assert marker not in data[2]
 | 
					            assert marker not in data[2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # SOF0, SOS, APP0 (JFIF header)
 | 
					        # SOF0, SOS, APP0 (JFIF header)
 | 
				
			||||||
        for marker in b"\xff\xc0", b"\xff\xda", b"\xff\xe0":
 | 
					        for marker in b"\xff\xc0", b"\xff\xda", b"\xff\xe0":
 | 
				
			||||||
            assert marker not in data[1]
 | 
					            assert marker not in data[1]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,11 @@ def test_sanity(tmp_path: Path) -> None:
 | 
				
			||||||
        im.save(f)
 | 
					        im.save(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_p_4_planes() -> None:
 | 
				
			||||||
 | 
					    with Image.open("Tests/images/p_4_planes.pcx") as im:
 | 
				
			||||||
 | 
					        assert im.getpixel((0, 0)) == 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_bad_image_size() -> None:
 | 
					def test_bad_image_size() -> None:
 | 
				
			||||||
    with open("Tests/images/pil184.pcx", "rb") as fp:
 | 
					    with open("Tests/images/pil184.pcx", "rb") as fp:
 | 
				
			||||||
        data = fp.read()
 | 
					        data = fp.read()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,11 +100,11 @@ class TestFilePng:
 | 
				
			||||||
            assert im.format == "PNG"
 | 
					            assert im.format == "PNG"
 | 
				
			||||||
            assert im.get_format_mimetype() == "image/png"
 | 
					            assert im.get_format_mimetype() == "image/png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for mode in ["1", "L", "P", "RGB", "I", "I;16", "I;16B"]:
 | 
					        for mode in ["1", "L", "P", "RGB", "I;16", "I;16B"]:
 | 
				
			||||||
            im = hopper(mode)
 | 
					            im = hopper(mode)
 | 
				
			||||||
            im.save(test_file)
 | 
					            im.save(test_file)
 | 
				
			||||||
            with Image.open(test_file) as reloaded:
 | 
					            with Image.open(test_file) as reloaded:
 | 
				
			||||||
                if mode in ("I", "I;16B"):
 | 
					                if mode == "I;16B":
 | 
				
			||||||
                    reloaded = reloaded.convert(mode)
 | 
					                    reloaded = reloaded.convert(mode)
 | 
				
			||||||
                assert_image_equal(reloaded, im)
 | 
					                assert_image_equal(reloaded, im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -801,6 +801,16 @@ class TestFilePng:
 | 
				
			||||||
        with Image.open("Tests/images/truncated_end_chunk.png") as im:
 | 
					        with Image.open("Tests/images/truncated_end_chunk.png") as im:
 | 
				
			||||||
            assert_image_equal_tofile(im, "Tests/images/hopper.png")
 | 
					            assert_image_equal_tofile(im, "Tests/images/hopper.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_deprecation(self, tmp_path: Path) -> None:
 | 
				
			||||||
 | 
					        test_file = tmp_path / "out.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        im = hopper("I")
 | 
				
			||||||
 | 
					        with pytest.warns(DeprecationWarning):
 | 
				
			||||||
 | 
					            im.save(test_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with Image.open(test_file) as reloaded:
 | 
				
			||||||
 | 
					            assert_image_equal(im, reloaded.convert("I"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
 | 
					@pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
 | 
				
			||||||
@skip_unless_feature("zlib")
 | 
					@skip_unless_feature("zlib")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -288,14 +288,16 @@ def test_non_integer_token(tmp_path: Path) -> None:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_header_token_too_long(tmp_path: Path) -> None:
 | 
					@pytest.mark.parametrize("data", (b"P3\x0cAAAAAAAAAA\xee", b"P6\n 01234567890"))
 | 
				
			||||||
 | 
					def test_header_token_too_long(tmp_path: Path, data: bytes) -> None:
 | 
				
			||||||
    path = tmp_path / "temp.ppm"
 | 
					    path = tmp_path / "temp.ppm"
 | 
				
			||||||
    with open(path, "wb") as f:
 | 
					    with open(path, "wb") as f:
 | 
				
			||||||
        f.write(b"P6\n 01234567890")
 | 
					        f.write(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(ValueError, match="Token too long in file header: 01234567890"):
 | 
					    with pytest.raises(ValueError) as e:
 | 
				
			||||||
        with Image.open(path):
 | 
					        with Image.open(path):
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					    assert "Token too long in file header: " in repr(e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_truncated_file(tmp_path: Path) -> None:
 | 
					def test_truncated_file(tmp_path: Path) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,12 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import Image, QoiImagePlugin
 | 
					from PIL import Image, QoiImagePlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import assert_image_equal_tofile
 | 
					from .helper import assert_image_equal_tofile, hopper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity() -> None:
 | 
					def test_sanity() -> None:
 | 
				
			||||||
| 
						 | 
					@ -28,3 +30,28 @@ def test_invalid_file() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(SyntaxError):
 | 
					    with pytest.raises(SyntaxError):
 | 
				
			||||||
        QoiImagePlugin.QoiImageFile(invalid_file)
 | 
					        QoiImagePlugin.QoiImageFile(invalid_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_op_index() -> None:
 | 
				
			||||||
 | 
					    # QOI_OP_INDEX as the first chunk
 | 
				
			||||||
 | 
					    with Image.open("Tests/images/op_index.qoi") as im:
 | 
				
			||||||
 | 
					        assert im.getpixel((0, 0)) == (0, 0, 0, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_save(tmp_path: Path) -> None:
 | 
				
			||||||
 | 
					    f = tmp_path / "temp.qoi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    im = hopper()
 | 
				
			||||||
 | 
					    im.save(f, colorspace="sRGB")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert_image_equal_tofile(im, f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for path in ("Tests/images/default_font.png", "Tests/images/pil123rgba.png"):
 | 
				
			||||||
 | 
					        with Image.open(path) as im:
 | 
				
			||||||
 | 
					            im.save(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert_image_equal_tofile(im, f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    im = hopper("P")
 | 
				
			||||||
 | 
					    with pytest.raises(ValueError, match="Unsupported QOI image mode"):
 | 
				
			||||||
 | 
					        im.save(f)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ from PIL import (
 | 
				
			||||||
    ImageFile,
 | 
					    ImageFile,
 | 
				
			||||||
    JpegImagePlugin,
 | 
					    JpegImagePlugin,
 | 
				
			||||||
    TiffImagePlugin,
 | 
					    TiffImagePlugin,
 | 
				
			||||||
 | 
					    TiffTags,
 | 
				
			||||||
    UnidentifiedImageError,
 | 
					    UnidentifiedImageError,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
 | 
					from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
 | 
				
			||||||
| 
						 | 
					@ -48,23 +49,8 @@ class TestFileTiff:
 | 
				
			||||||
        assert im.size == (128, 128)
 | 
					        assert im.size == (128, 128)
 | 
				
			||||||
        assert im.format == "TIFF"
 | 
					        assert im.format == "TIFF"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        hopper("1").save(filename)
 | 
					        for mode in ("1", "L", "P", "RGB", "I", "I;16", "I;16L"):
 | 
				
			||||||
        with Image.open(filename):
 | 
					            hopper(mode).save(filename)
 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        hopper("L").save(filename)
 | 
					 | 
				
			||||||
        with Image.open(filename):
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        hopper("P").save(filename)
 | 
					 | 
				
			||||||
        with Image.open(filename):
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        hopper("RGB").save(filename)
 | 
					 | 
				
			||||||
        with Image.open(filename):
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        hopper("I").save(filename)
 | 
					 | 
				
			||||||
            with Image.open(filename):
 | 
					            with Image.open(filename):
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -900,6 +886,29 @@ class TestFileTiff:
 | 
				
			||||||
                assert description[0]["format"] == "image/tiff"
 | 
					                assert description[0]["format"] == "image/tiff"
 | 
				
			||||||
                assert description[3]["BitsPerSample"]["Seq"]["li"] == ["8", "8", "8"]
 | 
					                assert description[3]["BitsPerSample"]["Seq"]["li"] == ["8", "8", "8"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_getxmp_undefined(self, tmp_path: Path) -> None:
 | 
				
			||||||
 | 
					        tmpfile = tmp_path / "temp.tif"
 | 
				
			||||||
 | 
					        im = Image.new("L", (1, 1))
 | 
				
			||||||
 | 
					        ifd = TiffImagePlugin.ImageFileDirectory_v2()
 | 
				
			||||||
 | 
					        ifd.tagtype[700] = TiffTags.UNDEFINED
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/lab.tif") as im_xmp:
 | 
				
			||||||
 | 
					            ifd[700] = im_xmp.info["xmp"]
 | 
				
			||||||
 | 
					        im.save(tmpfile, tiffinfo=ifd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with Image.open(tmpfile) as im_reloaded:
 | 
				
			||||||
 | 
					            if ElementTree is None:
 | 
				
			||||||
 | 
					                with pytest.warns(
 | 
				
			||||||
 | 
					                    UserWarning,
 | 
				
			||||||
 | 
					                    match="XMP data cannot be read without defusedxml dependency",
 | 
				
			||||||
 | 
					                ):
 | 
				
			||||||
 | 
					                    assert im_reloaded.getxmp() == {}
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                assert "xmp" in im_reloaded.info
 | 
				
			||||||
 | 
					                xmp = im_reloaded.getxmp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                description = xmp["xmpmeta"]["RDF"]["Description"]
 | 
				
			||||||
 | 
					                assert description[0]["format"] == "image/tiff"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_get_photoshop_blocks(self) -> None:
 | 
					    def test_get_photoshop_blocks(self) -> None:
 | 
				
			||||||
        with Image.open("Tests/images/lab.tif") as im:
 | 
					        with Image.open("Tests/images/lab.tif") as im:
 | 
				
			||||||
            assert isinstance(im, TiffImagePlugin.TiffImageFile)
 | 
					            assert isinstance(im, TiffImagePlugin.TiffImageFile)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -671,6 +671,7 @@ class TestImage:
 | 
				
			||||||
        im_remapped = im.remap_palette(list(range(256)))
 | 
					        im_remapped = im.remap_palette(list(range(256)))
 | 
				
			||||||
        assert_image_equal(im, im_remapped)
 | 
					        assert_image_equal(im, im_remapped)
 | 
				
			||||||
        assert im.palette is not None
 | 
					        assert im.palette is not None
 | 
				
			||||||
 | 
					        assert im_remapped.palette is not None
 | 
				
			||||||
        assert im.palette.palette == im_remapped.palette.palette
 | 
					        assert im.palette.palette == im_remapped.palette.palette
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Test illegal image mode
 | 
					        # Test illegal image mode
 | 
				
			||||||
| 
						 | 
					@ -973,6 +974,11 @@ class TestImage:
 | 
				
			||||||
                    assert tag not in exif.get_ifd(0x8769)
 | 
					                    assert tag not in exif.get_ifd(0x8769)
 | 
				
			||||||
                assert exif.get_ifd(0xA005)
 | 
					                assert exif.get_ifd(0xA005)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_exif_from_xmp_bytes(self) -> None:
 | 
				
			||||||
 | 
					        im = Image.new("RGB", (1, 1))
 | 
				
			||||||
 | 
					        im.info["xmp"] = b'\xff tiff:Orientation="2"'
 | 
				
			||||||
 | 
					        assert im.getexif()[274] == 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_empty_xmp(self) -> None:
 | 
					    def test_empty_xmp(self) -> None:
 | 
				
			||||||
        with Image.open("Tests/images/hopper.gif") as im:
 | 
					        with Image.open("Tests/images/hopper.gif") as im:
 | 
				
			||||||
            if ElementTree is None:
 | 
					            if ElementTree is None:
 | 
				
			||||||
| 
						 | 
					@ -989,7 +995,7 @@ class TestImage:
 | 
				
			||||||
        im = Image.new("RGB", (1, 1))
 | 
					        im = Image.new("RGB", (1, 1))
 | 
				
			||||||
        im.info["xmp"] = (
 | 
					        im.info["xmp"] = (
 | 
				
			||||||
            b'<?xpacket begin="\xef\xbb\xbf" id="W5M0MpCehiHzreSzNTczkc9d"?>\n'
 | 
					            b'<?xpacket begin="\xef\xbb\xbf" id="W5M0MpCehiHzreSzNTczkc9d"?>\n'
 | 
				
			||||||
            b'<x:xmpmeta xmlns:x="adobe:ns:meta/" />\n<?xpacket end="w"?>\x00\x00'
 | 
					            b'<x:xmpmeta xmlns:x="adobe:ns:meta/" />\n<?xpacket end="w"?>\x00\x00 '
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        if ElementTree is None:
 | 
					        if ElementTree is None:
 | 
				
			||||||
            with pytest.warns(
 | 
					            with pytest.warns(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,7 @@ def test_fromarray_strides_without_tobytes() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(ValueError):
 | 
					    with pytest.raises(ValueError):
 | 
				
			||||||
        wrapped = Wrapper({"shape": (1, 1), "strides": (1, 1)})
 | 
					        wrapped = Wrapper({"shape": (1, 1), "strides": (1, 1)})
 | 
				
			||||||
 | 
					        with pytest.warns(DeprecationWarning):
 | 
				
			||||||
            Image.fromarray(wrapped, "L")
 | 
					            Image.fromarray(wrapped, "L")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,6 +111,7 @@ def test_fromarray_palette() -> None:
 | 
				
			||||||
    a = numpy.array(i)
 | 
					    a = numpy.array(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
 | 
					    with pytest.warns(DeprecationWarning):
 | 
				
			||||||
        out = Image.fromarray(a, "P")
 | 
					        out = Image.fromarray(a, "P")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert that the Python and C palettes match
 | 
					    # Assert that the Python and C palettes match
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -783,9 +783,10 @@ def test_rectangle_I16(bbox: Coords) -> None:
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rectangle(bbox, outline=0xFFFF)
 | 
					    draw.rectangle(bbox, outline=0xCDEF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
 | 
					    assert im.getpixel((X0, Y0)) == 0xCDEF
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_I.tiff")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_I.tiff")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,3 +52,17 @@ def test_tiff_crashes(test_file: str) -> None:
 | 
				
			||||||
        pytest.skip("test image not found")
 | 
					        pytest.skip("test image not found")
 | 
				
			||||||
    except OSError:
 | 
					    except OSError:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_tiff_mmap() -> None:
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/crash_mmap.tif") as im:
 | 
				
			||||||
 | 
					            im.seek(1)
 | 
				
			||||||
 | 
					            im.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            im.seek(0)
 | 
				
			||||||
 | 
					            im.load()
 | 
				
			||||||
 | 
					    except FileNotFoundError:
 | 
				
			||||||
 | 
					        if on_ci():
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					        pytest.skip("test image not found")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -193,6 +193,28 @@ Image.Image.get_child_images()
 | 
				
			||||||
method uses an image's file pointer, and so child images could only be retrieved from
 | 
					method uses an image's file pointer, and so child images could only be retrieved from
 | 
				
			||||||
an :py:class:`PIL.ImageFile.ImageFile` instance.
 | 
					an :py:class:`PIL.ImageFile.ImageFile` instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Image.fromarray mode parameter
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. deprecated:: 11.3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The ``mode`` parameter in :py:meth:`~PIL.Image.fromarray()` has been deprecated. The
 | 
				
			||||||
 | 
					mode can be automatically determined from the object's shape and type instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Saving I mode images as PNG
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. deprecated:: 11.3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to fit the 32 bits of I mode images into PNG, when PNG images can only contain
 | 
				
			||||||
 | 
					at most 16 bits for a channel, Pillow has been clipping the values. Rather than quietly
 | 
				
			||||||
 | 
					changing the data, this is now deprecated. Instead, the image can be converted to
 | 
				
			||||||
 | 
					another mode before saving::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from PIL import Image
 | 
				
			||||||
 | 
					    im = Image.new("I", (1, 1))
 | 
				
			||||||
 | 
					    im.convert("I;16").save("out.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Removed features
 | 
					Removed features
 | 
				
			||||||
----------------
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1082,6 +1082,26 @@ Pillow reads and writes PBM, PGM, PPM and PNM files containing ``1``, ``L``, ``I
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Since Pillow 9.2.0, "plain" (P1 to P3) formats can be read as well.
 | 
					Since Pillow 9.2.0, "plain" (P1 to P3) formats can be read as well.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QOI
 | 
				
			||||||
 | 
					^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. versionadded:: 9.5.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pillow reads and writes images in Quite OK Image format using a Python codec. If you
 | 
				
			||||||
 | 
					wish to write code specifically for this format, :pypi:`qoi` is an alternative library
 | 
				
			||||||
 | 
					that uses C to decode the image and interfaces with NumPy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _qoi-saving:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Saving
 | 
				
			||||||
 | 
					~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**colorspace**
 | 
				
			||||||
 | 
					    If set to "sRGB", the colorspace will be written as sRGB with linear alpha, instead
 | 
				
			||||||
 | 
					    of all channels being linear.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SGI
 | 
					SGI
 | 
				
			||||||
^^^
 | 
					^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1578,15 +1598,6 @@ PSD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Pillow identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0.
 | 
					Pillow identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
QOI
 | 
					 | 
				
			||||||
^^^
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. versionadded:: 9.5.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Pillow reads images in Quite OK Image format using a Python decoder. If you wish to
 | 
					 | 
				
			||||||
write code specifically for this format, :pypi:`qoi` is an alternative library that
 | 
					 | 
				
			||||||
uses C to decode the image and interfaces with NumPy.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SUN
 | 
					SUN
 | 
				
			||||||
^^^
 | 
					^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,12 +40,12 @@ These platforms are built and tested for every change.
 | 
				
			||||||
| macOS 13 Ventura                 | 3.9                        | x86-64              |
 | 
					| macOS 13 Ventura                 | 3.9                        | x86-64              |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| macOS 14 Sonoma                  | 3.10, 3.11, 3.12, 3.13,    | arm64               |
 | 
					| macOS 14 Sonoma                  | 3.10, 3.11, 3.12, 3.13,    | arm64               |
 | 
				
			||||||
|                                  | PyPy3                      |                     |
 | 
					|                                  | 3.14, PyPy3                |                     |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Ubuntu Linux 22.04 LTS (Jammy)   | 3.10                       | x86-64              |
 | 
					| Ubuntu Linux 22.04 LTS (Jammy)   | 3.10                       | x86-64              |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Ubuntu Linux 24.04 LTS (Noble)   | 3.9, 3.10, 3.11,           | x86-64              |
 | 
					| Ubuntu Linux 24.04 LTS (Noble)   | 3.9, 3.10, 3.11,           | x86-64              |
 | 
				
			||||||
|                                  | 3.12, 3.13, PyPy3          |                     |
 | 
					|                                  | 3.12, 3.13, 3.14, PyPy3    |                     |
 | 
				
			||||||
|                                  +----------------------------+---------------------+
 | 
					|                                  +----------------------------+---------------------+
 | 
				
			||||||
|                                  | 3.12                       | arm64v8, ppc64le,   |
 | 
					|                                  | 3.12                       | arm64v8, ppc64le,   |
 | 
				
			||||||
|                                  |                            | s390x               |
 | 
					|                                  |                            | s390x               |
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@ These platforms are built and tested for every change.
 | 
				
			||||||
| Windows Server 2022              | 3.9                        | x86                 |
 | 
					| Windows Server 2022              | 3.9                        | x86                 |
 | 
				
			||||||
|                                  +----------------------------+---------------------+
 | 
					|                                  +----------------------------+---------------------+
 | 
				
			||||||
|                                  | 3.10, 3.11, 3.12, 3.13,    | x86-64              |
 | 
					|                                  | 3.10, 3.11, 3.12, 3.13,    | x86-64              |
 | 
				
			||||||
|                                  | PyPy3                      |                     |
 | 
					|                                  | 3.14, PyPy3                |                     |
 | 
				
			||||||
|                                  +----------------------------+---------------------+
 | 
					|                                  +----------------------------+---------------------+
 | 
				
			||||||
|                                  | 3.12 (MinGW)               | x86-64              |
 | 
					|                                  | 3.12 (MinGW)               | x86-64              |
 | 
				
			||||||
|                                  +----------------------------+---------------------+
 | 
					|                                  +----------------------------+---------------------+
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,9 @@ OpenType fonts (as well as other font formats supported by the FreeType
 | 
				
			||||||
library). For earlier versions, TrueType support is only available as part of
 | 
					library). For earlier versions, TrueType support is only available as part of
 | 
				
			||||||
the imToolkit package.
 | 
					the imToolkit package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When measuring text sizes, this module will not break at newline characters. For
 | 
				
			||||||
 | 
					multiline text, see the :py:mod:`~PIL.ImageDraw` module.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. warning::
 | 
					.. warning::
 | 
				
			||||||
    To protect against potential DOS attacks when using arbitrary strings as
 | 
					    To protect against potential DOS attacks when using arbitrary strings as
 | 
				
			||||||
    text input, Pillow will raise a :py:exc:`ValueError` if the number of characters
 | 
					    text input, Pillow will raise a :py:exc:`ValueError` if the number of characters
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										89
									
								
								docs/releasenotes/11.3.0.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								docs/releasenotes/11.3.0.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					11.3.0
 | 
				
			||||||
 | 
					------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Security
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:cve:`YYYY-XXXXX`: TODO
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Backwards incompatible changes
 | 
				
			||||||
 | 
					==============================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Deprecations
 | 
				
			||||||
 | 
					============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Image.fromarray mode parameter
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The ``mode`` parameter in :py:meth:`~PIL.Image.fromarray()` has been deprecated. The
 | 
				
			||||||
 | 
					mode can be automatically determined from the object's shape and type instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Saving I mode images as PNG
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to fit the 32 bits of I mode images into PNG, when PNG images can only contain
 | 
				
			||||||
 | 
					at most 16 bits for a channel, Pillow has been clipping the values. Rather than quietly
 | 
				
			||||||
 | 
					changing the data, this is now deprecated. Instead, the image can be converted to
 | 
				
			||||||
 | 
					another mode before saving::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from PIL import Image
 | 
				
			||||||
 | 
					    im = Image.new("I", (1, 1))
 | 
				
			||||||
 | 
					    im.convert("I;16").save("out.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					API changes
 | 
				
			||||||
 | 
					===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					API additions
 | 
				
			||||||
 | 
					=============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Other changes
 | 
				
			||||||
 | 
					=============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Added QOI saving
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Support has been added for saving QOI images. ``colorspace`` can be used to specify the
 | 
				
			||||||
 | 
					colorspace as sRGB with linear alpha, e.g. ``im.save("out.qoi", colorspace="sRGB")``.
 | 
				
			||||||
 | 
					By default, all channels will be linear.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Support using more screenshot utilities with ImageGrab on Linux
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:py:meth:`~PIL.ImageGrab.grab` is now able to use GNOME Screenshot, grim or Spectacle
 | 
				
			||||||
 | 
					on Linux in order to take a snapshot of the screen.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Do not build against libavif < 1
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pillow only supports libavif 1.0.0 or later. In order to prevent errors when building
 | 
				
			||||||
 | 
					from source, if a user happens to have an earlier libavif on their system, Pillow will
 | 
				
			||||||
 | 
					now ignore it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Python 3.14 beta
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To help other projects prepare for Python 3.14, wheels are now built for the
 | 
				
			||||||
 | 
					3.14 beta as a preview. This is not official support for Python 3.14, but rather
 | 
				
			||||||
 | 
					an opportunity for you to test how Pillow works with the beta and report any
 | 
				
			||||||
 | 
					problems.
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ expected to be backported to earlier versions.
 | 
				
			||||||
.. toctree::
 | 
					.. toctree::
 | 
				
			||||||
  :maxdepth: 2
 | 
					  :maxdepth: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  11.3.0
 | 
				
			||||||
  11.2.1
 | 
					  11.2.1
 | 
				
			||||||
  11.1.0
 | 
					  11.1.0
 | 
				
			||||||
  11.0.0
 | 
					  11.0.0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								setup.py
									
									
									
									
									
								
							| 
						 | 
					@ -16,7 +16,6 @@ import subprocess
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import warnings
 | 
					import warnings
 | 
				
			||||||
from collections.abc import Iterator
 | 
					from collections.abc import Iterator
 | 
				
			||||||
from typing import Any
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from pybind11.setup_helpers import ParallelCompile
 | 
					from pybind11.setup_helpers import ParallelCompile
 | 
				
			||||||
from setuptools import Extension, setup
 | 
					from setuptools import Extension, setup
 | 
				
			||||||
| 
						 | 
					@ -151,7 +150,7 @@ class RequiredDependencyException(Exception):
 | 
				
			||||||
PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version
 | 
					PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _dbg(s: str, tp: Any = None) -> None:
 | 
					def _dbg(s: str, tp: str | tuple[str, ...] | None = None) -> None:
 | 
				
			||||||
    if DEBUG:
 | 
					    if DEBUG:
 | 
				
			||||||
        if tp:
 | 
					        if tp:
 | 
				
			||||||
            print(s % tp)
 | 
					            print(s % tp)
 | 
				
			||||||
| 
						 | 
					@ -166,7 +165,7 @@ def _find_library_dirs_ldconfig() -> list[str]:
 | 
				
			||||||
    args: list[str]
 | 
					    args: list[str]
 | 
				
			||||||
    env: dict[str, str]
 | 
					    env: dict[str, str]
 | 
				
			||||||
    expr: str
 | 
					    expr: str
 | 
				
			||||||
    if sys.platform.startswith("linux") or sys.platform.startswith("gnu"):
 | 
					    if sys.platform.startswith(("linux", "gnu")):
 | 
				
			||||||
        if struct.calcsize("l") == 4:
 | 
					        if struct.calcsize("l") == 4:
 | 
				
			||||||
            machine = os.uname()[4] + "-32"
 | 
					            machine = os.uname()[4] + "-32"
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -512,11 +511,11 @@ class pil_build_ext(build_ext):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if root is None and pkg_config:
 | 
					            if root is None and pkg_config:
 | 
				
			||||||
                if isinstance(lib_name, str):
 | 
					                if isinstance(lib_name, str):
 | 
				
			||||||
                    _dbg(f"Looking for `{lib_name}` using pkg-config.")
 | 
					                    _dbg("Looking for `%s` using pkg-config.", lib_name)
 | 
				
			||||||
                    root = pkg_config(lib_name)
 | 
					                    root = pkg_config(lib_name)
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    for lib_name2 in lib_name:
 | 
					                    for lib_name2 in lib_name:
 | 
				
			||||||
                        _dbg(f"Looking for `{lib_name2}` using pkg-config.")
 | 
					                        _dbg("Looking for `%s` using pkg-config.", lib_name2)
 | 
				
			||||||
                        root = pkg_config(lib_name2)
 | 
					                        root = pkg_config(lib_name2)
 | 
				
			||||||
                        if root:
 | 
					                        if root:
 | 
				
			||||||
                            break
 | 
					                            break
 | 
				
			||||||
| 
						 | 
					@ -626,11 +625,7 @@ class pil_build_ext(build_ext):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for extension in self.extensions:
 | 
					                for extension in self.extensions:
 | 
				
			||||||
                    extension.extra_compile_args = ["-Wno-nullability-completeness"]
 | 
					                    extension.extra_compile_args = ["-Wno-nullability-completeness"]
 | 
				
			||||||
        elif (
 | 
					        elif sys.platform.startswith(("linux", "gnu", "freebsd")):
 | 
				
			||||||
            sys.platform.startswith("linux")
 | 
					 | 
				
			||||||
            or sys.platform.startswith("gnu")
 | 
					 | 
				
			||||||
            or sys.platform.startswith("freebsd")
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            for dirname in _find_library_dirs_ldconfig():
 | 
					            for dirname in _find_library_dirs_ldconfig():
 | 
				
			||||||
                _add_directory(library_dirs, dirname)
 | 
					                _add_directory(library_dirs, dirname)
 | 
				
			||||||
            if sys.platform.startswith("linux") and os.environ.get("ANDROID_ROOT"):
 | 
					            if sys.platform.startswith("linux") and os.environ.get("ANDROID_ROOT"):
 | 
				
			||||||
| 
						 | 
					@ -739,7 +734,7 @@ class pil_build_ext(build_ext):
 | 
				
			||||||
                            best_path = os.path.join(directory, name)
 | 
					                            best_path = os.path.join(directory, name)
 | 
				
			||||||
                            _dbg(
 | 
					                            _dbg(
 | 
				
			||||||
                                "Best openjpeg version %s so far in %s",
 | 
					                                "Best openjpeg version %s so far in %s",
 | 
				
			||||||
                                (best_version, best_path),
 | 
					                                (str(best_version), best_path),
 | 
				
			||||||
                            )
 | 
					                            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if best_version and _find_library_file(self, "openjp2"):
 | 
					            if best_version and _find_library_file(self, "openjp2"):
 | 
				
			||||||
| 
						 | 
					@ -761,12 +756,12 @@ class pil_build_ext(build_ext):
 | 
				
			||||||
        if feature.want("tiff"):
 | 
					        if feature.want("tiff"):
 | 
				
			||||||
            _dbg("Looking for tiff")
 | 
					            _dbg("Looking for tiff")
 | 
				
			||||||
            if _find_include_file(self, "tiff.h"):
 | 
					            if _find_include_file(self, "tiff.h"):
 | 
				
			||||||
                if _find_library_file(self, "tiff"):
 | 
					 | 
				
			||||||
                    feature.set("tiff", "tiff")
 | 
					 | 
				
			||||||
                if sys.platform in ["win32", "darwin"] and _find_library_file(
 | 
					                if sys.platform in ["win32", "darwin"] and _find_library_file(
 | 
				
			||||||
                    self, "libtiff"
 | 
					                    self, "libtiff"
 | 
				
			||||||
                ):
 | 
					                ):
 | 
				
			||||||
                    feature.set("tiff", "libtiff")
 | 
					                    feature.set("tiff", "libtiff")
 | 
				
			||||||
 | 
					                elif _find_library_file(self, "tiff"):
 | 
				
			||||||
 | 
					                    feature.set("tiff", "tiff")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if feature.want("freetype"):
 | 
					        if feature.want("freetype"):
 | 
				
			||||||
            _dbg("Looking for freetype")
 | 
					            _dbg("Looking for freetype")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ import os
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
from enum import IntEnum
 | 
					from enum import IntEnum
 | 
				
			||||||
from functools import cached_property
 | 
					from functools import cached_property
 | 
				
			||||||
from typing import IO, Any, Literal, NamedTuple, Union
 | 
					from typing import IO, Any, Literal, NamedTuple, Union, cast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import (
 | 
					from . import (
 | 
				
			||||||
    Image,
 | 
					    Image,
 | 
				
			||||||
| 
						 | 
					@ -350,12 +350,15 @@ class GifImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            if self._frame_palette:
 | 
					            if self._frame_palette:
 | 
				
			||||||
                if color * 3 + 3 > len(self._frame_palette.palette):
 | 
					                if color * 3 + 3 > len(self._frame_palette.palette):
 | 
				
			||||||
                    color = 0
 | 
					                    color = 0
 | 
				
			||||||
                return tuple(self._frame_palette.palette[color * 3 : color * 3 + 3])
 | 
					                return cast(
 | 
				
			||||||
 | 
					                    tuple[int, int, int],
 | 
				
			||||||
 | 
					                    tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]),
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                return (color, color, color)
 | 
					                return (color, color, color)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.dispose = None
 | 
					        self.dispose = None
 | 
				
			||||||
        self.dispose_extent = frame_dispose_extent
 | 
					        self.dispose_extent: tuple[int, int, int, int] | None = frame_dispose_extent
 | 
				
			||||||
        if self.dispose_extent and self.disposal_method >= 2:
 | 
					        if self.dispose_extent and self.disposal_method >= 2:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                if self.disposal_method == 2:
 | 
					                if self.disposal_method == 2:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -802,7 +802,9 @@ class Image:
 | 
				
			||||||
        e = _getencoder(self.mode, encoder_name, encoder_args)
 | 
					        e = _getencoder(self.mode, encoder_name, encoder_args)
 | 
				
			||||||
        e.setimage(self.im)
 | 
					        e.setimage(self.im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bufsize = max(65536, self.size[0] * 4)  # see RawEncode.c
 | 
					        from . import ImageFile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bufsize = max(ImageFile.MAXBLOCK, self.size[0] * 4)  # see RawEncode.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        output = []
 | 
					        output = []
 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
| 
						 | 
					@ -1509,7 +1511,7 @@ class Image:
 | 
				
			||||||
            return {}
 | 
					            return {}
 | 
				
			||||||
        if "xmp" not in self.info:
 | 
					        if "xmp" not in self.info:
 | 
				
			||||||
            return {}
 | 
					            return {}
 | 
				
			||||||
        root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00"))
 | 
					        root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00 "))
 | 
				
			||||||
        return {get_name(root.tag): get_value(root)}
 | 
					        return {get_name(root.tag): get_value(root)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getexif(self) -> Exif:
 | 
					    def getexif(self) -> Exif:
 | 
				
			||||||
| 
						 | 
					@ -1540,10 +1542,11 @@ class Image:
 | 
				
			||||||
        # XMP tags
 | 
					        # XMP tags
 | 
				
			||||||
        if ExifTags.Base.Orientation not in self._exif:
 | 
					        if ExifTags.Base.Orientation not in self._exif:
 | 
				
			||||||
            xmp_tags = self.info.get("XML:com.adobe.xmp")
 | 
					            xmp_tags = self.info.get("XML:com.adobe.xmp")
 | 
				
			||||||
 | 
					            pattern: str | bytes = r'tiff:Orientation(="|>)([0-9])'
 | 
				
			||||||
            if not xmp_tags and (xmp_tags := self.info.get("xmp")):
 | 
					            if not xmp_tags and (xmp_tags := self.info.get("xmp")):
 | 
				
			||||||
                xmp_tags = xmp_tags.decode("utf-8")
 | 
					                pattern = rb'tiff:Orientation(="|>)([0-9])'
 | 
				
			||||||
            if xmp_tags:
 | 
					            if xmp_tags:
 | 
				
			||||||
                match = re.search(r'tiff:Orientation(="|>)([0-9])', xmp_tags)
 | 
					                match = re.search(pattern, xmp_tags)
 | 
				
			||||||
                if match:
 | 
					                if match:
 | 
				
			||||||
                    self._exif[ExifTags.Base.Orientation] = int(match[2])
 | 
					                    self._exif[ExifTags.Base.Orientation] = int(match[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3269,7 +3272,7 @@ def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param obj: Object with array interface
 | 
					    :param obj: Object with array interface
 | 
				
			||||||
    :param mode: Optional mode to use when reading ``obj``. Will be determined from
 | 
					    :param mode: Optional mode to use when reading ``obj``. Will be determined from
 | 
				
			||||||
      type if ``None``.
 | 
					      type if ``None``. Deprecated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      This will not be used to convert the data after reading, but will be used to
 | 
					      This will not be used to convert the data after reading, but will be used to
 | 
				
			||||||
      change how the data is read::
 | 
					      change how the data is read::
 | 
				
			||||||
| 
						 | 
					@ -3304,6 +3307,7 @@ def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image:
 | 
				
			||||||
            msg = f"Cannot handle this data type: {typekey_shape}, {typestr}"
 | 
					            msg = f"Cannot handle this data type: {typekey_shape}, {typestr}"
 | 
				
			||||||
            raise TypeError(msg) from e
 | 
					            raise TypeError(msg) from e
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
 | 
					        deprecate("'mode' parameter", 13)
 | 
				
			||||||
        rawmode = mode
 | 
					        rawmode = mode
 | 
				
			||||||
    if mode in ["1", "L", "I", "P", "F"]:
 | 
					    if mode in ["1", "L", "I", "P", "F"]:
 | 
				
			||||||
        ndmax = 2
 | 
					        ndmax = 2
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -248,6 +248,9 @@ class ImageCmsProfile:
 | 
				
			||||||
            low-level profile object
 | 
					            low-level profile object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        self.filename = None
 | 
				
			||||||
 | 
					        self.product_name = None  # profile.product_name
 | 
				
			||||||
 | 
					        self.product_info = None  # profile.product_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if isinstance(profile, str):
 | 
					        if isinstance(profile, str):
 | 
				
			||||||
            if sys.platform == "win32":
 | 
					            if sys.platform == "win32":
 | 
				
			||||||
| 
						 | 
					@ -256,23 +259,18 @@ class ImageCmsProfile:
 | 
				
			||||||
                    profile_bytes_path.decode("ascii")
 | 
					                    profile_bytes_path.decode("ascii")
 | 
				
			||||||
                except UnicodeDecodeError:
 | 
					                except UnicodeDecodeError:
 | 
				
			||||||
                    with open(profile, "rb") as f:
 | 
					                    with open(profile, "rb") as f:
 | 
				
			||||||
                        self._set(core.profile_frombytes(f.read()))
 | 
					                        self.profile = core.profile_frombytes(f.read())
 | 
				
			||||||
                    return
 | 
					                    return
 | 
				
			||||||
            self._set(core.profile_open(profile), profile)
 | 
					            self.filename = profile
 | 
				
			||||||
 | 
					            self.profile = core.profile_open(profile)
 | 
				
			||||||
        elif hasattr(profile, "read"):
 | 
					        elif hasattr(profile, "read"):
 | 
				
			||||||
            self._set(core.profile_frombytes(profile.read()))
 | 
					            self.profile = core.profile_frombytes(profile.read())
 | 
				
			||||||
        elif isinstance(profile, core.CmsProfile):
 | 
					        elif isinstance(profile, core.CmsProfile):
 | 
				
			||||||
            self._set(profile)
 | 
					            self.profile = profile
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            msg = "Invalid type for Profile"  # type: ignore[unreachable]
 | 
					            msg = "Invalid type for Profile"  # type: ignore[unreachable]
 | 
				
			||||||
            raise TypeError(msg)
 | 
					            raise TypeError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None:
 | 
					 | 
				
			||||||
        self.profile = profile
 | 
					 | 
				
			||||||
        self.filename = filename
 | 
					 | 
				
			||||||
        self.product_name = None  # profile.product_name
 | 
					 | 
				
			||||||
        self.product_info = None  # profile.product_info
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def tobytes(self) -> bytes:
 | 
					    def tobytes(self) -> bytes:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Returns the profile in a format suitable for embedding in
 | 
					        Returns the profile in a format suitable for embedding in
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -365,22 +365,10 @@ class ImageDraw:
 | 
				
			||||||
                # use the fill as a mask
 | 
					                # use the fill as a mask
 | 
				
			||||||
                mask = Image.new("1", self.im.size)
 | 
					                mask = Image.new("1", self.im.size)
 | 
				
			||||||
                mask_ink = self._getink(1)[0]
 | 
					                mask_ink = self._getink(1)[0]
 | 
				
			||||||
 | 
					                draw = Draw(mask)
 | 
				
			||||||
                fill_im = mask.copy()
 | 
					 | 
				
			||||||
                draw = Draw(fill_im)
 | 
					 | 
				
			||||||
                draw.draw.draw_polygon(xy, mask_ink, 1)
 | 
					                draw.draw.draw_polygon(xy, mask_ink, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ink_im = mask.copy()
 | 
					                self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im)
 | 
				
			||||||
                draw = Draw(ink_im)
 | 
					 | 
				
			||||||
                width = width * 2 - 1
 | 
					 | 
				
			||||||
                draw.draw.draw_polygon(xy, mask_ink, 0, width)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                mask.paste(ink_im, mask=fill_im)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                im = Image.new(self.mode, self.im.size)
 | 
					 | 
				
			||||||
                draw = Draw(im)
 | 
					 | 
				
			||||||
                draw.draw.draw_polygon(xy, ink, 0, width)
 | 
					 | 
				
			||||||
                self.im.paste(im.im, (0, 0) + im.size, mask.im)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def regular_polygon(
 | 
					    def regular_polygon(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,10 +134,10 @@ def grabclipboard() -> Image.Image | list[str] | None:
 | 
				
			||||||
            import struct
 | 
					            import struct
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            o = struct.unpack_from("I", data)[0]
 | 
					            o = struct.unpack_from("I", data)[0]
 | 
				
			||||||
            if data[16] != 0:
 | 
					            if data[16] == 0:
 | 
				
			||||||
                files = data[o:].decode("utf-16le").split("\0")
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                files = data[o:].decode("mbcs").split("\0")
 | 
					                files = data[o:].decode("mbcs").split("\0")
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                files = data[o:].decode("utf-16le").split("\0")
 | 
				
			||||||
            return files[: files.index("")]
 | 
					            return files[: files.index("")]
 | 
				
			||||||
        if isinstance(data, bytes):
 | 
					        if isinstance(data, bytes):
 | 
				
			||||||
            data = io.BytesIO(data)
 | 
					            data = io.BytesIO(data)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,7 +175,9 @@ class MacViewer(Viewer):
 | 
				
			||||||
        if not os.path.exists(path):
 | 
					        if not os.path.exists(path):
 | 
				
			||||||
            raise FileNotFoundError
 | 
					            raise FileNotFoundError
 | 
				
			||||||
        subprocess.call(["open", "-a", "Preview.app", path])
 | 
					        subprocess.call(["open", "-a", "Preview.app", path])
 | 
				
			||||||
        executable = sys.executable or shutil.which("python3")
 | 
					
 | 
				
			||||||
 | 
					        pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
 | 
				
			||||||
 | 
					        executable = (not pyinstaller and sys.executable) or shutil.which("python3")
 | 
				
			||||||
        if executable:
 | 
					        if executable:
 | 
				
			||||||
            subprocess.Popen(
 | 
					            subprocess.Popen(
 | 
				
			||||||
                [
 | 
					                [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -762,8 +762,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
    extra = info.get("extra", b"")
 | 
					    extra = info.get("extra", b"")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MAX_BYTES_IN_MARKER = 65533
 | 
					    MAX_BYTES_IN_MARKER = 65533
 | 
				
			||||||
    xmp = info.get("xmp")
 | 
					    if xmp := info.get("xmp"):
 | 
				
			||||||
    if xmp:
 | 
					 | 
				
			||||||
        overhead_len = 29  # b"http://ns.adobe.com/xap/1.0/\x00"
 | 
					        overhead_len = 29  # b"http://ns.adobe.com/xap/1.0/\x00"
 | 
				
			||||||
        max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
 | 
					        max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
 | 
				
			||||||
        if len(xmp) > max_data_bytes_in_marker:
 | 
					        if len(xmp) > max_data_bytes_in_marker:
 | 
				
			||||||
| 
						 | 
					@ -772,8 +771,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
        size = o16(2 + overhead_len + len(xmp))
 | 
					        size = o16(2 + overhead_len + len(xmp))
 | 
				
			||||||
        extra += b"\xff\xe1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp
 | 
					        extra += b"\xff\xe1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    icc_profile = info.get("icc_profile")
 | 
					    if icc_profile := info.get("icc_profile"):
 | 
				
			||||||
    if icc_profile:
 | 
					 | 
				
			||||||
        overhead_len = 14  # b"ICC_PROFILE\0" + o8(i) + o8(len(markers))
 | 
					        overhead_len = 14  # b"ICC_PROFILE\0" + o8(i) + o8(len(markers))
 | 
				
			||||||
        max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
 | 
					        max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len
 | 
				
			||||||
        markers = []
 | 
					        markers = []
 | 
				
			||||||
| 
						 | 
					@ -831,7 +829,6 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
    # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is
 | 
					    # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is
 | 
				
			||||||
    # channels*size, this is a value that's been used in a django patch.
 | 
					    # channels*size, this is a value that's been used in a django patch.
 | 
				
			||||||
    # https://github.com/matthewwithanm/django-imagekit/issues/50
 | 
					    # https://github.com/matthewwithanm/django-imagekit/issues/50
 | 
				
			||||||
    bufsize = 0
 | 
					 | 
				
			||||||
    if optimize or progressive:
 | 
					    if optimize or progressive:
 | 
				
			||||||
        # CMYK can be bigger
 | 
					        # CMYK can be bigger
 | 
				
			||||||
        if im.mode == "CMYK":
 | 
					        if im.mode == "CMYK":
 | 
				
			||||||
| 
						 | 
					@ -848,7 +845,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        # The EXIF info needs to be written as one block, + APP1, + one spare byte.
 | 
					        # The EXIF info needs to be written as one block, + APP1, + one spare byte.
 | 
				
			||||||
        # Ensure that our buffer is big enough. Same with the icc_profile block.
 | 
					        # Ensure that our buffer is big enough. Same with the icc_profile block.
 | 
				
			||||||
        bufsize = max(bufsize, len(exif) + 5, len(extra) + 1)
 | 
					        bufsize = max(len(exif) + 5, len(extra) + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ImageFile._save(
 | 
					    ImageFile._save(
 | 
				
			||||||
        im, fp, [ImageFile._Tile("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize
 | 
					        im, fp, [ImageFile._Tile("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,11 +33,7 @@ class BitStream:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def peek(self, bits: int) -> int:
 | 
					    def peek(self, bits: int) -> int:
 | 
				
			||||||
        while self.bits < bits:
 | 
					        while self.bits < bits:
 | 
				
			||||||
            c = self.next()
 | 
					            self.bitbuffer = (self.bitbuffer << 8) + self.next()
 | 
				
			||||||
            if c < 0:
 | 
					 | 
				
			||||||
                self.bits = 0
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            self.bitbuffer = (self.bitbuffer << 8) + c
 | 
					 | 
				
			||||||
            self.bits += 8
 | 
					            self.bits += 8
 | 
				
			||||||
        return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1
 | 
					        return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        # header
 | 
					        # header
 | 
				
			||||||
        assert self.fp is not None
 | 
					        assert self.fp is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        s = self.fp.read(128)
 | 
					        s = self.fp.read(68)
 | 
				
			||||||
        if not _accept(s):
 | 
					        if not _accept(s):
 | 
				
			||||||
            msg = "not a PCX file"
 | 
					            msg = "not a PCX file"
 | 
				
			||||||
            raise SyntaxError(msg)
 | 
					            raise SyntaxError(msg)
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,8 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            raise SyntaxError(msg)
 | 
					            raise SyntaxError(msg)
 | 
				
			||||||
        logger.debug("BBox: %s %s %s %s", *bbox)
 | 
					        logger.debug("BBox: %s %s %s %s", *bbox)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offset = self.fp.tell() + 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # format
 | 
					        # format
 | 
				
			||||||
        version = s[1]
 | 
					        version = s[1]
 | 
				
			||||||
        bits = s[3]
 | 
					        bits = s[3]
 | 
				
			||||||
| 
						 | 
					@ -102,7 +104,6 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
                if mode == "P":
 | 
					                if mode == "P":
 | 
				
			||||||
                    self.palette = ImagePalette.raw("RGB", s[1:])
 | 
					                    self.palette = ImagePalette.raw("RGB", s[1:])
 | 
				
			||||||
            self.fp.seek(128)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif version == 5 and bits == 8 and planes == 3:
 | 
					        elif version == 5 and bits == 8 and planes == 3:
 | 
				
			||||||
            mode = "RGB"
 | 
					            mode = "RGB"
 | 
				
			||||||
| 
						 | 
					@ -128,9 +129,7 @@ class PcxImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        bbox = (0, 0) + self.size
 | 
					        bbox = (0, 0) + self.size
 | 
				
			||||||
        logger.debug("size: %sx%s", *self.size)
 | 
					        logger.debug("size: %sx%s", *self.size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.tile = [
 | 
					        self.tile = [ImageFile._Tile("pcx", bbox, offset, (rawmode, planes * stride))]
 | 
				
			||||||
            ImageFile._Tile("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# --------------------------------------------------------------------
 | 
					# --------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,6 +48,7 @@ from ._binary import i32be as i32
 | 
				
			||||||
from ._binary import o8
 | 
					from ._binary import o8
 | 
				
			||||||
from ._binary import o16be as o16
 | 
					from ._binary import o16be as o16
 | 
				
			||||||
from ._binary import o32be as o32
 | 
					from ._binary import o32be as o32
 | 
				
			||||||
 | 
					from ._deprecate import deprecate
 | 
				
			||||||
from ._util import DeferredError
 | 
					from ._util import DeferredError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TYPE_CHECKING = False
 | 
					TYPE_CHECKING = False
 | 
				
			||||||
| 
						 | 
					@ -1368,6 +1369,8 @@ def _save(
 | 
				
			||||||
    except KeyError as e:
 | 
					    except KeyError as e:
 | 
				
			||||||
        msg = f"cannot write mode {mode} as PNG"
 | 
					        msg = f"cannot write mode {mode} as PNG"
 | 
				
			||||||
        raise OSError(msg) from e
 | 
					        raise OSError(msg) from e
 | 
				
			||||||
 | 
					    if outmode == "I":
 | 
				
			||||||
 | 
					        deprecate("Saving I mode images as PNG", 13, stacklevel=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    # write minimal PNG file
 | 
					    # write minimal PNG file
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,8 +94,8 @@ class PpmImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            msg = "Reached EOF while reading header"
 | 
					            msg = "Reached EOF while reading header"
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
        elif len(token) > 10:
 | 
					        elif len(token) > 10:
 | 
				
			||||||
            msg = f"Token too long in file header: {token.decode()}"
 | 
					            msg_too_long = b"Token too long in file header: %s" % token
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg_too_long)
 | 
				
			||||||
        return token
 | 
					        return token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _open(self) -> None:
 | 
					    def _open(self) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,12 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					from typing import IO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import Image, ImageFile
 | 
					from . import Image, ImageFile
 | 
				
			||||||
from ._binary import i32be as i32
 | 
					from ._binary import i32be as i32
 | 
				
			||||||
 | 
					from ._binary import o8
 | 
				
			||||||
 | 
					from ._binary import o32be as o32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _accept(prefix: bytes) -> bool:
 | 
					def _accept(prefix: bytes) -> bool:
 | 
				
			||||||
| 
						 | 
					@ -51,7 +54,7 @@ class QoiDecoder(ImageFile.PyDecoder):
 | 
				
			||||||
        assert self.fd is not None
 | 
					        assert self.fd is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._previously_seen_pixels = {}
 | 
					        self._previously_seen_pixels = {}
 | 
				
			||||||
        self._add_to_previous_pixels(bytearray((0, 0, 0, 255)))
 | 
					        self._previous_pixel = bytearray((0, 0, 0, 255))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        data = bytearray()
 | 
					        data = bytearray()
 | 
				
			||||||
        bands = Image.getmodebands(self.mode)
 | 
					        bands = Image.getmodebands(self.mode)
 | 
				
			||||||
| 
						 | 
					@ -110,6 +113,122 @@ class QoiDecoder(ImageFile.PyDecoder):
 | 
				
			||||||
        return -1, 0
 | 
					        return -1, 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
 | 
					    if im.mode == "RGB":
 | 
				
			||||||
 | 
					        channels = 3
 | 
				
			||||||
 | 
					    elif im.mode == "RGBA":
 | 
				
			||||||
 | 
					        channels = 4
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        msg = "Unsupported QOI image mode"
 | 
				
			||||||
 | 
					        raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    colorspace = 0 if im.encoderinfo.get("colorspace") == "sRGB" else 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fp.write(b"qoif")
 | 
				
			||||||
 | 
					    fp.write(o32(im.size[0]))
 | 
				
			||||||
 | 
					    fp.write(o32(im.size[1]))
 | 
				
			||||||
 | 
					    fp.write(o8(channels))
 | 
				
			||||||
 | 
					    fp.write(o8(colorspace))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QoiEncoder(ImageFile.PyEncoder):
 | 
				
			||||||
 | 
					    _pushes_fd = True
 | 
				
			||||||
 | 
					    _previous_pixel: tuple[int, int, int, int] | None = None
 | 
				
			||||||
 | 
					    _previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {}
 | 
				
			||||||
 | 
					    _run = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _write_run(self) -> bytes:
 | 
				
			||||||
 | 
					        data = o8(0b11000000 | (self._run - 1))  # QOI_OP_RUN
 | 
				
			||||||
 | 
					        self._run = 0
 | 
				
			||||||
 | 
					        return data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _delta(self, left: int, right: int) -> int:
 | 
				
			||||||
 | 
					        result = (left - right) & 255
 | 
				
			||||||
 | 
					        if result >= 128:
 | 
				
			||||||
 | 
					            result -= 256
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encode(self, bufsize: int) -> tuple[int, int, bytes]:
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._previously_seen_pixels = {0: (0, 0, 0, 0)}
 | 
				
			||||||
 | 
					        self._previous_pixel = (0, 0, 0, 255)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = bytearray()
 | 
				
			||||||
 | 
					        w, h = self.im.size
 | 
				
			||||||
 | 
					        bands = Image.getmodebands(self.mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for y in range(h):
 | 
				
			||||||
 | 
					            for x in range(w):
 | 
				
			||||||
 | 
					                pixel = self.im.getpixel((x, y))
 | 
				
			||||||
 | 
					                if bands == 3:
 | 
				
			||||||
 | 
					                    pixel = (*pixel, 255)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if pixel == self._previous_pixel:
 | 
				
			||||||
 | 
					                    self._run += 1
 | 
				
			||||||
 | 
					                    if self._run == 62:
 | 
				
			||||||
 | 
					                        data += self._write_run()
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    if self._run:
 | 
				
			||||||
 | 
					                        data += self._write_run()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    r, g, b, a = pixel
 | 
				
			||||||
 | 
					                    hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
 | 
				
			||||||
 | 
					                    if self._previously_seen_pixels.get(hash_value) == pixel:
 | 
				
			||||||
 | 
					                        data += o8(hash_value)  # QOI_OP_INDEX
 | 
				
			||||||
 | 
					                    elif self._previous_pixel:
 | 
				
			||||||
 | 
					                        self._previously_seen_pixels[hash_value] = pixel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        prev_r, prev_g, prev_b, prev_a = self._previous_pixel
 | 
				
			||||||
 | 
					                        if prev_a == a:
 | 
				
			||||||
 | 
					                            delta_r = self._delta(r, prev_r)
 | 
				
			||||||
 | 
					                            delta_g = self._delta(g, prev_g)
 | 
				
			||||||
 | 
					                            delta_b = self._delta(b, prev_b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (
 | 
				
			||||||
 | 
					                                -2 <= delta_r < 2
 | 
				
			||||||
 | 
					                                and -2 <= delta_g < 2
 | 
				
			||||||
 | 
					                                and -2 <= delta_b < 2
 | 
				
			||||||
 | 
					                            ):
 | 
				
			||||||
 | 
					                                data += o8(
 | 
				
			||||||
 | 
					                                    0b01000000
 | 
				
			||||||
 | 
					                                    | (delta_r + 2) << 4
 | 
				
			||||||
 | 
					                                    | (delta_g + 2) << 2
 | 
				
			||||||
 | 
					                                    | (delta_b + 2)
 | 
				
			||||||
 | 
					                                )  # QOI_OP_DIFF
 | 
				
			||||||
 | 
					                            else:
 | 
				
			||||||
 | 
					                                delta_gr = self._delta(delta_r, delta_g)
 | 
				
			||||||
 | 
					                                delta_gb = self._delta(delta_b, delta_g)
 | 
				
			||||||
 | 
					                                if (
 | 
				
			||||||
 | 
					                                    -8 <= delta_gr < 8
 | 
				
			||||||
 | 
					                                    and -32 <= delta_g < 32
 | 
				
			||||||
 | 
					                                    and -8 <= delta_gb < 8
 | 
				
			||||||
 | 
					                                ):
 | 
				
			||||||
 | 
					                                    data += o8(
 | 
				
			||||||
 | 
					                                        0b10000000 | (delta_g + 32)
 | 
				
			||||||
 | 
					                                    )  # QOI_OP_LUMA
 | 
				
			||||||
 | 
					                                    data += o8((delta_gr + 8) << 4 | (delta_gb + 8))
 | 
				
			||||||
 | 
					                                else:
 | 
				
			||||||
 | 
					                                    data += o8(0b11111110)  # QOI_OP_RGB
 | 
				
			||||||
 | 
					                                    data += bytes(pixel[:3])
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            data += o8(0b11111111)  # QOI_OP_RGBA
 | 
				
			||||||
 | 
					                            data += bytes(pixel)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self._previous_pixel = pixel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self._run:
 | 
				
			||||||
 | 
					            data += self._write_run()
 | 
				
			||||||
 | 
					        data += bytes((0, 0, 0, 0, 0, 0, 0, 1))  # padding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return len(data), 0, data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
 | 
					Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
 | 
				
			||||||
Image.register_decoder("qoi", QoiDecoder)
 | 
					Image.register_decoder("qoi", QoiDecoder)
 | 
				
			||||||
Image.register_extension(QoiImageFile.format, ".qoi")
 | 
					Image.register_extension(QoiImageFile.format, ".qoi")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Image.register_save(QoiImageFile.format, _save)
 | 
				
			||||||
 | 
					Image.register_encoder("qoi", QoiEncoder)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1217,9 +1217,10 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        self._seek(frame)
 | 
					        self._seek(frame)
 | 
				
			||||||
        if self._im is not None and (
 | 
					        if self._im is not None and (
 | 
				
			||||||
            self.im.size != self._tile_size or self.im.mode != self.mode
 | 
					            self.im.size != self._tile_size
 | 
				
			||||||
 | 
					            or self.im.mode != self.mode
 | 
				
			||||||
 | 
					            or self.readonly
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            # The core image will no longer be used
 | 
					 | 
				
			||||||
            self._im = None
 | 
					            self._im = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _seek(self, frame: int) -> None:
 | 
					    def _seek(self, frame: int) -> None:
 | 
				
			||||||
| 
						 | 
					@ -1259,7 +1260,10 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        self.fp.seek(self._frame_pos[frame])
 | 
					        self.fp.seek(self._frame_pos[frame])
 | 
				
			||||||
        self.tag_v2.load(self.fp)
 | 
					        self.tag_v2.load(self.fp)
 | 
				
			||||||
        if XMP in self.tag_v2:
 | 
					        if XMP in self.tag_v2:
 | 
				
			||||||
            self.info["xmp"] = self.tag_v2[XMP]
 | 
					            xmp = self.tag_v2[XMP]
 | 
				
			||||||
 | 
					            if isinstance(xmp, tuple) and len(xmp) == 1:
 | 
				
			||||||
 | 
					                xmp = xmp[0]
 | 
				
			||||||
 | 
					            self.info["xmp"] = xmp
 | 
				
			||||||
        elif "xmp" in self.info:
 | 
					        elif "xmp" in self.info:
 | 
				
			||||||
            del self.info["xmp"]
 | 
					            del self.info["xmp"]
 | 
				
			||||||
        self._reload_exif()
 | 
					        self._reload_exif()
 | 
				
			||||||
| 
						 | 
					@ -1676,7 +1680,7 @@ SAVE_INFO = {
 | 
				
			||||||
    "PA": ("PA", II, 3, 1, (8, 8), 2),
 | 
					    "PA": ("PA", II, 3, 1, (8, 8), 2),
 | 
				
			||||||
    "I": ("I;32S", II, 1, 2, (32,), None),
 | 
					    "I": ("I;32S", II, 1, 2, (32,), None),
 | 
				
			||||||
    "I;16": ("I;16", II, 1, 1, (16,), None),
 | 
					    "I;16": ("I;16", II, 1, 1, (16,), None),
 | 
				
			||||||
    "I;16S": ("I;16S", II, 1, 2, (16,), None),
 | 
					    "I;16L": ("I;16L", II, 1, 1, (16,), None),
 | 
				
			||||||
    "F": ("F;32F", II, 1, 3, (32,), None),
 | 
					    "F": ("F;32F", II, 1, 3, (32,), None),
 | 
				
			||||||
    "RGB": ("RGB", II, 2, 1, (8, 8, 8), None),
 | 
					    "RGB": ("RGB", II, 2, 1, (8, 8, 8), None),
 | 
				
			||||||
    "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0),
 | 
					    "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0),
 | 
				
			||||||
| 
						 | 
					@ -1684,10 +1688,7 @@ SAVE_INFO = {
 | 
				
			||||||
    "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None),
 | 
					    "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None),
 | 
				
			||||||
    "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None),
 | 
					    "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None),
 | 
				
			||||||
    "LAB": ("LAB", II, 8, 1, (8, 8, 8), None),
 | 
					    "LAB": ("LAB", II, 8, 1, (8, 8, 8), None),
 | 
				
			||||||
    "I;32BS": ("I;32BS", MM, 1, 2, (32,), None),
 | 
					 | 
				
			||||||
    "I;16B": ("I;16B", MM, 1, 1, (16,), None),
 | 
					    "I;16B": ("I;16B", MM, 1, 1, (16,), None),
 | 
				
			||||||
    "I;16BS": ("I;16BS", MM, 1, 2, (16,), None),
 | 
					 | 
				
			||||||
    "F;32BF": ("F;32BF", MM, 1, 3, (32,), None),
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1963,7 +1964,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
        # we're storing image byte order. So, if the rawmode
 | 
					        # we're storing image byte order. So, if the rawmode
 | 
				
			||||||
        # contains I;16, we need to convert from native to image
 | 
					        # contains I;16, we need to convert from native to image
 | 
				
			||||||
        # byte order.
 | 
					        # byte order.
 | 
				
			||||||
        if im.mode in ("I;16B", "I;16"):
 | 
					        if im.mode in ("I;16", "I;16B", "I;16L"):
 | 
				
			||||||
            rawmode = "I;16N"
 | 
					            rawmode = "I;16N"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Pass tags as sorted list so that the tags are set in a fixed order.
 | 
					        # Pass tags as sorted list so that the tags are set in a fixed order.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ def deprecate(
 | 
				
			||||||
    *,
 | 
					    *,
 | 
				
			||||||
    action: str | None = None,
 | 
					    action: str | None = None,
 | 
				
			||||||
    plural: bool = False,
 | 
					    plural: bool = False,
 | 
				
			||||||
 | 
					    stacklevel: int = 3,
 | 
				
			||||||
) -> None:
 | 
					) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Deprecations helper.
 | 
					    Deprecations helper.
 | 
				
			||||||
| 
						 | 
					@ -67,5 +68,5 @@ def deprecate(
 | 
				
			||||||
    warnings.warn(
 | 
					    warnings.warn(
 | 
				
			||||||
        f"{deprecated} {is_} deprecated and will be removed in {removed}{action}",
 | 
					        f"{deprecated} {is_} deprecated and will be removed in {removed}{action}",
 | 
				
			||||||
        DeprecationWarning,
 | 
					        DeprecationWarning,
 | 
				
			||||||
        stacklevel=3,
 | 
					        stacklevel=stacklevel,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -308,8 +308,8 @@ _new_arrow(PyObject *self, PyObject *args) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ImagingBorrowArrow is responsible for retaining the array_capsule
 | 
					    // ImagingBorrowArrow is responsible for retaining the array_capsule
 | 
				
			||||||
    ret =
 | 
					    ret = PyImagingNew(
 | 
				
			||||||
        PyImagingNew(ImagingNewArrow(mode, xsize, ysize, schema_capsule, array_capsule)
 | 
					        ImagingNewArrow(mode, xsize, ysize, schema_capsule, array_capsule)
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    if (!ret) {
 | 
					    if (!ret) {
 | 
				
			||||||
        return ImagingError_ValueError("Invalid Arrow array mode or size mismatch");
 | 
					        return ImagingError_ValueError("Invalid Arrow array mode or size mismatch");
 | 
				
			||||||
| 
						 | 
					@ -338,12 +338,6 @@ static const char *no_palette = "image has no palette";
 | 
				
			||||||
static const char *readonly = "image is readonly";
 | 
					static const char *readonly = "image is readonly";
 | 
				
			||||||
/* static const char* no_content = "image has no content"; */
 | 
					/* static const char* no_content = "image has no content"; */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *
 | 
					 | 
				
			||||||
ImagingError_OSError(void) {
 | 
					 | 
				
			||||||
    PyErr_SetString(PyExc_OSError, "error when accessing file");
 | 
					 | 
				
			||||||
    return NULL;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void *
 | 
					void *
 | 
				
			||||||
ImagingError_MemoryError(void) {
 | 
					ImagingError_MemoryError(void) {
 | 
				
			||||||
    return PyErr_NoMemory();
 | 
					    return PyErr_NoMemory();
 | 
				
			||||||
| 
						 | 
					@ -369,11 +363,6 @@ ImagingError_ValueError(const char *message) {
 | 
				
			||||||
    return NULL;
 | 
					    return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					 | 
				
			||||||
ImagingError_Clear(void) {
 | 
					 | 
				
			||||||
    PyErr_Clear();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* -------------------------------------------------------------------- */
 | 
					/* -------------------------------------------------------------------- */
 | 
				
			||||||
/* HELPERS                                */
 | 
					/* HELPERS                                */
 | 
				
			||||||
/* -------------------------------------------------------------------- */
 | 
					/* -------------------------------------------------------------------- */
 | 
				
			||||||
| 
						 | 
					@ -1665,7 +1654,8 @@ _putdata(ImagingObject *self, PyObject *args) {
 | 
				
			||||||
                int bigendian = 0;
 | 
					                int bigendian = 0;
 | 
				
			||||||
                if (image->type == IMAGING_TYPE_SPECIAL) {
 | 
					                if (image->type == IMAGING_TYPE_SPECIAL) {
 | 
				
			||||||
                    // I;16*
 | 
					                    // I;16*
 | 
				
			||||||
                    if (strcmp(image->mode, "I;16B") == 0
 | 
					                    if (
 | 
				
			||||||
 | 
					                        strcmp(image->mode, "I;16B") == 0
 | 
				
			||||||
#ifdef WORDS_BIGENDIAN
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
                        || strcmp(image->mode, "I;16N") == 0
 | 
					                        || strcmp(image->mode, "I;16N") == 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -3219,7 +3209,8 @@ _draw_lines(ImagingDrawObject *self, PyObject *args) {
 | 
				
			||||||
                    (int)p[3],
 | 
					                    (int)p[3],
 | 
				
			||||||
                    &ink,
 | 
					                    &ink,
 | 
				
			||||||
                    width,
 | 
					                    width,
 | 
				
			||||||
                    self->blend
 | 
					                    self->blend,
 | 
				
			||||||
 | 
					                    NULL
 | 
				
			||||||
                ) < 0) {
 | 
					                ) < 0) {
 | 
				
			||||||
                free(xy);
 | 
					                free(xy);
 | 
				
			||||||
                return NULL;
 | 
					                return NULL;
 | 
				
			||||||
| 
						 | 
					@ -3357,7 +3348,10 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) {
 | 
				
			||||||
    int ink;
 | 
					    int ink;
 | 
				
			||||||
    int fill = 0;
 | 
					    int fill = 0;
 | 
				
			||||||
    int width = 0;
 | 
					    int width = 0;
 | 
				
			||||||
    if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) {
 | 
					    ImagingObject *maskp = NULL;
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTuple(
 | 
				
			||||||
 | 
					            args, "Oi|iiO!", &data, &ink, &fill, &width, &Imaging_Type, &maskp
 | 
				
			||||||
 | 
					        )) {
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3387,8 +3381,16 @@ _draw_polygon(ImagingDrawObject *self, PyObject *args) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    free(xy);
 | 
					    free(xy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, width, self->blend) <
 | 
					    if (ImagingDrawPolygon(
 | 
				
			||||||
        0) {
 | 
					            self->image->image,
 | 
				
			||||||
 | 
					            n,
 | 
				
			||||||
 | 
					            ixy,
 | 
				
			||||||
 | 
					            &ink,
 | 
				
			||||||
 | 
					            fill,
 | 
				
			||||||
 | 
					            width,
 | 
				
			||||||
 | 
					            self->blend,
 | 
				
			||||||
 | 
					            maskp ? maskp->image : NULL
 | 
				
			||||||
 | 
					        ) < 0) {
 | 
				
			||||||
        free(ixy);
 | 
					        free(ixy);
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -327,11 +327,11 @@ PyImaging_GrabScreenWin32(PyObject *self, PyObject *args) {
 | 
				
			||||||
    // added in Windows 10 (1607)
 | 
					    // added in Windows 10 (1607)
 | 
				
			||||||
    // loaded dynamically to avoid link errors
 | 
					    // loaded dynamically to avoid link errors
 | 
				
			||||||
    user32 = LoadLibraryA("User32.dll");
 | 
					    user32 = LoadLibraryA("User32.dll");
 | 
				
			||||||
    SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext
 | 
					    SetThreadDpiAwarenessContext_function = (Func_SetThreadDpiAwarenessContext)
 | 
				
			||||||
    )GetProcAddress(user32, "SetThreadDpiAwarenessContext");
 | 
					        GetProcAddress(user32, "SetThreadDpiAwarenessContext");
 | 
				
			||||||
    if (SetThreadDpiAwarenessContext_function != NULL) {
 | 
					    if (SetThreadDpiAwarenessContext_function != NULL) {
 | 
				
			||||||
        GetWindowDpiAwarenessContext_function = (Func_GetWindowDpiAwarenessContext
 | 
					        GetWindowDpiAwarenessContext_function = (Func_GetWindowDpiAwarenessContext)
 | 
				
			||||||
        )GetProcAddress(user32, "GetWindowDpiAwarenessContext");
 | 
					            GetProcAddress(user32, "GetWindowDpiAwarenessContext");
 | 
				
			||||||
        if (screens == -1 && GetWindowDpiAwarenessContext_function != NULL) {
 | 
					        if (screens == -1 && GetWindowDpiAwarenessContext_function != NULL) {
 | 
				
			||||||
            dpiAwareness = GetWindowDpiAwarenessContext_function(wnd);
 | 
					            dpiAwareness = GetWindowDpiAwarenessContext_function(wnd);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,7 +101,7 @@ export_imaging_schema(Imaging im, struct ArrowSchema *schema) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* for now, single block images */
 | 
					    /* for now, single block images */
 | 
				
			||||||
    if (!(im->blocks_count == 0 || im->blocks_count == 1)) {
 | 
					    if (im->blocks_count > 1) {
 | 
				
			||||||
        return IMAGING_ARROW_MEMORY_LAYOUT;
 | 
					        return IMAGING_ARROW_MEMORY_LAYOUT;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -159,7 +159,7 @@ export_single_channel_array(Imaging im, struct ArrowArray *array) {
 | 
				
			||||||
    int length = im->xsize * im->ysize;
 | 
					    int length = im->xsize * im->ysize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* for now, single block images */
 | 
					    /* for now, single block images */
 | 
				
			||||||
    if (!(im->blocks_count == 0 || im->blocks_count == 1)) {
 | 
					    if (im->blocks_count > 1) {
 | 
				
			||||||
        return IMAGING_ARROW_MEMORY_LAYOUT;
 | 
					        return IMAGING_ARROW_MEMORY_LAYOUT;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -202,7 +202,7 @@ export_fixed_pixel_array(Imaging im, struct ArrowArray *array) {
 | 
				
			||||||
    int length = im->xsize * im->ysize;
 | 
					    int length = im->xsize * im->ysize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* for now, single block images */
 | 
					    /* for now, single block images */
 | 
				
			||||||
    if (!(im->blocks_count == 0 || im->blocks_count == 1)) {
 | 
					    if (im->blocks_count > 1) {
 | 
				
			||||||
        return IMAGING_ARROW_MEMORY_LAYOUT;
 | 
					        return IMAGING_ARROW_MEMORY_LAYOUT;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,7 @@ typedef struct {
 | 
				
			||||||
} Edge;
 | 
					} Edge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Type used in "polygon*" functions */
 | 
					/* Type used in "polygon*" functions */
 | 
				
			||||||
typedef void (*hline_handler)(Imaging, int, int, int, int);
 | 
					typedef void (*hline_handler)(Imaging, int, int, int, int, Imaging);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void
 | 
					static inline void
 | 
				
			||||||
point8(Imaging im, int x, int y, int ink) {
 | 
					point8(Imaging im, int x, int y, int ink) {
 | 
				
			||||||
| 
						 | 
					@ -103,9 +103,7 @@ point32rgba(Imaging im, int x, int y, int ink) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void
 | 
					static inline void
 | 
				
			||||||
hline8(Imaging im, int x0, int y0, int x1, int ink) {
 | 
					hline8(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
 | 
				
			||||||
    int pixelwidth;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (y0 >= 0 && y0 < im->ysize) {
 | 
					    if (y0 >= 0 && y0 < im->ysize) {
 | 
				
			||||||
        if (x0 < 0) {
 | 
					        if (x0 < 0) {
 | 
				
			||||||
            x0 = 0;
 | 
					            x0 = 0;
 | 
				
			||||||
| 
						 | 
					@ -118,16 +116,41 @@ hline8(Imaging im, int x0, int y0, int x1, int ink) {
 | 
				
			||||||
            x1 = im->xsize - 1;
 | 
					            x1 = im->xsize - 1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (x0 <= x1) {
 | 
					        if (x0 <= x1) {
 | 
				
			||||||
            pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1;
 | 
					            int bigendian = -1;
 | 
				
			||||||
            memset(
 | 
					            if (strncmp(im->mode, "I;16", 4) == 0) {
 | 
				
			||||||
                im->image8[y0] + x0 * pixelwidth, (UINT8)ink, (x1 - x0 + 1) * pixelwidth
 | 
					                bigendian =
 | 
				
			||||||
            );
 | 
					                    (
 | 
				
			||||||
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
 | 
					                        strcmp(im->mode, "I;16") == 0 || strcmp(im->mode, "I;16L") == 0
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					                        strcmp(im->mode, "I;16B") == 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                        ? 1
 | 
				
			||||||
 | 
					                        : 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (mask == NULL && bigendian == -1) {
 | 
				
			||||||
 | 
					                memset(im->image8[y0] + x0, (UINT8)ink, (x1 - x0 + 1));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                UINT8 *p = im->image8[y0];
 | 
				
			||||||
 | 
					                while (x0 <= x1) {
 | 
				
			||||||
 | 
					                    if (mask == NULL || mask->image8[y0][x0]) {
 | 
				
			||||||
 | 
					                        if (bigendian == -1) {
 | 
				
			||||||
 | 
					                            p[x0] = ink;
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            p[x0 * 2 + (bigendian ? 1 : 0)] = ink;
 | 
				
			||||||
 | 
					                            p[x0 * 2 + (bigendian ? 0 : 1)] = ink >> 8;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    x0++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void
 | 
					static inline void
 | 
				
			||||||
hline32(Imaging im, int x0, int y0, int x1, int ink) {
 | 
					hline32(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
 | 
				
			||||||
    INT32 *p;
 | 
					    INT32 *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (y0 >= 0 && y0 < im->ysize) {
 | 
					    if (y0 >= 0 && y0 < im->ysize) {
 | 
				
			||||||
| 
						 | 
					@ -143,13 +166,16 @@ hline32(Imaging im, int x0, int y0, int x1, int ink) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        p = im->image32[y0];
 | 
					        p = im->image32[y0];
 | 
				
			||||||
        while (x0 <= x1) {
 | 
					        while (x0 <= x1) {
 | 
				
			||||||
            p[x0++] = ink;
 | 
					            if (mask == NULL || mask->image8[y0][x0]) {
 | 
				
			||||||
 | 
					                p[x0] = ink;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            x0++;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void
 | 
					static inline void
 | 
				
			||||||
hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
 | 
					hline32rgba(Imaging im, int x0, int y0, int x1, int ink, Imaging mask) {
 | 
				
			||||||
    unsigned int tmp;
 | 
					    unsigned int tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (y0 >= 0 && y0 < im->ysize) {
 | 
					    if (y0 >= 0 && y0 < im->ysize) {
 | 
				
			||||||
| 
						 | 
					@ -167,9 +193,11 @@ hline32rgba(Imaging im, int x0, int y0, int x1, int ink) {
 | 
				
			||||||
            UINT8 *out = (UINT8 *)im->image[y0] + x0 * 4;
 | 
					            UINT8 *out = (UINT8 *)im->image[y0] + x0 * 4;
 | 
				
			||||||
            UINT8 *in = (UINT8 *)&ink;
 | 
					            UINT8 *in = (UINT8 *)&ink;
 | 
				
			||||||
            while (x0 <= x1) {
 | 
					            while (x0 <= x1) {
 | 
				
			||||||
 | 
					                if (mask == NULL || mask->image8[y0][x0]) {
 | 
				
			||||||
                    out[0] = BLEND(in[3], out[0], in[0], tmp);
 | 
					                    out[0] = BLEND(in[3], out[0], in[0], tmp);
 | 
				
			||||||
                    out[1] = BLEND(in[3], out[1], in[1], tmp);
 | 
					                    out[1] = BLEND(in[3], out[1], in[1], tmp);
 | 
				
			||||||
                    out[2] = BLEND(in[3], out[2], in[2], tmp);
 | 
					                    out[2] = BLEND(in[3], out[2], in[2], tmp);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                x0++;
 | 
					                x0++;
 | 
				
			||||||
                out += 4;
 | 
					                out += 4;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -407,7 +435,14 @@ x_cmp(const void *x0, const void *x1) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
draw_horizontal_lines(
 | 
					draw_horizontal_lines(
 | 
				
			||||||
    Imaging im, int n, Edge *e, int ink, int *x_pos, int y, hline_handler hline
 | 
					    Imaging im,
 | 
				
			||||||
 | 
					    int n,
 | 
				
			||||||
 | 
					    Edge *e,
 | 
				
			||||||
 | 
					    int ink,
 | 
				
			||||||
 | 
					    int *x_pos,
 | 
				
			||||||
 | 
					    int y,
 | 
				
			||||||
 | 
					    hline_handler hline,
 | 
				
			||||||
 | 
					    Imaging mask
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    int i;
 | 
					    int i;
 | 
				
			||||||
    for (i = 0; i < n; i++) {
 | 
					    for (i = 0; i < n; i++) {
 | 
				
			||||||
| 
						 | 
					@ -429,7 +464,7 @@ draw_horizontal_lines(
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            (*hline)(im, xmin, e[i].ymin, xmax, ink);
 | 
					            (*hline)(im, xmin, e[i].ymin, xmax, ink, mask);
 | 
				
			||||||
            *x_pos = xmax + 1;
 | 
					            *x_pos = xmax + 1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -440,7 +475,7 @@ draw_horizontal_lines(
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static inline int
 | 
					static inline int
 | 
				
			||||||
polygon_generic(
 | 
					polygon_generic(
 | 
				
			||||||
    Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, int hasAlpha
 | 
					    Imaging im, int n, Edge *e, int ink, int eofill, hline_handler hline, Imaging mask
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    Edge **edge_table;
 | 
					    Edge **edge_table;
 | 
				
			||||||
    float *xx;
 | 
					    float *xx;
 | 
				
			||||||
| 
						 | 
					@ -461,6 +496,7 @@ polygon_generic(
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int hasAlpha = hline == hline32rgba;
 | 
				
			||||||
    for (i = 0; i < n; i++) {
 | 
					    for (i = 0; i < n; i++) {
 | 
				
			||||||
        if (ymin > e[i].ymin) {
 | 
					        if (ymin > e[i].ymin) {
 | 
				
			||||||
            ymin = e[i].ymin;
 | 
					            ymin = e[i].ymin;
 | 
				
			||||||
| 
						 | 
					@ -470,7 +506,7 @@ polygon_generic(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (e[i].ymin == e[i].ymax) {
 | 
					        if (e[i].ymin == e[i].ymax) {
 | 
				
			||||||
            if (hasAlpha != 1) {
 | 
					            if (hasAlpha != 1) {
 | 
				
			||||||
                (*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink);
 | 
					                (*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink, mask);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -558,7 +594,7 @@ polygon_generic(
 | 
				
			||||||
                    // Line would be before the current position
 | 
					                    // Line would be before the current position
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
 | 
					                draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline, mask);
 | 
				
			||||||
                if (x_end < x_pos) {
 | 
					                if (x_end < x_pos) {
 | 
				
			||||||
                    // Line would be before the current position
 | 
					                    // Line would be before the current position
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
| 
						 | 
					@ -574,13 +610,13 @@ polygon_generic(
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                (*hline)(im, x_start, ymin, x_end, ink);
 | 
					                (*hline)(im, x_start, ymin, x_end, ink, mask);
 | 
				
			||||||
                x_pos = x_end + 1;
 | 
					                x_pos = x_end + 1;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline);
 | 
					            draw_horizontal_lines(im, n, e, ink, &x_pos, ymin, hline, mask);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            for (i = 1; i < j; i += 2) {
 | 
					            for (i = 1; i < j; i += 2) {
 | 
				
			||||||
                (*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink);
 | 
					                (*hline)(im, ROUND_UP(xx[i - 1]), ymin, ROUND_DOWN(xx[i]), ink, mask);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -590,21 +626,6 @@ polygon_generic(
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline int
 | 
					 | 
				
			||||||
polygon8(Imaging im, int n, Edge *e, int ink, int eofill) {
 | 
					 | 
				
			||||||
    return polygon_generic(im, n, e, ink, eofill, hline8, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline int
 | 
					 | 
				
			||||||
polygon32(Imaging im, int n, Edge *e, int ink, int eofill) {
 | 
					 | 
				
			||||||
    return polygon_generic(im, n, e, ink, eofill, hline32, 0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline int
 | 
					 | 
				
			||||||
polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill) {
 | 
					 | 
				
			||||||
    return polygon_generic(im, n, e, ink, eofill, hline32rgba, 1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static inline void
 | 
					static inline void
 | 
				
			||||||
add_edge(Edge *e, int x0, int y0, int x1, int y1) {
 | 
					add_edge(Edge *e, int x0, int y0, int x1, int y1) {
 | 
				
			||||||
    /* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */
 | 
					    /* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */
 | 
				
			||||||
| 
						 | 
					@ -639,14 +660,13 @@ add_edge(Edge *e, int x0, int y0, int x1, int y1) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
    void (*point)(Imaging im, int x, int y, int ink);
 | 
					    void (*point)(Imaging im, int x, int y, int ink);
 | 
				
			||||||
    void (*hline)(Imaging im, int x0, int y0, int x1, int ink);
 | 
					    void (*hline)(Imaging im, int x0, int y0, int x1, int ink, Imaging mask);
 | 
				
			||||||
    void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);
 | 
					    void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);
 | 
				
			||||||
    int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill);
 | 
					 | 
				
			||||||
} DRAW;
 | 
					} DRAW;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DRAW draw8 = {point8, hline8, line8, polygon8};
 | 
					DRAW draw8 = {point8, hline8, line8};
 | 
				
			||||||
DRAW draw32 = {point32, hline32, line32, polygon32};
 | 
					DRAW draw32 = {point32, hline32, line32};
 | 
				
			||||||
DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba, polygon32rgba};
 | 
					DRAW draw32rgba = {point32rgba, hline32rgba, line32rgba};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* -------------------------------------------------------------------- */
 | 
					/* -------------------------------------------------------------------- */
 | 
				
			||||||
/* Interface                                                            */
 | 
					/* Interface                                                            */
 | 
				
			||||||
| 
						 | 
					@ -691,7 +711,15 @@ ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink_, in
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
ImagingDrawWideLine(
 | 
					ImagingDrawWideLine(
 | 
				
			||||||
    Imaging im, int x0, int y0, int x1, int y1, const void *ink_, int width, int op
 | 
					    Imaging im,
 | 
				
			||||||
 | 
					    int x0,
 | 
				
			||||||
 | 
					    int y0,
 | 
				
			||||||
 | 
					    int x1,
 | 
				
			||||||
 | 
					    int y1,
 | 
				
			||||||
 | 
					    const void *ink_,
 | 
				
			||||||
 | 
					    int width,
 | 
				
			||||||
 | 
					    int op,
 | 
				
			||||||
 | 
					    Imaging mask
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    DRAW *draw;
 | 
					    DRAW *draw;
 | 
				
			||||||
    INT32 ink;
 | 
					    INT32 ink;
 | 
				
			||||||
| 
						 | 
					@ -731,7 +759,7 @@ ImagingDrawWideLine(
 | 
				
			||||||
        add_edge(e + 2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]);
 | 
					        add_edge(e + 2, vertices[2][0], vertices[2][1], vertices[3][0], vertices[3][1]);
 | 
				
			||||||
        add_edge(e + 3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]);
 | 
					        add_edge(e + 3, vertices[3][0], vertices[3][1], vertices[0][0], vertices[0][1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        draw->polygon(im, 4, e, ink, 0);
 | 
					        polygon_generic(im, 4, e, ink, 0, draw->hline, mask);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -774,7 +802,7 @@ ImagingDrawRectangle(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (y = y0; y <= y1; y++) {
 | 
					        for (y = y0; y <= y1; y++) {
 | 
				
			||||||
            draw->hline(im, x0, y, x1, ink);
 | 
					            draw->hline(im, x0, y, x1, ink, NULL);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
| 
						 | 
					@ -783,8 +811,8 @@ ImagingDrawRectangle(
 | 
				
			||||||
            width = 1;
 | 
					            width = 1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        for (i = 0; i < width; i++) {
 | 
					        for (i = 0; i < width; i++) {
 | 
				
			||||||
            draw->hline(im, x0, y0 + i, x1, ink);
 | 
					            draw->hline(im, x0, y0 + i, x1, ink, NULL);
 | 
				
			||||||
            draw->hline(im, x0, y1 - i, x1, ink);
 | 
					            draw->hline(im, x0, y1 - i, x1, ink, NULL);
 | 
				
			||||||
            draw->line(im, x1 - i, y0 + width, x1 - i, y1 - width + 1, ink);
 | 
					            draw->line(im, x1 - i, y0 + width, x1 - i, y1 - width + 1, ink);
 | 
				
			||||||
            draw->line(im, x0 + i, y0 + width, x0 + i, y1 - width + 1, ink);
 | 
					            draw->line(im, x0 + i, y0 + width, x0 + i, y1 - width + 1, ink);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -795,7 +823,14 @@ ImagingDrawRectangle(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
ImagingDrawPolygon(
 | 
					ImagingDrawPolygon(
 | 
				
			||||||
    Imaging im, int count, int *xy, const void *ink_, int fill, int width, int op
 | 
					    Imaging im,
 | 
				
			||||||
 | 
					    int count,
 | 
				
			||||||
 | 
					    int *xy,
 | 
				
			||||||
 | 
					    const void *ink_,
 | 
				
			||||||
 | 
					    int fill,
 | 
				
			||||||
 | 
					    int width,
 | 
				
			||||||
 | 
					    int op,
 | 
				
			||||||
 | 
					    Imaging mask
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    int i, n, x0, y0, x1, y1;
 | 
					    int i, n, x0, y0, x1, y1;
 | 
				
			||||||
    DRAW *draw;
 | 
					    DRAW *draw;
 | 
				
			||||||
| 
						 | 
					@ -839,7 +874,7 @@ ImagingDrawPolygon(
 | 
				
			||||||
        if (xy[i * 2] != xy[0] || xy[i * 2 + 1] != xy[1]) {
 | 
					        if (xy[i * 2] != xy[0] || xy[i * 2 + 1] != xy[1]) {
 | 
				
			||||||
            add_edge(&e[n++], xy[i * 2], xy[i * 2 + 1], xy[0], xy[1]);
 | 
					            add_edge(&e[n++], xy[i * 2], xy[i * 2 + 1], xy[0], xy[1]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        draw->polygon(im, n, e, ink, 0);
 | 
					        polygon_generic(im, n, e, ink, 0, draw->hline, mask);
 | 
				
			||||||
        free(e);
 | 
					        free(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
| 
						 | 
					@ -861,11 +896,12 @@ ImagingDrawPolygon(
 | 
				
			||||||
                    xy[i * 2 + 3],
 | 
					                    xy[i * 2 + 3],
 | 
				
			||||||
                    ink_,
 | 
					                    ink_,
 | 
				
			||||||
                    width,
 | 
					                    width,
 | 
				
			||||||
                    op
 | 
					                    op,
 | 
				
			||||||
 | 
					                    mask
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ImagingDrawWideLine(
 | 
					            ImagingDrawWideLine(
 | 
				
			||||||
                im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op
 | 
					                im, xy[i * 2], xy[i * 2 + 1], xy[0], xy[1], ink_, width, op, mask
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -1536,7 +1572,9 @@ ellipseNew(
 | 
				
			||||||
    ellipse_init(&st, a, b, width);
 | 
					    ellipse_init(&st, a, b, width);
 | 
				
			||||||
    int32_t X0, Y, X1;
 | 
					    int32_t X0, Y, X1;
 | 
				
			||||||
    while (ellipse_next(&st, &X0, &Y, &X1) != -1) {
 | 
					    while (ellipse_next(&st, &X0, &Y, &X1) != -1) {
 | 
				
			||||||
        draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);
 | 
					        draw->hline(
 | 
				
			||||||
 | 
					            im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink, NULL
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1571,7 +1609,9 @@ clipEllipseNew(
 | 
				
			||||||
    int32_t X0, Y, X1;
 | 
					    int32_t X0, Y, X1;
 | 
				
			||||||
    int next_code;
 | 
					    int next_code;
 | 
				
			||||||
    while ((next_code = clip_ellipse_next(&st, &X0, &Y, &X1)) >= 0) {
 | 
					    while ((next_code = clip_ellipse_next(&st, &X0, &Y, &X1)) >= 0) {
 | 
				
			||||||
        draw->hline(im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink);
 | 
					        draw->hline(
 | 
				
			||||||
 | 
					            im, x0 + (X0 + a) / 2, y0 + (Y + b) / 2, x0 + (X1 + a) / 2, ink, NULL
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    clip_ellipse_free(&st);
 | 
					    clip_ellipse_free(&st);
 | 
				
			||||||
    return next_code == -1 ? 0 : -1;
 | 
					    return next_code == -1 ? 0 : -1;
 | 
				
			||||||
| 
						 | 
					@ -1989,7 +2029,7 @@ ImagingDrawOutline(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DRAWINIT();
 | 
					    DRAWINIT();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    draw->polygon(im, outline->count, outline->edges, ink, 0);
 | 
					    polygon_generic(im, outline->count, outline->edges, ink, 0, draw->hline, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@ ImagingSavePPM(Imaging im, const char *outfile) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fp = fopen(outfile, "wb");
 | 
					    fp = fopen(outfile, "wb");
 | 
				
			||||||
    if (!fp) {
 | 
					    if (!fp) {
 | 
				
			||||||
        (void)ImagingError_OSError();
 | 
					        PyErr_SetString(PyExc_OSError, "error when accessing file");
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -118,8 +118,9 @@ ImagingFillRadialGradient(const char *mode) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (y = 0; y < 256; y++) {
 | 
					    for (y = 0; y < 256; y++) {
 | 
				
			||||||
        for (x = 0; x < 256; x++) {
 | 
					        for (x = 0; x < 256; x++) {
 | 
				
			||||||
            d = (int
 | 
					            d = (int)sqrt(
 | 
				
			||||||
            )sqrt((double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0);
 | 
					                (double)((x - 128) * (x - 128) + (y - 128) * (y - 128)) * 2.0
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            if (d >= 255) {
 | 
					            if (d >= 255) {
 | 
				
			||||||
                d = 255;
 | 
					                d = 255;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,7 +155,8 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            int bigendian = 0;
 | 
					            int bigendian = 0;
 | 
				
			||||||
            if (im->type == IMAGING_TYPE_SPECIAL) {
 | 
					            if (im->type == IMAGING_TYPE_SPECIAL) {
 | 
				
			||||||
                if (strcmp(im->mode, "I;16B") == 0
 | 
					                if (
 | 
				
			||||||
 | 
					                    strcmp(im->mode, "I;16B") == 0
 | 
				
			||||||
#ifdef WORDS_BIGENDIAN
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
                    || strcmp(im->mode, "I;16N") == 0
 | 
					                    || strcmp(im->mode, "I;16N") == 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -308,7 +309,8 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            int bigendian = 0;
 | 
					            int bigendian = 0;
 | 
				
			||||||
            if (im->type == IMAGING_TYPE_SPECIAL) {
 | 
					            if (im->type == IMAGING_TYPE_SPECIAL) {
 | 
				
			||||||
                if (strcmp(im->mode, "I;16B") == 0
 | 
					                if (
 | 
				
			||||||
 | 
					                    strcmp(im->mode, "I;16B") == 0
 | 
				
			||||||
#ifdef WORDS_BIGENDIAN
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
                    || strcmp(im->mode, "I;16N") == 0
 | 
					                    || strcmp(im->mode, "I;16N") == 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,8 +270,6 @@ ImagingSectionLeave(ImagingSectionCookie *cookie);
 | 
				
			||||||
/* Exceptions */
 | 
					/* Exceptions */
 | 
				
			||||||
/* ---------- */
 | 
					/* ---------- */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern void *
 | 
					 | 
				
			||||||
ImagingError_OSError(void);
 | 
					 | 
				
			||||||
extern void *
 | 
					extern void *
 | 
				
			||||||
ImagingError_MemoryError(void);
 | 
					ImagingError_MemoryError(void);
 | 
				
			||||||
extern void *
 | 
					extern void *
 | 
				
			||||||
| 
						 | 
					@ -280,8 +278,6 @@ extern void *
 | 
				
			||||||
ImagingError_Mismatch(void); /* maps to ValueError by default */
 | 
					ImagingError_Mismatch(void); /* maps to ValueError by default */
 | 
				
			||||||
extern void *
 | 
					extern void *
 | 
				
			||||||
ImagingError_ValueError(const char *message);
 | 
					ImagingError_ValueError(const char *message);
 | 
				
			||||||
extern void
 | 
					 | 
				
			||||||
ImagingError_Clear(void);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Transform callbacks */
 | 
					/* Transform callbacks */
 | 
				
			||||||
/* ------------------- */
 | 
					/* ------------------- */
 | 
				
			||||||
| 
						 | 
					@ -510,7 +506,15 @@ extern int
 | 
				
			||||||
ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink, int op);
 | 
					ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void *ink, int op);
 | 
				
			||||||
extern int
 | 
					extern int
 | 
				
			||||||
ImagingDrawWideLine(
 | 
					ImagingDrawWideLine(
 | 
				
			||||||
    Imaging im, int x0, int y0, int x1, int y1, const void *ink, int width, int op
 | 
					    Imaging im,
 | 
				
			||||||
 | 
					    int x0,
 | 
				
			||||||
 | 
					    int y0,
 | 
				
			||||||
 | 
					    int x1,
 | 
				
			||||||
 | 
					    int y1,
 | 
				
			||||||
 | 
					    const void *ink,
 | 
				
			||||||
 | 
					    int width,
 | 
				
			||||||
 | 
					    int op,
 | 
				
			||||||
 | 
					    Imaging mask
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
extern int
 | 
					extern int
 | 
				
			||||||
ImagingDrawPieslice(
 | 
					ImagingDrawPieslice(
 | 
				
			||||||
| 
						 | 
					@ -530,7 +534,14 @@ extern int
 | 
				
			||||||
ImagingDrawPoint(Imaging im, int x, int y, const void *ink, int op);
 | 
					ImagingDrawPoint(Imaging im, int x, int y, const void *ink, int op);
 | 
				
			||||||
extern int
 | 
					extern int
 | 
				
			||||||
ImagingDrawPolygon(
 | 
					ImagingDrawPolygon(
 | 
				
			||||||
    Imaging im, int points, int *xy, const void *ink, int fill, int width, int op
 | 
					    Imaging im,
 | 
				
			||||||
 | 
					    int points,
 | 
				
			||||||
 | 
					    int *xy,
 | 
				
			||||||
 | 
					    const void *ink,
 | 
				
			||||||
 | 
					    int fill,
 | 
				
			||||||
 | 
					    int width,
 | 
				
			||||||
 | 
					    int op,
 | 
				
			||||||
 | 
					    Imaging mask
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
extern int
 | 
					extern int
 | 
				
			||||||
ImagingDrawRectangle(
 | 
					ImagingDrawRectangle(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,8 +207,8 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (params->cp_cinema == OPJ_CINEMA4K_24) {
 | 
					    if (params->cp_cinema == OPJ_CINEMA4K_24) {
 | 
				
			||||||
        float max_rate =
 | 
					        float max_rate =
 | 
				
			||||||
            ((float)(components * im->xsize * im->ysize * 8) / (CINEMA_24_CS_LENGTH * 8)
 | 
					            ((float)(components * im->xsize * im->ysize * 8) /
 | 
				
			||||||
            );
 | 
					             (CINEMA_24_CS_LENGTH * 8));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        params->POC[0].tile = 1;
 | 
					        params->POC[0].tile = 1;
 | 
				
			||||||
        params->POC[0].resno0 = 0;
 | 
					        params->POC[0].resno0 = 0;
 | 
				
			||||||
| 
						 | 
					@ -243,8 +243,8 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) {
 | 
				
			||||||
        params->max_comp_size = COMP_24_CS_MAX_LENGTH;
 | 
					        params->max_comp_size = COMP_24_CS_MAX_LENGTH;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        float max_rate =
 | 
					        float max_rate =
 | 
				
			||||||
            ((float)(components * im->xsize * im->ysize * 8) / (CINEMA_48_CS_LENGTH * 8)
 | 
					            ((float)(components * im->xsize * im->ysize * 8) /
 | 
				
			||||||
            );
 | 
					             (CINEMA_48_CS_LENGTH * 8));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (n = 0; n < params->tcp_numlayers; ++n) {
 | 
					        for (n = 0; n < params->tcp_numlayers; ++n) {
 | 
				
			||||||
            rate = 0;
 | 
					            rate = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,15 +60,25 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (state->x >= state->bytes) {
 | 
					        if (state->x >= state->bytes) {
 | 
				
			||||||
            if (state->bytes % state->xsize && state->bytes > state->xsize) {
 | 
					            int bands;
 | 
				
			||||||
                int bands = state->bytes / state->xsize;
 | 
					            int xsize = 0;
 | 
				
			||||||
                int stride = state->bytes / bands;
 | 
					            int stride = 0;
 | 
				
			||||||
 | 
					            if (state->bits == 2 || state->bits == 4) {
 | 
				
			||||||
 | 
					                xsize = (state->xsize + 7) / 8;
 | 
				
			||||||
 | 
					                bands = state->bits;
 | 
				
			||||||
 | 
					                stride = state->bytes / state->bits;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                xsize = state->xsize;
 | 
				
			||||||
 | 
					                bands = state->bytes / state->xsize;
 | 
				
			||||||
 | 
					                if (bands != 0) {
 | 
				
			||||||
 | 
					                    stride = state->bytes / bands;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (stride > xsize) {
 | 
				
			||||||
                int i;
 | 
					                int i;
 | 
				
			||||||
                for (i = 1; i < bands; i++) {  // note -- skipping first band
 | 
					                for (i = 1; i < bands; i++) {  // note -- skipping first band
 | 
				
			||||||
                    memmove(
 | 
					                    memmove(
 | 
				
			||||||
                        &state->buffer[i * state->xsize],
 | 
					                        &state->buffer[i * xsize], &state->buffer[i * stride], xsize
 | 
				
			||||||
                        &state->buffer[i * stride],
 | 
					 | 
				
			||||||
                        state->xsize
 | 
					 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,8 +197,9 @@ ImagingPoint(Imaging imIn, const char *mode, const void *table) {
 | 
				
			||||||
    return imOut;
 | 
					    return imOut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mode_mismatch:
 | 
					mode_mismatch:
 | 
				
			||||||
    return (Imaging
 | 
					    return (Imaging)ImagingError_ValueError(
 | 
				
			||||||
    )ImagingError_ValueError("point operation not supported for this mode");
 | 
					        "point operation not supported for this mode"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Imaging
 | 
					Imaging
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -470,7 +470,8 @@ ImagingResampleHorizontal_16bpc(
 | 
				
			||||||
    double *k;
 | 
					    double *k;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int bigendian = 0;
 | 
					    int bigendian = 0;
 | 
				
			||||||
    if (strcmp(imIn->mode, "I;16N") == 0
 | 
					    if (
 | 
				
			||||||
 | 
					        strcmp(imIn->mode, "I;16N") == 0
 | 
				
			||||||
#ifdef WORDS_BIGENDIAN
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
        || strcmp(imIn->mode, "I;16B") == 0
 | 
					        || strcmp(imIn->mode, "I;16B") == 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -509,7 +510,8 @@ ImagingResampleVertical_16bpc(
 | 
				
			||||||
    double *k;
 | 
					    double *k;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int bigendian = 0;
 | 
					    int bigendian = 0;
 | 
				
			||||||
    if (strcmp(imIn->mode, "I;16N") == 0
 | 
					    if (
 | 
				
			||||||
 | 
					        strcmp(imIn->mode, "I;16N") == 0
 | 
				
			||||||
#ifdef WORDS_BIGENDIAN
 | 
					#ifdef WORDS_BIGENDIAN
 | 
				
			||||||
        || strcmp(imIn->mode, "I;16B") == 0
 | 
					        || strcmp(imIn->mode, "I;16B") == 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -602,8 +602,9 @@ ImagingBorrowArrow(
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!borrowed_buffer) {
 | 
					    if (!borrowed_buffer) {
 | 
				
			||||||
        return (Imaging
 | 
					        return (Imaging)ImagingError_ValueError(
 | 
				
			||||||
        )ImagingError_ValueError("Arrow Array, exactly 2 buffers required");
 | 
					            "Arrow Array, exactly 2 buffers required"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (y = i = 0; y < im->ysize; y++) {
 | 
					    for (y = i = 0; y < im->ysize; y++) {
 | 
				
			||||||
| 
						 | 
					@ -644,7 +645,7 @@ ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
 | 
				
			||||||
        return im;
 | 
					        return im;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ImagingError_Clear();
 | 
					    PyErr_Clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Try to allocate the image once more with smallest possible block size
 | 
					    // Try to allocate the image once more with smallest possible block size
 | 
				
			||||||
    MUTEX_LOCK(&ImagingDefaultArena.mutex);
 | 
					    MUTEX_LOCK(&ImagingDefaultArena.mutex);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -557,7 +557,8 @@ _decodeStrip(
 | 
				
			||||||
                    (tdata_t)state->buffer,
 | 
					                    (tdata_t)state->buffer,
 | 
				
			||||||
                    strip_size
 | 
					                    strip_size
 | 
				
			||||||
                ) == -1) {
 | 
					                ) == -1) {
 | 
				
			||||||
                TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))
 | 
					                TRACE(
 | 
				
			||||||
 | 
					                    ("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                state->errcode = IMAGING_CODEC_BROKEN;
 | 
					                state->errcode = IMAGING_CODEC_BROKEN;
 | 
				
			||||||
                return -1;
 | 
					                return -1;
 | 
				
			||||||
| 
						 | 
					@ -1031,7 +1032,10 @@ ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8 *buffer, int byt
 | 
				
			||||||
                TRACE(("Encode Error, row %d\n", state->y));
 | 
					                TRACE(("Encode Error, row %d\n", state->y));
 | 
				
			||||||
                state->errcode = IMAGING_CODEC_BROKEN;
 | 
					                state->errcode = IMAGING_CODEC_BROKEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!clientstate->fp) {
 | 
					                if (clientstate->fp) {
 | 
				
			||||||
 | 
					                    TIFFCleanup(tiff);
 | 
				
			||||||
 | 
					                    clientstate->tiff = NULL;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
                    free(clientstate->data);
 | 
					                    free(clientstate->data);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return -1;
 | 
					                return -1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,6 +137,7 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    im->read_only = view.readonly;
 | 
				
			||||||
    im->destroy = mapping_destroy_buffer;
 | 
					    im->destroy = mapping_destroy_buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_INCREF(target);
 | 
					    Py_INCREF(target);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								tox.ini
									
									
									
									
									
								
							| 
						 | 
					@ -3,7 +3,7 @@ requires =
 | 
				
			||||||
    tox>=4.2
 | 
					    tox>=4.2
 | 
				
			||||||
env_list =
 | 
					env_list =
 | 
				
			||||||
    lint
 | 
					    lint
 | 
				
			||||||
    py{py3, 313, 312, 311, 310, 39}
 | 
					    py{py3, 314, 313, 312, 311, 310, 39}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[testenv]
 | 
					[testenv]
 | 
				
			||||||
deps =
 | 
					deps =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,11 +114,11 @@ V = {
 | 
				
			||||||
    "FREETYPE": "2.13.3",
 | 
					    "FREETYPE": "2.13.3",
 | 
				
			||||||
    "FRIBIDI": "1.0.16",
 | 
					    "FRIBIDI": "1.0.16",
 | 
				
			||||||
    "HARFBUZZ": "11.2.1",
 | 
					    "HARFBUZZ": "11.2.1",
 | 
				
			||||||
    "JPEGTURBO": "3.1.0",
 | 
					    "JPEGTURBO": "3.1.1",
 | 
				
			||||||
    "LCMS2": "2.17",
 | 
					    "LCMS2": "2.17",
 | 
				
			||||||
    "LIBAVIF": "1.3.0",
 | 
					    "LIBAVIF": "1.3.0",
 | 
				
			||||||
    "LIBIMAGEQUANT": "4.3.4",
 | 
					    "LIBIMAGEQUANT": "4.3.4",
 | 
				
			||||||
    "LIBPNG": "1.6.48",
 | 
					    "LIBPNG": "1.6.49",
 | 
				
			||||||
    "LIBWEBP": "1.5.0",
 | 
					    "LIBWEBP": "1.5.0",
 | 
				
			||||||
    "OPENJPEG": "2.5.3",
 | 
					    "OPENJPEG": "2.5.3",
 | 
				
			||||||
    "TIFF": "4.7.0",
 | 
					    "TIFF": "4.7.0",
 | 
				
			||||||
| 
						 | 
					@ -385,8 +385,8 @@ DEPS: dict[str, dict[str, Any]] = {
 | 
				
			||||||
        "bins": [r"*.dll"],
 | 
					        "bins": [r"*.dll"],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "libavif": {
 | 
					    "libavif": {
 | 
				
			||||||
        "url": f"https://github.com/AOMediaCodec/libavif/archive/v{V['LIBAVIF']}.zip",
 | 
					        "url": f"https://github.com/AOMediaCodec/libavif/archive/v{V['LIBAVIF']}.tar.gz",
 | 
				
			||||||
        "filename": f"libavif-{V['LIBAVIF']}.zip",
 | 
					        "filename": f"libavif-{V['LIBAVIF']}.tar.gz",
 | 
				
			||||||
        "license": "LICENSE",
 | 
					        "license": "LICENSE",
 | 
				
			||||||
        "build": [
 | 
					        "build": [
 | 
				
			||||||
            "rustup update",
 | 
					            "rustup update",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user