Merge branch 'master' into winbuild

This commit is contained in:
wiredfool 2014-06-05 00:15:58 +00:00
commit 64efea9ed8
34 changed files with 845 additions and 425 deletions

11
.coveragerc Normal file
View File

@ -0,0 +1,11 @@
# .coveragerc to control coverage.py
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma:
pragma: no cover
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:

View File

@ -26,8 +26,14 @@ script:
- coverage erase
- python setup.py clean
- python setup.py build_ext --inplace
- coverage run --append --include=PIL/* selftest.py
- python Tests/run.py --coverage
# Don't cover PyPy: it fails intermittently and is x5.8 slower (#640)
- if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python selftest.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "pypy" ]; then python Tests/run.py; fi
# Cover the others
- if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then coverage run --append --include=PIL/* selftest.py; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then python Tests/run.py --coverage; fi
after_success:
- coverage report
@ -35,8 +41,5 @@ after_success:
- pip install pep8 pyflakes
- pep8 PIL/*.py
- pyflakes PIL/*.py
matrix:
allow_failures:
- python: "pypy"
- python: 3.4
- pep8 Tests/*.py
- pyflakes Tests/*.py

View File

@ -3,6 +3,41 @@ Changelog (Pillow)
2.5.0 (unreleased)
------------------
- Added tests for Spider files
[hugovk]
- Use libtiff to write any compressed tiff files
[wiredfool]
- Support for pickling Image objects
[hugovk]
- Fixed resolution handling for EPS thumbnails
[eliempje]
- Fixed rendering of some binary EPS files (Issue #302)
[eliempje]
- Rename variables not to use built-in function names
[hugovk]
- Ignore junk JPEG markers
[hugovk]
- Change default interpolation for Image.thumbnail to Image.ANTIALIAS
[hugovk]
- Add tests and fixes for saving PDFs
[hugovk]
- Remove transparency resource after P->RGBA conversion
[hugovk]
- Clean up preprocessor cruft for Windows
[CounterPillow]
- Adjust Homebrew freetype detection logic
[jacknagel]
- Added Image.close, context manager support.
[wiredfool]

View File

@ -11,6 +11,7 @@
# 1996-08-23 fl Handle files from Macintosh (0.3)
# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5)
# 2014-05-07 e Handling of EPS with binary preview and fixed resolution resizing
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-2003 by Fredrik Lundh
@ -71,14 +72,15 @@ def Ghostscript(tile, size, fp, scale=1):
# Unpack decoder tile
decoder, tile, offset, data = tile[0]
length, bbox = data
#Hack to support hi-res rendering
scale = int(scale) or 1
orig_size = size
orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale]
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox)
# resolution is dependend on bbox and size
res = ( float((72.0 * size[0]) / (bbox[2]-bbox[0])), float((72.0 * size[1]) / (bbox[3]-bbox[1])) )
#print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res)
import tempfile, os, subprocess
@ -86,21 +88,30 @@ def Ghostscript(tile, size, fp, scale=1):
os.close(out_fd)
in_fd, infile = tempfile.mkstemp()
os.close(in_fd)
# ignore length and offset!
# ghostscript can read it
# copy whole file to read in ghostscript
with open(infile, 'wb') as f:
fp.seek(offset)
while length >0:
s = fp.read(100*1024)
# fetch length of fp
fp.seek(0, 2)
fsize = fp.tell()
# ensure start position
# go back
fp.seek(0)
lengthfile = fsize
while lengthfile > 0:
s = fp.read(min(lengthfile, 100*1024))
if not s:
break
length = length - len(s)
lengthfile = lengthfile - len(s)
f.write(s)
# Build ghostscript command
command = ["gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%d" % (72*scale), # set input DPI (dots per inch)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dNOPAUSE -dSAFER", # don't pause between pages, safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
@ -108,7 +119,7 @@ def Ghostscript(tile, size, fp, scale=1):
# adjust for image origin
"-f", infile, # input file
]
if gs_windows_binary is not None:
if not gs_windows_binary:
raise WindowsError('Unable to locate Ghostscript on paths')
@ -127,7 +138,7 @@ def Ghostscript(tile, size, fp, scale=1):
os.unlink(outfile)
os.unlink(infile)
except: pass
return im
@ -145,6 +156,8 @@ class PSFile:
self.fp.seek(offset, whence)
def read(self, count):
return self.fp.read(count).decode('latin-1')
def readbinary(self, count):
return self.fp.read(count)
def tell(self):
pos = self.fp.tell()
if self.char:
@ -182,26 +195,34 @@ class EpsImageFile(ImageFile.ImageFile):
def _open(self):
# FIXME: should check the first 512 bytes to see if this
# really is necessary (platform-dependent, though...)
fp = PSFile(self.fp)
# HEAD
s = fp.read(512)
# FIX for: Some EPS file not handled correctly / issue #302
# EPS can contain binary data
# or start directly with latin coding
# read header in both ways to handle both
# file types
# more info see http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
# for HEAD without binary preview
s = fp.read(4)
# for HEAD with binary preview
fp.seek(0)
sb = fp.readbinary(160)
if s[:4] == "%!PS":
offset = 0
fp.seek(0, 2)
length = fp.tell()
elif i32(s) == 0xC6D3D0C5:
offset = i32(s[4:])
length = i32(s[8:])
fp.seek(offset)
offset = 0
elif i32(sb[0:4]) == 0xC6D3D0C5:
offset = i32(sb[4:8])
length = i32(sb[8:12])
else:
raise SyntaxError("not an EPS file")
# go to offset - start of "%!PS"
fp.seek(offset)
box = None
self.mode = "RGB"
@ -211,7 +232,7 @@ class EpsImageFile(ImageFile.ImageFile):
# Load EPS header
s = fp.readline()
while s:
if len(s) > 255:

View File

@ -30,6 +30,7 @@ from PIL import VERSION, PILLOW_VERSION, _plugins
import warnings
class _imaging_not_installed:
# module placeholder
def __getattr__(self, id):
@ -52,8 +53,8 @@ try:
# directly; import Image and use the Image.core variable instead.
from PIL import _imaging as core
if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
raise ImportError("The _imaging extension was built for another "
" version of Pillow or PIL")
raise ImportError("The _imaging extension was built for another "
" version of Pillow or PIL")
except ImportError as v:
core = _imaging_not_installed()
@ -91,22 +92,26 @@ except ImportError:
builtins = __builtin__
from PIL import ImageMode
from PIL._binary import i8, o8
from PIL._util import isPath, isStringType, deferred_error
from PIL._binary import i8
from PIL._util import isPath
from PIL._util import isStringType
from PIL._util import deferred_error
import os, sys
import os
import sys
# type stuff
import collections
import numbers
# works everywhere, win for pypy, not cpython
USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info')
USE_CFFI_ACCESS = hasattr(sys, 'pypy_version_info')
try:
import cffi
HAS_CFFI=True
HAS_CFFI = True
except:
HAS_CFFI=False
HAS_CFFI = False
def isImageType(t):
"""
@ -148,16 +153,16 @@ MESH = 4
# resampling filters
NONE = 0
NEAREST = 0
ANTIALIAS = 1 # 3-lobed lanczos
ANTIALIAS = 1 # 3-lobed lanczos
LINEAR = BILINEAR = 2
CUBIC = BICUBIC = 3
# dithers
NONE = 0
NEAREST = 0
ORDERED = 1 # Not yet implemented
RASTERIZE = 2 # Not yet implemented
FLOYDSTEINBERG = 3 # default
ORDERED = 1 # Not yet implemented
RASTERIZE = 2 # Not yet implemented
FLOYDSTEINBERG = 3 # default
# palettes/quantizers
WEB = 0
@ -222,7 +227,7 @@ else:
_MODE_CONV = {
# official modes
"1": ('|b1', None), # broken
"1": ('|b1', None), # broken
"L": ('|u1', None),
"I": (_ENDIAN + 'i4', None),
"F": (_ENDIAN + 'f4', None),
@ -232,8 +237,8 @@ _MODE_CONV = {
"RGBA": ('|u1', 4),
"CMYK": ('|u1', 4),
"YCbCr": ('|u1', 3),
"LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1
# I;16 == I;16L, and I;32 == I;32L
"LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1
# I;16 == I;16L, and I;32 == I;32L
"I;16": ('<u2', None),
"I;16B": ('>u2', None),
"I;16L": ('<u2', None),
@ -248,6 +253,7 @@ _MODE_CONV = {
"I;32LS": ('<i4', None),
}
def _conv_type_shape(im):
shape = im.size[1], im.size[0]
typ, extra = _MODE_CONV[im.mode]
@ -368,8 +374,8 @@ def init():
for plugin in _plugins:
try:
if DEBUG:
print ("Importing %s"%plugin)
__import__("PIL.%s"%plugin, globals(), locals(), [])
print ("Importing %s" % plugin)
__import__("PIL.%s" % plugin, globals(), locals(), [])
except ImportError:
if DEBUG:
print("Image: failed to import", end=' ')
@ -379,6 +385,7 @@ def init():
_initialized = 2
return 1
# --------------------------------------------------------------------
# Codec factories (used by tobytes/frombytes and ImageFile.load)
@ -398,6 +405,7 @@ def _getdecoder(mode, decoder_name, args, extra=()):
except AttributeError:
raise IOError("decoder %s not available" % decoder_name)
def _getencoder(mode, encoder_name, args, extra=()):
# tweak arguments
@ -421,30 +429,36 @@ def _getencoder(mode, encoder_name, args, extra=()):
def coerce_e(value):
return value if isinstance(value, _E) else _E(value)
class _E:
def __init__(self, data):
self.data = data
def __add__(self, other):
return _E((self.data, "__add__", coerce_e(other).data))
def __mul__(self, other):
return _E((self.data, "__mul__", coerce_e(other).data))
def _getscaleoffset(expr):
stub = ["stub"]
data = expr(_E(stub)).data
try:
(a, b, c) = data # simplified syntax
(a, b, c) = data # simplified syntax
if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)):
return c, 0.0
if (a is stub and b == "__add__" and isinstance(c, numbers.Number)):
return 1.0, c
except TypeError: pass
except TypeError:
pass
try:
((a, b, c), d, e) = data # full syntax
((a, b, c), d, e) = data # full syntax
if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and
d == "__add__" and isinstance(e, numbers.Number)):
d == "__add__" and isinstance(e, numbers.Number)):
return c, e
except TypeError: pass
except TypeError:
pass
raise ValueError("illegal expression")
@ -495,14 +509,15 @@ class Image:
new.info[k] = v
return new
_makeself = _new # compatibility
_makeself = _new # compatibility
# Context Manager Support
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
"""
Closes the file pointer, if possible.
@ -518,13 +533,12 @@ class Image:
self.fp.close()
except Exception as msg:
if Image.DEBUG:
print ("Error closing: %s" %msg)
print ("Error closing: %s" % msg)
# Instead of simply setting to None, we're setting up a
# deferred error that will better explain that the core image
# object is gone.
self.im = deferred_error(ValueError("Operation on closed image"))
def _copy(self):
self.load()
@ -533,14 +547,15 @@ class Image:
self.readonly = 0
def _dump(self, file=None, format=None):
import tempfile, os
import os
import tempfile
suffix = ''
if format:
suffix = '.'+format
if not file:
f, file = tempfile.mkstemp(suffix)
os.close(f)
self.load()
if not format or format == "PPM":
self.im.save_ppm(file)
@ -550,6 +565,20 @@ class Image:
self.save(file, format)
return file
def __eq__(self, other):
a = (self.mode == other.mode)
b = (self.size == other.size)
c = (self.getpalette() == other.getpalette())
d = (self.info == other.info)
e = (self.category == other.category)
f = (self.readonly == other.readonly)
g = (self.tobytes() == other.tobytes())
return a and b and c and d and e and f and g
def __ne__(self, other):
eq = (self == other)
return not eq
def __repr__(self):
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
self.__class__.__module__, self.__class__.__name__,
@ -568,6 +597,26 @@ class Image:
return new
raise AttributeError(name)
def __getstate__(self):
return [
self.info,
self.mode,
self.size,
self.getpalette(),
self.tobytes()]
def __setstate__(self, state):
Image.__init__(self)
self.tile = []
info, mode, size, palette, data = state
self.info = info
self.mode = mode
self.size = size
self.im = core.new(mode, size)
if mode in ("L", "P"):
self.putpalette(palette)
self.frombytes(data)
def tobytes(self, encoder_name="raw", *args):
"""
Return image as a bytes object
@ -591,7 +640,7 @@ class Image:
e = _getencoder(self.mode, encoder_name, args)
e.setimage(self.im)
bufsize = max(65536, self.size[0] * 4) # see RawEncode.c
bufsize = max(65536, self.size[0] * 4) # see RawEncode.c
data = []
while True:
@ -628,9 +677,11 @@ class Image:
if self.mode != "1":
raise ValueError("not a bitmap")
data = self.tobytes("xbm")
return b"".join([("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'),
("#define %s_height %d\n"% (name, self.size[1])).encode('ascii'),
("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"])
return b"".join([
("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'),
("#define %s_height %d\n" % (name, self.size[1])).encode('ascii'),
("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"
])
def frombytes(self, data, decoder_name="raw", *args):
"""
@ -663,7 +714,9 @@ class Image:
.. deprecated:: 2.0
"""
warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning)
warnings.warn(
'fromstring() is deprecated. Please call frombytes() instead.',
DeprecationWarning)
return self.frombytes(*args, **kw)
def load(self):
@ -672,7 +725,7 @@ class Image:
normal cases, you don't need to call this method, since the
Image class automatically loads an opened image when it is
accessed for the first time. This method will close the file
associated with the image.
associated with the image.
:returns: An image access object.
"""
@ -774,36 +827,39 @@ class Image:
trns = None
delete_trns = False
# transparency handling
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':
# Use transparent conversion to promote from transparent
# color to an alpha channel.
# color to an alpha channel.
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'):
t = self.info['transparency']
if isinstance(t, bytes):
# Dragons. This can't be represented by a single color
warnings.warn('Palette images with Transparency expressed '+
warnings.warn('Palette images with Transparency expressed ' +
' in bytes should be converted to RGBA images')
delete_trns = True
else:
# get the new transparency color.
# use existing conversions
trns_im = Image()._new(core.new(self.mode, (1,1)))
trns_im = Image()._new(core.new(self.mode, (1, 1)))
if self.mode == 'P':
trns_im.putpalette(self.palette)
trns_im.putpixel((0,0), t)
trns_im.putpixel((0, 0), t)
if mode in ('L','RGB'):
if mode in ('L', 'RGB'):
trns_im = trns_im.convert(mode)
else:
# can't just retrieve the palette number, got to do it
# after quantization.
# after quantization.
trns_im = trns_im.convert('RGB')
trns = trns_im.getpixel((0,0))
elif self.mode == 'P' and mode == 'RGBA':
delete_trns = True
if mode == "P" and palette == ADAPTIVE:
im = self.im.quantize(colors)
new = self._new(im)
@ -811,7 +867,7 @@ class Image:
new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
if delete_trns:
# 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'])
if trns is not None:
try:
@ -820,13 +876,14 @@ class Image:
# if we can't make a transparent color, don't leave the old
# transparency hanging around to mess us up.
del(new.info['transparency'])
warnings.warn("Couldn't allocate palette entry for transparency")
warnings.warn("Couldn't allocate palette entry " +
"for transparency")
return new
# colorspace conversion
if dither is None:
dither = FLOYDSTEINBERG
try:
im = self.im.convert(mode, dither)
except ValueError:
@ -839,7 +896,7 @@ class Image:
new_im = self._new(im)
if delete_trns:
#crash fail if we leave a bytes transparency in an rgb/l mode.
# crash fail if we leave a bytes transparency in an rgb/l mode.
del(new_im.info['transparency'])
if trns is not None:
if new_im.mode == 'P':
@ -847,7 +904,8 @@ class Image:
new_im.info['transparency'] = new_im.palette.getcolor(trns)
except:
del(new_im.info['transparency'])
warnings.warn("Couldn't allocate palette entry for transparency")
warnings.warn("Couldn't allocate palette entry " +
"for transparency")
else:
new_im.info['transparency'] = trns
return new_im
@ -863,7 +921,7 @@ class Image:
# quantizer interface in a later version of PIL.
self.load()
if method is None:
# defaults:
method = 0
@ -871,10 +929,10 @@ class Image:
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 ' +
' only valid method for quantizing RGBA images')
if palette:
# use palette from reference image
palette.load()
@ -928,7 +986,7 @@ class Image:
def draft(self, mode, size):
"""
NYI
Configures the image file loader so it returns a version of the
image that as closely as possible matches the given mode and
size. For example, you can use this method to convert a color
@ -963,7 +1021,8 @@ class Image:
if isinstance(filter, collections.Callable):
filter = filter()
if not hasattr(filter, "filter"):
raise TypeError("filter argument should be ImageFilter.Filter instance or class")
raise TypeError("filter argument should be ImageFilter.Filter " +
"instance or class")
if self.im.bands == 1:
return self._new(filter.filter(self.im))
@ -1019,7 +1078,7 @@ class Image:
return out
return self.im.getcolors(maxcolors)
def getdata(self, band = None):
def getdata(self, band=None):
"""
Returns the contents of this image as a sequence object
containing pixel values. The sequence object is flattened, so
@ -1040,7 +1099,7 @@ class Image:
self.load()
if band is not None:
return self.im.getband(band)
return self.im # could be abused
return self.im # could be abused
def getextrema(self):
"""
@ -1070,7 +1129,6 @@ class Image:
self.load()
return self.im.ptr
def getpalette(self):
"""
Returns the image palette as a list.
@ -1086,8 +1144,7 @@ class Image:
else:
return list(self.im.getpalette())
except ValueError:
return None # no palette
return None # no palette
def getpixel(self, xy):
"""
@ -1209,7 +1266,8 @@ class Image:
if isImageType(box) and mask is None:
# abbreviated paste(im, mask) syntax
mask = box; box = None
mask = box
box = None
if box is None:
# cover all of self
@ -1277,7 +1335,7 @@ class Image:
if self.mode in ("I", "I;16", "F"):
# check if the function can be used with point_transform
# 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)
return self._new(self.im.point_transform(scale, offset))
# for other modes, convert the function to a table
@ -1315,7 +1373,7 @@ class Image:
# do things the hard way
im = self.im.convert(mode)
if im.mode not in ("LA", "RGBA"):
raise ValueError # sanity check
raise ValueError # sanity check
self.im = im
self.pyaccess = None
self.mode = self.im.mode
@ -1393,7 +1451,7 @@ class Image:
self.mode = "P"
self.palette = palette
self.palette.mode = "RGB"
self.load() # install new palette
self.load() # install new palette
def putpixel(self, xy, value):
"""
@ -1420,9 +1478,9 @@ class Image:
self._copy()
self.pyaccess = None
self.load()
if self.pyaccess:
return self.pyaccess.putpixel(xy,value)
if self.pyaccess:
return self.pyaccess.putpixel(xy, value)
return self.im.putpixel(xy, value)
def resize(self, size, resample=NEAREST):
@ -1489,9 +1547,11 @@ class Image:
import math
angle = -angle * math.pi / 180
matrix = [
math.cos(angle), math.sin(angle), 0.0,
math.cos(angle), math.sin(angle), 0.0,
-math.sin(angle), math.cos(angle), 0.0
]
]
def transform(x, y, matrix=matrix):
(a, b, c, d, e, f) = matrix
return a*x + b*y + c, d*x + e*y + f
@ -1579,13 +1639,13 @@ class Image:
try:
format = EXTENSION[ext]
except KeyError:
raise KeyError(ext) # unknown extension
raise KeyError(ext) # unknown extension
try:
save_handler = SAVE[format.upper()]
except KeyError:
init()
save_handler = SAVE[format.upper()] # unknown format
save_handler = SAVE[format.upper()] # unknown format
if isPath(fp):
fp = builtins.open(fp, "wb")
@ -1667,7 +1727,7 @@ class Image:
"""
return 0
def thumbnail(self, size, resample=NEAREST):
def thumbnail(self, size, resample=ANTIALIAS):
"""
Make this image into a thumbnail. This method modifies the
image to contain a thumbnail version of itself, no larger than
@ -1682,26 +1742,28 @@ class Image:
important than quality.
Also note that this function modifies the :py:class:`~PIL.Image.Image`
object in place. If you need to use the full resolution image as well, apply
this method to a :py:meth:`~PIL.Image.Image.copy` of the original image.
object in place. If you need to use the full resolution image as well,
apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original
image.
:param size: Requested size.
:param resample: Optional resampling filter. This can be one
of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`,
:py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS`
(best quality). If omitted, it defaults to
:py:attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a
future version).
:py:attr:`PIL.Image.ANTIALIAS`. (was :py:attr:`PIL.Image.NEAREST`
prior to version 2.5.0)
:returns: None
"""
# FIXME: the default resampling filter will be changed
# to ANTIALIAS in future versions
# preserve aspect ratio
x, y = self.size
if x > size[0]: y = int(max(y * size[0] / x, 1)); x = int(size[0])
if y > size[1]: x = int(max(x * size[1] / y, 1)); y = int(size[1])
if x > size[0]:
y = int(max(y * size[0] / x, 1))
x = int(size[0])
if y > size[1]:
x = int(max(x * size[1] / y, 1))
y = int(size[1])
size = x, y
if size == self.size:
@ -1716,7 +1778,7 @@ class Image:
except ValueError:
if resample != ANTIALIAS:
raise
im = self.resize(size, NEAREST) # fallback
im = self.resize(size, NEAREST) # fallback
self.im = im.im
self.mode = im.mode
@ -1752,7 +1814,8 @@ class Image:
"""
if self.mode == 'RGBA':
return self.convert('RGBa').transform(size, method, data, resample, fill).convert('RGBA')
return self.convert('RGBa').transform(
size, method, data, resample, fill).convert('RGBA')
if isinstance(method, ImageTransformHandler):
return method.transform(size, self, resample=resample, fill=fill)
@ -1799,8 +1862,13 @@ class Image:
elif method == QUAD:
# quadrilateral warp. data specifies the four corners
# given as NW, SW, SE, and NE.
nw = data[0:2]; sw = data[2:4]; se = data[4:6]; ne = data[6:8]
x0, y0 = nw; As = 1.0 / w; At = 1.0 / h
nw = data[0:2]
sw = data[2:4]
se = data[4:6]
ne = data[6:8]
x0, y0 = nw
As = 1.0 / w
At = 1.0 / h
data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At,
(se[0]-sw[0]-ne[0]+x0)*As*At,
y0, (ne[1]-y0)*As, (sw[1]-y0)*At,
@ -1834,6 +1902,7 @@ class Image:
im = self.im.transpose(method)
return self._new(im)
# --------------------------------------------------------------------
# Lazy operations
@ -1869,6 +1938,7 @@ class _ImageCrop(Image):
# FIXME: future versions should optimize crop/paste
# sequences!
# --------------------------------------------------------------------
# Abstract handlers.
@ -1876,10 +1946,12 @@ class ImagePointHandler:
# used as a mixin by point transforms (for use with im.point)
pass
class ImageTransformHandler:
# used as a mixin by geometry transforms (for use with im.transform)
pass
# --------------------------------------------------------------------
# Factories
@ -1955,6 +2027,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args):
im.frombytes(data, decoder_name, args)
return im
def fromstring(*args, **kw):
"""Deprecated alias to frombytes.
@ -2017,9 +2090,9 @@ def frombuffer(mode, size, data, decoder_name="raw", *args):
" frombuffer(mode, size, data, 'raw', mode, 0, 1)",
RuntimeWarning, stacklevel=2
)
args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6
args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6
if args[0] in _MAPMODES:
im = new(mode, (1,1))
im = new(mode, (1, 1))
im = im._new(
core.map_buffer(data, size, decoder_name, None, 0, args)
)
@ -2139,8 +2212,8 @@ def open(fp, mode="r"):
fp.seek(0)
return factory(fp, filename)
except (SyntaxError, IndexError, TypeError):
#import traceback
#traceback.print_exc()
# import traceback
# traceback.print_exc()
pass
if init():
@ -2152,13 +2225,14 @@ def open(fp, mode="r"):
fp.seek(0)
return factory(fp, filename)
except (SyntaxError, IndexError, TypeError):
#import traceback
#traceback.print_exc()
# import traceback
# traceback.print_exc()
pass
raise IOError("cannot identify image file %r"
% (filename if filename else fp))
#
# Image processing.
@ -2257,6 +2331,7 @@ def merge(mode, bands):
im.putband(bands[i].im, i)
return bands[0]._new(im)
# --------------------------------------------------------------------
# Plugin registry
@ -2315,6 +2390,7 @@ def _show(image, **options):
# override me, as necessary
_showxv(image, **options)
def _showxv(image, title=None, **options):
from PIL import ImageShow
ImageShow.show(image, title, **options)

View File

@ -34,7 +34,8 @@
__version__ = "0.6"
import array, struct
import array
import struct
from PIL import Image, ImageFile, _binary
from PIL.JpegPresets import presets
from PIL._util import isStringType
@ -44,6 +45,7 @@ o8 = _binary.o8
i16 = _binary.i16be
i32 = _binary.i32be
#
# Parser
@ -51,6 +53,7 @@ def Skip(self, marker):
n = i16(self.fp.read(2))-2
ImageFile._safe_read(self.fp, n)
def APP(self, marker):
#
# Application marker. Store these in the APP dictionary.
@ -59,14 +62,14 @@ def APP(self, marker):
n = i16(self.fp.read(2))-2
s = ImageFile._safe_read(self.fp, n)
app = "APP%d" % (marker&15)
app = "APP%d" % (marker & 15)
self.app[app] = s # compatibility
self.app[app] = s # compatibility
self.applist.append((app, s))
if marker == 0xFFE0 and s[:4] == b"JFIF":
# extract JFIF information
self.info["jfif"] = version = i16(s, 5) # version
self.info["jfif"] = version = i16(s, 5) # version
self.info["jfif_version"] = divmod(version, 256)
# extract JFIF properties
try:
@ -81,10 +84,10 @@ def APP(self, marker):
self.info["jfif_density"] = jfif_density
elif marker == 0xFFE1 and s[:5] == b"Exif\0":
# extract Exif information (incomplete)
self.info["exif"] = s # FIXME: value will change
self.info["exif"] = s # FIXME: value will change
elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
# extract FlashPix information (incomplete)
self.info["flashpix"] = s # FIXME: value will change
self.info["flashpix"] = s # FIXME: value will change
elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0":
# Since an ICC profile can be larger than the maximum size of
# a JPEG marker (64K), we need provisions to split it into
@ -108,16 +111,17 @@ def APP(self, marker):
else:
self.info["adobe_transform"] = adobe_transform
def COM(self, marker):
#
# Comment marker. Store these in the APP dictionary.
n = i16(self.fp.read(2))-2
s = ImageFile._safe_read(self.fp, n)
self.app["COM"] = s # compatibility
self.app["COM"] = s # compatibility
self.applist.append(("COM", s))
def SOF(self, marker):
#
# Start of frame marker. Defines the size and mode of the
@ -149,21 +153,22 @@ def SOF(self, marker):
if self.icclist:
# fixup icc profile
self.icclist.sort() # sort by sequence number
self.icclist.sort() # sort by sequence number
if i8(self.icclist[0][13]) == len(self.icclist):
profile = []
for p in self.icclist:
profile.append(p[14:])
icc_profile = b"".join(profile)
else:
icc_profile = None # wrong number of fragments
icc_profile = None # wrong number of fragments
self.info["icc_profile"] = icc_profile
self.icclist = None
for i in range(6, len(s), 3):
t = s[i:i+3]
# 4-tuples: id, vsamp, hsamp, qtable
self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2])))
self.layer.append((t[0], i8(t[1])//16, i8(t[1]) & 15, i8(t[2])))
def DQT(self, marker):
#
@ -181,10 +186,10 @@ def DQT(self, marker):
raise SyntaxError("bad quantization table marker")
v = i8(s[0])
if v//16 == 0:
self.quantization[v&15] = array.array("b", s[1:65])
self.quantization[v & 15] = array.array("b", s[1:65])
s = s[65:]
else:
return # FIXME: add code to read 16-bit tables!
return # FIXME: add code to read 16-bit tables!
# raise SyntaxError, "bad quantization table element size"
@ -261,6 +266,7 @@ MARKER = {
def _accept(prefix):
return prefix[0:1] == b"\377"
##
# Image plugin for JPEG and JFIF images.
@ -284,32 +290,37 @@ class JpegImageFile(ImageFile.ImageFile):
self.huffman_dc = {}
self.huffman_ac = {}
self.quantization = {}
self.app = {} # compatibility
self.app = {} # compatibility
self.applist = []
self.icclist = []
while True:
s = s + self.fp.read(1)
i = i16(s)
i = i8(s)
if i == 0xFF:
s = s + self.fp.read(1)
i = i16(s)
else:
# Skip non-0xFF junk
s = b"\xff"
continue
if i in MARKER:
name, description, handler = MARKER[i]
# print hex(i), name, description
if handler is not None:
handler(self, i)
if i == 0xFFDA: # start of scan
if i == 0xFFDA: # start of scan
rawmode = self.mode
if self.mode == "CMYK":
rawmode = "CMYK;I" # assume adobe conventions
self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))]
rawmode = "CMYK;I" # assume adobe conventions
self.tile = [("jpeg", (0, 0) + self.size, 0, (rawmode, ""))]
# self.__offset = self.fp.tell()
break
s = self.fp.read(1)
elif i == 0 or i == 65535:
elif i == 0 or i == 0xFFFF:
# padded marker or junk; move on
s = "\xff"
s = b"\xff"
else:
raise SyntaxError("no marker found")
@ -343,7 +354,8 @@ class JpegImageFile(ImageFile.ImageFile):
# ALTERNATIVE: handle JPEGs via the IJG command line utilities
import tempfile, os
import tempfile
import os
f, path = tempfile.mkstemp()
os.close(f)
if os.path.exists(self.filename):
@ -354,8 +366,10 @@ class JpegImageFile(ImageFile.ImageFile):
try:
self.im = Image.core.open_ppm(path)
finally:
try: os.unlink(path)
except: pass
try:
os.unlink(path)
except:
pass
self.mode = self.im.mode
self.size = self.im.size
@ -372,6 +386,7 @@ def _getexif(self):
# version.
from PIL import TiffImagePlugin
import io
def fixup(value):
if len(value) == 1:
return value[0]
@ -422,7 +437,7 @@ RAWMODE = {
"RGB": "RGB",
"RGBA": "RGB",
"RGBX": "RGB",
"CMYK": "CMYK;I", # assume adobe conventions
"CMYK": "CMYK;I", # assume adobe conventions
"YCbCr": "YCbCr",
}
@ -441,16 +456,19 @@ samplings = {
(2, 2, 1, 1, 1, 1): 2,
}
def convert_dict_qtables(qtables):
qtables = [qtables[key] for key in range(len(qtables)) if key in qtables]
for idx, table in enumerate(qtables):
qtables[idx] = [table[i] for i in zigzag_index]
return qtables
def get_sampling(im):
sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
return samplings.get(sampling, -1)
def _save(im, fp, filename):
try:
@ -563,12 +581,11 @@ def _save(im, fp, filename):
info.get("exif", b"")
)
# if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot.
# Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this
# is a value that's been used in a django patch.
# if we optimize, libjpeg needs a buffer big enough to hold the whole image
# in a shot. Guessing on the size, at im.size bytes. (raw pizel size is
# channels*size, this is a value that's been used in a django patch.
# https://github.com/jdriscoll/django-imagekit/issues/50
bufsize=0
bufsize = 0
if "optimize" in info or "progressive" in info or "progression" in info:
if quality >= 95:
bufsize = 2 * im.size[0] * im.size[1]
@ -577,17 +594,20 @@ def _save(im, fp, filename):
# The exif info needs to be written as one block, + APP1, + one spare byte.
# Ensure that our buffer is big enough
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 )
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5)
ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)
ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize)
def _save_cjpeg(im, fp, filename):
# ALTERNATIVE: handle JPEGs via the IJG command line utilities.
import os
file = im._dump()
os.system("cjpeg %s >%s" % (file, filename))
try: os.unlink(file)
except: pass
try:
os.unlink(file)
except:
pass
# -------------------------------------------------------------------q-
# Registry stuff

View File

@ -46,9 +46,11 @@ def _obj(fp, obj, **dict):
fp.write("/%s %s\n" % (k, v))
fp.write(">>\n")
def _endobj(fp):
fp.write("endobj\n")
##
# (Internal) Image save plugin for the PDF format.
@ -59,13 +61,15 @@ def _save(im, fp, filename):
# make sure image data is available
im.load()
xref = [0]*(5+1) # placeholders
xref = [0]*(5+1) # placeholders
class TextWriter:
def __init__(self, fp):
self.fp = fp
def __getattr__(self, name):
return getattr(self.fp, name)
def write(self, value):
self.fp.write(value.encode('latin-1'))
@ -89,13 +93,13 @@ def _save(im, fp, filename):
if im.mode == "1":
filter = "/ASCIIHexDecode"
colorspace = "/DeviceGray"
procset = "/ImageB" # grayscale
procset = "/ImageB" # grayscale
bits = 1
elif im.mode == "L":
filter = "/DCTDecode"
# params = "<< /Predictor 15 /Columns %d >>" % (width-2)
colorspace = "/DeviceGray"
procset = "/ImageB" # grayscale
procset = "/ImageB" # grayscale
elif im.mode == "P":
filter = "/ASCIIHexDecode"
colorspace = "[ /Indexed /DeviceRGB 255 <"
@ -105,16 +109,16 @@ def _save(im, fp, filename):
g = i8(palette[i*3+1])
b = i8(palette[i*3+2])
colorspace = colorspace + "%02x%02x%02x " % (r, g, b)
colorspace = colorspace + b"> ]"
procset = "/ImageI" # indexed color
colorspace = colorspace + "> ]"
procset = "/ImageI" # indexed color
elif im.mode == "RGB":
filter = "/DCTDecode"
colorspace = "/DeviceRGB"
procset = "/ImageC" # color images
procset = "/ImageC" # color images
elif im.mode == "CMYK":
filter = "/DCTDecode"
colorspace = "/DeviceCMYK"
procset = "/ImageC" # color images
procset = "/ImageC" # color images
else:
raise ValueError("cannot save mode %s" % im.mode)
@ -122,17 +126,21 @@ def _save(im, fp, filename):
# catalogue
xref[1] = fp.tell()
_obj(fp, 1, Type = "/Catalog",
Pages = "2 0 R")
_obj(
fp, 1,
Type="/Catalog",
Pages="2 0 R")
_endobj(fp)
#
# pages
xref[2] = fp.tell()
_obj(fp, 2, Type = "/Pages",
Count = 1,
Kids = "[4 0 R]")
_obj(
fp, 2,
Type="/Pages",
Count=1,
Kids="[4 0 R]")
_endobj(fp)
#
@ -144,29 +152,31 @@ def _save(im, fp, filename):
if bits == 1:
# FIXME: the hex encoder doesn't support packed 1-bit
# images; do things the hard way...
data = im.tostring("raw", "1")
data = im.tobytes("raw", "1")
im = Image.new("L", (len(data), 1), None)
im.putdata(data)
ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)])
ImageFile._save(im, op, [("hex", (0, 0)+im.size, 0, im.mode)])
elif filter == "/DCTDecode":
Image.SAVE["JPEG"](im, op, filename)
elif filter == "/FlateDecode":
ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)])
ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)])
elif filter == "/RunLengthDecode":
ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)])
ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)])
else:
raise ValueError("unsupported PDF filter (%s)" % filter)
xref[3] = fp.tell()
_obj(fp, 3, Type = "/XObject",
Subtype = "/Image",
Width = width, # * 72.0 / resolution,
Height = height, # * 72.0 / resolution,
Length = len(op.getvalue()),
Filter = filter,
BitsPerComponent = bits,
DecodeParams = params,
ColorSpace = colorspace)
_obj(
fp, 3,
Type="/XObject",
Subtype="/Image",
Width=width, # * 72.0 / resolution,
Height=height, # * 72.0 / resolution,
Length=len(op.getvalue()),
Filter=filter,
BitsPerComponent=bits,
DecodeParams=params,
ColorSpace=colorspace)
fp.write("stream\n")
fp.fp.write(op.getvalue())
@ -179,11 +189,14 @@ def _save(im, fp, filename):
xref[4] = fp.tell()
_obj(fp, 4)
fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\
"/Resources <<\n/ProcSet [ /PDF %s ]\n"\
"/XObject << /image 3 0 R >>\n>>\n"\
"/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\
(procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution)))
fp.write(
"<<\n/Type /Page\n/Parent 2 0 R\n"
"/Resources <<\n/ProcSet [ /PDF %s ]\n"
"/XObject << /image 3 0 R >>\n>>\n"
"/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" % (
procset,
int(width * 72.0 / resolution),
int(height * 72.0 / resolution)))
_endobj(fp)
#
@ -191,10 +204,13 @@ def _save(im, fp, filename):
op = TextWriter(io.BytesIO())
op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution)))
op.write(
"q %d 0 0 %d 0 0 cm /image Do Q\n" % (
int(width * 72.0 / resolution),
int(height * 72.0 / resolution)))
xref[5] = fp.tell()
_obj(fp, 5, Length = len(op.fp.getvalue()))
_obj(fp, 5, Length=len(op.fp.getvalue()))
fp.write("stream\n")
fp.fp.write(op.fp.getvalue())

View File

@ -89,33 +89,33 @@ class ChunkStream:
"Fetch a new chunk. Returns header information."
if self.queue:
cid, pos, len = self.queue[-1]
cid, pos, length = self.queue[-1]
del self.queue[-1]
self.fp.seek(pos)
else:
s = self.fp.read(8)
cid = s[4:]
pos = self.fp.tell()
len = i32(s)
length = i32(s)
if not is_cid(cid):
raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
return cid, pos, len
return cid, pos, length
def close(self):
self.queue = self.crc = self.fp = None
def push(self, cid, pos, len):
def push(self, cid, pos, length):
self.queue.append((cid, pos, len))
self.queue.append((cid, pos, length))
def call(self, cid, pos, len):
def call(self, cid, pos, length):
"Call the appropriate chunk handler"
if Image.DEBUG:
print("STREAM", cid, pos, len)
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len)
print("STREAM", cid, pos, length)
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
def crc(self, cid, data):
"Read and verify checksum"
@ -139,10 +139,10 @@ class ChunkStream:
cids = []
while True:
cid, pos, len = self.read()
cid, pos, length = self.read()
if cid == endchunk:
break
self.crc(cid, ImageFile._safe_read(self.fp, len))
self.crc(cid, ImageFile._safe_read(self.fp, length))
cids.append(cid)
return cids
@ -190,10 +190,10 @@ class PngStream(ChunkStream):
self.im_tile = None
self.im_palette = None
def chunk_iCCP(self, pos, len):
def chunk_iCCP(self, pos, length):
# ICC profile
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
# according to PNG spec, the iCCP chunk contains:
# Profile name 1-79 bytes (character string)
# Null separator 1 byte (null character)
@ -213,10 +213,10 @@ class PngStream(ChunkStream):
self.im_info["icc_profile"] = icc_profile
return s
def chunk_IHDR(self, pos, len):
def chunk_IHDR(self, pos, length):
# image header
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
self.im_size = i32(s), i32(s[4:])
try:
self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))]
@ -228,30 +228,30 @@ class PngStream(ChunkStream):
raise SyntaxError("unknown filter category")
return s
def chunk_IDAT(self, pos, len):
def chunk_IDAT(self, pos, length):
# image data
self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
self.im_idat = len
self.im_idat = length
raise EOFError
def chunk_IEND(self, pos, len):
def chunk_IEND(self, pos, length):
# end of PNG image
raise EOFError
def chunk_PLTE(self, pos, len):
def chunk_PLTE(self, pos, length):
# palette
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
if self.im_mode == "P":
self.im_palette = "RGB", s
return s
def chunk_tRNS(self, pos, len):
def chunk_tRNS(self, pos, length):
# transparency
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
if self.im_mode == "P":
if _simple_palette.match(s):
i = s.find(b"\0")
@ -265,17 +265,17 @@ class PngStream(ChunkStream):
self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
return s
def chunk_gAMA(self, pos, len):
def chunk_gAMA(self, pos, length):
# gamma setting
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
self.im_info["gamma"] = i32(s) / 100000.0
return s
def chunk_pHYs(self, pos, len):
def chunk_pHYs(self, pos, length):
# pixels per unit
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
px, py = i32(s), i32(s[4:])
unit = i8(s[8])
if unit == 1: # meter
@ -285,10 +285,10 @@ class PngStream(ChunkStream):
self.im_info["aspect"] = px, py
return s
def chunk_tEXt(self, pos, len):
def chunk_tEXt(self, pos, length):
# text
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
try:
k, v = s.split(b"\0", 1)
except ValueError:
@ -301,10 +301,10 @@ class PngStream(ChunkStream):
self.im_info[k] = self.im_text[k] = v
return s
def chunk_zTXt(self, pos, len):
def chunk_zTXt(self, pos, length):
# compressed text
s = ImageFile._safe_read(self.fp, len)
s = ImageFile._safe_read(self.fp, length)
try:
k, v = s.split(b"\0", 1)
except ValueError:
@ -358,16 +358,16 @@ class PngImageFile(ImageFile.ImageFile):
#
# get next chunk
cid, pos, len = self.png.read()
cid, pos, length = self.png.read()
try:
s = self.png.call(cid, pos, len)
s = self.png.call(cid, pos, length)
except EOFError:
break
except AttributeError:
if Image.DEBUG:
print(cid, pos, len, "(unknown)")
s = ImageFile._safe_read(self.fp, len)
print(cid, pos, length, "(unknown)")
s = ImageFile._safe_read(self.fp, length)
self.png.crc(cid, s)
@ -388,7 +388,7 @@ class PngImageFile(ImageFile.ImageFile):
rawmode, data = self.png.im_palette
self.palette = ImagePalette.raw(rawmode, data)
self.__idat = len # used by load_read()
self.__idat = length # used by load_read()
def verify(self):
@ -413,7 +413,7 @@ class PngImageFile(ImageFile.ImageFile):
ImageFile.ImageFile.load_prepare(self)
def load_read(self, bytes):
def load_read(self, read_bytes):
"internal: read more image data"
while self.__idat == 0:
@ -421,23 +421,23 @@ class PngImageFile(ImageFile.ImageFile):
self.fp.read(4) # CRC
cid, pos, len = self.png.read()
cid, pos, length = self.png.read()
if cid not in [b"IDAT", b"DDAT"]:
self.png.push(cid, pos, len)
self.png.push(cid, pos, length)
return b""
self.__idat = len # empty chunks are allowed
self.__idat = length # empty chunks are allowed
# read more data from this chunk
if bytes <= 0:
bytes = self.__idat
if read_bytes <= 0:
read_bytes = self.__idat
else:
bytes = min(bytes, self.__idat)
read_bytes = min(read_bytes, self.__idat)
self.__idat = self.__idat - bytes
self.__idat = self.__idat - read_bytes
return self.fp.read(bytes)
return self.fp.read(read_bytes)
def load_end(self):
@ -560,7 +560,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
chunk(fp, b"PLTE", palette_bytes)
transparency = im.encoderinfo.get('transparency',im.info.get('transparency', None))
if transparency or transparency == 0:
if im.mode == "P":
# limit to actual palette size
@ -580,7 +580,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
else:
if "transparency" in im.encoderinfo:
# don't bother with transparency if it's an RGBA
# and it's in the info dict. It's probably just stale.
# and it's in the info dict. It's probably just stale.
raise IOError("cannot use transparency for this mode")
else:
if im.mode == "P" and im.im.getpalettemode() == "RGBA":

View File

@ -36,17 +36,23 @@
from __future__ import print_function
from PIL import Image, ImageFile
import os, struct, sys
import os
import struct
import sys
def isInt(f):
try:
i = int(f)
if f-i == 0: return 1
else: return 0
if f-i == 0:
return 1
else:
return 0
except:
return 0
iforms = [1,3,-11,-12,-21,-22]
iforms = [1, 3, -11, -12, -21, -22]
# There is no magic number to identify Spider files, so just check a
# series of header locations to see if they have reasonable values.
@ -56,30 +62,32 @@ iforms = [1,3,-11,-12,-21,-22]
def isSpiderHeader(t):
h = (99,) + t # add 1 value so can use spider header index start=1
# header values 1,2,5,12,13,22,23 should be integers
for i in [1,2,5,12,13,22,23]:
if not isInt(h[i]): return 0
for i in [1, 2, 5, 12, 13, 22, 23]:
if not isInt(h[i]):
return 0
# check iform
iform = int(h[5])
if not iform in iforms: return 0
if iform not in iforms:
return 0
# check other header values
labrec = int(h[13]) # no. records in file header
labbyt = int(h[22]) # total no. of bytes in header
lenbyt = int(h[23]) # record length in bytes
#print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
if labbyt != (labrec * lenbyt): return 0
# print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
if labbyt != (labrec * lenbyt):
return 0
# looks like a valid header
return labbyt
def isSpiderImage(filename):
fp = open(filename,'rb')
fp = open(filename, 'rb')
f = fp.read(92) # read 23 * 4 bytes
fp.close()
bigendian = 1
t = struct.unpack('>23f',f) # try big-endian first
t = struct.unpack('>23f', f) # try big-endian first
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
bigendian = 0
t = struct.unpack('<23f',f) # little-endian
t = struct.unpack('<23f', f) # little-endian
hdrlen = isSpiderHeader(t)
return hdrlen
@ -96,11 +104,11 @@ class SpiderImageFile(ImageFile.ImageFile):
try:
self.bigendian = 1
t = struct.unpack('>27f',f) # try big-endian first
t = struct.unpack('>27f', f) # try big-endian first
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
self.bigendian = 0
t = struct.unpack('<27f',f) # little-endian
t = struct.unpack('<27f', f) # little-endian
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
raise SyntaxError("not a valid Spider file")
@ -112,7 +120,7 @@ class SpiderImageFile(ImageFile.ImageFile):
if iform != 1:
raise SyntaxError("not a Spider 2D image")
self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
self.istack = int(h[24])
self.imgnumber = int(h[27])
@ -141,9 +149,10 @@ class SpiderImageFile(ImageFile.ImageFile):
self.rawmode = "F;32F"
self.mode = "F"
self.tile = [("raw", (0, 0) + self.size, offset,
(self.rawmode, 0, 1))]
self.__fp = self.fp # FIXME: hack
self.tile = [
("raw", (0, 0) + self.size, offset,
(self.rawmode, 0, 1))]
self.__fp = self.fp # FIXME: hack
# 1st image index is zero (although SPIDER imgnumber starts at 1)
def tell(self):
@ -176,6 +185,7 @@ class SpiderImageFile(ImageFile.ImageFile):
from PIL import ImageTk
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
# --------------------------------------------------------------------
# Image series
@ -200,17 +210,19 @@ def loadImageSeries(filelist=None):
imglist.append(im)
return imglist
# --------------------------------------------------------------------
# For saving images in Spider format
def makeSpiderHeader(im):
nsam,nrow = im.size
nsam, nrow = im.size
lenbyt = nsam * 4 # There are labrec records in the header
labrec = 1024 / lenbyt
if 1024%lenbyt != 0: labrec += 1
if 1024 % lenbyt != 0:
labrec += 1
labbyt = labrec * lenbyt
hdr = []
nvalues = labbyt / 4
nvalues = int(labbyt / 4)
for i in range(nvalues):
hdr.append(0.0)
@ -218,13 +230,13 @@ def makeSpiderHeader(im):
return []
# NB these are Fortran indices
hdr[1] = 1.0 # nslice (=1 for an image)
hdr[2] = float(nrow) # number of rows per slice
hdr[5] = 1.0 # iform for 2D image
hdr[12] = float(nsam) # number of pixels per line
hdr[13] = float(labrec) # number of records in file header
hdr[22] = float(labbyt) # total number of bytes in header
hdr[23] = float(lenbyt) # record length in bytes
hdr[1] = 1.0 # nslice (=1 for an image)
hdr[2] = float(nrow) # number of rows per slice
hdr[5] = 1.0 # iform for 2D image
hdr[12] = float(nsam) # number of pixels per line
hdr[13] = float(labrec) # number of records in file header
hdr[22] = float(labbyt) # total number of bytes in header
hdr[23] = float(lenbyt) # record length in bytes
# adjust for Fortran indexing
hdr = hdr[1:]
@ -232,9 +244,10 @@ def makeSpiderHeader(im):
# pack binary data into a string
hdrstr = []
for v in hdr:
hdrstr.append(struct.pack('f',v))
hdrstr.append(struct.pack('f', v))
return hdrstr
def _save(im, fp, filename):
if im.mode[0] != "F":
im = im.convert('F')
@ -250,11 +263,12 @@ def _save(im, fp, filename):
raise IOError("Unable to open %s for writing" % filename)
fp.writelines(hdr)
rawmode = "F;32NF" #32-bit native floating point
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))])
rawmode = "F;32NF" # 32-bit native floating point
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
fp.close()
def _save_spider(im, fp, filename):
# get the filename extension and register it with Image
fn, ext = os.path.splitext(filename)
@ -292,5 +306,7 @@ if __name__ == "__main__":
if outfile != "":
# perform some image operation
im = im.transpose(Image.FLIP_LEFT_RIGHT)
print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile))
print(
"saving a flipped version of %s as %s " %
(os.path.basename(filename), outfile))
im.save(outfile, "SPIDER")

View File

@ -984,11 +984,7 @@ def _save(im, fp, filename):
compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
libtiff = WRITE_LIBTIFF or compression in ["tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate",
"tiff_thunderscan", "tiff_deflate",
"tiff_sgilog", "tiff_sgilog24",
"tiff_raw_16"]
libtiff = WRITE_LIBTIFF or compression != 'raw'
# required for color libtiff images
ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
Tests/images/lena.spider Normal file

Binary file not shown.

View File

@ -1,25 +1,27 @@
from tester import *
from PIL import Image, EpsImagePlugin
import sys
import io
if not EpsImagePlugin.has_ghostscript():
skip()
#Our two EPS test files (they are identical except for their bounding boxes)
# Our two EPS test files (they are identical except for their bounding boxes)
file1 = "Tests/images/zero_bb.eps"
file2 = "Tests/images/non_zero_bb.eps"
#Due to palletization, we'll need to convert these to RGB after load
# Due to palletization, we'll need to convert these to RGB after load
file1_compare = "Tests/images/zero_bb.png"
file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
file2_compare = "Tests/images/non_zero_bb.png"
file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
# EPS test files with binary preview
file3 = "Tests/images/binary_preview_map.eps"
def test_sanity():
#Regular scale
# Regular scale
image1 = Image.open(file1)
image1.load()
assert_equal(image1.mode, "RGB")
@ -32,7 +34,7 @@ def test_sanity():
assert_equal(image2.size, (360, 252))
assert_equal(image2.format, "EPS")
#Double scale
# Double scale
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
assert_equal(image1_scale2.mode, "RGB")
@ -45,54 +47,96 @@ def test_sanity():
assert_equal(image2_scale2.size, (720, 504))
assert_equal(image2_scale2.format, "EPS")
def test_file_object():
#issue 479
# issue 479
image1 = Image.open(file1)
with open(tempfile('temp_file.eps'), 'wb') as fh:
image1.save(fh, 'EPS')
def test_iobase_object():
#issue 479
# issue 479
image1 = Image.open(file1)
with io.open(tempfile('temp_iobase.eps'), 'wb') as fh:
image1.save(fh, 'EPS')
def test_render_scale1():
#We need png support for these render test
# We need png support for these render test
codecs = dir(Image.core)
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
skip("zip/deflate support not available")
#Zero bounding box
# Zero bounding box
image1_scale1 = Image.open(file1)
image1_scale1.load()
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
image1_scale1_compare.load()
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
#Non-Zero bounding box
# Non-Zero bounding box
image2_scale1 = Image.open(file2)
image2_scale1.load()
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
image2_scale1_compare.load()
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
def test_render_scale2():
#We need png support for these render test
# We need png support for these render test
codecs = dir(Image.core)
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
skip("zip/deflate support not available")
#Zero bounding box
# Zero bounding box
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
image1_scale2_compare.load()
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
#Non-Zero bounding box
# Non-Zero bounding box
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
image2_scale2_compare.load()
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
def test_resize():
# Arrange
image1 = Image.open(file1)
image2 = Image.open(file2)
new_size = (100, 100)
# Act
image1 = image1.resize(new_size)
image2 = image2.resize(new_size)
# Assert
assert_equal(image1.size, new_size)
assert_equal(image2.size, new_size)
def test_thumbnail():
# Issue #619
# Arrange
image1 = Image.open(file1)
image2 = Image.open(file2)
new_size = (100, 100)
# Act
image1.thumbnail(new_size)
image2.thumbnail(new_size)
# Assert
assert_equal(max(image1.size), max(new_size))
assert_equal(max(image2.size), max(new_size))
def test_read_binary_preview():
# Issue 302
# open image with binary preview
image1 = Image.open(file3)
# End of file

View File

@ -12,17 +12,19 @@ if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
test_file = "Images/lena.jpg"
def roundtrip(im, **options):
out = BytesIO()
im.save(out, "JPEG", **options)
bytes = out.tell()
out.seek(0)
im = Image.open(out)
im.bytes = bytes # for testing only
im.bytes = bytes # for testing only
return im
# --------------------------------------------------------------------
def test_sanity():
# internal version number
@ -34,6 +36,7 @@ def test_sanity():
assert_equal(im.size, (128, 128))
assert_equal(im.format, "JPEG")
# --------------------------------------------------------------------
def test_app():
@ -44,6 +47,7 @@ def test_app():
assert_equal(im.applist[1], ("COM", b"Python Imaging Library"))
assert_equal(len(im.applist), 2)
def test_cmyk():
# Test CMYK handling. Thanks to Tim and Charlie for test data,
# Michael for getting me to look one more time.
@ -62,6 +66,7 @@ def test_cmyk():
c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))]
assert_true(k > 0.9)
def test_dpi():
def test(xdpi, ydpi=None):
im = Image.open(test_file)
@ -70,7 +75,8 @@ def test_dpi():
assert_equal(test(72), (72, 72))
assert_equal(test(300), (300, 300))
assert_equal(test(100, 200), (100, 200))
assert_equal(test(0), None) # square pixels
assert_equal(test(0), None) # square pixels
def test_icc():
# Test ICC support
@ -89,6 +95,7 @@ def test_icc():
assert_false(im1.info.get("icc_profile"))
assert_true(im2.info.get("icc_profile"))
def test_icc_big():
# Make sure that the "extra" support handles large blocks
def test(n):
@ -96,16 +103,20 @@ def test_icc_big():
# using a 4-byte test code should allow us to detect out of
# order issues.
icc_profile = (b"Test"*int(n/4+1))[:n]
assert len(icc_profile) == n # sanity
assert len(icc_profile) == n # sanity
im1 = roundtrip(lena(), icc_profile=icc_profile)
assert_equal(im1.info.get("icc_profile"), icc_profile or None)
test(0); test(1)
test(3); test(4); test(5)
test(65533-14) # full JPEG marker block
test(65533-14+1) # full block plus one byte
test(ImageFile.MAXBLOCK) # full buffer block
test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte
test(ImageFile.MAXBLOCK*4+3) # large block
test(0)
test(1)
test(3)
test(4)
test(5)
test(65533-14) # full JPEG marker block
test(65533-14+1) # full block plus one byte
test(ImageFile.MAXBLOCK) # full buffer block
test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte
test(ImageFile.MAXBLOCK*4+3) # large block
def test_optimize():
im1 = roundtrip(lena())
@ -113,25 +124,29 @@ def test_optimize():
assert_image_equal(im1, im2)
assert_true(im1.bytes >= im2.bytes)
def test_optimize_large_buffer():
#https://github.com/python-imaging/Pillow/issues/148
# https://github.com/python-imaging/Pillow/issues/148
f = tempfile('temp.jpg')
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096,4096), 0xff3333)
im = Image.new("RGB", (4096, 4096), 0xff3333)
im.save(f, format="JPEG", optimize=True)
def test_progressive():
im1 = roundtrip(lena())
im2 = roundtrip(lena(), progressive=True)
assert_image_equal(im1, im2)
assert_true(im1.bytes >= im2.bytes)
def test_progressive_large_buffer():
f = tempfile('temp.jpg')
# this requires ~ 1.5x Image.MAXBLOCK
im = Image.new("RGB", (4096,4096), 0xff3333)
im = Image.new("RGB", (4096, 4096), 0xff3333)
im.save(f, format="JPEG", progressive=True)
def test_progressive_large_buffer_highest_quality():
f = tempfile('temp.jpg')
if py3:
@ -142,16 +157,18 @@ def test_progressive_large_buffer_highest_quality():
# this requires more bytes than pixels in the image
im.save(f, format="JPEG", progressive=True, quality=100)
def test_large_exif():
#https://github.com/python-imaging/Pillow/issues/148
# https://github.com/python-imaging/Pillow/issues/148
f = tempfile('temp.jpg')
im = lena()
im.save(f,'JPEG', quality=90, exif=b"1"*65532)
im.save(f, 'JPEG', quality=90, exif=b"1"*65532)
def test_progressive_compat():
im1 = roundtrip(lena())
im2 = roundtrip(lena(), progressive=1)
im3 = roundtrip(lena(), progression=1) # compatibility
im3 = roundtrip(lena(), progression=1) # compatibility
assert_image_equal(im1, im2)
assert_image_equal(im1, im3)
assert_false(im1.info.get("progressive"))
@ -161,31 +178,34 @@ def test_progressive_compat():
assert_true(im3.info.get("progressive"))
assert_true(im3.info.get("progression"))
def test_quality():
im1 = roundtrip(lena())
im2 = roundtrip(lena(), quality=50)
assert_image(im1, im2.mode, im2.size)
assert_true(im1.bytes >= im2.bytes)
def test_smooth():
im1 = roundtrip(lena())
im2 = roundtrip(lena(), smooth=100)
assert_image(im1, im2.mode, im2.size)
def test_subsampling():
def getsampling(im):
layer = im.layer
return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]
# experimental API
im = roundtrip(lena(), subsampling=-1) # default
im = roundtrip(lena(), subsampling=-1) # default
assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
im = roundtrip(lena(), subsampling=0) # 4:4:4
im = roundtrip(lena(), subsampling=0) # 4:4:4
assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1))
im = roundtrip(lena(), subsampling=1) # 4:2:2
im = roundtrip(lena(), subsampling=1) # 4:2:2
assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1))
im = roundtrip(lena(), subsampling=2) # 4:1:1
im = roundtrip(lena(), subsampling=2) # 4:1:1
assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
im = roundtrip(lena(), subsampling=3) # default (undefined)
im = roundtrip(lena(), subsampling=3) # default (undefined)
assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
im = roundtrip(lena(), subsampling="4:4:4")
@ -197,6 +217,7 @@ def test_subsampling():
assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1"))
def test_exif():
im = Image.open("Tests/images/pil_sample_rgb.jpg")
info = im._getexif()
@ -207,3 +228,11 @@ def test_quality_keep():
im = Image.open("Images/lena.jpg")
f = tempfile('temp.jpg')
assert_no_exception(lambda: im.save(f, quality='keep'))
def test_junk_jpeg_header():
# https://github.com/python-imaging/Pillow/issues/630
filename = "Tests/images/junk_jpeg_header.jpg"
assert_no_exception(lambda: Image.open(filename))
# End of file

View File

@ -258,9 +258,6 @@ def test_compressions():
im = lena('RGB')
out = tempfile('temp.tif')
TiffImagePlugin.READ_LIBTIFF = True
TiffImagePlugin.WRITE_LIBTIFF = True
for compression in ('packbits', 'tiff_lzw'):
im.save(out, compression=compression)
im2 = Image.open(out)
@ -270,11 +267,6 @@ def test_compressions():
im2 = Image.open(out)
assert_image_similar(im, im2, 30)
TiffImagePlugin.READ_LIBTIFF = False
TiffImagePlugin.WRITE_LIBTIFF = False
def test_cmyk_save():
im = lena('CMYK')

58
Tests/test_file_pdf.py Normal file
View File

@ -0,0 +1,58 @@
from tester import *
import os.path
def helper_save_as_pdf(mode):
# Arrange
im = lena(mode)
outfile = tempfile("temp_" + mode + ".pdf")
# Act
im.save(outfile)
# Assert
assert_true(os.path.isfile(outfile))
assert_greater(os.path.getsize(outfile), 0)
def test_monochrome():
# Arrange
mode = "1"
# Act / Assert
helper_save_as_pdf(mode)
def test_greyscale():
# Arrange
mode = "L"
# Act / Assert
helper_save_as_pdf(mode)
def test_rgb():
# Arrange
mode = "RGB"
# Act / Assert
helper_save_as_pdf(mode)
def test_p_mode():
# Arrange
mode = "P"
# Act / Assert
helper_save_as_pdf(mode)
def test_cmyk_mode():
# Arrange
mode = "CMYK"
# Act / Assert
helper_save_as_pdf(mode)
# End of file

36
Tests/test_file_spider.py Normal file
View File

@ -0,0 +1,36 @@
from tester import *
from PIL import Image
from PIL import SpiderImagePlugin
test_file = "Tests/images/lena.spider"
def test_sanity():
im = Image.open(test_file)
im.load()
assert_equal(im.mode, "F")
assert_equal(im.size, (128, 128))
assert_equal(im.format, "SPIDER")
def test_save():
# Arrange
temp = tempfile('temp.spider')
im = lena()
# Act
im.save(temp, "SPIDER")
# Assert
im2 = Image.open(temp)
assert_equal(im2.mode, "F")
assert_equal(im2.size, (128, 128))
assert_equal(im2.format, "SPIDER")
def test_isSpiderImage():
assert_true(SpiderImagePlugin.isSpiderImage(test_file))
# End of file

View File

@ -2,6 +2,7 @@ from tester import *
from PIL import Image
def test_sanity():
def convert(im, mode):
@ -16,6 +17,7 @@ def test_sanity():
for mode in modes:
yield_test(convert, im, mode)
def test_default():
im = lena("P")
@ -26,26 +28,29 @@ def test_default():
assert_image(im, "RGB", im.size)
# ref https://github.com/python-imaging/Pillow/issues/274
def _test_float_conversion(im):
orig = im.getpixel((5,5))
converted = im.convert('F').getpixel((5,5))
orig = im.getpixel((5, 5))
converted = im.convert('F').getpixel((5, 5))
assert_equal(orig, converted)
def test_8bit():
im = Image.open('Images/lena.jpg')
_test_float_conversion(im.convert('L'))
def test_16bit():
im = Image.open('Tests/images/16bit.cropped.tif')
_test_float_conversion(im)
def test_16bit_workaround():
im = Image.open('Tests/images/16bit.cropped.tif')
_test_float_conversion(im.convert('I'))
def test_rgba_p():
im = lena('RGBA')
im.putalpha(lena('L'))
@ -54,30 +59,45 @@ def test_rgba_p():
comparable = converted.convert('RGBA')
assert_image_similar(im, comparable, 20)
def test_trns_p():
def test_trns_p():
im = lena('P')
im.info['transparency']=0
im.info['transparency'] = 0
f = tempfile('temp.png')
l = im.convert('L')
assert_equal(l.info['transparency'], 0) # undone
assert_equal(l.info['transparency'], 0) # undone
assert_no_exception(lambda: l.save(f))
rgb = im.convert('RGB')
assert_equal(rgb.info['transparency'], (0,0,0)) # undone
assert_equal(rgb.info['transparency'], (0, 0, 0)) # undone
assert_no_exception(lambda: rgb.save(f))
# ref https://github.com/python-imaging/Pillow/issues/664
def test_trns_p_rgba():
# Arrange
im = lena('P')
im.info['transparency'] = 128
# Act
rgba = im.convert('RGBA')
# Assert
assert_false('transparency' in rgba.info)
def test_trns_l():
im = lena('L')
im.info['transparency'] = 128
f = tempfile('temp.png')
rgb = im.convert('RGB')
assert_equal(rgb.info['transparency'], (128,128,128)) # undone
assert_equal(rgb.info['transparency'], (128, 128, 128)) # undone
assert_no_exception(lambda: rgb.save(f))
p = im.convert('P')
@ -85,28 +105,26 @@ def test_trns_l():
assert_no_exception(lambda: p.save(f))
p = assert_warning(UserWarning,
lambda: im.convert('P', palette = Image.ADAPTIVE))
lambda: im.convert('P', palette=Image.ADAPTIVE))
assert_false('transparency' in p.info)
assert_no_exception(lambda: p.save(f))
def test_trns_RGB():
im = lena('RGB')
im.info['transparency'] = im.getpixel((0,0))
im.info['transparency'] = im.getpixel((0, 0))
f = tempfile('temp.png')
l = im.convert('L')
assert_equal(l.info['transparency'], l.getpixel((0,0))) # undone
assert_equal(l.info['transparency'], l.getpixel((0, 0))) # undone
assert_no_exception(lambda: l.save(f))
p = im.convert('P')
assert_true('transparency' in p.info)
assert_no_exception(lambda: p.save(f))
p = assert_warning(UserWarning,
lambda: im.convert('P', palette = Image.ADAPTIVE))
lambda: im.convert('P', palette=Image.ADAPTIVE))
assert_false('transparency' in p.info)
assert_no_exception(lambda: p.save(f))

70
Tests/test_pickle.py Normal file
View File

@ -0,0 +1,70 @@
from tester import *
from PIL import Image
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_completely_equal(im, loaded_im)
def helper_test_pickle_string(pickle, protocol=0, file='Images/lena.jpg'):
im = Image.open(file)
# Act
dumped_string = pickle.dumps(im, protocol)
loaded_im = pickle.loads(dumped_string)
# Assert
assert_image_completely_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
try:
import cPickle
except ImportError:
return
# Act / Assert
for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1):
helper_test_pickle_string(cPickle, protocol)
helper_test_pickle_file(cPickle, protocol)
def test_pickle_p_mode():
# Arrange
import pickle
# Act / Assert
for file in [
"Tests/images/test-card.png",
"Tests/images/zero_bb.png",
"Tests/images/zero_bb_scale2.png",
"Tests/images/non_zero_bb.png",
"Tests/images/non_zero_bb_scale2.png",
"Tests/images/p_trns_single.png",
"Tests/images/pil123p.png"
]:
helper_test_pickle_string(pickle, file=file)
# End of file

View File

@ -242,7 +242,13 @@ def assert_image_equal(a, b, msg=None):
failure(msg or "got size %r, expected %r" % (a.size, b.size))
elif a.tobytes() != b.tobytes():
failure(msg or "got different content")
# generate better diff?
else:
success()
def assert_image_completely_equal(a, b, msg=None):
if a != b:
failure(msg or "images different")
else:
success()
@ -345,7 +351,10 @@ def _setup():
import sys
if "--coverage" in sys.argv:
import coverage
# Temporary: ignore PendingDeprecationWarning from Coverage (Py3.4)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
import coverage
cov = coverage.coverage(auto_data=True, include="PIL/*")
cov.start()

View File

@ -3350,7 +3350,7 @@ extern PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args);
/* Display support etc (in display.c) */
#ifdef WIN32
#ifdef _WIN32
extern PyObject* PyImaging_CreateWindowWin32(PyObject* self, PyObject* args);
extern PyObject* PyImaging_DisplayWin32(PyObject* self, PyObject* args);
extern PyObject* PyImaging_DisplayModeWin32(PyObject* self, PyObject* args);
@ -3423,14 +3423,14 @@ static PyMethodDef functions[] = {
/* Memory mapping */
#ifdef WITH_MAPPING
#ifdef WIN32
#ifdef _WIN32
{"map", (PyCFunction)PyImaging_Mapper, 1},
#endif
{"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1},
#endif
/* Display support */
#ifdef WIN32
#ifdef _WIN32
{"display", (PyCFunction)PyImaging_DisplayWin32, 1},
{"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, 1},
{"grabscreen", (PyCFunction)PyImaging_GrabScreenWin32, 1},

View File

@ -28,12 +28,6 @@ http://www.cazabon.com\n\
#include "Imaging.h"
#include "py3.h"
#ifdef WIN32
#include <windows.h>
#include <windef.h>
#include <wingdi.h>
#endif
#define PYCMSVERSION "1.0.0 pil"
/* version history */
@ -450,7 +444,7 @@ cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args)
return PyInt_FromLong(result != 0);
}
#ifdef WIN32
#ifdef _WIN32
static PyObject *
cms_get_display_profile_win32(PyObject* self, PyObject* args)
{
@ -496,7 +490,7 @@ static PyMethodDef pyCMSdll_methods[] = {
{"createProfile", createProfile, 1},
/* platform specific tools */
#ifdef WIN32
#ifdef _WIN32
{"get_display_profile_win32", cms_get_display_profile_win32, 1},
#endif

View File

@ -433,9 +433,6 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args)
#include "TiffDecode.h"
#include <string.h>
#ifdef __WIN32__
#define strcasecmp(s1, s2) stricmp(s1, s2)
#endif
PyObject*
PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)

View File

@ -31,7 +31,7 @@
/* -------------------------------------------------------------------- */
/* Windows DIB support */
#ifdef WIN32
#ifdef _WIN32
#include "ImDib.h"
@ -864,4 +864,4 @@ error:
return buffer;
}
#endif /* WIN32 */
#endif /* _WIN32 */

