mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Added type hints to Image.__init__()
This commit is contained in:
		
							parent
							
								
									d8447de24d
								
							
						
					
					
						commit
						8f1157a61d
					
				| 
						 | 
					@ -24,6 +24,7 @@ def test_imageops_box_blur() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def box_blur(image: Image.Image, radius: float = 1, n: int = 1) -> Image.Image:
 | 
					def box_blur(image: Image.Image, radius: float = 1, n: int = 1) -> Image.Image:
 | 
				
			||||||
 | 
					    assert image.im is not None
 | 
				
			||||||
    return image._new(image.im.box_blur((radius, radius), n))
 | 
					    return image._new(image.im.box_blur((radius, radius), n))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_wrong_args(self) -> None:
 | 
					    def test_wrong_args(self) -> None:
 | 
				
			||||||
        im = Image.new("RGB", (10, 10), 0)
 | 
					        im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(ValueError, match="filter"):
 | 
					        with pytest.raises(ValueError, match="filter"):
 | 
				
			||||||
            im.im.color_lut_3d(
 | 
					            im.im.color_lut_3d(
 | 
				
			||||||
| 
						 | 
					@ -107,6 +108,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_correct_args(self) -> None:
 | 
					    def test_correct_args(self) -> None:
 | 
				
			||||||
        im = Image.new("RGB", (10, 10), 0)
 | 
					        im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        im.im.color_lut_3d(
 | 
					        im.im.color_lut_3d(
 | 
				
			||||||
            "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					            "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
| 
						 | 
					@ -143,51 +145,60 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
    def test_wrong_mode(self) -> None:
 | 
					    def test_wrong_mode(self) -> None:
 | 
				
			||||||
        with pytest.raises(ValueError, match="wrong mode"):
 | 
					        with pytest.raises(ValueError, match="wrong mode"):
 | 
				
			||||||
            im = Image.new("L", (10, 10), 0)
 | 
					            im = Image.new("L", (10, 10), 0)
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            im.im.color_lut_3d(
 | 
					            im.im.color_lut_3d(
 | 
				
			||||||
                "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					                "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(ValueError, match="wrong mode"):
 | 
					        with pytest.raises(ValueError, match="wrong mode"):
 | 
				
			||||||
            im = Image.new("RGB", (10, 10), 0)
 | 
					            im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            im.im.color_lut_3d(
 | 
					            im.im.color_lut_3d(
 | 
				
			||||||
                "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					                "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(ValueError, match="wrong mode"):
 | 
					        with pytest.raises(ValueError, match="wrong mode"):
 | 
				
			||||||
            im = Image.new("L", (10, 10), 0)
 | 
					            im = Image.new("L", (10, 10), 0)
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            im.im.color_lut_3d(
 | 
					            im.im.color_lut_3d(
 | 
				
			||||||
                "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					                "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(ValueError, match="wrong mode"):
 | 
					        with pytest.raises(ValueError, match="wrong mode"):
 | 
				
			||||||
            im = Image.new("RGB", (10, 10), 0)
 | 
					            im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            im.im.color_lut_3d(
 | 
					            im.im.color_lut_3d(
 | 
				
			||||||
                "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					                "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        with pytest.raises(ValueError, match="wrong mode"):
 | 
					        with pytest.raises(ValueError, match="wrong mode"):
 | 
				
			||||||
            im = Image.new("RGB", (10, 10), 0)
 | 
					            im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            im.im.color_lut_3d(
 | 
					            im.im.color_lut_3d(
 | 
				
			||||||
                "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
 | 
					                "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_correct_mode(self) -> None:
 | 
					    def test_correct_mode(self) -> None:
 | 
				
			||||||
        im = Image.new("RGBA", (10, 10), 0)
 | 
					        im = Image.new("RGBA", (10, 10), 0)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        im.im.color_lut_3d(
 | 
					        im.im.color_lut_3d(
 | 
				
			||||||
            "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					            "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        im = Image.new("RGBA", (10, 10), 0)
 | 
					        im = Image.new("RGBA", (10, 10), 0)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        im.im.color_lut_3d(
 | 
					        im.im.color_lut_3d(
 | 
				
			||||||
            "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
 | 
					            "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        im = Image.new("RGB", (10, 10), 0)
 | 
					        im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        im.im.color_lut_3d(
 | 
					        im.im.color_lut_3d(
 | 
				
			||||||
            "HSV", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
					            "HSV", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        im = Image.new("RGB", (10, 10), 0)
 | 
					        im = Image.new("RGB", (10, 10), 0)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        im.im.color_lut_3d(
 | 
					        im.im.color_lut_3d(
 | 
				
			||||||
            "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
 | 
					            "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					@ -202,6 +213,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
                g.transpose(Image.Transpose.ROTATE_180),
 | 
					                g.transpose(Image.Transpose.ROTATE_180),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Fast test with small cubes
 | 
					        # Fast test with small cubes
 | 
				
			||||||
        for size in [2, 3, 5, 7, 11, 16, 17]:
 | 
					        for size in [2, 3, 5, 7, 11, 16, 17]:
 | 
				
			||||||
| 
						 | 
					@ -238,6 +250,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
                g.transpose(Image.Transpose.ROTATE_180),
 | 
					                g.transpose(Image.Transpose.ROTATE_180),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Red channel copied to alpha
 | 
					        # Red channel copied to alpha
 | 
				
			||||||
        assert_image_equal(
 | 
					        assert_image_equal(
 | 
				
			||||||
| 
						 | 
					@ -262,6 +275,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
                g.transpose(Image.Transpose.ROTATE_270),
 | 
					                g.transpose(Image.Transpose.ROTATE_270),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert_image_equal(
 | 
					        assert_image_equal(
 | 
				
			||||||
            im,
 | 
					            im,
 | 
				
			||||||
| 
						 | 
					@ -284,6 +298,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
                g.transpose(Image.Transpose.ROTATE_180),
 | 
					                g.transpose(Image.Transpose.ROTATE_180),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Reverse channels by splitting and using table
 | 
					        # Reverse channels by splitting and using table
 | 
				
			||||||
        # fmt: off
 | 
					        # fmt: off
 | 
				
			||||||
| 
						 | 
					@ -309,6 +324,7 @@ class TestColorLut3DCoreAPI:
 | 
				
			||||||
                g.transpose(Image.Transpose.ROTATE_180),
 | 
					                g.transpose(Image.Transpose.ROTATE_180),
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # fmt: off
 | 
					        # fmt: off
 | 
				
			||||||
        transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR,
 | 
					        transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -250,6 +250,7 @@ class TestFileWebp:
 | 
				
			||||||
        temp_file = str(tmp_path / "temp.webp")
 | 
					        temp_file = str(tmp_path / "temp.webp")
 | 
				
			||||||
        im = Image.new("RGBA", (1, 1)).convert("P")
 | 
					        im = Image.new("RGBA", (1, 1)).convert("P")
 | 
				
			||||||
        assert im.mode == "P"
 | 
					        assert im.mode == "P"
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        assert im.palette.mode == "RGBA"
 | 
					        assert im.palette.mode == "RGBA"
 | 
				
			||||||
        im.save(temp_file)
 | 
					        im.save(temp_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -695,11 +695,13 @@ class TestImage:
 | 
				
			||||||
            image: Image.Image,
 | 
					            image: Image.Image,
 | 
				
			||||||
            palette_result: ImagePalette.ImagePalette | None = None,
 | 
					            palette_result: ImagePalette.ImagePalette | None = None,
 | 
				
			||||||
        ) -> None:
 | 
					        ) -> None:
 | 
				
			||||||
 | 
					            assert image.im is not None
 | 
				
			||||||
            new_image = base_image._new(image.im)
 | 
					            new_image = base_image._new(image.im)
 | 
				
			||||||
            assert new_image.mode == image.mode
 | 
					            assert new_image.mode == image.mode
 | 
				
			||||||
            assert new_image.size == image.size
 | 
					            assert new_image.size == image.size
 | 
				
			||||||
            assert new_image.info == base_image.info
 | 
					            assert new_image.info == base_image.info
 | 
				
			||||||
            if palette_result is not None:
 | 
					            if palette_result is not None:
 | 
				
			||||||
 | 
					                assert new_image.palette is not None
 | 
				
			||||||
                assert new_image.palette.tobytes() == palette_result.tobytes()
 | 
					                assert new_image.palette.tobytes() == palette_result.tobytes()
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                assert new_image.palette is None
 | 
					                assert new_image.palette is None
 | 
				
			||||||
| 
						 | 
					@ -990,12 +992,14 @@ class TestImage:
 | 
				
			||||||
        # P mode with RGBA palette
 | 
					        # P mode with RGBA palette
 | 
				
			||||||
        im = Image.new("RGBA", (1, 1)).convert("P")
 | 
					        im = Image.new("RGBA", (1, 1)).convert("P")
 | 
				
			||||||
        assert im.mode == "P"
 | 
					        assert im.mode == "P"
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        assert im.palette.mode == "RGBA"
 | 
					        assert im.palette.mode == "RGBA"
 | 
				
			||||||
        assert im.has_transparency_data
 | 
					        assert im.has_transparency_data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_apply_transparency(self) -> None:
 | 
					    def test_apply_transparency(self) -> None:
 | 
				
			||||||
        im = Image.new("P", (1, 1))
 | 
					        im = Image.new("P", (1, 1))
 | 
				
			||||||
        im.putpalette((0, 0, 0, 1, 1, 1))
 | 
					        im.putpalette((0, 0, 0, 1, 1, 1))
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1}
 | 
					        assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Test that no transformation is applied without transparency
 | 
					        # Test that no transformation is applied without transparency
 | 
				
			||||||
| 
						 | 
					@ -1013,13 +1017,16 @@ class TestImage:
 | 
				
			||||||
        im.putpalette((0, 0, 0, 255, 1, 1, 1, 128), "RGBA")
 | 
					        im.putpalette((0, 0, 0, 255, 1, 1, 1, 128), "RGBA")
 | 
				
			||||||
        im.info["transparency"] = 0
 | 
					        im.info["transparency"] = 0
 | 
				
			||||||
        im.apply_transparency()
 | 
					        im.apply_transparency()
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 128): 1}
 | 
					        assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 128): 1}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Test that transparency bytes are applied
 | 
					        # Test that transparency bytes are applied
 | 
				
			||||||
        with Image.open("Tests/images/pil123p.png") as im:
 | 
					        with Image.open("Tests/images/pil123p.png") as im:
 | 
				
			||||||
            assert isinstance(im.info["transparency"], bytes)
 | 
					            assert isinstance(im.info["transparency"], bytes)
 | 
				
			||||||
 | 
					            assert im.palette is not None
 | 
				
			||||||
            assert im.palette.colors[(27, 35, 6)] == 24
 | 
					            assert im.palette.colors[(27, 35, 6)] == 24
 | 
				
			||||||
            im.apply_transparency()
 | 
					            im.apply_transparency()
 | 
				
			||||||
 | 
					            assert im.palette is not None
 | 
				
			||||||
            assert im.palette.colors[(27, 35, 6, 214)] == 24
 | 
					            assert im.palette.colors[(27, 35, 6, 214)] == 24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_constants(self) -> None:
 | 
					    def test_constants(self) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,4 +113,6 @@ def test_fromarray_palette() -> None:
 | 
				
			||||||
    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
 | 
				
			||||||
 | 
					    assert out.palette is not None
 | 
				
			||||||
 | 
					    assert out.im is not None
 | 
				
			||||||
    assert len(out.palette.colors) == len(out.im.getpalette()) / 3
 | 
					    assert len(out.palette.colors) == len(out.im.getpalette()) / 3
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -218,6 +218,7 @@ def test_trns_RGB(tmp_path: Path) -> None:
 | 
				
			||||||
def test_l_macro_rounding(convert_mode: str) -> None:
 | 
					def test_l_macro_rounding(convert_mode: str) -> None:
 | 
				
			||||||
    for mode in ("P", "PA"):
 | 
					    for mode in ("P", "PA"):
 | 
				
			||||||
        im = Image.new(mode, (1, 1))
 | 
					        im = Image.new(mode, (1, 1))
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        im.palette.getcolor((0, 1, 2))
 | 
					        im.palette.getcolor((0, 1, 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        converted_im = im.convert(convert_mode)
 | 
					        converted_im = im.convert(convert_mode)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,7 +137,9 @@ def test_builtinfilter_p() -> None:
 | 
				
			||||||
    builtin_filter = ImageFilter.BuiltinFilter()
 | 
					    builtin_filter = ImageFilter.BuiltinFilter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(ValueError):
 | 
					    with pytest.raises(ValueError):
 | 
				
			||||||
        builtin_filter.filter(hopper("P").im)
 | 
					        im = hopper("P").im
 | 
				
			||||||
 | 
					        assert im is not None
 | 
				
			||||||
 | 
					        builtin_filter.filter(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_kernel_not_enough_coefficients() -> None:
 | 
					def test_kernel_not_enough_coefficients() -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,4 +8,5 @@ def test_sanity() -> None:
 | 
				
			||||||
    type_repr = repr(type(im.getim()))
 | 
					    type_repr = repr(type(im.getim()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert "PyCapsule" in type_repr
 | 
					    assert "PyCapsule" in type_repr
 | 
				
			||||||
 | 
					    assert im.im is not None
 | 
				
			||||||
    assert isinstance(im.im.id, int)
 | 
					    assert isinstance(im.im.id, int)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,6 +86,7 @@ def test_rgba_palette(mode: str, palette: tuple[int, ...]) -> None:
 | 
				
			||||||
    im = Image.new("P", (1, 1))
 | 
					    im = Image.new("P", (1, 1))
 | 
				
			||||||
    im.putpalette(palette, mode)
 | 
					    im.putpalette(palette, mode)
 | 
				
			||||||
    assert im.getpalette() == [1, 2, 3]
 | 
					    assert im.getpalette() == [1, 2, 3]
 | 
				
			||||||
 | 
					    assert im.palette is not None
 | 
				
			||||||
    assert im.palette.colors == {(1, 2, 3, 4): 0}
 | 
					    assert im.palette.colors == {(1, 2, 3, 4): 0}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@ def test_quantize_no_dither() -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    converted = image.quantize(dither=Image.Dither.NONE, palette=palette)
 | 
					    converted = image.quantize(dither=Image.Dither.NONE, palette=palette)
 | 
				
			||||||
    assert converted.mode == "P"
 | 
					    assert converted.mode == "P"
 | 
				
			||||||
 | 
					    assert converted.palette is not None
 | 
				
			||||||
    assert converted.palette.palette == palette.palette.palette
 | 
					    assert converted.palette.palette == palette.palette.palette
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,6 +82,7 @@ def test_quantize_no_dither2() -> None:
 | 
				
			||||||
    palette.putpalette(data)
 | 
					    palette.putpalette(data)
 | 
				
			||||||
    quantized = im.quantize(dither=Image.Dither.NONE, palette=palette)
 | 
					    quantized = im.quantize(dither=Image.Dither.NONE, palette=palette)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert quantized.palette is not None
 | 
				
			||||||
    assert tuple(quantized.palette.palette) == data
 | 
					    assert tuple(quantized.palette.palette) == data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    px = quantized.load()
 | 
					    px = quantized.load()
 | 
				
			||||||
| 
						 | 
					@ -117,6 +119,7 @@ def test_colors() -> None:
 | 
				
			||||||
    im = hopper()
 | 
					    im = hopper()
 | 
				
			||||||
    colors = 2
 | 
					    colors = 2
 | 
				
			||||||
    converted = im.quantize(colors)
 | 
					    converted = im.quantize(colors)
 | 
				
			||||||
 | 
					    assert converted.palette is not None
 | 
				
			||||||
    assert len(converted.palette.palette) == colors * len("RGB")
 | 
					    assert len(converted.palette.palette) == colors * len("RGB")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,6 +150,7 @@ def test_palette(method: Image.Quantize, color: tuple[int, ...]) -> None:
 | 
				
			||||||
    converted = im.quantize(method=method)
 | 
					    converted = im.quantize(method=method)
 | 
				
			||||||
    converted_px = converted.load()
 | 
					    converted_px = converted.load()
 | 
				
			||||||
    assert converted_px is not None
 | 
					    assert converted_px is not None
 | 
				
			||||||
 | 
					    assert converted.palette is not None
 | 
				
			||||||
    assert converted_px[0, 0] == converted.palette.colors[color]
 | 
					    assert converted_px[0, 0] == converted.palette.colors[color]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ class TestImagingResampleVulnerability:
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            with pytest.raises(MemoryError):
 | 
					            with pytest.raises(MemoryError):
 | 
				
			||||||
                # any resampling filter will do here
 | 
					                # any resampling filter will do here
 | 
				
			||||||
 | 
					                assert im.im is not None
 | 
				
			||||||
                im.im.resize((xsize, ysize), Image.Resampling.BILINEAR)
 | 
					                im.im.resize((xsize, ysize), Image.Resampling.BILINEAR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_invalid_size(self) -> None:
 | 
					    def test_invalid_size(self) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ class TestImagingCoreResize:
 | 
				
			||||||
    ) -> Image.Image:
 | 
					    ) -> Image.Image:
 | 
				
			||||||
        # Image class independent version of resize.
 | 
					        # Image class independent version of resize.
 | 
				
			||||||
        im.load()
 | 
					        im.load()
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        return im._new(im.im.resize(size, f))
 | 
					        return im._new(im.im.resize(size, f))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @pytest.mark.parametrize(
 | 
					    @pytest.mark.parametrize(
 | 
				
			||||||
| 
						 | 
					@ -37,6 +38,8 @@ class TestImagingCoreResize:
 | 
				
			||||||
        r = self.resize(im, (15, 12), Image.Resampling.NEAREST)
 | 
					        r = self.resize(im, (15, 12), Image.Resampling.NEAREST)
 | 
				
			||||||
        assert r.mode == mode
 | 
					        assert r.mode == mode
 | 
				
			||||||
        assert r.size == (15, 12)
 | 
					        assert r.size == (15, 12)
 | 
				
			||||||
 | 
					        assert r.im is not None
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        assert r.im.bands == im.im.bands
 | 
					        assert r.im.bands == im.im.bands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_convolution_modes(self) -> None:
 | 
					    def test_convolution_modes(self) -> None:
 | 
				
			||||||
| 
						 | 
					@ -51,6 +54,8 @@ class TestImagingCoreResize:
 | 
				
			||||||
            r = self.resize(im, (15, 12), Image.Resampling.BILINEAR)
 | 
					            r = self.resize(im, (15, 12), Image.Resampling.BILINEAR)
 | 
				
			||||||
            assert r.mode == mode
 | 
					            assert r.mode == mode
 | 
				
			||||||
            assert r.size == (15, 12)
 | 
					            assert r.size == (15, 12)
 | 
				
			||||||
 | 
					            assert r.im is not None
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            assert r.im.bands == im.im.bands
 | 
					            assert r.im.bands == im.im.bands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @pytest.mark.parametrize(
 | 
					    @pytest.mark.parametrize(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,6 +325,8 @@ def test_wrong_mode() -> None:
 | 
				
			||||||
    lut = ImageMorph.LutBuilder(op_name="corner").build_lut()
 | 
					    lut = ImageMorph.LutBuilder(op_name="corner").build_lut()
 | 
				
			||||||
    imrgb = Image.new("RGB", (10, 10))
 | 
					    imrgb = Image.new("RGB", (10, 10))
 | 
				
			||||||
    iml = Image.new("L", (10, 10))
 | 
					    iml = Image.new("L", (10, 10))
 | 
				
			||||||
 | 
					    assert imrgb.im is not None
 | 
				
			||||||
 | 
					    assert iml.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(RuntimeError):
 | 
					    with pytest.raises(RuntimeError):
 | 
				
			||||||
        _imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id)
 | 
					        _imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,10 +98,12 @@ def test_blur_accuracy(test_images: dict[str, ImageFile.ImageFile]) -> None:
 | 
				
			||||||
        (4, 3, 2),
 | 
					        (4, 3, 2),
 | 
				
			||||||
        (4, 2, 2),
 | 
					        (4, 2, 2),
 | 
				
			||||||
    ]:
 | 
					    ]:
 | 
				
			||||||
 | 
					        assert i.im is not None
 | 
				
			||||||
        assert i.im.getpixel((x, y))[c] >= 250
 | 
					        assert i.im.getpixel((x, y))[c] >= 250
 | 
				
			||||||
    # Fuzzy match.
 | 
					    # Fuzzy match.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def gp(x: int, y: int) -> tuple[int, ...]:
 | 
					    def gp(x: int, y: int) -> tuple[int, ...]:
 | 
				
			||||||
 | 
					        assert i.im is not None
 | 
				
			||||||
        return i.im.getpixel((x, y))
 | 
					        return i.im.getpixel((x, y))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert 236 <= gp(7, 4)[0] <= 239
 | 
					    assert 236 <= gp(7, 4)[0] <= 239
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,18 +7,21 @@ from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_setmode() -> None:
 | 
					def test_setmode() -> None:
 | 
				
			||||||
    im = Image.new("L", (1, 1), 255)
 | 
					    im = Image.new("L", (1, 1), 255)
 | 
				
			||||||
 | 
					    assert im.im is not None
 | 
				
			||||||
    im.im.setmode("1")
 | 
					    im.im.setmode("1")
 | 
				
			||||||
    assert im.im.getpixel((0, 0)) == 255
 | 
					    assert im.im.getpixel((0, 0)) == 255
 | 
				
			||||||
    im.im.setmode("L")
 | 
					    im.im.setmode("L")
 | 
				
			||||||
    assert im.im.getpixel((0, 0)) == 255
 | 
					    assert im.im.getpixel((0, 0)) == 255
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    im = Image.new("1", (1, 1), 1)
 | 
					    im = Image.new("1", (1, 1), 1)
 | 
				
			||||||
 | 
					    assert im.im is not None
 | 
				
			||||||
    im.im.setmode("L")
 | 
					    im.im.setmode("L")
 | 
				
			||||||
    assert im.im.getpixel((0, 0)) == 255
 | 
					    assert im.im.getpixel((0, 0)) == 255
 | 
				
			||||||
    im.im.setmode("1")
 | 
					    im.im.setmode("1")
 | 
				
			||||||
    assert im.im.getpixel((0, 0)) == 255
 | 
					    assert im.im.getpixel((0, 0)) == 255
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    im = Image.new("RGB", (1, 1), (1, 2, 3))
 | 
					    im = Image.new("RGB", (1, 1), (1, 2, 3))
 | 
				
			||||||
 | 
					    assert im.im is not None
 | 
				
			||||||
    im.im.setmode("RGB")
 | 
					    im.im.setmode("RGB")
 | 
				
			||||||
    assert im.im.getpixel((0, 0)) == (1, 2, 3)
 | 
					    assert im.im.getpixel((0, 0)) == (1, 2, 3)
 | 
				
			||||||
    im.im.setmode("RGBA")
 | 
					    im.im.setmode("RGBA")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -467,6 +467,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
    magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2"
 | 
					    magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2"
 | 
				
			||||||
    fp.write(magic)
 | 
					    fp.write(magic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert im.palette is not None
 | 
				
			||||||
    fp.write(struct.pack("<i", 1))  # Uncompressed or DirectX compression
 | 
					    fp.write(struct.pack("<i", 1))  # Uncompressed or DirectX compression
 | 
				
			||||||
    fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
 | 
					    fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
 | 
				
			||||||
    fp.write(struct.pack("<b", 1 if im.palette.mode == "RGBA" else 0))
 | 
					    fp.write(struct.pack("<b", 1 if im.palette.mode == "RGBA" else 0))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -170,6 +170,8 @@ class BmpImageFile(ImageFile.ImageFile):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # ------------------ Special case : header is reported 40, which
 | 
					        # ------------------ Special case : header is reported 40, which
 | 
				
			||||||
        # ---------------------- is shorter than real size for bpp >= 16
 | 
					        # ---------------------- is shorter than real size for bpp >= 16
 | 
				
			||||||
 | 
					        assert isinstance(file_info["width"], int)
 | 
				
			||||||
 | 
					        assert isinstance(file_info["height"], int)
 | 
				
			||||||
        self._size = file_info["width"], file_info["height"]
 | 
					        self._size = file_info["width"], file_info["height"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # ------- If color count was not found in the header, compute from bits
 | 
					        # ------- If color count was not found in the header, compute from bits
 | 
				
			||||||
| 
						 | 
					@ -443,6 +445,7 @@ def _save(
 | 
				
			||||||
    elif im.mode == "L":
 | 
					    elif im.mode == "L":
 | 
				
			||||||
        palette = b"".join(o8(i) * 4 for i in range(256))
 | 
					        palette = b"".join(o8(i) * 4 for i in range(256))
 | 
				
			||||||
    elif im.mode == "P":
 | 
					    elif im.mode == "P":
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        palette = im.im.getpalette("RGB", "BGRX")
 | 
					        palette = im.im.getpalette("RGB", "BGRX")
 | 
				
			||||||
        colors = len(palette) // 4
 | 
					        colors = len(palette) // 4
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ def Ghostscript(
 | 
				
			||||||
    fp: IO[bytes],
 | 
					    fp: IO[bytes],
 | 
				
			||||||
    scale: int = 1,
 | 
					    scale: int = 1,
 | 
				
			||||||
    transparency: bool = False,
 | 
					    transparency: bool = False,
 | 
				
			||||||
) -> Image.Image:
 | 
					) -> Image.core.ImagingCore:
 | 
				
			||||||
    """Render an image using Ghostscript"""
 | 
					    """Render an image using Ghostscript"""
 | 
				
			||||||
    global gs_binary
 | 
					    global gs_binary
 | 
				
			||||||
    if not has_ghostscript():
 | 
					    if not has_ghostscript():
 | 
				
			||||||
| 
						 | 
					@ -161,6 +161,7 @@ def Ghostscript(
 | 
				
			||||||
        except OSError:
 | 
					        except OSError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert out_im.im is not None
 | 
				
			||||||
    im = out_im.im.copy()
 | 
					    im = out_im.im.copy()
 | 
				
			||||||
    out_im.close()
 | 
					    out_im.close()
 | 
				
			||||||
    return im
 | 
					    return im
 | 
				
			||||||
| 
						 | 
					@ -190,7 +191,6 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        self.fp.seek(offset)
 | 
					        self.fp.seek(offset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._mode = "RGB"
 | 
					        self._mode = "RGB"
 | 
				
			||||||
        self._size = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        byte_arr = bytearray(255)
 | 
					        byte_arr = bytearray(255)
 | 
				
			||||||
        bytes_mv = memoryview(byte_arr)
 | 
					        bytes_mv = memoryview(byte_arr)
 | 
				
			||||||
| 
						 | 
					@ -228,7 +228,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            if k == "BoundingBox":
 | 
					            if k == "BoundingBox":
 | 
				
			||||||
                if v == "(atend)":
 | 
					                if v == "(atend)":
 | 
				
			||||||
                    reading_trailer_comments = True
 | 
					                    reading_trailer_comments = True
 | 
				
			||||||
                elif not self._size or (trailer_reached and reading_trailer_comments):
 | 
					                elif not self.tile or (trailer_reached and reading_trailer_comments):
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        # Note: The DSC spec says that BoundingBox
 | 
					                        # Note: The DSC spec says that BoundingBox
 | 
				
			||||||
                        # fields should be integers, but some drivers
 | 
					                        # fields should be integers, but some drivers
 | 
				
			||||||
| 
						 | 
					@ -346,7 +346,7 @@ class EpsImageFile(ImageFile.ImageFile):
 | 
				
			||||||
                trailer_reached = True
 | 
					                trailer_reached = True
 | 
				
			||||||
            bytes_read = 0
 | 
					            bytes_read = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self._size:
 | 
					        if not self.tile:
 | 
				
			||||||
            msg = "cannot determine EPS bounding box"
 | 
					            msg = "cannot determine EPS bounding box"
 | 
				
			||||||
            raise OSError(msg)
 | 
					            raise OSError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,6 +81,8 @@ class FpxImageFile(ImageFile.ImageFile):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # size (highest resolution)
 | 
					        # size (highest resolution)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert isinstance(prop[0x1000002], int)
 | 
				
			||||||
 | 
					        assert isinstance(prop[0x1000003], int)
 | 
				
			||||||
        self._size = prop[0x1000002], prop[0x1000003]
 | 
					        self._size = prop[0x1000002], prop[0x1000003]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        size = max(self.size)
 | 
					        size = max(self.size)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -307,6 +307,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        self.tile = []
 | 
					        self.tile = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.dispose:
 | 
					        if self.dispose:
 | 
				
			||||||
 | 
					            assert self.im is not None
 | 
				
			||||||
            self.im.paste(self.dispose, self.dispose_extent)
 | 
					            self.im.paste(self.dispose, self.dispose_extent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._frame_palette = palette if palette is not None else self.global_palette
 | 
					        self._frame_palette = palette if palette is not None else self.global_palette
 | 
				
			||||||
| 
						 | 
					@ -320,17 +321,21 @@ class GifImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self._mode = "L"
 | 
					                self._mode = "L"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not palette and self.global_palette:
 | 
					            if palette:
 | 
				
			||||||
 | 
					                self.palette = palette
 | 
				
			||||||
 | 
					            elif self.global_palette:
 | 
				
			||||||
                from copy import copy
 | 
					                from copy import copy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                palette = copy(self.global_palette)
 | 
					                self.palette = copy(self.global_palette)
 | 
				
			||||||
            self.palette = palette
 | 
					            else:
 | 
				
			||||||
 | 
					                self.palette = None
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if self.mode == "P":
 | 
					            if self.mode == "P":
 | 
				
			||||||
                if (
 | 
					                if (
 | 
				
			||||||
                    LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY
 | 
					                    LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY
 | 
				
			||||||
                    or palette
 | 
					                    or palette
 | 
				
			||||||
                ):
 | 
					                ):
 | 
				
			||||||
 | 
					                    assert self.im is not 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)
 | 
				
			||||||
| 
						 | 
					@ -432,6 +437,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            self._prev_im = self.im
 | 
					            self._prev_im = self.im
 | 
				
			||||||
            if self._frame_palette:
 | 
					            if self._frame_palette:
 | 
				
			||||||
                self.im = Image.core.fill("P", self.size, self._frame_transparency or 0)
 | 
					                self.im = Image.core.fill("P", self.size, self._frame_transparency or 0)
 | 
				
			||||||
 | 
					                assert self.im is not None
 | 
				
			||||||
                self.im.putpalette("RGB", *self._frame_palette.getdata())
 | 
					                self.im.putpalette("RGB", *self._frame_palette.getdata())
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.im = None
 | 
					                self.im = None
 | 
				
			||||||
| 
						 | 
					@ -441,6 +447,7 @@ class GifImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        super().load_prepare()
 | 
					        super().load_prepare()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load_end(self) -> None:
 | 
					    def load_end(self) -> None:
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if self.__frame == 0:
 | 
					        if self.__frame == 0:
 | 
				
			||||||
            if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS:
 | 
					            if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS:
 | 
				
			||||||
                if self._frame_transparency is not None:
 | 
					                if self._frame_transparency is not None:
 | 
				
			||||||
| 
						 | 
					@ -495,6 +502,7 @@ def _normalize_mode(im: Image.Image) -> Image.Image:
 | 
				
			||||||
        return im
 | 
					        return im
 | 
				
			||||||
    if Image.getmodebase(im.mode) == "RGB":
 | 
					    if Image.getmodebase(im.mode) == "RGB":
 | 
				
			||||||
        im = im.convert("P", palette=Image.Palette.ADAPTIVE)
 | 
					        im = im.convert("P", palette=Image.Palette.ADAPTIVE)
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        if im.palette.mode == "RGBA":
 | 
					        if im.palette.mode == "RGBA":
 | 
				
			||||||
            for rgba in im.palette.colors:
 | 
					            for rgba in im.palette.colors:
 | 
				
			||||||
                if rgba[3] == 0:
 | 
					                if rgba[3] == 0:
 | 
				
			||||||
| 
						 | 
					@ -531,16 +539,17 @@ def _normalize_palette(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if im.mode == "P":
 | 
					    if im.mode == "P":
 | 
				
			||||||
        if not source_palette:
 | 
					        if not source_palette:
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            source_palette = im.im.getpalette("RGB")[:768]
 | 
					            source_palette = im.im.getpalette("RGB")[:768]
 | 
				
			||||||
    else:  # L-mode
 | 
					    else:  # L-mode
 | 
				
			||||||
        if not source_palette:
 | 
					        if not source_palette:
 | 
				
			||||||
            source_palette = bytearray(i // 3 for i in range(768))
 | 
					            source_palette = bytearray(i // 3 for i in range(768))
 | 
				
			||||||
        im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette)
 | 
					        im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    used_palette_colors: list[int] | None
 | 
					 | 
				
			||||||
    if palette:
 | 
					 | 
				
			||||||
        used_palette_colors = []
 | 
					 | 
				
			||||||
    assert source_palette is not None
 | 
					    assert source_palette is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if palette:
 | 
				
			||||||
 | 
					        used_palette_colors: list[int | None] = []
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        for i in range(0, len(source_palette), 3):
 | 
					        for i in range(0, len(source_palette), 3):
 | 
				
			||||||
            source_color = tuple(source_palette[i : i + 3])
 | 
					            source_color = tuple(source_palette[i : i + 3])
 | 
				
			||||||
            index = im.palette.colors.get(source_color)
 | 
					            index = im.palette.colors.get(source_color)
 | 
				
			||||||
| 
						 | 
					@ -553,20 +562,25 @@ def _normalize_palette(
 | 
				
			||||||
                    if j not in used_palette_colors:
 | 
					                    if j not in used_palette_colors:
 | 
				
			||||||
                        used_palette_colors[i] = j
 | 
					                        used_palette_colors[i] = j
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
        im = im.remap_palette(used_palette_colors)
 | 
					        dest_map: list[int] = []
 | 
				
			||||||
 | 
					        for index in used_palette_colors:
 | 
				
			||||||
 | 
					            assert index is not None
 | 
				
			||||||
 | 
					            dest_map.append(index)
 | 
				
			||||||
 | 
					        im = im.remap_palette(dest_map)
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        used_palette_colors = _get_optimize(im, info)
 | 
					        optimized_palette_colors = _get_optimize(im, info)
 | 
				
			||||||
        if used_palette_colors is not None:
 | 
					        if optimized_palette_colors is not None:
 | 
				
			||||||
            im = im.remap_palette(used_palette_colors, source_palette)
 | 
					            im = im.remap_palette(optimized_palette_colors, source_palette)
 | 
				
			||||||
            if "transparency" in info:
 | 
					            if "transparency" in info:
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    info["transparency"] = used_palette_colors.index(
 | 
					                    info["transparency"] = optimized_palette_colors.index(
 | 
				
			||||||
                        info["transparency"]
 | 
					                        info["transparency"]
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                except ValueError:
 | 
					                except ValueError:
 | 
				
			||||||
                    del info["transparency"]
 | 
					                    del info["transparency"]
 | 
				
			||||||
            return im
 | 
					            return im
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert im.palette is not None
 | 
				
			||||||
    im.palette.palette = source_palette
 | 
					    im.palette.palette = source_palette
 | 
				
			||||||
    return im
 | 
					    return im
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -578,6 +592,7 @@ def _write_single_frame(
 | 
				
			||||||
) -> None:
 | 
					) -> None:
 | 
				
			||||||
    im_out = _normalize_mode(im)
 | 
					    im_out = _normalize_mode(im)
 | 
				
			||||||
    for k, v in im_out.info.items():
 | 
					    for k, v in im_out.info.items():
 | 
				
			||||||
 | 
					        if isinstance(k, str):
 | 
				
			||||||
            im.encoderinfo.setdefault(k, v)
 | 
					            im.encoderinfo.setdefault(k, v)
 | 
				
			||||||
    im_out = _normalize_palette(im_out, palette, im.encoderinfo)
 | 
					    im_out = _normalize_palette(im_out, palette, im.encoderinfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -630,6 +645,7 @@ def _write_multiple_frames(
 | 
				
			||||||
                for k, v in im_frame.info.items():
 | 
					                for k, v in im_frame.info.items():
 | 
				
			||||||
                    if k == "transparency":
 | 
					                    if k == "transparency":
 | 
				
			||||||
                        continue
 | 
					                        continue
 | 
				
			||||||
 | 
					                    if isinstance(k, str):
 | 
				
			||||||
                        im.encoderinfo.setdefault(k, v)
 | 
					                        im.encoderinfo.setdefault(k, v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            encoderinfo = im.encoderinfo.copy()
 | 
					            encoderinfo = im.encoderinfo.copy()
 | 
				
			||||||
| 
						 | 
					@ -660,10 +676,12 @@ def _write_multiple_frames(
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                        background = _get_background(im_frame, color)
 | 
					                        background = _get_background(im_frame, color)
 | 
				
			||||||
                        background_im = Image.new("P", im_frame.size, background)
 | 
					                        background_im = Image.new("P", im_frame.size, background)
 | 
				
			||||||
 | 
					                        assert im_frames[0].im.palette is not None
 | 
				
			||||||
                        background_im.putpalette(im_frames[0].im.palette)
 | 
					                        background_im.putpalette(im_frames[0].im.palette)
 | 
				
			||||||
                    bbox = _getbbox(background_im, im_frame)[1]
 | 
					                    bbox = _getbbox(background_im, im_frame)[1]
 | 
				
			||||||
                elif encoderinfo.get("optimize") and im_frame.mode != "1":
 | 
					                elif encoderinfo.get("optimize") and im_frame.mode != "1":
 | 
				
			||||||
                    if "transparency" not in encoderinfo:
 | 
					                    if "transparency" not in encoderinfo:
 | 
				
			||||||
 | 
					                        assert im_frame.palette is not None
 | 
				
			||||||
                        try:
 | 
					                        try:
 | 
				
			||||||
                            encoderinfo["transparency"] = (
 | 
					                            encoderinfo["transparency"] = (
 | 
				
			||||||
                                im_frame.palette._new_color_index(im_frame)
 | 
					                                im_frame.palette._new_color_index(im_frame)
 | 
				
			||||||
| 
						 | 
					@ -901,6 +919,7 @@ def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None:
 | 
				
			||||||
            if optimise or max(used_palette_colors) >= len(used_palette_colors):
 | 
					            if optimise or max(used_palette_colors) >= len(used_palette_colors):
 | 
				
			||||||
                return used_palette_colors
 | 
					                return used_palette_colors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert im.palette is not None
 | 
				
			||||||
            num_palette_colors = len(im.palette.palette) // Image.getmodebands(
 | 
					            num_palette_colors = len(im.palette.palette) // Image.getmodebands(
 | 
				
			||||||
                im.palette.mode
 | 
					                im.palette.mode
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
| 
						 | 
					@ -950,7 +969,7 @@ def _get_palette_bytes(im: Image.Image) -> bytes:
 | 
				
			||||||
    :param im: Image object
 | 
					    :param im: Image object
 | 
				
			||||||
    :returns: Bytes, len<=768 suitable for inclusion in gif header
 | 
					    :returns: Bytes, len<=768 suitable for inclusion in gif header
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    return im.palette.palette if im.palette else b""
 | 
					    return bytes(im.palette.palette) if im.palette else b""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _get_background(
 | 
					def _get_background(
 | 
				
			||||||
| 
						 | 
					@ -963,6 +982,7 @@ def _get_background(
 | 
				
			||||||
            # WebPImagePlugin stores an RGBA value in info["background"]
 | 
					            # WebPImagePlugin stores an RGBA value in info["background"]
 | 
				
			||||||
            # So it must be converted to the same format as GifImagePlugin's
 | 
					            # So it must be converted to the same format as GifImagePlugin's
 | 
				
			||||||
            # info["background"] - a global color table index
 | 
					            # info["background"] - a global color table index
 | 
				
			||||||
 | 
					            assert im.palette is not None
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                background = im.palette.getcolor(info_background, im)
 | 
					                background = im.palette.getcolor(info_background, im)
 | 
				
			||||||
            except ValueError as e:
 | 
					            except ValueError as e:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,6 +92,7 @@ def read_32(
 | 
				
			||||||
                msg = f"Error reading channel [{repr(bytesleft)} left]"
 | 
					                msg = f"Error reading channel [{repr(bytesleft)} left]"
 | 
				
			||||||
                raise SyntaxError(msg)
 | 
					                raise SyntaxError(msg)
 | 
				
			||||||
            band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1)
 | 
					            band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1)
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            im.im.putband(band.im, band_ix)
 | 
					            im.im.putband(band.im, band_ix)
 | 
				
			||||||
    return {"RGB": im}
 | 
					    return {"RGB": im}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,6 +353,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
        fp.write(b"Lut: 1\r\n")
 | 
					        fp.write(b"Lut: 1\r\n")
 | 
				
			||||||
    fp.write(b"\000" * (511 - fp.tell()) + b"\032")
 | 
					    fp.write(b"\000" * (511 - fp.tell()) + b"\032")
 | 
				
			||||||
    if im.mode in ["P", "PA"]:
 | 
					    if im.mode in ["P", "PA"]:
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        im_palette = im.im.getpalette("RGB", "RGB;L")
 | 
					        im_palette = im.im.getpalette("RGB", "RGB;L")
 | 
				
			||||||
        colors = len(im_palette) // 3
 | 
					        colors = len(im_palette) // 3
 | 
				
			||||||
        palette = b""
 | 
					        palette = b""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -532,16 +532,16 @@ class Image:
 | 
				
			||||||
    format_description: str | None = None
 | 
					    format_description: str | None = None
 | 
				
			||||||
    _close_exclusive_fp_after_loading = True
 | 
					    _close_exclusive_fp_after_loading = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self) -> None:
 | 
				
			||||||
        # FIXME: take "new" parameters / other image?
 | 
					        # FIXME: take "new" parameters / other image?
 | 
				
			||||||
        # FIXME: turn mode and size into delegating properties?
 | 
					        # FIXME: turn mode and size into delegating properties?
 | 
				
			||||||
        self.im = None
 | 
					        self.im: core.ImagingCore | None = None
 | 
				
			||||||
        self._mode = ""
 | 
					        self._mode = ""
 | 
				
			||||||
        self._size = (0, 0)
 | 
					        self._size = (0, 0)
 | 
				
			||||||
        self.palette = None
 | 
					        self.palette: ImagePalette.ImagePalette | None = None
 | 
				
			||||||
        self.info = {}
 | 
					        self.info: dict[str | tuple[int, int], Any] = {}
 | 
				
			||||||
        self.readonly = 0
 | 
					        self.readonly = 0
 | 
				
			||||||
        self._exif = None
 | 
					        self._exif: Exif | None = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def width(self) -> int:
 | 
					    def width(self) -> int:
 | 
				
			||||||
| 
						 | 
					@ -617,10 +617,11 @@ class Image:
 | 
				
			||||||
        # Instead of simply setting to None, we're setting up a
 | 
					        # Instead of simply setting to None, we're setting up a
 | 
				
			||||||
        # deferred error that will better explain that the core image
 | 
					        # deferred error that will better explain that the core image
 | 
				
			||||||
        # object is gone.
 | 
					        # object is gone.
 | 
				
			||||||
        self.im = DeferredError(ValueError("Operation on closed image"))
 | 
					        self.im = DeferredError(ValueError("Operation on closed image"))  # type: ignore[assignment]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _copy(self) -> None:
 | 
					    def _copy(self) -> None:
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        if self.im:
 | 
				
			||||||
            self.im = self.im.copy()
 | 
					            self.im = self.im.copy()
 | 
				
			||||||
        self.readonly = 0
 | 
					        self.readonly = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -648,6 +649,7 @@ class Image:
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not format or format == "PPM":
 | 
					        if not format or format == "PPM":
 | 
				
			||||||
 | 
					            assert self.im is not None
 | 
				
			||||||
            self.im.save_ppm(filename)
 | 
					            self.im.save_ppm(filename)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.save(filename, format, **options)
 | 
					            self.save(filename, format, **options)
 | 
				
			||||||
| 
						 | 
					@ -796,6 +798,7 @@ class Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # unpack data
 | 
					        # unpack data
 | 
				
			||||||
        e = _getencoder(self.mode, encoder_name, encoder_args)
 | 
					        e = _getencoder(self.mode, encoder_name, encoder_args)
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        e.setimage(self.im)
 | 
					        e.setimage(self.im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bufsize = max(65536, self.size[0] * 4)  # see RawEncode.c
 | 
					        bufsize = max(65536, self.size[0] * 4)  # see RawEncode.c
 | 
				
			||||||
| 
						 | 
					@ -862,6 +865,7 @@ class Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # unpack data
 | 
					        # unpack data
 | 
				
			||||||
        d = _getdecoder(self.mode, decoder_name, decoder_args)
 | 
					        d = _getdecoder(self.mode, decoder_name, decoder_args)
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        d.setimage(self.im)
 | 
					        d.setimage(self.im)
 | 
				
			||||||
        s = d.decode(data)
 | 
					        s = d.decode(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -975,6 +979,7 @@ class Image:
 | 
				
			||||||
            deprecate(mode, 12)
 | 
					            deprecate(mode, 12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        has_transparency = "transparency" in self.info
 | 
					        has_transparency = "transparency" in self.info
 | 
				
			||||||
        if not mode and self.mode == "P":
 | 
					        if not mode and self.mode == "P":
 | 
				
			||||||
| 
						 | 
					@ -1045,9 +1050,11 @@ class Image:
 | 
				
			||||||
                    # use existing conversions
 | 
					                    # use existing conversions
 | 
				
			||||||
                    trns_im = new(self.mode, (1, 1))
 | 
					                    trns_im = new(self.mode, (1, 1))
 | 
				
			||||||
                    if self.mode == "P":
 | 
					                    if self.mode == "P":
 | 
				
			||||||
 | 
					                        assert self.palette is not None
 | 
				
			||||||
                        trns_im.putpalette(self.palette)
 | 
					                        trns_im.putpalette(self.palette)
 | 
				
			||||||
                        if isinstance(t, tuple):
 | 
					                        if isinstance(t, tuple):
 | 
				
			||||||
                            err = "Couldn't allocate a palette color for transparency"
 | 
					                            err = "Couldn't allocate a palette color for transparency"
 | 
				
			||||||
 | 
					                            assert trns_im.palette is not None
 | 
				
			||||||
                            try:
 | 
					                            try:
 | 
				
			||||||
                                t = trns_im.palette.getcolor(t, self)
 | 
					                                t = trns_im.palette.getcolor(t, self)
 | 
				
			||||||
                            except ValueError as e:
 | 
					                            except ValueError as e:
 | 
				
			||||||
| 
						 | 
					@ -1087,6 +1094,7 @@ class Image:
 | 
				
			||||||
            new_im = self._new(im)
 | 
					            new_im = self._new(im)
 | 
				
			||||||
            from . import ImagePalette
 | 
					            from . import ImagePalette
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            assert new_im.im is not None
 | 
				
			||||||
            new_im.palette = ImagePalette.ImagePalette(
 | 
					            new_im.palette = ImagePalette.ImagePalette(
 | 
				
			||||||
                "RGB", new_im.im.getpalette("RGB")
 | 
					                "RGB", new_im.im.getpalette("RGB")
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
| 
						 | 
					@ -1149,7 +1157,9 @@ class Image:
 | 
				
			||||||
        if trns is not None:
 | 
					        if trns is not None:
 | 
				
			||||||
            if new_im.mode == "P" and new_im.palette:
 | 
					            if new_im.mode == "P" and new_im.palette:
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    new_im.info["transparency"] = new_im.palette.getcolor(trns, new_im)
 | 
					                    new_im.info["transparency"] = new_im.palette.getcolor(
 | 
				
			||||||
 | 
					                        cast(tuple[int, ...], trns), new_im  # trns was converted to RGB
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
                except ValueError as e:
 | 
					                except ValueError as e:
 | 
				
			||||||
                    del new_im.info["transparency"]
 | 
					                    del new_im.info["transparency"]
 | 
				
			||||||
                    if str(e) != "cannot allocate more than 256 colors":
 | 
					                    if str(e) != "cannot allocate more than 256 colors":
 | 
				
			||||||
| 
						 | 
					@ -1216,6 +1226,7 @@ class Image:
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if palette:
 | 
					        if palette:
 | 
				
			||||||
            # use palette from reference image
 | 
					            # use palette from reference image
 | 
				
			||||||
            palette.load()
 | 
					            palette.load()
 | 
				
			||||||
| 
						 | 
					@ -1227,6 +1238,7 @@ class Image:
 | 
				
			||||||
                raise ValueError(msg)
 | 
					                raise ValueError(msg)
 | 
				
			||||||
            im = self.im.convert("P", dither, palette.im)
 | 
					            im = self.im.convert("P", dither, palette.im)
 | 
				
			||||||
            new_im = self._new(im)
 | 
					            new_im = self._new(im)
 | 
				
			||||||
 | 
					            assert palette.palette is not None
 | 
				
			||||||
            new_im.palette = palette.palette.copy()
 | 
					            new_im.palette = palette.palette.copy()
 | 
				
			||||||
            return new_im
 | 
					            return new_im
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1253,6 +1265,7 @@ class Image:
 | 
				
			||||||
        :returns: An :py:class:`~PIL.Image.Image` object.
 | 
					        :returns: An :py:class:`~PIL.Image.Image` object.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.copy())
 | 
					        return self._new(self.im.copy())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __copy__ = copy
 | 
					    __copy__ = copy
 | 
				
			||||||
| 
						 | 
					@ -1281,6 +1294,7 @@ class Image:
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self._crop(self.im, box))
 | 
					        return self._new(self._crop(self.im, box))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _crop(
 | 
					    def _crop(
 | 
				
			||||||
| 
						 | 
					@ -1334,6 +1348,7 @@ class Image:
 | 
				
			||||||
        if ymargin is None:
 | 
					        if ymargin is None:
 | 
				
			||||||
            ymargin = xmargin
 | 
					            ymargin = xmargin
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.expand(xmargin, ymargin))
 | 
					        return self._new(self.im.expand(xmargin, ymargin))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if TYPE_CHECKING:
 | 
					    if TYPE_CHECKING:
 | 
				
			||||||
| 
						 | 
					@ -1358,6 +1373,7 @@ class Image:
 | 
				
			||||||
            raise TypeError(msg)
 | 
					            raise TypeError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        multiband = isinstance(filter, ImageFilter.MultibandFilter)
 | 
					        multiband = isinstance(filter, ImageFilter.MultibandFilter)
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if self.im.bands == 1 or multiband:
 | 
					        if self.im.bands == 1 or multiband:
 | 
				
			||||||
            return self._new(filter.filter(self.im))
 | 
					            return self._new(filter.filter(self.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1393,6 +1409,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self.im.getbbox(alpha_only)
 | 
					        return self.im.getbbox(alpha_only)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getcolors(
 | 
					    def getcolors(
 | 
				
			||||||
| 
						 | 
					@ -1412,6 +1429,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if self.mode in ("1", "L", "P"):
 | 
					        if self.mode in ("1", "L", "P"):
 | 
				
			||||||
            h = self.im.histogram()
 | 
					            h = self.im.histogram()
 | 
				
			||||||
            out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]]
 | 
					            out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]]
 | 
				
			||||||
| 
						 | 
					@ -1439,6 +1457,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if band is not None:
 | 
					        if band is not None:
 | 
				
			||||||
            return self.im.getband(band)
 | 
					            return self.im.getband(band)
 | 
				
			||||||
        return self.im  # could be abused
 | 
					        return self.im  # could be abused
 | 
				
			||||||
| 
						 | 
					@ -1454,6 +1473,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if self.im.bands > 1:
 | 
					        if self.im.bands > 1:
 | 
				
			||||||
            return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands))
 | 
					            return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands))
 | 
				
			||||||
        return self.im.getextrema()
 | 
					        return self.im.getextrema()
 | 
				
			||||||
| 
						 | 
					@ -1592,6 +1612,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self.im.ptr
 | 
					        return self.im.ptr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None:
 | 
					    def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None:
 | 
				
			||||||
| 
						 | 
					@ -1608,6 +1629,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            mode = self.im.getpalettemode()
 | 
					            mode = self.im.getpalettemode()
 | 
				
			||||||
        except ValueError:
 | 
					        except ValueError:
 | 
				
			||||||
| 
						 | 
					@ -1628,11 +1650,15 @@ class Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :returns: A boolean.
 | 
					        :returns: A boolean.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return (
 | 
					        if (
 | 
				
			||||||
            self.mode in ("LA", "La", "PA", "RGBA", "RGBa")
 | 
					            self.mode in ("LA", "La", "PA", "RGBA", "RGBa")
 | 
				
			||||||
            or (self.mode == "P" and self.palette.mode.endswith("A"))
 | 
					 | 
				
			||||||
            or "transparency" in self.info
 | 
					            or "transparency" in self.info
 | 
				
			||||||
        )
 | 
					        ):
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        if self.mode == "P":
 | 
				
			||||||
 | 
					            assert self.palette is not None
 | 
				
			||||||
 | 
					            return self.palette.mode.endswith("A")
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def apply_transparency(self) -> None:
 | 
					    def apply_transparency(self) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -1671,6 +1697,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        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]]:
 | 
				
			||||||
| 
						 | 
					@ -1682,6 +1709,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        x, y = self.im.getprojection()
 | 
					        x, y = self.im.getprojection()
 | 
				
			||||||
        return list(x), list(y)
 | 
					        return list(x), list(y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1709,6 +1737,7 @@ class Image:
 | 
				
			||||||
        :returns: A list containing pixel counts.
 | 
					        :returns: A list containing pixel counts.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if mask:
 | 
					        if mask:
 | 
				
			||||||
            mask.load()
 | 
					            mask.load()
 | 
				
			||||||
            return self.im.histogram((0, 0), mask.im)
 | 
					            return self.im.histogram((0, 0), mask.im)
 | 
				
			||||||
| 
						 | 
					@ -1737,6 +1766,7 @@ class Image:
 | 
				
			||||||
        :returns: A float value representing the image entropy
 | 
					        :returns: A float value representing the image entropy
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if mask:
 | 
					        if mask:
 | 
				
			||||||
            mask.load()
 | 
					            mask.load()
 | 
				
			||||||
            return self.im.entropy((0, 0), mask.im)
 | 
					            return self.im.entropy((0, 0), mask.im)
 | 
				
			||||||
| 
						 | 
					@ -1815,26 +1845,32 @@ class Image:
 | 
				
			||||||
                raise ValueError(msg)
 | 
					                raise ValueError(msg)
 | 
				
			||||||
            box += (box[0] + size[0], box[1] + size[1])
 | 
					            box += (box[0] + size[0], box[1] + size[1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        source: core.ImagingCore | str | float | tuple[float, ...]
 | 
				
			||||||
        if isinstance(im, str):
 | 
					        if isinstance(im, str):
 | 
				
			||||||
            from . import ImageColor
 | 
					            from . import ImageColor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            im = ImageColor.getcolor(im, self.mode)
 | 
					            source = ImageColor.getcolor(im, self.mode)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        elif isImageType(im):
 | 
					        elif isImageType(im):
 | 
				
			||||||
            im.load()
 | 
					            im.load()
 | 
				
			||||||
            if self.mode != im.mode:
 | 
					            if self.mode != im.mode:
 | 
				
			||||||
                if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"):
 | 
					                if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"):
 | 
				
			||||||
                    # should use an adapter for this!
 | 
					                    # should use an adapter for this!
 | 
				
			||||||
                    im = im.convert(self.mode)
 | 
					                    im = im.convert(self.mode)
 | 
				
			||||||
            im = im.im
 | 
					            assert im.im is not None
 | 
				
			||||||
 | 
					            source = im.im
 | 
				
			||||||
 | 
					        elif isinstance(im, tuple):
 | 
				
			||||||
 | 
					            source = im
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            source = cast(float, im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._ensure_mutable()
 | 
					        self._ensure_mutable()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if mask:
 | 
					        if mask:
 | 
				
			||||||
            mask.load()
 | 
					            mask.load()
 | 
				
			||||||
            self.im.paste(im, box, mask.im)
 | 
					            self.im.paste(source, box, mask.im)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.im.paste(im, box)
 | 
					            self.im.paste(source, box)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def alpha_composite(
 | 
					    def alpha_composite(
 | 
				
			||||||
        self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0)
 | 
					        self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0)
 | 
				
			||||||
| 
						 | 
					@ -1924,6 +1960,7 @@ class Image:
 | 
				
			||||||
        if isinstance(lut, ImagePointHandler):
 | 
					        if isinstance(lut, ImagePointHandler):
 | 
				
			||||||
            return lut.point(self)
 | 
					            return lut.point(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if callable(lut):
 | 
					        if callable(lut):
 | 
				
			||||||
            # if it isn't a list, it should be a function
 | 
					            # if it isn't a list, it should be a function
 | 
				
			||||||
            if self.mode in ("I", "I;16", "F"):
 | 
					            if self.mode in ("I", "I;16", "F"):
 | 
				
			||||||
| 
						 | 
					@ -1960,17 +1997,20 @@ class Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.mode not in ("LA", "PA", "RGBA"):
 | 
					        if self.mode not in ("LA", "PA", "RGBA"):
 | 
				
			||||||
            # attempt to promote self to a matching alpha mode
 | 
					            # attempt to promote self to a matching alpha mode
 | 
				
			||||||
 | 
					            assert self.im is not None
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                mode = getmodebase(self.mode) + "A"
 | 
					                mode = getmodebase(self.mode) + "A"
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    self.im.setmode(mode)
 | 
					                    self.im.setmode(mode)
 | 
				
			||||||
                except (AttributeError, ValueError) as e:
 | 
					                except (AttributeError, ValueError) as e:
 | 
				
			||||||
                    # do things the hard way
 | 
					                    # do things the hard way
 | 
				
			||||||
 | 
					                    assert self.im is not None
 | 
				
			||||||
                    im = self.im.convert(mode)
 | 
					                    im = self.im.convert(mode)
 | 
				
			||||||
                    if im.mode not in ("LA", "PA", "RGBA"):
 | 
					                    if im.mode not in ("LA", "PA", "RGBA"):
 | 
				
			||||||
                        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
 | 
				
			||||||
 | 
					                    assert self.im is not 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"
 | 
				
			||||||
| 
						 | 
					@ -1981,6 +2021,7 @@ class Image:
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            band = 3
 | 
					            band = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if isImageType(alpha):
 | 
					        if isImageType(alpha):
 | 
				
			||||||
            # alpha layer
 | 
					            # alpha layer
 | 
				
			||||||
            if alpha.mode not in ("1", "L"):
 | 
					            if alpha.mode not in ("1", "L"):
 | 
				
			||||||
| 
						 | 
					@ -2023,6 +2064,7 @@ class Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._ensure_mutable()
 | 
					        self._ensure_mutable()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        self.im.putdata(data, scale, offset)
 | 
					        self.im.putdata(data, scale, offset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def putpalette(
 | 
					    def putpalette(
 | 
				
			||||||
| 
						 | 
					@ -2105,8 +2147,10 @@ class Image:
 | 
				
			||||||
            if self.mode == "PA":
 | 
					            if self.mode == "PA":
 | 
				
			||||||
                alpha = value[3] if len(value) == 4 else 255
 | 
					                alpha = value[3] if len(value) == 4 else 255
 | 
				
			||||||
                value = value[:3]
 | 
					                value = value[:3]
 | 
				
			||||||
            palette_index = self.palette.getcolor(value, self)
 | 
					            assert self.palette is not None
 | 
				
			||||||
 | 
					            palette_index = self.palette.getcolor(tuple(value), self)
 | 
				
			||||||
            value = (palette_index, alpha) if self.mode == "PA" else palette_index
 | 
					            value = (palette_index, alpha) if self.mode == "PA" else palette_index
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self.im.putpixel(xy, value)
 | 
					        return self.im.putpixel(xy, value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def remap_palette(
 | 
					    def remap_palette(
 | 
				
			||||||
| 
						 | 
					@ -2133,6 +2177,7 @@ class Image:
 | 
				
			||||||
        if source_palette is None:
 | 
					        if source_palette is None:
 | 
				
			||||||
            if self.mode == "P":
 | 
					            if self.mode == "P":
 | 
				
			||||||
                self.load()
 | 
					                self.load()
 | 
				
			||||||
 | 
					                assert self.im is not None
 | 
				
			||||||
                palette_mode = self.im.getpalettemode()
 | 
					                palette_mode = self.im.getpalettemode()
 | 
				
			||||||
                if palette_mode == "RGBA":
 | 
					                if palette_mode == "RGBA":
 | 
				
			||||||
                    bands = 4
 | 
					                    bands = 4
 | 
				
			||||||
| 
						 | 
					@ -2180,6 +2225,7 @@ class Image:
 | 
				
			||||||
        # m_im.putpalette(mapping_palette, 'L')  # converts to 'P'
 | 
					        # m_im.putpalette(mapping_palette, 'L')  # converts to 'P'
 | 
				
			||||||
        # or just force it.
 | 
					        # or just force it.
 | 
				
			||||||
        # UNDONE -- this is part of the general issue with palettes
 | 
					        # UNDONE -- this is part of the general issue with palettes
 | 
				
			||||||
 | 
					        assert m_im.im is not None
 | 
				
			||||||
        m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes())
 | 
					        m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m_im = m_im.convert("L")
 | 
					        m_im = m_im.convert("L")
 | 
				
			||||||
| 
						 | 
					@ -2325,6 +2371,7 @@ class Image:
 | 
				
			||||||
                    (box[3] - reduce_box[1]) / factor_y,
 | 
					                    (box[3] - reduce_box[1]) / factor_y,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.resize(size, resample, box))
 | 
					        return self._new(self.im.resize(size, resample, box))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reduce(
 | 
					    def reduce(
 | 
				
			||||||
| 
						 | 
					@ -2359,7 +2406,7 @@ class Image:
 | 
				
			||||||
            return im.convert(self.mode)
 | 
					            return im.convert(self.mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.reduce(factor, box))
 | 
					        return self._new(self.im.reduce(factor, box))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def rotate(
 | 
					    def rotate(
 | 
				
			||||||
| 
						 | 
					@ -2637,6 +2684,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        if self.im.bands == 1:
 | 
					        if self.im.bands == 1:
 | 
				
			||||||
            return (self.copy(),)
 | 
					            return (self.copy(),)
 | 
				
			||||||
        return tuple(map(self._new, self.im.split()))
 | 
					        return tuple(map(self._new, self.im.split()))
 | 
				
			||||||
| 
						 | 
					@ -2661,6 +2709,7 @@ class Image:
 | 
				
			||||||
                msg = f'The image has no channel "{channel}"'
 | 
					                msg = f'The image has no channel "{channel}"'
 | 
				
			||||||
                raise ValueError(msg) from e
 | 
					                raise ValueError(msg) from e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.getband(channel))
 | 
					        return self._new(self.im.getband(channel))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tell(self) -> int:
 | 
					    def tell(self) -> int:
 | 
				
			||||||
| 
						 | 
					@ -2765,6 +2814,7 @@ class Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.im = im.im
 | 
					            self.im = im.im
 | 
				
			||||||
            self._size = final_size
 | 
					            self._size = final_size
 | 
				
			||||||
 | 
					            assert self.im is not None
 | 
				
			||||||
            self._mode = self.im.mode
 | 
					            self._mode = self.im.mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.readonly = 0
 | 
					        self.readonly = 0
 | 
				
			||||||
| 
						 | 
					@ -2949,6 +2999,7 @@ class Image:
 | 
				
			||||||
        if image.mode in ("1", "P"):
 | 
					        if image.mode in ("1", "P"):
 | 
				
			||||||
            resample = Resampling.NEAREST
 | 
					            resample = Resampling.NEAREST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        self.im.transform(box, image.im, method, data, resample, fill)
 | 
					        self.im.transform(box, image.im, method, data, resample, fill)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def transpose(self, method: Transpose) -> Image:
 | 
					    def transpose(self, method: Transpose) -> Image:
 | 
				
			||||||
| 
						 | 
					@ -2963,6 +3014,7 @@ class Image:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.transpose(method))
 | 
					        return self._new(self.im.transpose(method))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def effect_spread(self, distance: int) -> Image:
 | 
					    def effect_spread(self, distance: int) -> Image:
 | 
				
			||||||
| 
						 | 
					@ -2972,6 +3024,7 @@ class Image:
 | 
				
			||||||
        :param distance: Distance to spread pixels.
 | 
					        :param distance: Distance to spread pixels.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        return self._new(self.im.effect_spread(distance))
 | 
					        return self._new(self.im.effect_spread(distance))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def toqimage(self):
 | 
					    def toqimage(self):
 | 
				
			||||||
| 
						 | 
					@ -3205,6 +3258,7 @@ def frombuffer(
 | 
				
			||||||
            if mode == "P":
 | 
					            if mode == "P":
 | 
				
			||||||
                from . import ImagePalette
 | 
					                from . import ImagePalette
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                assert im.im is not None
 | 
				
			||||||
                im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB"))
 | 
					                im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB"))
 | 
				
			||||||
            im.readonly = 1
 | 
					            im.readonly = 1
 | 
				
			||||||
            return im
 | 
					            return im
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,6 +48,7 @@ def invert(image: Image.Image) -> Image.Image:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image.load()
 | 
					    image.load()
 | 
				
			||||||
 | 
					    assert image.im is not None
 | 
				
			||||||
    return image._new(image.im.chop_invert())
 | 
					    return image._new(image.im.chop_invert())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,6 +64,7 @@ def lighter(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_lighter(image2.im))
 | 
					    return image1._new(image1.im.chop_lighter(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,6 +80,7 @@ def darker(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_darker(image2.im))
 | 
					    return image1._new(image1.im.chop_darker(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,6 +96,7 @@ def difference(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_difference(image2.im))
 | 
					    return image1._new(image1.im.chop_difference(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,6 +114,7 @@ def multiply(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_multiply(image2.im))
 | 
					    return image1._new(image1.im.chop_multiply(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,6 +129,7 @@ def screen(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_screen(image2.im))
 | 
					    return image1._new(image1.im.chop_screen(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,6 +142,7 @@ def soft_light(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_soft_light(image2.im))
 | 
					    return image1._new(image1.im.chop_soft_light(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -148,6 +155,7 @@ def hard_light(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_hard_light(image2.im))
 | 
					    return image1._new(image1.im.chop_hard_light(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -160,6 +168,7 @@ def overlay(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_overlay(image2.im))
 | 
					    return image1._new(image1.im.chop_overlay(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -177,6 +186,7 @@ def add(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_add(image2.im, scale, offset))
 | 
					    return image1._new(image1.im.chop_add(image2.im, scale, offset))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,6 +204,7 @@ def subtract(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_subtract(image2.im, scale, offset))
 | 
					    return image1._new(image1.im.chop_subtract(image2.im, scale, offset))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,6 +218,7 @@ def add_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_add_modulo(image2.im))
 | 
					    return image1._new(image1.im.chop_add_modulo(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,6 +232,7 @@ def subtract_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_subtract_modulo(image2.im))
 | 
					    return image1._new(image1.im.chop_subtract_modulo(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -238,6 +251,7 @@ def logical_and(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_and(image2.im))
 | 
					    return image1._new(image1.im.chop_and(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -253,6 +267,7 @@ def logical_or(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_or(image2.im))
 | 
					    return image1._new(image1.im.chop_or(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -268,6 +283,7 @@ def logical_xor(image1: Image.Image, image2: Image.Image) -> Image.Image:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    image1.load()
 | 
					    image1.load()
 | 
				
			||||||
    image2.load()
 | 
					    image2.load()
 | 
				
			||||||
 | 
					    assert image1.im is not None
 | 
				
			||||||
    return image1._new(image1.im.chop_xor(image2.im))
 | 
					    return image1._new(image1.im.chop_xor(image2.im))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -308,4 +324,5 @@ def offset(image: Image.Image, xoffset: int, yoffset: int | None = None) -> Imag
 | 
				
			||||||
    if yoffset is None:
 | 
					    if yoffset is None:
 | 
				
			||||||
        yoffset = xoffset
 | 
					        yoffset = xoffset
 | 
				
			||||||
    image.load()
 | 
					    image.load()
 | 
				
			||||||
 | 
					    assert image.im is not None
 | 
				
			||||||
    return image._new(image.im.offset(xoffset, yoffset))
 | 
					    return image._new(image.im.offset(xoffset, yoffset))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -352,6 +352,8 @@ class ImageCmsTransform(Image.ImagePointHandler):
 | 
				
			||||||
        im.load()
 | 
					        im.load()
 | 
				
			||||||
        if imOut is None:
 | 
					        if imOut is None:
 | 
				
			||||||
            imOut = Image.new(self.output_mode, im.size, None)
 | 
					            imOut = Image.new(self.output_mode, im.size, None)
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					        assert imOut.im is not None
 | 
				
			||||||
        self.transform.apply(im.im.id, imOut.im.id)
 | 
					        self.transform.apply(im.im.id, imOut.im.id)
 | 
				
			||||||
        imOut.info["icc_profile"] = self.output_profile.tobytes()
 | 
					        imOut.info["icc_profile"] = self.output_profile.tobytes()
 | 
				
			||||||
        return imOut
 | 
					        return imOut
 | 
				
			||||||
| 
						 | 
					@ -361,6 +363,7 @@ class ImageCmsTransform(Image.ImagePointHandler):
 | 
				
			||||||
        if im.mode != self.output_mode:
 | 
					        if im.mode != self.output_mode:
 | 
				
			||||||
            msg = "mode mismatch"
 | 
					            msg = "mode mismatch"
 | 
				
			||||||
            raise ValueError(msg)  # wrong output mode
 | 
					            raise ValueError(msg)  # wrong output mode
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        self.transform.apply(im.im.id, im.im.id)
 | 
					        self.transform.apply(im.im.id, im.im.id)
 | 
				
			||||||
        im.info["icc_profile"] = self.output_profile.tobytes()
 | 
					        im.info["icc_profile"] = self.output_profile.tobytes()
 | 
				
			||||||
        return im
 | 
					        return im
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,6 @@
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import math
 | 
					import math
 | 
				
			||||||
import numbers
 | 
					 | 
				
			||||||
import struct
 | 
					import struct
 | 
				
			||||||
from collections.abc import Sequence
 | 
					from collections.abc import Sequence
 | 
				
			||||||
from types import ModuleType
 | 
					from types import ModuleType
 | 
				
			||||||
| 
						 | 
					@ -160,13 +159,13 @@ class ImageDraw:
 | 
				
			||||||
            if ink is not None:
 | 
					            if ink is not None:
 | 
				
			||||||
                if isinstance(ink, str):
 | 
					                if isinstance(ink, str):
 | 
				
			||||||
                    ink = ImageColor.getcolor(ink, self.mode)
 | 
					                    ink = ImageColor.getcolor(ink, self.mode)
 | 
				
			||||||
                if self.palette and not isinstance(ink, numbers.Number):
 | 
					                if self.palette and isinstance(ink, tuple):
 | 
				
			||||||
                    ink = self.palette.getcolor(ink, self._image)
 | 
					                    ink = self.palette.getcolor(ink, self._image)
 | 
				
			||||||
                result_ink = self.draw.draw_ink(ink)
 | 
					                result_ink = self.draw.draw_ink(ink)
 | 
				
			||||||
            if fill is not None:
 | 
					            if fill is not None:
 | 
				
			||||||
                if isinstance(fill, str):
 | 
					                if isinstance(fill, str):
 | 
				
			||||||
                    fill = ImageColor.getcolor(fill, self.mode)
 | 
					                    fill = ImageColor.getcolor(fill, self.mode)
 | 
				
			||||||
                if self.palette and not isinstance(fill, numbers.Number):
 | 
					                if self.palette and isinstance(fill, tuple):
 | 
				
			||||||
                    fill = self.palette.getcolor(fill, self._image)
 | 
					                    fill = self.palette.getcolor(fill, self._image)
 | 
				
			||||||
                result_fill = self.draw.draw_ink(fill)
 | 
					                result_fill = self.draw.draw_ink(fill)
 | 
				
			||||||
        return result_ink, result_fill
 | 
					        return result_ink, result_fill
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -475,6 +475,7 @@ class Parser:
 | 
				
			||||||
                    d, e, o, a = im.tile[0]
 | 
					                    d, e, o, a = im.tile[0]
 | 
				
			||||||
                    im.tile = []
 | 
					                    im.tile = []
 | 
				
			||||||
                    self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig)
 | 
					                    self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig)
 | 
				
			||||||
 | 
					                    assert im.im is not None
 | 
				
			||||||
                    self.decoder.setimage(im.im, e)
 | 
					                    self.decoder.setimage(im.im, e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # calculate decoder offset
 | 
					                    # calculate decoder offset
 | 
				
			||||||
| 
						 | 
					@ -566,6 +567,7 @@ def _encode_tile(
 | 
				
			||||||
            fp.seek(offset)
 | 
					            fp.seek(offset)
 | 
				
			||||||
        encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig)
 | 
					        encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            encoder.setimage(im.im, extents)
 | 
					            encoder.setimage(im.im, extents)
 | 
				
			||||||
            if encoder.pushes_fd:
 | 
					            if encoder.pushes_fd:
 | 
				
			||||||
                encoder.setfd(fp)
 | 
					                encoder.setfd(fp)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -142,6 +142,7 @@ class ImageFont:
 | 
				
			||||||
            raise TypeError(msg)
 | 
					            raise TypeError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        image.load()
 | 
					        image.load()
 | 
				
			||||||
 | 
					        assert image.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.font = Image.core.font(image.im, data)
 | 
					        self.font = Image.core.font(image.im, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,6 +65,8 @@ class _Operand:
 | 
				
			||||||
            except AttributeError as e:
 | 
					            except AttributeError as e:
 | 
				
			||||||
                msg = f"bad operand type for '{op}'"
 | 
					                msg = f"bad operand type for '{op}'"
 | 
				
			||||||
                raise TypeError(msg) from e
 | 
					                raise TypeError(msg) from e
 | 
				
			||||||
 | 
					            assert out.im is not None
 | 
				
			||||||
 | 
					            assert im_1.im is not None
 | 
				
			||||||
            _imagingmath.unop(op, out.im.id, im_1.im.id)
 | 
					            _imagingmath.unop(op, out.im.id, im_1.im.id)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            # binary operation
 | 
					            # binary operation
 | 
				
			||||||
| 
						 | 
					@ -93,6 +95,9 @@ class _Operand:
 | 
				
			||||||
            except AttributeError as e:
 | 
					            except AttributeError as e:
 | 
				
			||||||
                msg = f"bad operand type for '{op}'"
 | 
					                msg = f"bad operand type for '{op}'"
 | 
				
			||||||
                raise TypeError(msg) from e
 | 
					                raise TypeError(msg) from e
 | 
				
			||||||
 | 
					            assert out.im is not None
 | 
				
			||||||
 | 
					            assert im_1.im is not None
 | 
				
			||||||
 | 
					            assert im_2.im is not None
 | 
				
			||||||
            _imagingmath.binop(op, out.im.id, im_1.im.id, im_2.im.id)
 | 
					            _imagingmath.binop(op, out.im.id, im_1.im.id, im_2.im.id)
 | 
				
			||||||
        return _Operand(out)
 | 
					        return _Operand(out)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,6 +213,8 @@ class MorphOp:
 | 
				
			||||||
            msg = "Image mode must be L"
 | 
					            msg = "Image mode must be L"
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
        outimage = Image.new(image.mode, image.size, None)
 | 
					        outimage = Image.new(image.mode, image.size, None)
 | 
				
			||||||
 | 
					        assert image.im is not None
 | 
				
			||||||
 | 
					        assert outimage.im is not None
 | 
				
			||||||
        count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id)
 | 
					        count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id)
 | 
				
			||||||
        return count, outimage
 | 
					        return count, outimage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,6 +231,7 @@ class MorphOp:
 | 
				
			||||||
        if image.mode != "L":
 | 
					        if image.mode != "L":
 | 
				
			||||||
            msg = "Image mode must be L"
 | 
					            msg = "Image mode must be L"
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					        assert image.im is not None
 | 
				
			||||||
        return _imagingmorph.match(bytes(self.lut), image.im.id)
 | 
					        return _imagingmorph.match(bytes(self.lut), image.im.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]:
 | 
					    def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]:
 | 
				
			||||||
| 
						 | 
					@ -240,6 +243,7 @@ class MorphOp:
 | 
				
			||||||
        if image.mode != "L":
 | 
					        if image.mode != "L":
 | 
				
			||||||
            msg = "Image mode must be L"
 | 
					            msg = "Image mode must be L"
 | 
				
			||||||
            raise ValueError(msg)
 | 
					            raise ValueError(msg)
 | 
				
			||||||
 | 
					        assert image.im is not None
 | 
				
			||||||
        return _imagingmorph.get_on_pixels(image.im.id)
 | 
					        return _imagingmorph.get_on_pixels(image.im.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load_lut(self, filename: str) -> None:
 | 
					    def load_lut(self, filename: str) -> None:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@ def _pilbitmap_check() -> int:
 | 
				
			||||||
    if _pilbitmap_ok is None:
 | 
					    if _pilbitmap_ok is None:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            im = Image.new("1", (1, 1))
 | 
					            im = Image.new("1", (1, 1))
 | 
				
			||||||
 | 
					            assert im.im is not None
 | 
				
			||||||
            tkinter.BitmapImage(data=f"PIL:{im.im.id}")
 | 
					            tkinter.BitmapImage(data=f"PIL:{im.im.id}")
 | 
				
			||||||
            _pilbitmap_ok = 1
 | 
					            _pilbitmap_ok = 1
 | 
				
			||||||
        except tkinter.TclError:
 | 
					        except tkinter.TclError:
 | 
				
			||||||
| 
						 | 
					@ -127,10 +128,7 @@ class PhotoImage:
 | 
				
			||||||
                # palette mapped data
 | 
					                # palette mapped data
 | 
				
			||||||
                image.apply_transparency()
 | 
					                image.apply_transparency()
 | 
				
			||||||
                image.load()
 | 
					                image.load()
 | 
				
			||||||
                try:
 | 
					                mode = image.palette.mode if image.palette else "RGB"
 | 
				
			||||||
                    mode = image.palette.mode
 | 
					 | 
				
			||||||
                except AttributeError:
 | 
					 | 
				
			||||||
                    mode = "RGB"  # default
 | 
					 | 
				
			||||||
            size = image.size
 | 
					            size = image.size
 | 
				
			||||||
            kw["width"], kw["height"] = size
 | 
					            kw["width"], kw["height"] = size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,6 +188,7 @@ class PhotoImage:
 | 
				
			||||||
        # convert to blittable
 | 
					        # convert to blittable
 | 
				
			||||||
        im.load()
 | 
					        im.load()
 | 
				
			||||||
        image = im.im
 | 
					        image = im.im
 | 
				
			||||||
 | 
					        assert image is not None
 | 
				
			||||||
        if image.isblock() and im.mode == self.__mode:
 | 
					        if image.isblock() and im.mode == self.__mode:
 | 
				
			||||||
            block = image
 | 
					            block = image
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -231,6 +230,7 @@ class BitmapImage:
 | 
				
			||||||
        if _pilbitmap_check():
 | 
					        if _pilbitmap_check():
 | 
				
			||||||
            # fast way (requires the pilbitmap booster patch)
 | 
					            # fast way (requires the pilbitmap booster patch)
 | 
				
			||||||
            image.load()
 | 
					            image.load()
 | 
				
			||||||
 | 
					            assert image.im is not None
 | 
				
			||||||
            kw["data"] = f"PIL:{image.im.id}"
 | 
					            kw["data"] = f"PIL:{image.im.id}"
 | 
				
			||||||
            self.__im = image  # must keep a reference
 | 
					            self.__im = image  # must keep a reference
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -277,6 +277,7 @@ def getimage(photo: PhotoImage) -> Image.Image:
 | 
				
			||||||
    """Copies the contents of a PhotoImage to a PIL image memory."""
 | 
					    """Copies the contents of a PhotoImage to a PIL image memory."""
 | 
				
			||||||
    im = Image.new("RGBA", (photo.width(), photo.height()))
 | 
					    im = Image.new("RGBA", (photo.width(), photo.height()))
 | 
				
			||||||
    block = im.im
 | 
					    block = im.im
 | 
				
			||||||
 | 
					    assert block is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _pyimagingtkcall("PyImagingPhotoGet", photo, block.id)
 | 
					    _pyimagingtkcall("PyImagingPhotoGet", photo, block.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -199,9 +199,13 @@ def getiptcinfo(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    data = None
 | 
					    data = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    info: dict[tuple[int, int], bytes | list[bytes]] = {}
 | 
				
			||||||
    if isinstance(im, IptcImageFile):
 | 
					    if isinstance(im, IptcImageFile):
 | 
				
			||||||
        # return info dictionary right away
 | 
					        # return info dictionary right away
 | 
				
			||||||
        return im.info
 | 
					        for k, v in im.info.items():
 | 
				
			||||||
 | 
					            if isinstance(k, tuple):
 | 
				
			||||||
 | 
					                info[k] = v
 | 
				
			||||||
 | 
					        return info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elif isinstance(im, JpegImagePlugin.JpegImageFile):
 | 
					    elif isinstance(im, JpegImagePlugin.JpegImageFile):
 | 
				
			||||||
        # extract the IPTC/NAA resource
 | 
					        # extract the IPTC/NAA resource
 | 
				
			||||||
| 
						 | 
					@ -237,4 +241,7 @@ def getiptcinfo(
 | 
				
			||||||
    except (IndexError, KeyError):
 | 
					    except (IndexError, KeyError):
 | 
				
			||||||
        pass  # expected failure
 | 
					        pass  # expected failure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return iptc_im.info
 | 
					    for k, v in iptc_im.info.items():
 | 
				
			||||||
 | 
					        if isinstance(k, tuple):
 | 
				
			||||||
 | 
					            info[k] = v
 | 
				
			||||||
 | 
					    return info
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -473,6 +473,7 @@ class JpegImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            except OSError:
 | 
					            except OSError:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        self._mode = self.im.mode
 | 
					        self._mode = self.im.mode
 | 
				
			||||||
        self._size = self.im.size
 | 
					        self._size = self.im.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,6 +173,7 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    flags = 0
 | 
					    flags = 0
 | 
				
			||||||
    if im.mode == "P" and "custom-colormap" in im.info:
 | 
					    if im.mode == "P" and "custom-colormap" in im.info:
 | 
				
			||||||
 | 
					        assert im.palette is not None
 | 
				
			||||||
        flags = flags & _FLAGS["custom-colormap"]
 | 
					        flags = flags & _FLAGS["custom-colormap"]
 | 
				
			||||||
        colormapsize = 4 * 256 + 2
 | 
					        colormapsize = 4 * 256 + 2
 | 
				
			||||||
        colormapmode = im.palette.mode
 | 
					        colormapmode = im.palette.mode
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,7 @@ class PcdImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            assert self.im is not None
 | 
					            assert self.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.im = self.im.rotate(self.tile_post_rotate)
 | 
					            self.im = self.im.rotate(self.tile_post_rotate)
 | 
				
			||||||
 | 
					            assert self.im is not None
 | 
				
			||||||
            self._size = self.im.size
 | 
					            self._size = self.im.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -863,6 +863,7 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        assert self.png is not None
 | 
					        assert self.png is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.dispose: _imaging.ImagingCore | None
 | 
					        self.dispose: _imaging.ImagingCore | None
 | 
				
			||||||
 | 
					        dispose_extent = None
 | 
				
			||||||
        if frame == 0:
 | 
					        if frame == 0:
 | 
				
			||||||
            if rewind:
 | 
					            if rewind:
 | 
				
			||||||
                self._fp.seek(self.__rewind)
 | 
					                self._fp.seek(self.__rewind)
 | 
				
			||||||
| 
						 | 
					@ -877,7 +878,7 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            self.default_image = self.info.get("default_image", False)
 | 
					            self.default_image = self.info.get("default_image", False)
 | 
				
			||||||
            self.dispose_op = self.info.get("disposal")
 | 
					            self.dispose_op = self.info.get("disposal")
 | 
				
			||||||
            self.blend_op = self.info.get("blend")
 | 
					            self.blend_op = self.info.get("blend")
 | 
				
			||||||
            self.dispose_extent = self.info.get("bbox")
 | 
					            dispose_extent = self.info.get("bbox")
 | 
				
			||||||
            self.__frame = 0
 | 
					            self.__frame = 0
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if frame != self.__frame + 1:
 | 
					            if frame != self.__frame + 1:
 | 
				
			||||||
| 
						 | 
					@ -886,6 +887,7 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # ensure previous frame was loaded
 | 
					            # ensure previous frame was loaded
 | 
				
			||||||
            self.load()
 | 
					            self.load()
 | 
				
			||||||
 | 
					            assert self.im is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if self.dispose:
 | 
					            if self.dispose:
 | 
				
			||||||
                self.im.paste(self.dispose, self.dispose_extent)
 | 
					                self.im.paste(self.dispose, self.dispose_extent)
 | 
				
			||||||
| 
						 | 
					@ -935,11 +937,13 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            self.tile = self.png.im_tile
 | 
					            self.tile = self.png.im_tile
 | 
				
			||||||
            self.dispose_op = self.info.get("disposal")
 | 
					            self.dispose_op = self.info.get("disposal")
 | 
				
			||||||
            self.blend_op = self.info.get("blend")
 | 
					            self.blend_op = self.info.get("blend")
 | 
				
			||||||
            self.dispose_extent = self.info.get("bbox")
 | 
					            dispose_extent = self.info.get("bbox")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not self.tile:
 | 
					            if not self.tile:
 | 
				
			||||||
                msg = "image not found in APNG frame"
 | 
					                msg = "image not found in APNG frame"
 | 
				
			||||||
                raise EOFError(msg)
 | 
					                raise EOFError(msg)
 | 
				
			||||||
 | 
					        if dispose_extent:
 | 
				
			||||||
 | 
					            self.dispose_extent: tuple[float, float, float, float] = dispose_extent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # setup frame disposal (actual disposal done when needed in the next _seek())
 | 
					        # setup frame disposal (actual disposal done when needed in the next _seek())
 | 
				
			||||||
        if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS:
 | 
					        if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS:
 | 
				
			||||||
| 
						 | 
					@ -1046,6 +1050,7 @@ class PngImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            self.png = None
 | 
					            self.png = None
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if self._prev_im and self.blend_op == Blend.OP_OVER:
 | 
					            if self._prev_im and self.blend_op == Blend.OP_OVER:
 | 
				
			||||||
 | 
					                assert self.im is not None
 | 
				
			||||||
                updated = self._crop(self.im, self.dispose_extent)
 | 
					                updated = self._crop(self.im, self.dispose_extent)
 | 
				
			||||||
                if self.im.mode == "RGB" and "transparency" in self.info:
 | 
					                if self.im.mode == "RGB" and "transparency" in self.info:
 | 
				
			||||||
                    mask = updated.convert_transparent(
 | 
					                    mask = updated.convert_transparent(
 | 
				
			||||||
| 
						 | 
					@ -1400,6 +1405,7 @@ def _save(
 | 
				
			||||||
                    chunk(fp, cid, data)
 | 
					                    chunk(fp, cid, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if im.mode == "P":
 | 
					    if im.mode == "P":
 | 
				
			||||||
 | 
					        assert im.im is not None
 | 
				
			||||||
        palette_byte_number = colors * 3
 | 
					        palette_byte_number = colors * 3
 | 
				
			||||||
        palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
 | 
					        palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
 | 
				
			||||||
        while len(palette_bytes) < palette_byte_number:
 | 
					        while len(palette_bytes) < palette_byte_number:
 | 
				
			||||||
| 
						 | 
					@ -1430,8 +1436,9 @@ def _save(
 | 
				
			||||||
                # and it's in the info dict. It's probably just stale.
 | 
					                # and it's in the info dict. It's probably just stale.
 | 
				
			||||||
                msg = "cannot use transparency for this mode"
 | 
					                msg = "cannot use transparency for this mode"
 | 
				
			||||||
                raise OSError(msg)
 | 
					                raise OSError(msg)
 | 
				
			||||||
    else:
 | 
					    elif im.mode == "P":
 | 
				
			||||||
        if im.mode == "P" and im.im.getpalettemode() == "RGBA":
 | 
					        assert im.im is not None
 | 
				
			||||||
 | 
					        if im.im.getpalettemode() == "RGBA":
 | 
				
			||||||
            alpha = im.im.getpalette("RGBA", "A")
 | 
					            alpha = im.im.getpalette("RGBA", "A")
 | 
				
			||||||
            alpha_bytes = colors
 | 
					            alpha_bytes = colors
 | 
				
			||||||
            chunk(fp, b"tRNS", alpha[:alpha_bytes])
 | 
					            chunk(fp, b"tRNS", alpha[:alpha_bytes])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ class QoiImageFile(ImageFile.ImageFile):
 | 
				
			||||||
            msg = "not a QOI file"
 | 
					            msg = "not a QOI file"
 | 
				
			||||||
            raise SyntaxError(msg)
 | 
					            raise SyntaxError(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._size = tuple(i32(self.fp.read(4)) for i in range(2))
 | 
					        self._size = i32(self.fp.read(4)), i32(self.fp.read(4))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        channels = self.fp.read(1)[0]
 | 
					        channels = self.fp.read(1)[0]
 | 
				
			||||||
        self._mode = "RGB" if channels == 3 else "RGBA"
 | 
					        self._mode = "RGB" if channels == 3 else "RGBA"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1313,6 +1313,7 @@ class TiffImageFile(ImageFile.ImageFile):
 | 
				
			||||||
        decoder = Image._getdecoder(
 | 
					        decoder = Image._getdecoder(
 | 
				
			||||||
            self.mode, "libtiff", tuple(args), self.decoderconfig
 | 
					            self.mode, "libtiff", tuple(args), self.decoderconfig
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        assert self.im is not None
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            decoder.setimage(self.im, extents)
 | 
					            decoder.setimage(self.im, extents)
 | 
				
			||||||
        except ValueError as e:
 | 
					        except ValueError as e:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user