From 0f94a2cfe5a8625e0417108745427f112926c24a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 Jul 2018 14:11:55 +1000 Subject: [PATCH] Allow plugins to specify their supported modes --- Tests/test_image.py | 31 +++++++++++++++++++++++++---- src/PIL/GifImagePlugin.py | 7 ++----- src/PIL/Image.py | 40 ++++++++++++++++++++++++++++++++------ src/PIL/JpegImagePlugin.py | 11 ++--------- src/PIL/PngImagePlugin.py | 6 ++---- src/PIL/WebPImagePlugin.py | 13 ++----------- 6 files changed, 69 insertions(+), 39 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index c6502d5ff0d..2232a3c2a32 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -2,7 +2,10 @@ from PIL import Image, TiffImagePlugin from PIL._util import py3 + +from io import BytesIO import os +import sys class TestImage(PillowTestCase): @@ -62,8 +65,7 @@ def test_width_height(self): def test_invalid_image(self): if py3: - import io - im = io.BytesIO(b'') + im = BytesIO(b'') else: import StringIO im = StringIO.StringIO('') @@ -324,8 +326,29 @@ def test_registered_extensions(self): for ext in ['.cur', '.icns', '.tif', '.tiff']: self.assertIn(ext, extensions) - def test_no_convert_mode(self): - self.assertTrue(not hasattr(TiffImagePlugin, '_convert_mode')) + def test_supported_modes(self): + for format in Image.MIME.keys(): + try: + save_handler = Image.SAVE[format] + except KeyError: + continue + plugin = sys.modules[save_handler.__module__] + if not hasattr(plugin, '_supported_modes'): + continue + + # Check that the supported modes list is accurate + supported_modes = plugin._supported_modes() + for mode in ['1', 'L', 'P', 'RGB', 'RGBA', 'CMYK', 'YCbCr', 'LAB', + 'HSV', 'I', 'F', 'LA', 'La', 'RGBX', 'RGBa']: + out = BytesIO() + im = Image.new(mode, (100, 100)) + if mode in supported_modes: + im.save(out, format) + else: + self.assertRaises(Exception, im.save, out, format) + + def test_no_supported_modes_method(self): + self.assertTrue(not hasattr(TiffImagePlugin, '_supported_modes')) temp_file = self.tempfile("temp.tiff") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 266a790b448..d7828b6727d 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -798,11 +798,8 @@ def write(self, data): return fp.data -def _convert_mode(im): - return { - 'LA':'P', - 'CMYK':'RGB' - }.get(im.mode) +def _supported_modes(): + return ['RGB', 'RGBA', 'P', 'I', 'F', 'LA', 'L', '1'] # -------------------------------------------------------------------- diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 7b3d2391f05..21241788f0c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1936,9 +1936,11 @@ def save(self, fp, format=None, **params): if params.get('convert_mode'): plugin = sys.modules[save_handler.__module__] - converted_im = self._convert_mode(plugin, params) - if converted_im: - return converted_im.save(fp, format, **params) + if hasattr(plugin, '_supported_modes'): + modes = plugin._supported_modes() + converted_im = self._convert_mode(modes, params) + if converted_im: + return converted_im.save(fp, format, **params) self.encoderinfo = params self.encoderconfig = () @@ -1958,10 +1960,36 @@ def save(self, fp, format=None, **params): if open_fp: fp.close() - def _convert_mode(self, plugin, params): - if not hasattr(plugin, '_convert_mode'): + def _convert_mode(self, modes, params): + if not modes or self.mode in modes: return - new_mode = plugin._convert_mode(self) + if self.mode == 'P': + preferred_modes = [] + if 'A' in self.im.getpalettemode(): + preferred_modes.append('RGBA') + preferred_modes.append('RGB') + else: + preferred_modes = { + 'CMYK': ['RGB'], + 'RGB': ['CMYK'], + 'RGBX': ['RGB'], + 'RGBa': ['RGBA', 'RGB'], + 'RGBA': ['RGB'], + 'LA': ['RGBA', 'P', 'L'], + 'La': ['LA', 'L'], + 'L': ['RGB'], + 'F': ['I'], + 'I': ['L', 'RGB'], + '1': ['L'], + 'YCbCr': ['RGB'], + 'LAB': ['RGB'], + 'HSV': ['RGB'] + }.get(self.mode, []) + for new_mode in preferred_modes: + if new_mode in modes: + break + else: + new_mode = modes[0] if self.mode == 'LA' and new_mode == 'P': alpha = self.getchannel('A') # Convert the image into P mode but only use 255 colors diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 533dfb0265a..96de76bcbc6 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -792,15 +792,8 @@ def jpeg_factory(fp=None, filename=None): return im -def _convert_mode(im): - mode = im.mode - if mode == 'P': - return 'RGBA' if 'A' in im.im.getpalettemode() else 'RGB' - return { - 'RGBA':'RGB', - 'LA':'L', - 'I':'L' - }.get(mode) +def _supported_modes(): + return ['RGB', 'CMYK', 'YCbCr', 'RGBX', 'L', '1'] # -------------------------------------------------------------------q- diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index bb2191475d6..2cb6218a5aa 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -859,10 +859,8 @@ def append(fp, cid, *data): return fp.data -def _convert_mode(im): - return { - 'CMYK':'RGB' - }.get(im.mode) +def _supported_modes(): + return ['RGB', 'RGBA', 'P', 'I', 'LA', 'L', '1'] # -------------------------------------------------------------------- diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index b8694f6d9a9..e32299b36ef 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -308,17 +308,8 @@ def _save(im, fp, filename): fp.write(data) -def _convert_mode(im): - mode = im.mode - if mode == 'P': - return 'RGBA' if 'A' in im.im.getpalettemode() else 'RGB' - return { - # Pillow doesn't support L modes for webp for now. - 'L':'RGB', - 'LA':'RGBA', - 'I':'RGB', - 'CMYK':'RGB' - }.get(mode) +def _supported_modes(): + return ['RGB', 'RGBA', 'RGBX', 'CMYK', 'YCbCr', 'HSV', 'I', 'F', 'P', 'LA', 'L', '1'] Image.register_open(WebPImageFile.format, WebPImageFile, _accept)