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 Jun 12, 2018
1 parent 948e303 commit 9e29cef
Show file tree
Hide file tree
Showing 11 changed files with 139 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.
10 changes: 10 additions & 0 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,16 @@ def test_save_I(self):
reloaded = Image.open(out)
self.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(self):
# test getheader/getdata against legacy values
# Create a 'P' image with holes in the palette
Expand Down
14 changes: 13 additions & 1 deletion Tests/test_file_jpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,10 +509,22 @@ def test_save_correct_modes(self):
def test_save_wrong_modes(self):
# 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))
self.assertRaises(IOError, img.save, out, "JPEG")

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

img = Image.open('Tests/images/pil123rgba.png')
temp_file = self.tempfile("temp.jpg")
img.save(temp_file, convert_mode=True, fill_color='red')

reloaded = Image.open(temp_file)
target = Image.open('Tests/images/pil123rgba_red.jpg')
self.assert_image_similar(reloaded, target, 4)

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

def test_save_CMYK(self):
out = BytesIO()
img = Image.new('CMYK', (20, 20))
self.assertRaises(IOError, img.save, out, "PNG")

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

def test_save_p_transparent_palette(self):
in_file = "Tests/images/pil123p.png"
im = Image.open(in_file)
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 @@ -2,6 +2,8 @@

from PIL import Image

from io import BytesIO

try:
from PIL import _webp
HAVE_WEBP = True
Expand Down Expand Up @@ -44,6 +46,12 @@ def test_read_rgb(self):
target = target.convert(self.rgb_mode)
self.assert_image_similar(image, target, 20.0)

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):
"""
Can we write a RGB mode file to webp without error.
Expand Down
10 changes: 9 additions & 1 deletion Tests/test_image.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, hopper

from PIL import Image
from PIL import Image, TiffImagePlugin
from PIL._util import py3
import os

Expand Down Expand Up @@ -324,6 +324,14 @@ 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'))

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

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 @@ -798,6 +798,13 @@ def write(self, data):
return fp.data


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


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

Expand Down
49 changes: 43 additions & 6 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1903,12 +1903,6 @@ def save(self, fp, format=None, **params):
# may mutate self!
self.load()

save_all = False
if 'save_all' in params:
save_all = params.pop('save_all')
self.encoderinfo = params
self.encoderconfig = ()

preinit()

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

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

if params.get('convert_mode'):
plugin = sys.modules[save_handler.__module__]
convertedIm = self._convert_mode(plugin, params)
if convertedIm:
return convertedIm.save(fp, format, **params)

self.encoderinfo = params
self.encoderconfig = ()

if open_fp:
if params.get('append', False):
fp = builtins.open(filename, "r+b")
Expand All @@ -1943,6 +1949,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
17 changes: 17 additions & 0 deletions src/PIL/JpegImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,23 @@ def jpeg_factory(fp=None, filename=None):
return im


def _convert_mode(im):
mode = im.mode
if mode == 'P':
new_mode = 'RGB'
try:
if 'A' in im.im.getpalettemode():
new_mode = 'RGBA'
except ValueError:
pass
return new_mode
return {
'RGBA':'RGB',
'LA':'L',
'I':'L'
}.get(mode)


# -------------------------------------------------------------------q-
# 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 @@ -859,6 +859,12 @@ def append(fp, cid, *data):
return fp.data


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


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

Expand Down
19 changes: 19 additions & 0 deletions src/PIL/WebPImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,25 @@ def _save(im, fp, filename):
fp.write(data)


def _convert_mode(im):
mode = im.mode
if mode == 'P':
new_mode = 'RGB'
try:
if 'A' in im.im.getpalettemode():
new_mode = 'RGBA'
except ValueError:
pass
return new_mode
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)
Image.register_save(WebPImageFile.format, _save)
if _webp.HAVE_WEBPANIM:
Expand Down

0 comments on commit 9e29cef

Please sign in to comment.