mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Merge pull request #120 from radarhere/rm-deprecated-psfile
This commit is contained in:
		
						commit
						4ab4fa4cd7
					
				| 
						 | 
					@ -28,8 +28,6 @@ fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
python3 -m pip install --upgrade pip
 | 
					python3 -m pip install --upgrade pip
 | 
				
			||||||
python3 -m pip install --upgrade wheel
 | 
					python3 -m pip install --upgrade wheel
 | 
				
			||||||
# TODO Update condition when cffi supports 3.13
 | 
					 | 
				
			||||||
if ! [[ "$GHA_PYTHON_VERSION" == "3.13" ]]; then PYTHONOPTIMIZE=0 python3 -m pip install cffi ; fi
 | 
					 | 
				
			||||||
python3 -m pip install coverage
 | 
					python3 -m pip install coverage
 | 
				
			||||||
python3 -m pip install defusedxml
 | 
					python3 -m pip install defusedxml
 | 
				
			||||||
python3 -m pip install olefile
 | 
					python3 -m pip install olefile
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,5 @@ exclude_also =
 | 
				
			||||||
[run]
 | 
					[run]
 | 
				
			||||||
omit =
 | 
					omit =
 | 
				
			||||||
    Tests/32bit_segfault_check.py
 | 
					    Tests/32bit_segfault_check.py
 | 
				
			||||||
    Tests/bench_cffi_access.py
 | 
					 | 
				
			||||||
    Tests/check_*.py
 | 
					    Tests/check_*.py
 | 
				
			||||||
    Tests/createfontdatachunk.py
 | 
					    Tests/createfontdatachunk.py
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								.github/workflows/macos-install.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/macos-install.sh
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -18,9 +18,6 @@ else
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
 | 
					export PKG_CONFIG_PATH="/usr/local/opt/openblas/lib/pkgconfig"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO Update condition when cffi supports 3.13
 | 
					 | 
				
			||||||
if ! [[ "$GHA_PYTHON_VERSION" == "3.13" ]]; then PYTHONOPTIMIZE=0 python3 -m pip install cffi ; fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
python3 -m pip install coverage
 | 
					python3 -m pip install coverage
 | 
				
			||||||
python3 -m pip install defusedxml
 | 
					python3 -m pip install defusedxml
 | 
				
			||||||
