Merge pull request #3848 from radarhere/pa

Improved palette handling for LA and PA modes
This commit is contained in:
Hugo 2019-06-05 22:32:44 +03:00 committed by GitHub
commit 169961649d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 53 additions and 14 deletions

View File

@ -48,7 +48,7 @@ class TestFileIm(PillowTestCase):
im.seek(n_frames-1) im.seek(n_frames-1)
def test_roundtrip(self): def test_roundtrip(self):
for mode in ["RGB", "P"]: for mode in ["RGB", "P", "PA"]:
out = self.tempfile('temp.im') out = self.tempfile('temp.im')
im = hopper(mode) im = hopper(mode)
im.save(out) im.save(out)

View File

@ -489,6 +489,16 @@ class TestFileTiff(PillowTestCase):
self.assert_image_equal_tofile(im, self.assert_image_equal_tofile(im,
"Tests/images/tiff_adobe_deflate.png") "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): def test_tiff_save_all(self):
import io import io
import os import os

View File

@ -75,6 +75,15 @@ class TestPickle(PillowTestCase):
protocol=protocol, protocol=protocol,
test_file=test_file) 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): def test_pickle_l_mode(self):
# Arrange # Arrange
import pickle import pickle
@ -84,6 +93,24 @@ class TestPickle(PillowTestCase):
self.helper_pickle_string(pickle, protocol, mode="L") self.helper_pickle_string(pickle, protocol, mode="L")
self.helper_pickle_file(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): def test_cpickle_l_mode(self):
# Arrange # Arrange
try: try:

View File

@ -210,9 +210,9 @@ Instances of the :py:class:`Image` class have the following attributes:
.. py:attribute:: palette .. py:attribute:: palette
Colour palette table, if any. If mode is “P”, this should be an instance of Colour palette table, if any. If mode is "P" or "PA", this should be an
the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should instance of the :py:class:`~PIL.ImagePalette.ImagePalette` class.
be set to ``None``. Otherwise, it should be set to ``None``.
:type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None`` :type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None``

View File

@ -71,6 +71,7 @@ OPEN = {
"RYB3 image": ("RGB", "RYB;T"), "RYB3 image": ("RGB", "RYB;T"),
# extensions # extensions
"LA image": ("LA", "LA;L"), "LA image": ("LA", "LA;L"),
"PA image": ("LA", "PA;L"),
"RGBA image": ("RGBA", "RGBA;L"), "RGBA image": ("RGBA", "RGBA;L"),
"RGBX image": ("RGBX", "RGBX;L"), "RGBX image": ("RGBX", "RGBX;L"),
"CMYK image": ("CMYK", "CMYK;L"), "CMYK image": ("CMYK", "CMYK;L"),
@ -218,15 +219,16 @@ class ImImageFile(ImageFile.ImageFile):
linear = 0 linear = 0
else: else:
greyscale = 0 greyscale = 0
if self.mode == "L" or self.mode == "LA": if self.mode in ["L", "LA", "P", "PA"]:
if greyscale: if greyscale:
if not linear: if not linear:
self.lut = [i8(c) for c in palette[:256]] self.lut = [i8(c) for c in palette[:256]]
else: else:
if self.mode == "L": if self.mode in ["L", "P"]:
self.mode = self.rawmode = "P" self.mode = self.rawmode = "P"
elif self.mode == "LA": elif self.mode in ["LA", "PA"]:
self.mode = self.rawmode = "PA" self.mode = "PA"
self.rawmode = "PA;L"
self.palette = ImagePalette.raw("RGB;L", palette) self.palette = ImagePalette.raw("RGB;L", palette)
elif self.mode == "RGB": elif self.mode == "RGB":
if not greyscale or not linear: 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(("Name: %s\r\n" % filename).encode('ascii'))
fp.write(("Image size (x*y): %d*%d\r\n" % im.size).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')) 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"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 == "P": if im.mode in ["P", "PA"]:
fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))]) ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))])

View File

@ -715,7 +715,7 @@ class Image(object):
self.mode = mode self.mode = mode
self._size = size self._size = size
self.im = core.new(mode, 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.putpalette(palette)
self.frombytes(data) self.frombytes(data)

View File

@ -1357,7 +1357,7 @@ class TiffImageFile(ImageFile.ImageFile):
# fixup palette descriptor # fixup palette descriptor
if self.mode == "P": if self.mode in ["P", "PA"]:
palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]]
self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
@ -1483,7 +1483,7 @@ def _save(im, fp, filename):
ifd[PHOTOMETRIC_INTERPRETATION] = photo ifd[PHOTOMETRIC_INTERPRETATION] = photo
if im.mode == "P": if im.mode in ["P", "PA"]:
lut = im.im.getpalette("RGB", "RGB;L") lut = im.im.getpalette("RGB", "RGB;L")
ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)
# data orientation # data orientation

View File

@ -1588,7 +1588,7 @@ _putpalette(ImagingObject* self, PyObject* args)
ImagingPaletteDelete(self->image->palette); 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"); self->image->palette = ImagingPaletteNew("RGB");