From e9c0cf61f38e9e67c95d0298a7e1d08c0ded2cff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 May 2019 14:01:23 +1000 Subject: [PATCH 1/3] Fixed palette for LA and PA mode when pickling --- Tests/test_pickle.py | 27 +++++++++++++++++++++++++++ src/PIL/Image.py | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 45ef0f1db..ccd08b1e1 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -75,6 +75,15 @@ class TestPickle(PillowTestCase): protocol=protocol, test_file=test_file) + def test_pickle_pa_mode(self): + # Arrange + import pickle + + # Act / Assert + for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): + self.helper_pickle_string(pickle, protocol, mode="PA") + self.helper_pickle_file(pickle, protocol, mode="PA") + def test_pickle_l_mode(self): # Arrange import pickle @@ -84,6 +93,24 @@ class TestPickle(PillowTestCase): self.helper_pickle_string(pickle, protocol, mode="L") self.helper_pickle_file(pickle, protocol, mode="L") + def test_pickle_la_mode_with_palette(self): + # Arrange + import pickle + im = Image.open('Tests/images/hopper.jpg') + filename = self.tempfile('temp.pkl') + im = im.convert("PA") + + # Act / Assert + for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): + im.mode = "LA" + with open(filename, 'wb') as f: + pickle.dump(im, f, protocol) + with open(filename, 'rb') as f: + loaded_im = pickle.load(f) + + im.mode = "PA" + self.assertEqual(im, loaded_im) + def test_cpickle_l_mode(self): # Arrange try: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2ee7869b6..f42b882ac 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -715,7 +715,7 @@ class Image(object): self.mode = mode self._size = size self.im = core.new(mode, size) - if mode in ("L", "P") and palette: + if mode in ("L", "LA", "P", "PA") and palette: self.putpalette(palette) self.frombytes(data) From e131fa22e2994a5e9113a8f0fe3a0fd08a8af4a9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 May 2019 14:43:48 +1000 Subject: [PATCH 2/3] Fixed reading and saving for TIFF and IM in PA mode --- Tests/test_file_im.py | 2 +- Tests/test_file_tiff.py | 10 ++++++++++ src/PIL/ImImagePlugin.py | 14 ++++++++------ src/PIL/TiffImagePlugin.py | 4 ++-- src/_imaging.c | 2 +- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 8e774ce0a..46858bb1f 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -48,7 +48,7 @@ class TestFileIm(PillowTestCase): im.seek(n_frames-1) def test_roundtrip(self): - for mode in ["RGB", "P"]: + for mode in ["RGB", "P", "PA"]: out = self.tempfile('temp.im') im = hopper(mode) im.save(out) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 9f2a04bb9..64d03361b 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -489,6 +489,16 @@ class TestFileTiff(PillowTestCase): self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") + def test_palette(self): + for mode in ["P", "PA"]: + outfile = self.tempfile("temp.tif") + + im = hopper(mode) + im.save(outfile) + + reloaded = Image.open(outfile) + self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) + def test_tiff_save_all(self): import io import os diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 08250e959..443ee4f39 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -71,6 +71,7 @@ OPEN = { "RYB3 image": ("RGB", "RYB;T"), # extensions "LA image": ("LA", "LA;L"), + "PA image": ("LA", "PA;L"), "RGBA image": ("RGBA", "RGBA;L"), "RGBX image": ("RGBX", "RGBX;L"), "CMYK image": ("CMYK", "CMYK;L"), @@ -218,15 +219,16 @@ class ImImageFile(ImageFile.ImageFile): linear = 0 else: greyscale = 0 - if self.mode == "L" or self.mode == "LA": + if self.mode in ["L", "LA", "P", "PA"]: if greyscale: if not linear: self.lut = [i8(c) for c in palette[:256]] else: - if self.mode == "L": + if self.mode in ["L", "P"]: self.mode = self.rawmode = "P" - elif self.mode == "LA": - self.mode = self.rawmode = "PA" + elif self.mode in ["LA", "PA"]: + self.mode = "PA" + self.rawmode = "PA;L" self.palette = ImagePalette.raw("RGB;L", palette) elif self.mode == "RGB": if not greyscale or not linear: @@ -340,10 +342,10 @@ def _save(im, fp, filename): fp.write(("Name: %s\r\n" % filename).encode('ascii')) fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii')) - if im.mode == "P": + if im.mode in ["P", "PA"]: fp.write(b"Lut: 1\r\n") fp.write(b"\000" * (511-fp.tell()) + b"\032") - if im.mode == "P": + if im.mode in ["P", "PA"]: fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))]) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 0056dc7f5..8d99f68a1 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1358,7 +1358,7 @@ class TiffImageFile(ImageFile.ImageFile): # fixup palette descriptor - if self.mode == "P": + if self.mode in ["P", "PA"]: palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) @@ -1484,7 +1484,7 @@ def _save(im, fp, filename): ifd[PHOTOMETRIC_INTERPRETATION] = photo - if im.mode == "P": + if im.mode in ["P", "PA"]: lut = im.im.getpalette("RGB", "RGB;L") ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation diff --git a/src/_imaging.c b/src/_imaging.c index 037348baa..1391ffae6 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1588,7 +1588,7 @@ _putpalette(ImagingObject* self, PyObject* args) ImagingPaletteDelete(self->image->palette); - strcpy(self->image->mode, "P"); + strcpy(self->image->mode, strlen(self->image->mode) == 2 ? "PA" : "P"); self->image->palette = ImagingPaletteNew("RGB"); From 8be66092430e4e6644ad2c49cf3cf0fe6a0b36ab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 18 May 2019 20:36:22 +1000 Subject: [PATCH 3/3] Added PA mode to docs [ci skip] --- docs/reference/Image.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 388116a10..598e73abd 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -210,9 +210,9 @@ Instances of the :py:class:`Image` class have the following attributes: .. py:attribute:: palette - Colour palette table, if any. If mode is ā€œPā€, this should be an instance of - the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should - be set to ``None``. + Colour palette table, if any. If mode is "P" or "PA", this should be an + instance of the :py:class:`~PIL.ImagePalette.ImagePalette` class. + Otherwise, it should be set to ``None``. :type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None``