mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-28 02:04:36 +03:00
Tests and partial implementation of pickling (#629)
This commit is contained in:
parent
8c5ed8a873
commit
adfbe8323a
47
PIL/Image.py
47
PIL/Image.py
|
@ -101,7 +101,7 @@ import collections
|
||||||
import numbers
|
import numbers
|
||||||
|
|
||||||
# works everywhere, win for pypy, not cpython
|
# works everywhere, win for pypy, not cpython
|
||||||
USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info')
|
USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info')
|
||||||
try:
|
try:
|
||||||
import cffi
|
import cffi
|
||||||
HAS_CFFI=True
|
HAS_CFFI=True
|
||||||
|
@ -233,7 +233,7 @@ _MODE_CONV = {
|
||||||
"CMYK": ('|u1', 4),
|
"CMYK": ('|u1', 4),
|
||||||
"YCbCr": ('|u1', 3),
|
"YCbCr": ('|u1', 3),
|
||||||
"LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1
|
"LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1
|
||||||
# I;16 == I;16L, and I;32 == I;32L
|
# I;16 == I;16L, and I;32 == I;32L
|
||||||
"I;16": ('<u2', None),
|
"I;16": ('<u2', None),
|
||||||
"I;16B": ('>u2', None),
|
"I;16B": ('>u2', None),
|
||||||
"I;16L": ('<u2', None),
|
"I;16L": ('<u2', None),
|
||||||
|
@ -502,7 +502,7 @@ class Image:
|
||||||
return self
|
return self
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
Closes the file pointer, if possible.
|
Closes the file pointer, if possible.
|
||||||
|
@ -524,7 +524,7 @@ class Image:
|
||||||
# deferred error that will better explain that the core image
|
# deferred error that will better explain that the core image
|
||||||
# object is gone.
|
# object is gone.
|
||||||
self.im = deferred_error(ValueError("Operation on closed image"))
|
self.im = deferred_error(ValueError("Operation on closed image"))
|
||||||
|
|
||||||
|
|
||||||
def _copy(self):
|
def _copy(self):
|
||||||
self.load()
|
self.load()
|
||||||
|
@ -540,7 +540,7 @@ class Image:
|
||||||
if not file:
|
if not file:
|
||||||
f, file = tempfile.mkstemp(suffix)
|
f, file = tempfile.mkstemp(suffix)
|
||||||
os.close(f)
|
os.close(f)
|
||||||
|
|
||||||
self.load()
|
self.load()
|
||||||
if not format or format == "PPM":
|
if not format or format == "PPM":
|
||||||
self.im.save_ppm(file)
|
self.im.save_ppm(file)
|
||||||
|
@ -568,6 +568,15 @@ class Image:
|
||||||
return new
|
return new
|
||||||
raise AttributeError(name)
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
def __getstate__(self):
|
||||||
|
return [self.mode, self.size, self.tobytes()]
|
||||||
|
|
||||||
|
def __setstate__(self, state):
|
||||||
|
mode, size, data = state
|
||||||
|
self.mode = mode
|
||||||
|
self.size = size
|
||||||
|
self.frombytes(data)
|
||||||
|
|
||||||
def tobytes(self, encoder_name="raw", *args):
|
def tobytes(self, encoder_name="raw", *args):
|
||||||
"""
|
"""
|
||||||
Return image as a bytes object
|
Return image as a bytes object
|
||||||
|
@ -672,7 +681,7 @@ class Image:
|
||||||
normal cases, you don't need to call this method, since the
|
normal cases, you don't need to call this method, since the
|
||||||
Image class automatically loads an opened image when it is
|
Image class automatically loads an opened image when it is
|
||||||
accessed for the first time. This method will close the file
|
accessed for the first time. This method will close the file
|
||||||
associated with the image.
|
associated with the image.
|
||||||
|
|
||||||
:returns: An image access object.
|
:returns: An image access object.
|
||||||
"""
|
"""
|
||||||
|
@ -777,7 +786,7 @@ class Image:
|
||||||
if "transparency" in self.info and self.info['transparency'] is not None:
|
if "transparency" in self.info and self.info['transparency'] is not None:
|
||||||
if self.mode in ('L', 'RGB') and mode == 'RGBA':
|
if self.mode in ('L', 'RGB') and mode == 'RGBA':
|
||||||
# Use transparent conversion to promote from transparent
|
# Use transparent conversion to promote from transparent
|
||||||
# color to an alpha channel.
|
# color to an alpha channel.
|
||||||
return self._new(self.im.convert_transparent(
|
return self._new(self.im.convert_transparent(
|
||||||
mode, self.info['transparency']))
|
mode, self.info['transparency']))
|
||||||
elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'):
|
elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'):
|
||||||
|
@ -799,11 +808,11 @@ class Image:
|
||||||
trns_im = trns_im.convert(mode)
|
trns_im = trns_im.convert(mode)
|
||||||
else:
|
else:
|
||||||
# can't just retrieve the palette number, got to do it
|
# can't just retrieve the palette number, got to do it
|
||||||
# after quantization.
|
# after quantization.
|
||||||
trns_im = trns_im.convert('RGB')
|
trns_im = trns_im.convert('RGB')
|
||||||
trns = trns_im.getpixel((0,0))
|
trns = trns_im.getpixel((0,0))
|
||||||
|
|
||||||
|
|
||||||
if mode == "P" and palette == ADAPTIVE:
|
if mode == "P" and palette == ADAPTIVE:
|
||||||
im = self.im.quantize(colors)
|
im = self.im.quantize(colors)
|
||||||
new = self._new(im)
|
new = self._new(im)
|
||||||
|
@ -811,7 +820,7 @@ class Image:
|
||||||
new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
|
new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
|
||||||
if delete_trns:
|
if delete_trns:
|
||||||
# This could possibly happen if we requantize to fewer colors.
|
# This could possibly happen if we requantize to fewer colors.
|
||||||
# The transparency would be totally off in that case.
|
# The transparency would be totally off in that case.
|
||||||
del(new.info['transparency'])
|
del(new.info['transparency'])
|
||||||
if trns is not None:
|
if trns is not None:
|
||||||
try:
|
try:
|
||||||
|
@ -826,7 +835,7 @@ class Image:
|
||||||
# colorspace conversion
|
# colorspace conversion
|
||||||
if dither is None:
|
if dither is None:
|
||||||
dither = FLOYDSTEINBERG
|
dither = FLOYDSTEINBERG
|
||||||
|
|
||||||
try:
|
try:
|
||||||
im = self.im.convert(mode, dither)
|
im = self.im.convert(mode, dither)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -863,7 +872,7 @@ class Image:
|
||||||
# quantizer interface in a later version of PIL.
|
# quantizer interface in a later version of PIL.
|
||||||
|
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
if method is None:
|
if method is None:
|
||||||
# defaults:
|
# defaults:
|
||||||
method = 0
|
method = 0
|
||||||
|
@ -871,10 +880,10 @@ class Image:
|
||||||
method = 2
|
method = 2
|
||||||
|
|
||||||
if self.mode == 'RGBA' and method != 2:
|
if self.mode == 'RGBA' and method != 2:
|
||||||
# Caller specified an invalid mode.
|
# Caller specified an invalid mode.
|
||||||
raise ValueError('Fast Octree (method == 2) is the ' +
|
raise ValueError('Fast Octree (method == 2) is the ' +
|
||||||
' only valid method for quantizing RGBA images')
|
' only valid method for quantizing RGBA images')
|
||||||
|
|
||||||
if palette:
|
if palette:
|
||||||
# use palette from reference image
|
# use palette from reference image
|
||||||
palette.load()
|
palette.load()
|
||||||
|
@ -928,7 +937,7 @@ class Image:
|
||||||
def draft(self, mode, size):
|
def draft(self, mode, size):
|
||||||
"""
|
"""
|
||||||
NYI
|
NYI
|
||||||
|
|
||||||
Configures the image file loader so it returns a version of the
|
Configures the image file loader so it returns a version of the
|
||||||
image that as closely as possible matches the given mode and
|
image that as closely as possible matches the given mode and
|
||||||
size. For example, you can use this method to convert a color
|
size. For example, you can use this method to convert a color
|
||||||
|
@ -1277,7 +1286,7 @@ class Image:
|
||||||
if self.mode in ("I", "I;16", "F"):
|
if self.mode in ("I", "I;16", "F"):
|
||||||
# check if the function can be used with point_transform
|
# check if the function can be used with point_transform
|
||||||
# UNDONE wiredfool -- I think this prevents us from ever doing
|
# UNDONE wiredfool -- I think this prevents us from ever doing
|
||||||
# a gamma function point transform on > 8bit images.
|
# a gamma function point transform on > 8bit images.
|
||||||
scale, offset = _getscaleoffset(lut)
|
scale, offset = _getscaleoffset(lut)
|
||||||
return self._new(self.im.point_transform(scale, offset))
|
return self._new(self.im.point_transform(scale, offset))
|
||||||
# for other modes, convert the function to a table
|
# for other modes, convert the function to a table
|
||||||
|
@ -1420,8 +1429,8 @@ class Image:
|
||||||
self._copy()
|
self._copy()
|
||||||
self.pyaccess = None
|
self.pyaccess = None
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
if self.pyaccess:
|
if self.pyaccess:
|
||||||
return self.pyaccess.putpixel(xy,value)
|
return self.pyaccess.putpixel(xy,value)
|
||||||
return self.im.putpixel(xy, value)
|
return self.im.putpixel(xy, value)
|
||||||
|
|
||||||
|
|
63
Tests/test_pickle.py
Normal file
63
Tests/test_pickle.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
from tester import *
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def test_frombytes_tobytes():
|
||||||
|
# Arrange
|
||||||
|
im = Image.open('Images/lena.jpg')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
data = im.tobytes()
|
||||||
|
new_im = Image.frombytes(im.mode, im.size, data)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_equal(im, new_im)
|
||||||
|
|
||||||
|
|
||||||
|
def helper_test_pickle_file(pickle, protocol=0):
|
||||||
|
im = Image.open('Images/lena.jpg')
|
||||||
|
filename = tempfile('temp.pkl')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
pickle.dump(im, f, protocol)
|
||||||
|
with open(filename, 'rb') as f:
|
||||||
|
loaded_im = pickle.load(f)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_equal(im, loaded_im)
|
||||||
|
|
||||||
|
|
||||||
|
def helper_test_pickle_string(pickle, protocol=0):
|
||||||
|
im = Image.open('Images/lena.jpg')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
dumped_string = pickle.dumps(im, protocol)
|
||||||
|
loaded_im = pickle.loads(dumped_string)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_equal(im, loaded_im)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pickle_image():
|
||||||
|
# Arrange
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
helper_test_pickle_string(pickle, protocol)
|
||||||
|
helper_test_pickle_file(pickle, protocol)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpickle_image():
|
||||||
|
# Arrange
|
||||||
|
import cPickle
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
helper_test_pickle_string(cPickle, protocol)
|
||||||
|
helper_test_pickle_file(cPickle, protocol)
|
||||||
|
|
||||||
|
|
||||||
|
# End of file
|
Loading…
Reference in New Issue
Block a user