diff --git a/Tests/images/palette_negative.png b/Tests/images/palette_negative.png new file mode 100644 index 00000000000..938a7285fd7 Binary files /dev/null and b/Tests/images/palette_negative.png differ diff --git a/Tests/images/palette_sepia.png b/Tests/images/palette_sepia.png new file mode 100644 index 00000000000..f3fc932531f Binary files /dev/null and b/Tests/images/palette_sepia.png differ diff --git a/Tests/images/palette_wedge.png b/Tests/images/palette_wedge.png new file mode 100644 index 00000000000..23fb7940d6d Binary files /dev/null and b/Tests/images/palette_wedge.png differ diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 2632ab7c04f..05e2ddcbebb 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -833,7 +833,7 @@ def test_palette_save_ImagePalette(tmp_path): with Image.open(out) as reloaded: im.putpalette(palette) - assert_image_equal(reloaded, im) + assert_image_equal(reloaded.convert("RGB"), im.convert("RGB")) def test_save_I(tmp_path): diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 5a9df11b1db..012a57a0999 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -2,7 +2,7 @@ from PIL import Image, ImagePalette -from .helper import assert_image_equal, hopper +from .helper import assert_image_equal, assert_image_equal_tofile, hopper def test_putpalette(): @@ -36,9 +36,15 @@ def palette(mode): def test_imagepalette(): im = hopper("P") im.putpalette(ImagePalette.negative()) + assert_image_equal_tofile(im.convert("RGB"), "Tests/images/palette_negative.png") + im.putpalette(ImagePalette.random()) + im.putpalette(ImagePalette.sepia()) + assert_image_equal_tofile(im.convert("RGB"), "Tests/images/palette_sepia.png") + im.putpalette(ImagePalette.wedge()) + assert_image_equal_tofile(im.convert("RGB"), "Tests/images/palette_wedge.png") def test_putpalette_with_alpha_values(): diff --git a/docs/reference/ImagePalette.rst b/docs/reference/ImagePalette.rst index f14c1c3a446..72ccfac7d83 100644 --- a/docs/reference/ImagePalette.rst +++ b/docs/reference/ImagePalette.rst @@ -9,10 +9,6 @@ represent the color palette of palette mapped images. .. note:: - This module was never well-documented. It hasn't changed since 2001, - though, so it's probably safe for you to read the source code and puzzle - out the internals if you need to. - The :py:class:`~PIL.ImagePalette.ImagePalette` class has several methods, but they are all marked as "experimental." Read that as you will. The ``[source]`` link is there for a reason. diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index fe57e24e277..ad97b81a52a 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -396,15 +396,7 @@ def _normalize_palette(im, palette, info): if isinstance(palette, (bytes, bytearray, list)): source_palette = bytearray(palette[:768]) if isinstance(palette, ImagePalette.ImagePalette): - source_palette = bytearray( - itertools.chain.from_iterable( - zip( - palette.palette[:256], - palette.palette[256:512], - palette.palette[512:768], - ) - ) - ) + source_palette = bytearray(palette.palette) if im.mode == "P": if not source_palette: diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index 214059f5108..6763d8c7af7 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -25,12 +25,12 @@ class ImagePalette: """ Color palette for palette mapped images - :param mode: The mode to use for the Palette. See: + :param mode: The mode to use for the palette. See: :ref:`concept-modes`. Defaults to "RGB" :param palette: An optional palette. If given, it must be a bytearray, - an array or a list of ints between 0-255. The list must be aligned - by channel (All R values must be contiguous in the list before G - and B values.) Defaults to 0 through 255 per channel. + an array or a list of ints between 0-255. The list must consist of + all channels for one color followed by the next color (e.g. RGBRGBRGB). + Defaults to an empty palette. :param size: An optional palette size. If given, an error is raised if ``palette`` is not of equal length. """ @@ -204,9 +204,9 @@ def make_gamma_lut(exp): def negative(mode="RGB"): - palette = list(range(256)) + palette = list(range(256 * len(mode))) palette.reverse() - return ImagePalette(mode, palette * len(mode)) + return ImagePalette(mode, [i // len(mode) for i in palette]) def random(mode="RGB"): @@ -219,15 +219,13 @@ def random(mode="RGB"): def sepia(white="#fff0c0"): - r, g, b = ImageColor.getrgb(white) - r = make_linear_lut(0, r) - g = make_linear_lut(0, g) - b = make_linear_lut(0, b) - return ImagePalette("RGB", r + g + b) + bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)] + return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)]) def wedge(mode="RGB"): - return ImagePalette(mode, list(range(256)) * len(mode)) + palette = list(range(256 * len(mode))) + return ImagePalette(mode, [i // len(mode) for i in palette]) def load(filename):