diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index c52d3420e8f..124f2a7535a 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -106,6 +106,34 @@ def test_crop( assert_image_equal(ImageOps.crop(im, border), im.crop(expected_box)) +def test_zero() -> None: + def _check( + size: tuple[int, int], + operation: Callable[[Image.Image], Image.Image], + expected_size: tuple[int, int], + ) -> None: + im = Image.new("P", size) + im.putpalette(list(range(256)) * 3) + im.info["transparency"] = 0 + new_im = operation(im) + assert new_im.getpalette(None) == im.getpalette(None) + assert new_im.info["transparency"] == 0 + assert new_im.size == expected_size + + _check((5, 5), lambda im: ImageOps.fit(im, (0, 5)), (0, 5)) + _check((5, 0), lambda im: ImageOps.fit(im, (5, 5)), (5, 5)) + _check((5, 5), lambda im: ImageOps.fit(im, (10, 0)), (10, 0)) + + _check((1, 10), lambda im: ImageOps.contain(im, (5, 5)), (0, 5)) + _check((10, 1), lambda im: ImageOps.contain(im, (5, 5)), (5, 0)) + _check((10, 0), lambda im: ImageOps.contain(im, (5, 5)), (5, 0)) + _check((5, 5), lambda im: ImageOps.contain(im, (2, 0)), (0, 0)) + + _check((0, 10), lambda im: ImageOps.cover(im, (5, 5)), (0, 5)) + _check((10, 0), lambda im: ImageOps.cover(im, (5, 5)), (5, 0)) + _check((5, 5), lambda im: ImageOps.cover(im, (2, 0)), (0, 0)) + + def test_1pxfit() -> None: # Division by zero in equalize if image is 1 pixel high newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35)) @@ -129,20 +157,6 @@ def test_fit_same_ratio() -> None: assert new_im.size == (1000, 755) -def test_fit_zero() -> None: - im = Image.new("1", (5, 5)) - new_im = ImageOps.fit(im, (0, 5)) - assert new_im.width == 0 - - im = Image.new("1", (5, 0)) - new_im = ImageOps.fit(im, (5, 5)) - assert new_im.size == (5, 5) - - im = Image.new("1", (5, 5)) - new_im = ImageOps.fit(im, (10, 0)) - assert new_im.size == (10, 0) - - @pytest.mark.parametrize("new_size", ((256, 256), (512, 256), (256, 512))) def test_contain(new_size: tuple[int, int]) -> None: im = hopper() @@ -160,24 +174,6 @@ def test_contain_round() -> None: assert new_im.height == 5 -def test_contain_zero() -> None: - im = Image.new("1", (1, 10)) - new_im = ImageOps.contain(im, (5, 5)) - assert new_im.width == 0 - - im = Image.new("1", (10, 1)) - new_im = ImageOps.contain(im, (5, 5)) - assert new_im.height == 0 - - im = Image.new("1", (10, 0)) - new_im = ImageOps.contain(im, (5, 5)) - assert new_im.size == (5, 0) - - im = Image.new("1", (5, 5)) - new_im = ImageOps.contain(im, (2, 0)) - assert new_im.size == (0, 0) - - @pytest.mark.parametrize( "image_name, expected_size", ( @@ -192,20 +188,6 @@ def test_cover(image_name: str, expected_size: tuple[int, int]) -> None: assert new_im.size == expected_size -def test_cover_zero() -> None: - im = Image.new("1", (0, 10)) - new_im = ImageOps.cover(im, (5, 5)) - assert new_im.size == (0, 5) - - im = Image.new("1", (10, 0)) - new_im = ImageOps.cover(im, (5, 5)) - assert new_im.size == (5, 0) - - im = Image.new("1", (5, 5)) - new_im = ImageOps.cover(im, (2, 0)) - assert new_im.size == (0, 0) - - def test_pad() -> None: # Same ratio im = hopper() diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f008b689e1f..40e43501081 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -686,6 +686,7 @@ def _new(self, im: core.ImagingCore) -> Image: if im.mode in ("P", "PA"): if self.palette: new.palette = self.palette.copy() + new.palette.dirty = 1 else: from . import ImagePalette diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index b40af6330bb..ceff85e1c1a 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -89,6 +89,10 @@ def _lut(image: Image.Image, lut: list[int], palette: bool = False) -> Image.Ima raise OSError(msg) +def _new_empty(image: Image.Image, size: tuple[int, int]) -> Image.Image: + return image._new(Image.core.new(image.mode, size)) + + # # actions @@ -313,10 +317,10 @@ def contain( """ if image.height == 0: - return Image.new(image.mode, (size[0], 0)) + return _new_empty(image, (size[0], 0)) im_ratio = image.width / image.height if size[1] == 0: - return Image.new(image.mode, (0, 0)) + return _new_empty(image, (0, 0)) dest_ratio = size[0] / size[1] if im_ratio != dest_ratio: @@ -329,7 +333,7 @@ def contain( if new_width != size[0]: size = (new_width, size[1]) if min(size) == 0: - return Image.new(image.mode, size) + return _new_empty(image, size) return image.resize(size, resample=method) @@ -350,12 +354,12 @@ def cover( """ if image.width == 0: - return Image.new(image.mode, (0, size[1])) + return _new_empty(image, (0, size[1])) if image.height == 0: - return Image.new(image.mode, (size[0], 0)) + return _new_empty(image, (size[0], 0)) im_ratio = image.width / image.height if size[1] == 0: - return Image.new(image.mode, (0, 0)) + return _new_empty(image, (0, 0)) dest_ratio = size[0] / size[1] if im_ratio != dest_ratio: @@ -606,7 +610,7 @@ def fit( # https://www.cazabon.com if min(size) == 0: - return Image.new(image.mode, size) + return _new_empty(image, size) centering_x, centering_y = centering if not 0.0 <= centering_x <= 1.0: @@ -630,7 +634,7 @@ def fit( # calculate the aspect ratio of the live_size if live_size[1] == 0: - return Image.new(image.mode, size) + return _new_empty(image, size) live_size_ratio = live_size[0] / live_size[1] # calculate the aspect ratio of the output image