View File

@ -670,9 +670,6 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
#include "TiffDecode.h"
#include <string.h>
#ifdef __WIN32__
#define strcasecmp(s1, s2) stricmp(s1, s2)
#endif
PyObject*
PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)

View File

@ -22,7 +22,7 @@
#include "Imaging.h"
#ifdef WIN32
#ifdef _WIN32
#include "ImDib.h"
@ -308,4 +308,4 @@ ImagingDeleteDIB(ImagingDIB dib)
free(dib->info);
}
#endif /* WIN32 */
#endif /* _WIN32 */

View File

@ -59,7 +59,7 @@ typedef struct {
unsigned char buffer[GIFTABLE];
/* Symbol table */
unsigned INT16 link[GIFTABLE];
UINT16 link[GIFTABLE];
unsigned char data[GIFTABLE];
int next;

View File

@ -10,20 +10,9 @@
* See the README file for information on usage and redistribution.
*/
#ifdef WIN32
#ifdef _WIN32
#if (defined(_MSC_VER) && _MSC_VER >= 1200) || (defined __GNUC__)
/* already defined in basetsd.h */
#undef INT8
#undef UINT8
#undef INT16
#undef UINT16
#undef INT32
#undef INT64
#undef UINT32
#endif
#include <windows.h>
#include "ImPlatform.h"
#if defined(__cplusplus)
extern "C" {

View File

@ -17,26 +17,22 @@
#error Sorry, this library requires ANSI header files.
#endif
#if defined(_MSC_VER)
#ifndef WIN32
#define WIN32
#endif
/* VC++ 4.0 is a bit annoying when it comes to precision issues (like
claiming that "float a = 0.0;" would lead to loss of precision). I
don't like to see warnings from my code, but since I still want to
keep it readable, I simply switch off a few warnings instead of adding
the tons of casts that VC++ seem to require. This code is compiled
with numerous other compilers as well, so any real errors are likely
to be catched anyway. */
#pragma warning(disable: 4244) /* conversion from 'float' to 'int' */
#if defined(_MSC_VER) && !defined(__GNUC__)
#define inline __inline
#endif
#if defined(_MSC_VER)
#define inline __inline
#elif !defined(USE_INLINE)
#define inline
#if !defined(PIL_USE_INLINE)
#define inline
#endif
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#else
/* For System that are not Windows, we'll need to define these. */
#if SIZEOF_SHORT == 2
#define INT16 short
#elif SIZEOF_INT == 2
@ -61,12 +57,16 @@
#define INT64 long
#endif
/* assume IEEE; tweak if necessary (patches are welcome) */
#define FLOAT32 float
#define FLOAT64 double
#define INT8 signed char
#define UINT8 unsigned char
#define UINT16 unsigned INT16
#define UINT32 unsigned INT32
#endif
/* assume IEEE; tweak if necessary (patches are welcome) */
#define FLOAT32 float
#define FLOAT64 double

View File

@ -41,7 +41,6 @@
two cases. */
#ifdef _WIN32
#include <windows.h>
#include <process.h>
#else
#include <pthread.h>

View File

@ -45,7 +45,7 @@ typedef struct {
unsigned char buffer[LZWTABLE];
/* Symbol table */
unsigned INT16 link[LZWTABLE];
UINT16 link[LZWTABLE];
unsigned char data[LZWTABLE];
int next;

18
map.c
View File

@ -22,18 +22,6 @@
#include "Imaging.h"
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#undef INT8
#undef UINT8
#undef INT16
#undef UINT16
#undef INT32
#undef INT64
#undef UINT32
#include "windows.h"
#endif
#include "py3.h"
/* compatibility wrappers (defined in _imaging.c) */
@ -48,7 +36,7 @@ typedef struct {
char* base;
int size;
int offset;
#ifdef WIN32
#ifdef _WIN32
HANDLE hFile;
HANDLE hMap;
#endif
@ -71,7 +59,7 @@ PyImaging_MapperNew(const char* filename, int readonly)
mapper->base = NULL;
mapper->size = mapper->offset = 0;
#ifdef WIN32
#ifdef _WIN32
mapper->hFile = (HANDLE)-1;
mapper->hMap = (HANDLE)-1;
@ -114,7 +102,7 @@ PyImaging_MapperNew(const char* filename, int readonly)
static void
mapping_dealloc(ImagingMapperObject* mapper)
{
#ifdef WIN32
#ifdef _WIN32
if (mapper->base != 0)
UnmapViewOfFile(mapper->base);
if (mapper->hMap != (HANDLE)-1)

View File

@ -205,25 +205,31 @@ class pil_build_ext(build_ext):
# darwin ports installation directories
_add_directory(library_dirs, "/opt/local/lib")
_add_directory(include_dirs, "/opt/local/include")
# if homebrew is installed, use its lib and include directories
# if Homebrew is installed, use its lib and include directories
import subprocess
try:
prefix = subprocess.check_output(['brew', '--prefix'])
if prefix:
prefix = prefix.strip()
_add_directory(library_dirs, os.path.join(prefix, 'lib'))
_add_directory(include_dirs, os.path.join(prefix, 'include'))
# freetype2 is a key-only brew under opt/
_add_directory(library_dirs, os.path.join(prefix, 'opt', 'freetype', 'lib'))
_add_directory(include_dirs, os.path.join(prefix, 'opt', 'freetype', 'include'))
prefix = subprocess.check_output(['brew', '--prefix']).strip()
except:
pass # homebrew not installed
# freetype2 ships with X11 (after homebrew, so that homebrew freetype is preferred)
_add_directory(library_dirs, "/usr/X11/lib")
_add_directory(include_dirs, "/usr/X11/include")
# Homebrew not installed
prefix = None
ft_prefix = None
if prefix:
# add Homebrew's include and lib directories
_add_directory(library_dirs, os.path.join(prefix, 'lib'))
_add_directory(include_dirs, os.path.join(prefix, 'include'))
ft_prefix = os.path.join(prefix, 'opt', 'freetype')
if ft_prefix and os.path.isdir(ft_prefix):
# freetype might not be linked into Homebrew's prefix
_add_directory(library_dirs, os.path.join(ft_prefix, 'lib'))
_add_directory(include_dirs, os.path.join(ft_prefix, 'include'))
else:
# fall back to freetype from XQuartz if Homebrew's freetype is missing
_add_directory(library_dirs, "/usr/X11/lib")
_add_directory(include_dirs, "/usr/X11/include")
elif sys.platform.startswith("linux"):
arch_tp = (plat.processor(), plat.architecture()[0])