python3 -m pip install olefile
 | 
					python3 -m pip install olefile
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								.github/workflows/test-cygwin.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/test-cygwin.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -72,7 +72,6 @@ jobs:
 | 
				
			||||||
            make
 | 
					            make
 | 
				
			||||||
            netpbm
 | 
					            netpbm
 | 
				
			||||||
            perl
 | 
					            perl
 | 
				
			||||||
            python3${{ matrix.python-minor-version }}-cffi
 | 
					 | 
				
			||||||
            python3${{ matrix.python-minor-version }}-cython
 | 
					            python3${{ matrix.python-minor-version }}-cython
 | 
				
			||||||
            python3${{ matrix.python-minor-version }}-devel
 | 
					            python3${{ matrix.python-minor-version }}-devel
 | 
				
			||||||
            python3${{ matrix.python-minor-version }}-numpy
 | 
					            python3${{ matrix.python-minor-version }}-numpy
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								.github/workflows/test-mingw.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/test-mingw.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -64,7 +64,6 @@ jobs:
 | 
				
			||||||
              mingw-w64-x86_64-libtiff \
 | 
					              mingw-w64-x86_64-libtiff \
 | 
				
			||||||
              mingw-w64-x86_64-libwebp \
 | 
					              mingw-w64-x86_64-libwebp \
 | 
				
			||||||
              mingw-w64-x86_64-openjpeg2 \
 | 
					              mingw-w64-x86_64-openjpeg2 \
 | 
				
			||||||
              mingw-w64-x86_64-python3-cffi \
 | 
					 | 
				
			||||||
              mingw-w64-x86_64-python3-numpy \
 | 
					              mingw-w64-x86_64-python3-numpy \
 | 
				
			||||||
              mingw-w64-x86_64-python3-olefile \
 | 
					              mingw-w64-x86_64-python3-olefile \
 | 
				
			||||||
              mingw-w64-x86_64-python3-setuptools \
 | 
					              mingw-w64-x86_64-python3-setuptools \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,54 +0,0 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from PIL import PyAccess
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from .helper import hopper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Not running this test by default. No DOS against CI.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def iterate_get(size, access) -> None:
 | 
					 | 
				
			||||||
    (w, h) = size
 | 
					 | 
				
			||||||
    for x in range(w):
 | 
					 | 
				
			||||||
        for y in range(h):
 | 
					 | 
				
			||||||
            access[(x, y)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def iterate_set(size, access) -> None:
 | 
					 | 
				
			||||||
    (w, h) = size
 | 
					 | 
				
			||||||
    for x in range(w):
 | 
					 | 
				
			||||||
        for y in range(h):
 | 
					 | 
				
			||||||
            access[(x, y)] = (x % 256, y % 256, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def timer(func, label, *args) -> None:
 | 
					 | 
				
			||||||
    iterations = 5000
 | 
					 | 
				
			||||||
    starttime = time.time()
 | 
					 | 
				
			||||||
    for x in range(iterations):
 | 
					 | 
				
			||||||
        func(*args)
 | 
					 | 
				
			||||||
        if time.time() - starttime > 10:
 | 
					 | 
				
			||||||
            break
 | 
					 | 
				
			||||||
    endtime = time.time()
 | 
					 | 
				
			||||||
    print(
 | 
					 | 
				
			||||||
        f"{label}: completed {x + 1} iterations in {endtime - starttime:.4f}s, "
 | 
					 | 
				
			||||||
        f"{(endtime - starttime) / (x + 1.0):.6f}s per iteration"
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_direct() -> None:
 | 
					 | 
				
			||||||
    im = hopper()
 | 
					 | 
				
			||||||
    im.load()
 | 
					 | 
				
			||||||
    # im = Image.new("RGB", (2000, 2000), (1, 3, 2))
 | 
					 | 
				
			||||||
    caccess = im.im.pixel_access(False)
 | 
					 | 
				
			||||||
    access = PyAccess.new(im, False)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert access is not None
 | 
					 | 
				
			||||||
    assert caccess[(0, 0)] == access[(0, 0)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    print(f"Size: {im.width}x{im.height}")
 | 
					 | 
				
			||||||
    timer(iterate_get, "PyAccess - get", im.size, access)
 | 
					 | 
				
			||||||
    timer(iterate_set, "PyAccess - set", im.size, access)
 | 
					 | 
				
			||||||
    timer(iterate_get, "C-api - get", im.size, caccess)
 | 
					 | 
				
			||||||
    timer(iterate_set, "C-api - set", im.size, caccess)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -12,19 +12,6 @@ from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import assert_image_equal, hopper, is_win32
 | 
					from .helper import assert_image_equal, hopper, is_win32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CFFI imports pycparser which doesn't support PYTHONOPTIMIZE=2
 | 
					 | 
				
			||||||
# https://github.com/eliben/pycparser/pull/198#issuecomment-317001670
 | 
					 | 
				
			||||||
cffi: ModuleType | None
 | 
					 | 
				
			||||||
if os.environ.get("PYTHONOPTIMIZE") == "2":
 | 
					 | 
				
			||||||
    cffi = None
 | 
					 | 
				
			||||||
else:
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        import cffi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        from PIL import PyAccess
 | 
					 | 
				
			||||||
    except ImportError:
 | 
					 | 
				
			||||||
        cffi = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
numpy: ModuleType | None
 | 
					numpy: ModuleType | None
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    import numpy
 | 
					    import numpy
 | 
				
			||||||
| 
						 | 
					@ -32,21 +19,7 @@ except ImportError:
 | 
				
			||||||
    numpy = None
 | 
					    numpy = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AccessTest:
 | 
					class TestImagePutPixel:
 | 
				
			||||||
    # Initial value
 | 
					 | 
				
			||||||
    _init_cffi_access = Image.USE_CFFI_ACCESS
 | 
					 | 
				
			||||||
    _need_cffi_access = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def setup_class(cls) -> None:
 | 
					 | 
				
			||||||
        Image.USE_CFFI_ACCESS = cls._need_cffi_access
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def teardown_class(cls) -> None:
 | 
					 | 
				
			||||||
        Image.USE_CFFI_ACCESS = cls._init_cffi_access
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TestImagePutPixel(AccessTest):
 | 
					 | 
				
			||||||
    def test_sanity(self) -> None:
 | 
					    def test_sanity(self) -> None:
 | 
				
			||||||
        im1 = hopper()
 | 
					        im1 = hopper()
 | 
				
			||||||
        im2 = Image.new(im1.mode, im1.size, 0)
 | 
					        im2 = Image.new(im1.mode, im1.size, 0)
 | 
				
			||||||
| 
						 | 
					@ -131,7 +104,7 @@ class TestImagePutPixel(AccessTest):
 | 
				
			||||||
        assert pix[numpy.int32(1), numpy.int32(2)] == (18, 20, 59)
 | 
					        assert pix[numpy.int32(1), numpy.int32(2)] == (18, 20, 59)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestImageGetPixel(AccessTest):
 | 
					class TestImageGetPixel:
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def color(mode: str) -> int | tuple[int, ...]:
 | 
					    def color(mode: str) -> int | tuple[int, ...]:
 | 
				
			||||||
        bands = Image.getmodebands(mode)
 | 
					        bands = Image.getmodebands(mode)
 | 
				
			||||||
| 
						 | 
					@ -144,9 +117,6 @@ class TestImageGetPixel(AccessTest):
 | 
				
			||||||
        return tuple(range(1, bands + 1))
 | 
					        return tuple(range(1, bands + 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def check(self, mode: str, expected_color_int: int | None = None) -> None:
 | 
					    def check(self, mode: str, expected_color_int: int | None = None) -> None:
 | 
				
			||||||
        if self._need_cffi_access and mode.startswith("BGR;"):
 | 
					 | 
				
			||||||
            pytest.skip("Support not added to deprecated module for BGR;* modes")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expected_color = (
 | 
					        expected_color = (
 | 
				
			||||||
            self.color(mode) if expected_color_int is None else expected_color_int
 | 
					            self.color(mode) if expected_color_int is None else expected_color_int
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					@ -171,15 +141,14 @@ class TestImageGetPixel(AccessTest):
 | 
				
			||||||
        # Check 0x0 image with None initial color
 | 
					        # Check 0x0 image with None initial color
 | 
				
			||||||
        im = Image.new(mode, (0, 0), None)
 | 
					        im = Image.new(mode, (0, 0), None)
 | 
				
			||||||
        assert im.load() is not None
 | 
					        assert im.load() is not None
 | 
				
			||||||
        error = ValueError if self._need_cffi_access else IndexError
 | 
					        with pytest.raises(IndexError):
 | 
				
			||||||
        with pytest.raises(error):
 | 
					 | 
				
			||||||
            im.putpixel((0, 0), expected_color)
 | 
					            im.putpixel((0, 0), expected_color)
 | 
				
			||||||
        with pytest.raises(error):
 | 
					        with pytest.raises(IndexError):
 | 
				
			||||||
            im.getpixel((0, 0))
 | 
					            im.getpixel((0, 0))
 | 
				
			||||||
        # Check negative index
 | 
					        # Check negative index
 | 
				
			||||||
        with pytest.raises(error):
 | 
					        with pytest.raises(IndexError):
 | 
				
			||||||
            im.putpixel((-1, -1), expected_color)
 | 
					            im.putpixel((-1, -1), expected_color)
 | 
				
			||||||
        with pytest.raises(error):
 | 
					        with pytest.raises(IndexError):
 | 
				
			||||||
            im.getpixel((-1, -1))
 | 
					            im.getpixel((-1, -1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check initial color
 | 
					        # Check initial color
 | 
				
			||||||
| 
						 | 
					@ -199,10 +168,10 @@ class TestImageGetPixel(AccessTest):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check 0x0 image with initial color
 | 
					        # Check 0x0 image with initial color
 | 
				
			||||||
        im = Image.new(mode, (0, 0), expected_color)
 | 
					        im = Image.new(mode, (0, 0), expected_color)
 | 
				
			||||||
        with pytest.raises(error):
 | 
					        with pytest.raises(IndexError):
 | 
				
			||||||
            im.getpixel((0, 0))
 | 
					            im.getpixel((0, 0))
 | 
				
			||||||
        # Check negative index
 | 
					        # Check negative index
 | 
				
			||||||
        with pytest.raises(error):
 | 
					        with pytest.raises(IndexError):
 | 
				
			||||||
            im.getpixel((-1, -1))
 | 
					            im.getpixel((-1, -1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @pytest.mark.parametrize("mode", Image.MODES)
 | 
					    @pytest.mark.parametrize("mode", Image.MODES)
 | 
				
			||||||
| 
						 | 
					@ -235,126 +204,7 @@ class TestImageGetPixel(AccessTest):
 | 
				
			||||||
        assert im.convert("RGBA").getpixel((0, 0)) == (255, 0, 0, alpha)
 | 
					        assert im.convert("RGBA").getpixel((0, 0)) == (255, 0, 0, alpha)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
 | 
					class TestImagePutPixelError:
 | 
				
			||||||
@pytest.mark.skipif(cffi is None, reason="No CFFI")
 | 
					 | 
				
			||||||
class TestCffiPutPixel(TestImagePutPixel):
 | 
					 | 
				
			||||||
    _need_cffi_access = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
 | 
					 | 
				
			||||||
@pytest.mark.skipif(cffi is None, reason="No CFFI")
 | 
					 | 
				
			||||||
class TestCffiGetPixel(TestImageGetPixel):
 | 
					 | 
				
			||||||
    _need_cffi_access = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@pytest.mark.skipif(cffi is None, reason="No CFFI")
 | 
					 | 
				
			||||||
class TestCffi(AccessTest):
 | 
					 | 
				
			||||||
    _need_cffi_access = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _test_get_access(self, im: Image.Image) -> None:
 | 
					 | 
				
			||||||
        """Do we get the same thing as the old pixel access
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Using private interfaces, forcing a capi access and
 | 
					 | 
				
			||||||
        a pyaccess for the same image"""
 | 
					 | 
				
			||||||
        caccess = im.im.pixel_access(False)
 | 
					 | 
				
			||||||
        with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
            access = PyAccess.new(im, False)
 | 
					 | 
				
			||||||
        assert access is not None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        w, h = im.size
 | 
					 | 
				
			||||||
        for x in range(0, w, 10):
 | 
					 | 
				
			||||||
            for y in range(0, h, 10):
 | 
					 | 
				
			||||||
                assert access[(x, y)] == caccess[(x, y)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Access an out-of-range pixel
 | 
					 | 
				
			||||||
        with pytest.raises(ValueError):
 | 
					 | 
				
			||||||
            access[(access.xsize + 1, access.ysize + 1)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_get_vs_c(self) -> None:
 | 
					 | 
				
			||||||
        with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
            rgb = hopper("RGB")
 | 
					 | 
				
			||||||
            rgb.load()
 | 
					 | 
				
			||||||
            self._test_get_access(rgb)
 | 
					 | 
				
			||||||
            for mode in ("RGBA", "L", "LA", "1", "P", "F"):
 | 
					 | 
				
			||||||
                self._test_get_access(hopper(mode))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
 | 
					 | 
				
			||||||
                im = Image.new(mode, (10, 10), 40000)
 | 
					 | 
				
			||||||
                self._test_get_access(im)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _test_set_access(self, im: Image.Image, color: tuple[int, ...] | float) -> None:
 | 
					 | 
				
			||||||
        """Are we writing the correct bits into the image?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Using private interfaces, forcing a capi access and
 | 
					 | 
				
			||||||
        a pyaccess for the same image"""
 | 
					 | 
				
			||||||
        caccess = im.im.pixel_access(False)
 | 
					 | 
				
			||||||
        with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
            access = PyAccess.new(im, False)
 | 
					 | 
				
			||||||
        assert access is not None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        w, h = im.size
 | 
					 | 
				
			||||||
        for x in range(0, w, 10):
 | 
					 | 
				
			||||||
            for y in range(0, h, 10):
 | 
					 | 
				
			||||||
                access[(x, y)] = color
 | 
					 | 
				
			||||||
                assert color == caccess[(x, y)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Attempt to set the value on a read-only image
 | 
					 | 
				
			||||||
        with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
            access = PyAccess.new(im, True)
 | 
					 | 
				
			||||||
        assert access is not None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        with pytest.raises(ValueError):
 | 
					 | 
				
			||||||
            access[(0, 0)] = color
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_set_vs_c(self) -> None:
 | 
					 | 
				
			||||||
        rgb = hopper("RGB")
 | 
					 | 
				
			||||||
        with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
            rgb.load()
 | 
					 | 
				
			||||||
        self._test_set_access(rgb, (255, 128, 0))
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("RGBA"), (255, 192, 128, 0))
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("L"), 128)
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("LA"), (128, 128))
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("1"), 255)
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("P"), 128)
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("PA"), (128, 128))
 | 
					 | 
				
			||||||
        self._test_set_access(hopper("F"), 1024.0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for mode in ("I;16", "I;16L", "I;16B", "I;16N", "I"):
 | 
					 | 
				
			||||||
            im = Image.new(mode, (10, 10), 40000)
 | 
					 | 
				
			||||||
            self._test_set_access(im, 45000)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @pytest.mark.filterwarnings("ignore::DeprecationWarning")
 | 
					 | 
				
			||||||
    def test_not_implemented(self) -> None:
 | 
					 | 
				
			||||||
        assert PyAccess.new(hopper("BGR;15")) is None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Ref https://github.com/python-pillow/Pillow/pull/2009
 | 
					 | 
				
			||||||
    def test_reference_counting(self) -> None:
 | 
					 | 
				
			||||||
        size = 10
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for _ in range(10):
 | 
					 | 
				
			||||||
            # Do not save references to the image, only to the access object
 | 
					 | 
				
			||||||
            with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
                px = Image.new("L", (size, 1), 0).load()
 | 
					 | 
				
			||||||
            for i in range(size):
 | 
					 | 
				
			||||||
                # Pixels can contain garbage if image is released
 | 
					 | 
				
			||||||
                assert px[i, 0] == 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @pytest.mark.parametrize("mode", ("P", "PA"))
 | 
					 | 
				
			||||||
    def test_p_putpixel_rgb_rgba(self, mode: str) -> None:
 | 
					 | 
				
			||||||
        for color in ((255, 0, 0), (255, 0, 0, 127 if mode == "PA" else 255)):
 | 
					 | 
				
			||||||
            im = Image.new(mode, (1, 1))
 | 
					 | 
				
			||||||
            with pytest.warns(DeprecationWarning):
 | 
					 | 
				
			||||||
                access = PyAccess.new(im, False)
 | 
					 | 
				
			||||||
                assert access is not None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                access.putpixel((0, 0), color)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if len(color) == 3:
 | 
					 | 
				
			||||||
                    color += (255,)
 | 
					 | 
				
			||||||
                assert im.convert("RGBA").getpixel((0, 0)) == color
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TestImagePutPixelError(AccessTest):
 | 
					 | 
				
			||||||
    IMAGE_MODES1 = ["LA", "RGB", "RGBA", "BGR;15"]
 | 
					    IMAGE_MODES1 = ["LA", "RGB", "RGBA", "BGR;15"]
 | 
				
			||||||
    IMAGE_MODES2 = ["L", "I", "I;16"]
 | 
					    IMAGE_MODES2 = ["L", "I", "I;16"]
 | 
				
			||||||
    INVALID_TYPES = ["foo", 1.0, None]
 | 
					    INVALID_TYPES = ["foo", 1.0, None]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,5 @@ coverage:
 | 
				
			||||||
# Matches 'omit:' in .coveragerc
 | 
					# Matches 'omit:' in .coveragerc
 | 
				
			||||||
ignore:
 | 
					ignore:
 | 
				
			||||||
  - "Tests/32bit_segfault_check.py"
 | 
					  - "Tests/32bit_segfault_check.py"
 | 
				
			||||||
  - "Tests/bench_cffi_access.py"
 | 
					 | 
				
			||||||
  - "Tests/check_*.py"
 | 
					  - "Tests/check_*.py"
 | 
				
			||||||
  - "Tests/createfontdatachunk.py"
 | 
					  - "Tests/createfontdatachunk.py"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,18 +12,6 @@ Deprecated features
 | 
				
			||||||
Below are features which are considered deprecated. Where appropriate,
 | 
					Below are features which are considered deprecated. Where appropriate,
 | 
				
			||||||
a :py:exc:`DeprecationWarning` is issued.
 | 
					a :py:exc:`DeprecationWarning` is issued.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyAccess and Image.USE_CFFI_ACCESS
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. deprecated:: 10.0.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Since Pillow's C API is now faster than PyAccess on PyPy,
 | 
					 | 
				
			||||||
:py:mod:`~PIL.PyAccess` has been deprecated and will be removed in Pillow
 | 
					 | 
				
			||||||
11.0.0 (2024-10-15). Pillow's C API will now be used by default on PyPy instead.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, is
 | 
					 | 
				
			||||||
similarly deprecated.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ImageFile.raise_oserror
 | 
					ImageFile.raise_oserror
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -138,6 +126,18 @@ This class was only made as a helper to be used internally,
 | 
				
			||||||
so there is no replacement. If you need this functionality though,
 | 
					so there is no replacement. If you need this functionality though,
 | 
				
			||||||
it is a very short class that can easily be recreated in your own code.
 | 
					it is a very short class that can easily be recreated in your own code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyAccess and Image.USE_CFFI_ACCESS
 | 
				
			||||||
 | 
					~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. deprecated:: 10.0.0
 | 
				
			||||||
 | 
					.. versionremoved:: 11.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since Pillow's C API is now faster than PyAccess on PyPy, ``PyAccess`` has been
 | 
				
			||||||
 | 
					removed. Pillow's C API will now be used on PyPy instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, was
 | 
				
			||||||
 | 
					similarly removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Tk/Tcl 8.4
 | 
					Tk/Tcl 8.4
 | 
				
			||||||
~~~~~~~~~~
 | 
					~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,47 +0,0 @@
 | 
				
			||||||
.. py:module:: PIL.PyAccess
 | 
					 | 
				
			||||||
.. py:currentmodule:: PIL.PyAccess
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:py:mod:`~PIL.PyAccess` Module
 | 
					 | 
				
			||||||
==============================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The :py:mod:`~PIL.PyAccess` module provides a CFFI/Python implementation of the :ref:`PixelAccess`. This implementation is far faster on PyPy than the PixelAccess version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note:: Accessing individual pixels is fairly slow. If you are
 | 
					 | 
				
			||||||
          looping over all of the pixels in an image, there is likely
 | 
					 | 
				
			||||||
          a faster way using other parts of the Pillow API.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          :mod:`~PIL.Image`, :mod:`~PIL.ImageChops` and :mod:`~PIL.ImageOps`
 | 
					 | 
				
			||||||
          have methods for many standard operations. If you wish to perform
 | 
					 | 
				
			||||||
          a custom mapping, check out :py:meth:`~PIL.Image.Image.point`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Example
 | 
					 | 
				
			||||||
-------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The following script loads an image, accesses one pixel from it, then changes it. ::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from PIL import Image
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    with Image.open("hopper.jpg") as im:
 | 
					 | 
				
			||||||
        px = im.load()
 | 
					 | 
				
			||||||
    print(px[4, 4])
 | 
					 | 
				
			||||||
    px[4, 4] = (0, 0, 0)
 | 
					 | 
				
			||||||
    print(px[4, 4])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Results in the following::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    (23, 24, 68)
 | 
					 | 
				
			||||||
    (0, 0, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Access using negative indexes is also possible. ::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    px[-1, -1] = (0, 0, 0)
 | 
					 | 
				
			||||||
    print(px[-1, -1])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:py:class:`PyAccess` Class
 | 
					 | 
				
			||||||
--------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: PIL.PyAccess.PyAccess()
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
    :special-members: __getitem__, __setitem__
 | 
					 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,6 @@ Reference
 | 
				
			||||||
   JpegPresets
 | 
					   JpegPresets
 | 
				
			||||||
   PSDraw
 | 
					   PSDraw
 | 
				
			||||||
   PixelAccess
 | 
					   PixelAccess
 | 
				
			||||||
   PyAccess
 | 
					 | 
				
			||||||
   features
 | 
					   features
 | 
				
			||||||
   ../PIL
 | 
					   ../PIL
 | 
				
			||||||
   plugins
 | 
					   plugins
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -158,7 +158,7 @@ PyAccess and Image.USE_CFFI_ACCESS
 | 
				
			||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Since Pillow's C API is now faster than PyAccess on PyPy,
 | 
					Since Pillow's C API is now faster than PyAccess on PyPy,
 | 
				
			||||||
:py:mod:`~PIL.PyAccess` has been deprecated and will be removed in Pillow
 | 
					:py:mod:`!PyAccess` has been deprecated and will be removed in Pillow
 | 
				
			||||||
11.0.0 (2024-10-15). Pillow's C API will now be used by default on PyPy instead.
 | 
					11.0.0 (2024-10-15). Pillow's C API will now be used by default on PyPy instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, is
 | 
					``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,15 @@ This class was only made as a helper to be used internally,
 | 
				
			||||||
so there is no replacement. If you need this functionality though,
 | 
					so there is no replacement. If you need this functionality though,
 | 
				
			||||||
it is a very short class that can easily be recreated in your own code.
 | 
					it is a very short class that can easily be recreated in your own code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyAccess and Image.USE_CFFI_ACCESS
 | 
				
			||||||
 | 
					~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since Pillow's C API is now faster than PyAccess on PyPy, ``PyAccess`` has been
 | 
				
			||||||
 | 
					removed. Pillow's C API will now be used on PyPy instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``Image.USE_CFFI_ACCESS``, for switching from the C API to PyAccess, was
 | 
				
			||||||
 | 
					similarly removed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Deprecations
 | 
					Deprecations
 | 
				
			||||||
============
 | 
					============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -331,7 +331,6 @@ class GifImageFile(ImageFile.ImageFile):
 | 
				
			||||||
                    LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY
 | 
					                    LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY
 | 
				
			||||||
                    or palette
 | 
					                    or palette
 | 
				
			||||||
                ):
 | 
					                ):
 | 
				
			||||||
                    self.pyaccess = None
 | 
					 | 
				
			||||||
                    if "transparency" in self.info:
 | 
					                    if "transparency" in self.info:
 | 
				
			||||||
                        self.im.putpalettealpha(self.info["transparency"], 0)
 | 
					                        self.im.putpalettealpha(self.info["transparency"], 0)
 | 
				
			||||||
                        self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG)
 | 
					                        self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -329,7 +329,6 @@ class IcoImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        # if tile is PNG, it won't really be loaded yet
 | 
					        # if tile is PNG, it won't really be loaded yet
 | 
				
			||||||
        im.load()
 | 
					        im.load()
 | 
				
			||||||
        self.im = im.im
 | 
					        self.im = im.im
 | 
				
			||||||
        self.pyaccess = None
 | 
					 | 
				
			||||||
        self._mode = im.mode
 | 
					        self._mode = im.mode
 | 
				
			||||||
        if im.palette:
 | 
					        if im.palette:
 | 
				
			||||||
            self.palette = im.palette
 | 
					            self.palette = im.palette
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,14 +125,6 @@ except ImportError as v:
 | 
				
			||||||
    raise
 | 
					    raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
USE_CFFI_ACCESS = False
 | 
					 | 
				
			||||||
cffi: ModuleType | None
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    import cffi
 | 
					 | 
				
			||||||
except ImportError:
 | 
					 | 
				
			||||||
    cffi = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def isImageType(t: Any) -> TypeGuard[Image]:
 | 
					def isImageType(t: Any) -> TypeGuard[Image]:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Checks if an object is an image object.
 | 
					    Checks if an object is an image object.
 | 
				
			||||||
| 
						 | 
					@ -229,7 +221,7 @@ if hasattr(core, "DEFAULT_STRATEGY"):
 | 
				
			||||||
# Registries
 | 
					# Registries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if TYPE_CHECKING:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
    from . import ImageFile, PyAccess
 | 
					    from . import ImageFile
 | 
				
			||||||
ID: list[str] = []
 | 
					ID: list[str] = []
 | 
				
			||||||
OPEN: dict[
 | 
					OPEN: dict[
 | 
				
			||||||
    str,
 | 
					    str,
 | 
				
			||||||
| 
						 | 
					@ -549,7 +541,6 @@ class Image:
 | 
				
			||||||
        self.palette = None
 | 
					        self.palette = None
 | 
				
			||||||
        self.info = {}
 | 
					        self.info = {}
 | 
				
			||||||
        self.readonly = 0
 | 
					        self.readonly = 0
 | 
				
			||||||
        self.pyaccess = None
 | 
					 | 
				
			||||||
        self._exif = None
 | 
					        self._exif = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
| 
						 | 
					@ -631,7 +622,6 @@ class Image:
 | 
				
			||||||
    def _copy(self) -> None:
 | 
					    def _copy(self) -> None:
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
        self.im = self.im.copy()
 | 
					        self.im = self.im.copy()
 | 
				
			||||||
        self.pyaccess = None
 | 
					 | 
				
			||||||
        self.readonly = 0
 | 
					        self.readonly = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _ensure_mutable(self) -> None:
 | 
					    def _ensure_mutable(self) -> None:
 | 
				
			||||||
| 
						 | 
					@ -882,7 +872,7 @@ class Image:
 | 
				
			||||||
            msg = "cannot decode image data"
 | 
					            msg = "cannot decode image data"
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load(self) -> core.PixelAccess | PyAccess.PyAccess | None:
 | 
					    def load(self) -> core.PixelAccess | None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Allocates storage for the image and loads the pixel data.  In
 | 
					        Allocates storage for the image and loads the pixel data.  In
 | 
				
			||||||
        normal cases, you don't need to call this method, since the
 | 
					        normal cases, you don't need to call this method, since the
 | 
				
			||||||
| 
						 | 
					@ -895,7 +885,7 @@ class Image:
 | 
				
			||||||
        operations. See :ref:`file-handling` for more information.
 | 
					        operations. See :ref:`file-handling` for more information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :returns: An image access object.
 | 
					        :returns: An image access object.
 | 
				
			||||||
        :rtype: :py:class:`.PixelAccess` or :py:class:`.PyAccess`
 | 
					        :rtype: :py:class:`.PixelAccess`
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self.im is not None and self.palette and self.palette.dirty:
 | 
					        if self.im is not None and self.palette and self.palette.dirty:
 | 
				
			||||||
            # realize palette
 | 
					            # realize palette
 | 
				
			||||||
| 
						 | 
					@ -915,14 +905,6 @@ class Image:
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.im is not None:
 | 
					        if self.im is not None:
 | 
				
			||||||
            if cffi and USE_CFFI_ACCESS:
 | 
					 | 
				
			||||||
                if self.pyaccess:
 | 
					 | 
				
			||||||
                    return self.pyaccess
 | 
					 | 
				
			||||||
                from . import PyAccess
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.pyaccess = PyAccess.new(self, self.readonly)
 | 
					 | 
				
			||||||
                if self.pyaccess:
 | 
					 | 
				
			||||||
                    return self.pyaccess
 | 
					 | 
				
			||||||
            return self.im.pixel_access(self.readonly)
 | 
					            return self.im.pixel_access(self.readonly)
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1685,8 +1667,6 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
        if self.pyaccess:
 | 
					 | 
				
			||||||
            return self.pyaccess.getpixel(xy)
 | 
					 | 
				
			||||||
        return self.im.getpixel(tuple(xy))
 | 
					        return self.im.getpixel(tuple(xy))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getprojection(self) -> tuple[list[int], list[int]]:
 | 
					    def getprojection(self) -> tuple[list[int], list[int]]:
 | 
				
			||||||
| 
						 | 
					@ -1983,7 +1963,6 @@ class Image:
 | 
				
			||||||
                        msg = "alpha channel could not be added"
 | 
					                        msg = "alpha channel could not be added"
 | 
				
			||||||
                        raise ValueError(msg) from e  # sanity check
 | 
					                        raise ValueError(msg) from e  # sanity check
 | 
				
			||||||
                    self.im = im
 | 
					                    self.im = im
 | 
				
			||||||
                self.pyaccess = None
 | 
					 | 
				
			||||||
                self._mode = self.im.mode
 | 
					                self._mode = self.im.mode
 | 
				
			||||||
            except KeyError as e:
 | 
					            except KeyError as e:
 | 
				
			||||||
                msg = "illegal image mode"
 | 
					                msg = "illegal image mode"
 | 
				
			||||||
| 
						 | 
					@ -2101,9 +2080,6 @@ class Image:
 | 
				
			||||||
            self._copy()
 | 
					            self._copy()
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.pyaccess:
 | 
					 | 
				
			||||||
            return self.pyaccess.putpixel(xy, value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (
 | 
					        if (
 | 
				
			||||||
            self.mode in ("P", "PA")
 | 
					            self.mode in ("P", "PA")
 | 
				
			||||||
            and isinstance(value, (list, tuple))
 | 
					            and isinstance(value, (list, tuple))
 | 
				
			||||||
| 
						 | 
					@ -2768,7 +2744,6 @@ class Image:
 | 
				
			||||||
            self._mode = self.im.mode
 | 
					            self._mode = self.im.mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.readonly = 0
 | 
					        self.readonly = 0
 | 
				
			||||||
        self.pyaccess = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # FIXME: the different transform methods need further explanation
 | 
					    # FIXME: the different transform methods need further explanation
 | 
				
			||||||
    # instead of bloating the method docs, add a separate chapter.
 | 
					    # instead of bloating the method docs, add a separate chapter.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -698,7 +698,6 @@ def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image
 | 
				
			||||||
        transposed_image = image.transpose(method)
 | 
					        transposed_image = image.transpose(method)
 | 
				
			||||||
        if in_place:
 | 
					        if in_place:
 | 
				
			||||||
            image.im = transposed_image.im
 | 
					            image.im = transposed_image.im
 | 
				
			||||||
            image.pyaccess = None
 | 
					 | 
				
			||||||
            image._size = transposed_image._size
 | 
					            image._size = transposed_image._size
 | 
				
			||||||
        exif_image = image if in_place else transposed_image
 | 
					        exif_image = image if in_place else transposed_image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -851,8 +851,6 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
                self.png.rewind()
 | 
					                self.png.rewind()
 | 
				
			||||||
                self.__prepare_idat = self.__rewind_idat
 | 
					                self.__prepare_idat = self.__rewind_idat
 | 
				
			||||||
                self.im = None
 | 
					                self.im = None
 | 
				
			||||||
                if self.pyaccess:
 | 
					 | 
				
			||||||
                    self.pyaccess = None
 | 
					 | 
				
			||||||
                self.info = self.png.im_info
 | 
					                self.info = self.png.im_info
 | 
				
			||||||
                self.tile = self.png.im_tile
 | 
					                self.tile = self.png.im_tile
 | 
				
			||||||
                self.fp = self._fp
 | 
					                self.fp = self._fp
 | 
				
			||||||
| 
						 | 
					@ -1039,8 +1037,6 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
                    mask = updated.convert("RGBA")
 | 
					                    mask = updated.convert("RGBA")
 | 
				
			||||||
                self._prev_im.paste(updated, self.dispose_extent, mask)
 | 
					                self._prev_im.paste(updated, self.dispose_extent, mask)
 | 
				
			||||||
                self.im = self._prev_im
 | 
					                self.im = self._prev_im
 | 
				
			||||||
                if self.pyaccess:
 | 
					 | 
				
			||||||
                    self.pyaccess = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _getexif(self) -> dict[str, Any] | None:
 | 
					    def _getexif(self) -> dict[str, Any] | None:
 | 
				
			||||||
        if "exif" not in self.info:
 | 
					        if "exif" not in self.info:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,381 +0,0 @@
 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# The Python Imaging Library
 | 
					 | 
				
			||||||
# Pillow fork
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Python implementation of the PixelAccess Object
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Copyright (c) 1997-2009 by Secret Labs AB.  All rights reserved.
 | 
					 | 
				
			||||||
# Copyright (c) 1995-2009 by Fredrik Lundh.
 | 
					 | 
				
			||||||
# Copyright (c) 2013 Eric Soroos
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# See the README file for information on usage and redistribution
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Notes:
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
#  * Implements the pixel access object following Access.c
 | 
					 | 
				
			||||||
#  * Taking only the tuple form, which is used from python.
 | 
					 | 
				
			||||||
#    * Fill.c uses the integer form, but it's still going to use the old
 | 
					 | 
				
			||||||
#      Access.c implementation.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
from __future__ import annotations
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
from typing import TYPE_CHECKING
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from ._deprecate import deprecate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FFI: type
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    from cffi import FFI
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    defs = """
 | 
					 | 
				
			||||||
    struct Pixel_RGBA {
 | 
					 | 
				
			||||||
        unsigned char r,g,b,a;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    struct Pixel_I16 {
 | 
					 | 
				
			||||||
        unsigned char l,r;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    ffi = FFI()
 | 
					 | 
				
			||||||
    ffi.cdef(defs)
 | 
					 | 
				
			||||||
except ImportError as ex:
 | 
					 | 
				
			||||||
    # Allow error import for doc purposes, but error out when accessing
 | 
					 | 
				
			||||||
    # anything in core.
 | 
					 | 
				
			||||||
    from ._util import DeferredError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    FFI = ffi = DeferredError.new(ex)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if TYPE_CHECKING:
 | 
					 | 
				
			||||||
    from . import Image
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PyAccess:
 | 
					 | 
				
			||||||
    def __init__(self, img: Image.Image, readonly: bool = False) -> None:
 | 
					 | 
				
			||||||
        deprecate("PyAccess", 11)
 | 
					 | 
				
			||||||
        vals = dict(img.im.unsafe_ptrs)
 | 
					 | 
				
			||||||
        self.readonly = readonly
 | 
					 | 
				
			||||||
        self.image8 = ffi.cast("unsigned char **", vals["image8"])
 | 
					 | 
				
			||||||
        self.image32 = ffi.cast("int **", vals["image32"])
 | 
					 | 
				
			||||||
        self.image = ffi.cast("unsigned char **", vals["image"])
 | 
					 | 
				
			||||||
        self.xsize, self.ysize = img.im.size
 | 
					 | 
				
			||||||
        self._img = img
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Keep pointer to im object to prevent dereferencing.
 | 
					 | 
				
			||||||
        self._im = img.im
 | 
					 | 
				
			||||||
        if self._im.mode in ("P", "PA"):
 | 
					 | 
				
			||||||
            self._palette = img.palette
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Debugging is polluting test traces, only useful here
 | 
					 | 
				
			||||||
        # when hacking on PyAccess
 | 
					 | 
				
			||||||
        # logger.debug("%s", vals)
 | 
					 | 
				
			||||||
        self._post_init()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self) -> None:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __setitem__(
 | 
					 | 
				
			||||||
        self,
 | 
					 | 
				
			||||||
        xy: tuple[int, int] | list[int],
 | 
					 | 
				
			||||||
        color: float | tuple[int, ...] | list[int],
 | 
					 | 
				
			||||||
    ) -> None:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Modifies the pixel at x,y. The color is given as a single
 | 
					 | 
				
			||||||
        numerical value for single band images, and a tuple for
 | 
					 | 
				
			||||||
        multi-band images. In addition to this, RGB and RGBA tuples
 | 
					 | 
				
			||||||
        are accepted for P and PA images.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param xy: The pixel coordinate, given as (x, y). See
 | 
					 | 
				
			||||||
           :ref:`coordinate-system`.
 | 
					 | 
				
			||||||
        :param color: The pixel value.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if self.readonly:
 | 
					 | 
				
			||||||
            msg = "Attempt to putpixel a read only image"
 | 
					 | 
				
			||||||
            raise ValueError(msg)
 | 
					 | 
				
			||||||
        (x, y) = xy
 | 
					 | 
				
			||||||
        if x < 0:
 | 
					 | 
				
			||||||
            x = self.xsize + x
 | 
					 | 
				
			||||||
        if y < 0:
 | 
					 | 
				
			||||||
            y = self.ysize + y
 | 
					 | 
				
			||||||
        (x, y) = self.check_xy((x, y))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (
 | 
					 | 
				
			||||||
            self._im.mode in ("P", "PA")
 | 
					 | 
				
			||||||
            and isinstance(color, (list, tuple))
 | 
					 | 
				
			||||||
            and len(color) in [3, 4]
 | 
					 | 
				
			||||||
        ):
 | 
					 | 
				
			||||||
            # RGB or RGBA value for a P or PA image
 | 
					 | 
				
			||||||
            if self._im.mode == "PA":
 | 
					 | 
				
			||||||
                alpha = color[3] if len(color) == 4 else 255
 | 
					 | 
				
			||||||
                color = color[:3]
 | 
					 | 
				
			||||||
            palette_index = self._palette.getcolor(color, self._img)
 | 
					 | 
				
			||||||
            color = (palette_index, alpha) if self._im.mode == "PA" else palette_index
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return self.set_pixel(x, y, color)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __getitem__(self, xy: tuple[int, int] | list[int]) -> float | tuple[int, ...]:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Returns the pixel at x,y. The pixel is returned as a single
 | 
					 | 
				
			||||||
        value for single band images or a tuple for multiple band
 | 
					 | 
				
			||||||
        images
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        :param xy: The pixel coordinate, given as (x, y). See
 | 
					 | 
				
			||||||
          :ref:`coordinate-system`.
 | 
					 | 
				
			||||||
        :returns: a pixel value for single band images, a tuple of
 | 
					 | 
				
			||||||
          pixel values for multiband images.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        (x, y) = xy
 | 
					 | 
				
			||||||
        if x < 0:
 | 
					 | 
				
			||||||
            x = self.xsize + x
 | 
					 | 
				
			||||||
        if y < 0:
 | 
					 | 
				
			||||||
            y = self.ysize + y
 | 
					 | 
				
			||||||
        (x, y) = self.check_xy((x, y))
 | 
					 | 
				
			||||||
        return self.get_pixel(x, y)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    putpixel = __setitem__
 | 
					 | 
				
			||||||
    getpixel = __getitem__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]:
 | 
					 | 
				
			||||||
        (x, y) = xy
 | 
					 | 
				
			||||||
        if not (0 <= x < self.xsize and 0 <= y < self.ysize):
 | 
					 | 
				
			||||||
            msg = "pixel location out of range"
 | 
					 | 
				
			||||||
            raise ValueError(msg)
 | 
					 | 
				
			||||||
        return xy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> float | tuple[int, ...]:
 | 
					 | 
				
			||||||
        raise NotImplementedError()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(
 | 
					 | 
				
			||||||
        self, x: int, y: int, color: float | tuple[int, ...] | list[int]
 | 
					 | 
				
			||||||
    ) -> None:
 | 
					 | 
				
			||||||
        raise NotImplementedError()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccess32_2(PyAccess):
 | 
					 | 
				
			||||||
    """PA, LA, stored in first and last bytes of a 32 bit word"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> tuple[int, int]:
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        return pixel.r, pixel.a
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        # tuple
 | 
					 | 
				
			||||||
        pixel.r = min(color[0], 255)
 | 
					 | 
				
			||||||
        pixel.a = min(color[1], 255)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccess32_3(PyAccess):
 | 
					 | 
				
			||||||
    """RGB and friends, stored in the first three bytes of a 32 bit word"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> tuple[int, int, int]:
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        return pixel.r, pixel.g, pixel.b
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        # tuple
 | 
					 | 
				
			||||||
        pixel.r = min(color[0], 255)
 | 
					 | 
				
			||||||
        pixel.g = min(color[1], 255)
 | 
					 | 
				
			||||||
        pixel.b = min(color[2], 255)
 | 
					 | 
				
			||||||
        pixel.a = 255
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccess32_4(PyAccess):
 | 
					 | 
				
			||||||
    """RGBA etc, all 4 bytes of a 32 bit word"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]:
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        return pixel.r, pixel.g, pixel.b, pixel.a
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        # tuple
 | 
					 | 
				
			||||||
        pixel.r = min(color[0], 255)
 | 
					 | 
				
			||||||
        pixel.g = min(color[1], 255)
 | 
					 | 
				
			||||||
        pixel.b = min(color[2], 255)
 | 
					 | 
				
			||||||
        pixel.a = min(color[3], 255)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccess8(PyAccess):
 | 
					 | 
				
			||||||
    """1, L, P, 8 bit images stored as uint8"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = self.image8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> int:
 | 
					 | 
				
			||||||
        return self.pixels[y][x]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            # integer
 | 
					 | 
				
			||||||
            self.pixels[y][x] = min(color, 255)
 | 
					 | 
				
			||||||
        except TypeError:
 | 
					 | 
				
			||||||
            # tuple
 | 
					 | 
				
			||||||
            self.pixels[y][x] = min(color[0], 255)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccessI16_N(PyAccess):
 | 
					 | 
				
			||||||
    """I;16 access, native bitendian without conversion"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("unsigned short **", self.image)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> int:
 | 
					 | 
				
			||||||
        return self.pixels[y][x]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            # integer
 | 
					 | 
				
			||||||
            self.pixels[y][x] = min(color, 65535)
 | 
					 | 
				
			||||||
        except TypeError:
 | 
					 | 
				
			||||||
            # tuple
 | 
					 | 
				
			||||||
            self.pixels[y][x] = min(color[0], 65535)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccessI16_L(PyAccess):
 | 
					 | 
				
			||||||
    """I;16L access, with conversion"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("struct Pixel_I16 **", self.image)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> int:
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        return pixel.l + pixel.r * 256
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            color = min(color, 65535)
 | 
					 | 
				
			||||||
        except TypeError:
 | 
					 | 
				
			||||||
            color = min(color[0], 65535)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pixel.l = color & 0xFF
 | 
					 | 
				
			||||||
        pixel.r = color >> 8
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccessI16_B(PyAccess):
 | 
					 | 
				
			||||||
    """I;16B access, with conversion"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("struct Pixel_I16 **", self.image)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> int:
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        return pixel.l * 256 + pixel.r
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        pixel = self.pixels[y][x]
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            color = min(color, 65535)
 | 
					 | 
				
			||||||
        except Exception:
 | 
					 | 
				
			||||||
            color = min(color[0], 65535)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        pixel.l = color >> 8
 | 
					 | 
				
			||||||
        pixel.r = color & 0xFF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccessI32_N(PyAccess):
 | 
					 | 
				
			||||||
    """Signed Int32 access, native endian"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = self.image32
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> int:
 | 
					 | 
				
			||||||
        return self.pixels[y][x]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        self.pixels[y][x] = color
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccessI32_Swap(PyAccess):
 | 
					 | 
				
			||||||
    """I;32L/B access, with byteswapping conversion"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = self.image32
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def reverse(self, i):
 | 
					 | 
				
			||||||
        orig = ffi.new("int *", i)
 | 
					 | 
				
			||||||
        chars = ffi.cast("unsigned char *", orig)
 | 
					 | 
				
			||||||
        chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0]
 | 
					 | 
				
			||||||
        return ffi.cast("int *", chars)[0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> int:
 | 
					 | 
				
			||||||
        return self.reverse(self.pixels[y][x])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        self.pixels[y][x] = self.reverse(color)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class _PyAccessF(PyAccess):
 | 
					 | 
				
			||||||
    """32 bit float access"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _post_init(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        self.pixels = ffi.cast("float **", self.image32)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_pixel(self, x: int, y: int) -> float:
 | 
					 | 
				
			||||||
        return self.pixels[y][x]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def set_pixel(self, x, y, color):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            # not a tuple
 | 
					 | 
				
			||||||
            self.pixels[y][x] = color
 | 
					 | 
				
			||||||
        except TypeError:
 | 
					 | 
				
			||||||
            # tuple
 | 
					 | 
				
			||||||
            self.pixels[y][x] = color[0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mode_map = {
 | 
					 | 
				
			||||||
    "1": _PyAccess8,
 | 
					 | 
				
			||||||
    "L": _PyAccess8,
 | 
					 | 
				
			||||||
    "P": _PyAccess8,
 | 
					 | 
				
			||||||
    "I;16N": _PyAccessI16_N,
 | 
					 | 
				
			||||||
    "LA": _PyAccess32_2,
 | 
					 | 
				
			||||||
    "La": _PyAccess32_2,
 | 
					 | 
				
			||||||
    "PA": _PyAccess32_2,
 | 
					 | 
				
			||||||
    "RGB": _PyAccess32_3,
 | 
					 | 
				
			||||||
    "LAB": _PyAccess32_3,
 | 
					 | 
				
			||||||
    "HSV": _PyAccess32_3,
 | 
					 | 
				
			||||||
    "YCbCr": _PyAccess32_3,
 | 
					 | 
				
			||||||
    "RGBA": _PyAccess32_4,
 | 
					 | 
				
			||||||
    "RGBa": _PyAccess32_4,
 | 
					 | 
				
			||||||
    "RGBX": _PyAccess32_4,
 | 
					 | 
				
			||||||
    "CMYK": _PyAccess32_4,
 | 
					 | 
				
			||||||
    "F": _PyAccessF,
 | 
					 | 
				
			||||||
    "I": _PyAccessI32_N,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if sys.byteorder == "little":
 | 
					 | 
				
			||||||
    mode_map["I;16"] = _PyAccessI16_N
 | 
					 | 
				
			||||||
    mode_map["I;16L"] = _PyAccessI16_N
 | 
					 | 
				
			||||||
    mode_map["I;16B"] = _PyAccessI16_B
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mode_map["I;32L"] = _PyAccessI32_N
 | 
					 | 
				
			||||||
    mode_map["I;32B"] = _PyAccessI32_Swap
 | 
					 | 
				
			||||||
else:
 | 
					 | 
				
			||||||
    mode_map["I;16"] = _PyAccessI16_L
 | 
					 | 
				
			||||||
    mode_map["I;16L"] = _PyAccessI16_L
 | 
					 | 
				
			||||||
    mode_map["I;16B"] = _PyAccessI16_N
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mode_map["I;32L"] = _PyAccessI32_Swap
 | 
					 | 
				
			||||||
    mode_map["I;32B"] = _PyAccessI32_N
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def new(img: Image.Image, readonly: bool = False) -> PyAccess | None:
 | 
					 | 
				
			||||||
    access_type = mode_map.get(img.mode, None)
 | 
					 | 
				
			||||||
    if not access_type:
 | 
					 | 
				
			||||||
        logger.debug("PyAccess Not Implemented: %s", img.mode)
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
    return access_type(img, readonly)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -45,8 +45,6 @@ def deprecate(
 | 
				
			||||||
    elif when <= int(__version__.split(".")[0]):
 | 
					    elif when <= int(__version__.split(".")[0]):
 | 
				
			||||||
        msg = f"{deprecated} {is_} deprecated and should be removed."
 | 
					        msg = f"{deprecated} {is_} deprecated and should be removed."
 | 
				
			||||||
        raise RuntimeError(msg)
 | 
					        raise RuntimeError(msg)
 | 
				
			||||||
    elif when == 11:
 | 
					 | 
				
			||||||
        removed = "Pillow 11 (2024-10-15)"
 | 
					 | 
				
			||||||
    elif when == 12:
 | 
					    elif when == 12:
 | 
				
			||||||
        removed = "Pillow 12 (2025-10-15)"
 | 
					        removed = "Pillow 12 (2025-10-15)"
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user