Skip to content

Commit

Permalink
Added convert_mode param when saving
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Aug 9, 2020
1 parent 33dca34 commit aee5d22
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 8 deletions.
Binary file added Tests/images/pil123rgba_red.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,17 @@ def test_save_I(tmp_path):
assert_image_equal(reloaded.convert("L"), im.convert("L"))


def test_save_wrong_modes(self):
out = BytesIO()
for mode in ["CMYK"]:
img = Image.new(mode, (20, 20))
self.assertRaises(ValueError, img.save, out, "GIF")

for mode in ["CMYK", "LA"]:
img = Image.new(mode, (20, 20))
img.save(out, "GIF", convert_mode=True)


def test_getdata():
# Test getheader/getdata against legacy values.
# Create a 'P' image with holes in the palette.
Expand Down
16 changes: 14 additions & 2 deletions Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,14 +558,26 @@ def test_save_correct_modes(self):
img = Image.new(mode, (20, 20))
img.save(out, "JPEG")

def test_save_wrong_modes(self):
def test_save_wrong_modes(self, tmp_path):
# ref https://github.com/python-pillow/Pillow/issues/2005
out = BytesIO()
for mode in ["LA", "La", "RGBA", "RGBa", "P"]:
for mode in ["LA", "La", "RGBA", "RGBa", "P", "I"]:
img = Image.new(mode, (20, 20))
with pytest.raises(OSError):
img.save(out, "JPEG")

for mode in ["LA", "RGBA", "P", "I"]:
img = Image.new(mode, (20, 20))
img.save(out, "JPEG", convert_mode=True)

temp_file = str(tmp_path / "temp.jpg")
with Image.open("Tests/images/pil123rgba.png") as img:
img.save(temp_file, convert_mode=True, fill_color="red")

with Image.open(temp_file) as reloaded:
with Image.open("Tests/images/pil123rgba_red.jpg") as target:
assert_image_similar(reloaded, target, 4)

def test_save_tiff_with_dpi(self, tmp_path):
# Arrange
outfile = str(tmp_path / "temp.tif")
Expand Down
8 changes: 8 additions & 0 deletions Tests/test_file_png.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ def test_load_transparent_rgb(self):
# image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876

def test_save_CMYK(self):
out = BytesIO()
im = Image.new("CMYK", (20, 20))
with pytest.raises(IOError):
im.save(out, "PNG")

im.save(out, "PNG", convert_mode=True)

def test_save_p_transparent_palette(self, tmp_path):
in_file = "Tests/images/pil123p.png"
with Image.open(in_file) as im:
Expand Down
8 changes: 8 additions & 0 deletions Tests/test_file_webp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
skip_unless_feature,
)

from io import BytesIO

try:
from PIL import _webp

Expand Down Expand Up @@ -85,6 +87,12 @@ def _roundtrip(self, tmp_path, mode, epsilon, args={}):
target = target.convert(self.rgb_mode)
assert_image_similar(image, target, epsilon)

def test_save_convert_mode(self):
out = BytesIO()
for mode in ["CMYK", "I", "L", "LA", "P"]:
img = Image.new(mode, (20, 20))
img.save(out, "WEBP", convert_mode=True)

def test_write_rgb(self, tmp_path):
"""
Can we write a RGB mode file to webp without error?
Expand Down
17 changes: 16 additions & 1 deletion Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
import pytest

import PIL
from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError
from PIL import (
Image,
ImageDraw,
ImagePalette,
ImageShow,
TiffImagePlugin,
UnidentifiedImageError,
)

from .helper import (
assert_image_equal,
Expand Down Expand Up @@ -377,6 +384,14 @@ def test_registered_extensions(self):
for ext in [".cur", ".icns", ".tif", ".tiff"]:
assert ext in extensions

def test_no_convert_mode(self):
self.assertTrue(not hasattr(TiffImagePlugin, "_convert_mode"))

temp_file = self.tempfile("temp.tiff")

im = hopper()
im.save(temp_file, convert_mode=True)

def test_effect_mandelbrot(self):
# Arrange
size = (512, 512)
Expand Down
7 changes: 7 additions & 0 deletions src/PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,13 @@ def write(self, data):
return fp.data


def _convert_mode(im):
return {
'LA':'P',
'CMYK':'RGB'
}.get(im.mode)


# --------------------------------------------------------------------
# Registry

Expand Down
46 changes: 41 additions & 5 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2115,10 +2115,6 @@ def save(self, fp, format=None, **params):
# may mutate self!
self._ensure_mutable()

save_all = params.pop("save_all", False)
self.encoderinfo = params
self.encoderconfig = ()

preinit()

ext = os.path.splitext(filename)[1].lower()
Expand All @@ -2133,11 +2129,20 @@ def save(self, fp, format=None, **params):

if format.upper() not in SAVE:
init()
if save_all:
if params.pop('save_all', False):
save_handler = SAVE_ALL[format.upper()]
else:
save_handler = SAVE[format.upper()]

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)

self.encoderinfo = params
self.encoderconfig = ()

if open_fp:
if params.get("append", False):
# Open also for reading ("+"), because TIFF save_all
Expand All @@ -2153,6 +2158,37 @@ def save(self, fp, format=None, **params):
if open_fp:
fp.close()

def _convert_mode(self, plugin, params):
if not hasattr(plugin, '_convert_mode'):
return
new_mode = plugin._convert_mode(self)
if self.mode == 'LA' and new_mode == 'P':
alpha = self.getchannel('A')
# Convert the image into P mode but only use 255 colors
# in the palette out of 256.
im = self.convert('L') \
.convert('P', palette=ADAPTIVE, colors=255)
# Set all pixel values below 128 to 255, and the rest to 0.
mask = eval(alpha, lambda px: 255 if px < 128 else 0)
# Paste the color of index 255 and use alpha as a mask.
im.paste(255, mask)
# The transparency index is 255.
im.info['transparency'] = 255
return im

elif self.mode == 'I':
im = self.point([i//256 for i in range(65536)], 'L')
return im.convert(new_mode) if new_mode != 'L' else im

elif self.mode in ('RGBA', 'LA') and new_mode in ('RGB', 'L'):
fill_color = params.get('fill_color', 'white')
background = new(new_mode, self.size, fill_color)
background.paste(self, self.getchannel('A'))
return background

elif new_mode:
return self.convert(new_mode)

def seek(self, frame):
"""
Seeks to the given frame in this sequence file. If you seek
Expand Down
11 changes: 11 additions & 0 deletions src/PIL/JpegImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,17 @@ 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)


# ---------------------------------------------------------------------
# Registry stuff

Expand Down
6 changes: 6 additions & 0 deletions src/PIL/PngImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,12 @@ def append(fp, cid, *data):
return fp.data


def _convert_mode(im):
return {
'CMYK':'RGB'
}.get(im.mode)


# --------------------------------------------------------------------
# Registry

Expand Down
13 changes: 13 additions & 0 deletions src/PIL/WebPImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,19 @@ 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)


Image.register_open(WebPImageFile.format, WebPImageFile, _accept)
if SUPPORTED:
Image.register_save(WebPImageFile.format, _save)
Expand Down

0 comments on commit aee5d22

Please sign in to comment.