Skip to content

Commit

Permalink
Merge pull request #3848 from radarhere/pa
Browse files Browse the repository at this point in the history
Improved palette handling for LA and PA modes
  • Loading branch information
hugovk committed Jun 5, 2019
2 parents 41f3e7c + 8be6609 commit 1699616
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Tests/test_file_im.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_eoferror(self):
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)
Expand Down
10 changes: 10 additions & 0 deletions Tests/test_file_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,16 @@ def test_tiled_planar_raw(self):
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
Expand Down
27 changes: 27 additions & 0 deletions Tests/test_pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ def test_pickle_p_mode(self):
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
Expand All @@ -84,6 +93,24 @@ def test_pickle_l_mode(self):
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:
Expand Down
6 changes: 3 additions & 3 deletions docs/reference/Image.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``

Expand Down
14 changes: 8 additions & 6 deletions src/PIL/ImImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"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"),
Expand Down Expand Up @@ -218,15 +219,16 @@ def _open(self):
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:
Expand Down Expand Up @@ -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))])

Expand Down
2 changes: 1 addition & 1 deletion src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ def __setstate__(self, state):
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)

Expand Down
4 changes: 2 additions & 2 deletions src/PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1357,7 +1357,7 @@ def _setup(self):

# 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))

Expand Down Expand Up @@ -1483,7 +1483,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
Expand Down
2 changes: 1 addition & 1 deletion src/_imaging.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down

0 comments on commit 1699616

Please sign in to comment.