Merge from master, pre-unittest conversion
11
.coveragerc
Normal 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__.:
|
22
.travis.yml
|
@ -26,18 +26,20 @@ 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
|
||||
- coveralls
|
||||
- pip install pep8 pyflakes
|
||||
- pep8 PIL/*.py
|
||||
- pyflakes PIL/*.py
|
||||
- pep8 Tests/*.py
|
||||
- pyflakes Tests/*.py
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: "pypy"
|
||||
- pep8 --statistics --count PIL/*.py
|
||||
- pep8 --statistics --count Tests/*.py
|
||||
- pyflakes PIL/*.py | tee >(wc -l)
|
||||
- pyflakes Tests/*.py | tee >(wc -l)
|
||||
|
|
40
CHANGES.rst
|
@ -3,6 +3,44 @@ Changelog (Pillow)
|
|||
|
||||
2.5.0 (unreleased)
|
||||
------------------
|
||||
- Added more ImageDraw tests
|
||||
[hugovk]
|
||||
|
||||
- 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]
|
||||
|
@ -399,7 +437,7 @@ Changelog (Pillow)
|
|||
- Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.)
|
||||
[fluggo]
|
||||
|
||||
- Add PyPy support (experimental, please see: https://github.com/python-imaging/Pillow/issues/67)
|
||||
- Add PyPy support (experimental, please see: https://github.com/python-pillow/Pillow/issues/67)
|
||||
|
||||
- Add WebP support.
|
||||
[lqs]
|
||||
|
|
|
@ -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
|
||||
|
@ -77,8 +78,9 @@ def Ghostscript(tile, size, fp, scale=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
|
||||
|
||||
|
@ -87,10 +89,19 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
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 -= len(s)
|
||||
|
@ -100,7 +111,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
|||
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
|
||||
|
@ -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,24 +195,32 @@ 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
|
||||
|
|
225
PIL/Image.py
|
@ -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,10 +92,13 @@ 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
|
||||
|
@ -104,9 +108,10 @@ import numbers
|
|||
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
|
||||
if a is stub and b == "__mul__" and isinstance(c, numbers.Number):
|
||||
(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,11 +509,12 @@ 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()
|
||||
|
||||
|
@ -518,14 +533,13 @@ 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()
|
||||
self.im = self.im.copy()
|
||||
|
@ -533,7 +547,8 @@ class Image:
|
|||
self.readonly = 0
|
||||
|
||||
def _dump(self, file=None, format=None):
|
||||
import tempfile, os
|
||||
import os
|
||||
import tempfile
|
||||
suffix = ''
|
||||
if format:
|
||||
suffix = '.'+format
|
||||
|
@ -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):
|
||||
|
@ -774,28 +827,29 @@ 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.
|
||||
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
|
||||
|
@ -803,6 +857,8 @@ class Image:
|
|||
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)
|
||||
|
@ -820,7 +876,8 @@ 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -1422,7 +1480,7 @@ class Image:
|
|||
self.load()
|
||||
|
||||
if self.pyaccess:
|
||||
return self.pyaccess.putpixel(xy,value)
|
||||
return self.pyaccess.putpixel(xy, value)
|
||||
return self.im.putpixel(xy, value)
|
||||
|
||||
def resize(self, size, resample=NEAREST):
|
||||
|
@ -1471,7 +1529,7 @@ class Image:
|
|||
clockwise around its centre.
|
||||
|
||||
:param angle: In degrees counter clockwise.
|
||||
:param filter: An optional resampling filter. This can be
|
||||
:param resample: An optional resampling filter. This can be
|
||||
one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
|
||||
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
|
||||
environment), or :py:attr:`PIL.Image.BICUBIC`
|
||||
|
@ -1489,9 +1547,10 @@ 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 +1638,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 +1726,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 +1741,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 +1777,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 +1813,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 +1861,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 +1901,7 @@ class Image:
|
|||
im = self.im.transpose(method)
|
||||
return self._new(im)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Lazy operations
|
||||
|
||||
|
@ -1869,6 +1937,7 @@ class _ImageCrop(Image):
|
|||
# FIXME: future versions should optimize crop/paste
|
||||
# sequences!
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Abstract handlers.
|
||||
|
||||
|
@ -1876,10 +1945,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 +2026,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.
|
||||
|
||||
|
@ -2016,9 +2088,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)
|
||||
)
|
||||
|
@ -2138,8 +2210,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():
|
||||
|
@ -2151,13 +2223,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.
|
||||
|
||||
|
@ -2256,6 +2329,7 @@ def merge(mode, bands):
|
|||
im.putband(bands[i].im, i)
|
||||
return bands[0]._new(im)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Plugin registry
|
||||
|
||||
|
@ -2314,6 +2388,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)
|
||||
|
|
351
PIL/ImageCms.py
|
@ -1,19 +1,19 @@
|
|||
#
|
||||
# The Python Imaging Library.
|
||||
# $Id$
|
||||
#
|
||||
# optional color managment support, based on Kevin Cazabon's PyCMS
|
||||
# library.
|
||||
#
|
||||
# History:
|
||||
# 2009-03-08 fl Added to PIL.
|
||||
#
|
||||
# Copyright (C) 2002-2003 Kevin Cazabon
|
||||
# Copyright (c) 2009 by Fredrik Lundh
|
||||
#
|
||||
# See the README file for information on usage and redistribution. See
|
||||
# below for the original description.
|
||||
#
|
||||
"""
|
||||
The Python Imaging Library.
|
||||
$Id$
|
||||
|
||||
Optional color managment support, based on Kevin Cazabon's PyCMS
|
||||
library.
|
||||
|
||||
History:
|
||||
2009-03-08 fl Added to PIL.
|
||||
|
||||
Copyright (C) 2002-2003 Kevin Cazabon
|
||||
Copyright (c) 2009 by Fredrik Lundh
|
||||
|
||||
See the README file for information on usage and redistribution. See
|
||||
below for the original description.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
|
@ -66,7 +66,8 @@ pyCMS
|
|||
|
||||
Added try/except statements arount type() checks of
|
||||
potential CObjects... Python won't let you use type()
|
||||
on them, and raises a TypeError (stupid, if you ask me!)
|
||||
on them, and raises a TypeError (stupid, if you ask
|
||||
me!)
|
||||
|
||||
Added buildProofTransformFromOpenProfiles() function.
|
||||
Additional fixes in DLL, see DLL code for details.
|
||||
|
@ -89,8 +90,8 @@ try:
|
|||
except ImportError as ex:
|
||||
# Allow error import for doc purposes, but error out when accessing
|
||||
# anything in core.
|
||||
from _util import deferred_error
|
||||
_imagingcms = deferred_error(ex)
|
||||
from _util import import_err
|
||||
_imagingcms = import_err(ex)
|
||||
from PIL._util import isStringType
|
||||
|
||||
core = _imagingcms
|
||||
|
@ -113,22 +114,24 @@ DIRECTION_PROOF = 2
|
|||
FLAGS = {
|
||||
"MATRIXINPUT": 1,
|
||||
"MATRIXOUTPUT": 2,
|
||||
"MATRIXONLY": (1|2),
|
||||
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
|
||||
"NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use)
|
||||
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
|
||||
"NOTCACHE": 64, # Inhibit 1-pixel cache
|
||||
"MATRIXONLY": (1 | 2),
|
||||
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
|
||||
# Don't create prelinearization tables on precalculated transforms
|
||||
# (internal use):
|
||||
"NOPRELINEARIZATION": 16,
|
||||
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
|
||||
"NOTCACHE": 64, # Inhibit 1-pixel cache
|
||||
"NOTPRECALC": 256,
|
||||
"NULLTRANSFORM": 512, # Don't transform anyway
|
||||
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
|
||||
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
|
||||
"NULLTRANSFORM": 512, # Don't transform anyway
|
||||
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
|
||||
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
|
||||
"WHITEBLACKCOMPENSATION": 8192,
|
||||
"BLACKPOINTCOMPENSATION": 8192,
|
||||
"GAMUTCHECK": 4096, # Out of Gamut alarm
|
||||
"SOFTPROOFING": 16384, # Do softproofing
|
||||
"PRESERVEBLACK": 32768, # Black preservation
|
||||
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
|
||||
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
|
||||
"GAMUTCHECK": 4096, # Out of Gamut alarm
|
||||
"SOFTPROOFING": 16384, # Do softproofing
|
||||
"PRESERVEBLACK": 32768, # Black preservation
|
||||
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
|
||||
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
|
||||
}
|
||||
|
||||
_MAX_FLAG = 0
|
||||
|
@ -136,6 +139,7 @@ for flag in FLAGS.values():
|
|||
if isinstance(flag, int):
|
||||
_MAX_FLAG = _MAX_FLAG | flag
|
||||
|
||||
|
||||
# --------------------------------------------------------------------.
|
||||
# Experimental PIL-level API
|
||||
# --------------------------------------------------------------------.
|
||||
|
@ -153,40 +157,42 @@ class ImageCmsProfile:
|
|||
elif hasattr(profile, "read"):
|
||||
self._set(core.profile_frombytes(profile.read()))
|
||||
else:
|
||||
self._set(profile) # assume it's already a profile
|
||||
self._set(profile) # assume it's already a profile
|
||||
|
||||
def _set(self, profile, filename=None):
|
||||
self.profile = profile
|
||||
self.filename = filename
|
||||
if profile:
|
||||
self.product_name = None #profile.product_name
|
||||
self.product_info = None #profile.product_info
|
||||
self.product_name = None # profile.product_name
|
||||
self.product_info = None # profile.product_info
|
||||
else:
|
||||
self.product_name = None
|
||||
self.product_info = None
|
||||
|
||||
|
||||
class ImageCmsTransform(Image.ImagePointHandler):
|
||||
|
||||
"""Transform. This can be used with the procedural API, or with the
|
||||
standard Image.point() method.
|
||||
"""
|
||||
|
||||
def __init__(self, input, output, input_mode, output_mode,
|
||||
intent=INTENT_PERCEPTUAL,
|
||||
proof=None, proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
|
||||
intent=INTENT_PERCEPTUAL, proof=None,
|
||||
proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
|
||||
if proof is None:
|
||||
self.transform = core.buildTransform(
|
||||
input.profile, output.profile,
|
||||
input_mode, output_mode,
|
||||
intent,
|
||||
flags
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.transform = core.buildProofTransform(
|
||||
input.profile, output.profile, proof.profile,
|
||||
input_mode, output_mode,
|
||||
intent, proof_intent,
|
||||
flags
|
||||
)
|
||||
)
|
||||
# Note: inputMode and outputMode are for pyCMS compatibility only
|
||||
self.input_mode = self.inputMode = input_mode
|
||||
self.output_mode = self.outputMode = output_mode
|
||||
|
@ -198,16 +204,17 @@ class ImageCmsTransform(Image.ImagePointHandler):
|
|||
im.load()
|
||||
if imOut is None:
|
||||
imOut = Image.new(self.output_mode, im.size, None)
|
||||
result = self.transform.apply(im.im.id, imOut.im.id)
|
||||
self.transform.apply(im.im.id, imOut.im.id)
|
||||
return imOut
|
||||
|
||||
def apply_in_place(self, im):
|
||||
im.load()
|
||||
if im.mode != self.output_mode:
|
||||
raise ValueError("mode mismatch") # wrong output mode
|
||||
result = self.transform.apply(im.im.id, im.im.id)
|
||||
raise ValueError("mode mismatch") # wrong output mode
|
||||
self.transform.apply(im.im.id, im.im.id)
|
||||
return im
|
||||
|
||||
|
||||
def get_display_profile(handle=None):
|
||||
""" (experimental) Fetches the profile for the current display device.
|
||||
:returns: None if the profile is not known.
|
||||
|
@ -229,15 +236,21 @@ def get_display_profile(handle=None):
|
|||
profile = get()
|
||||
return ImageCmsProfile(profile)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------.
|
||||
# pyCMS compatible layer
|
||||
# --------------------------------------------------------------------.
|
||||
|
||||
class PyCMSError(Exception):
|
||||
""" (pyCMS) Exception class. This is used for all errors in the pyCMS API. """
|
||||
|
||||
""" (pyCMS) Exception class.
|
||||
This is used for all errors in the pyCMS API. """
|
||||
pass
|
||||
|
||||
def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0):
|
||||
|
||||
def profileToProfile(
|
||||
im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL,
|
||||
outputMode=None, inPlace=0, flags=0):
|
||||
"""
|
||||
(pyCMS) Applies an ICC transformation to a given image, mapping from
|
||||
inputProfile to outputProfile.
|
||||
|
@ -259,40 +272,45 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER
|
|||
profiles, the input profile must handle RGB data, and the output
|
||||
profile must handle CMYK data.
|
||||
|
||||
:param im: An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.)
|
||||
:param inputProfile: String, as a valid filename path to the ICC input profile
|
||||
you wish to use for this image, or a profile object
|
||||
:param im: An open PIL image object (i.e. Image.new(...) or
|
||||
Image.open(...), etc.)
|
||||
:param inputProfile: String, as a valid filename path to the ICC input
|
||||
profile you wish to use for this image, or a profile object
|
||||
:param outputProfile: String, as a valid filename path to the ICC output
|
||||
profile you wish to use for this image, or a profile object
|
||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you wish
|
||||
to use for the transform
|
||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for the transform
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what they do.
|
||||
:param outputMode: A valid PIL mode for the output image (i.e. "RGB", "CMYK",
|
||||
etc.). Note: if rendering the image "inPlace", outputMode MUST be the
|
||||
same mode as the input, or omitted completely. If omitted, the outputMode
|
||||
will be the same as the mode of the input image (im.mode)
|
||||
:param inPlace: Boolean (1 = True, None or 0 = False). If True, the original
|
||||
image is modified in-place, and None is returned. If False (default), a
|
||||
new Image object is returned with the transform applied.
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param outputMode: A valid PIL mode for the output image (i.e. "RGB",
|
||||
"CMYK", etc.). Note: if rendering the image "inPlace", outputMode
|
||||
MUST be the same mode as the input, or omitted completely. If
|
||||
omitted, the outputMode will be the same as the mode of the input
|
||||
image (im.mode)
|
||||
:param inPlace: Boolean (1 = True, None or 0 = False). If True, the
|
||||
original image is modified in-place, and None is returned. If False
|
||||
(default), a new Image object is returned with the transform applied.
|
||||
:param flags: Integer (0-...) specifying additional flags
|
||||
:returns: Either None or a new PIL image object, depending on value of inPlace
|
||||
:returns: Either None or a new PIL image object, depending on value of
|
||||
inPlace
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
if outputMode is None:
|
||||
outputMode = im.mode
|
||||
|
||||
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
|
||||
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
|
||||
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
|
||||
|
||||
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
|
||||
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
|
||||
raise PyCMSError(
|
||||
"flags must be an integer between 0 and %s" + _MAX_FLAG)
|
||||
|
||||
try:
|
||||
if not isinstance(inputProfile, ImageCmsProfile):
|
||||
|
@ -300,8 +318,9 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER
|
|||
if not isinstance(outputProfile, ImageCmsProfile):
|
||||
outputProfile = ImageCmsProfile(outputProfile)
|
||||
transform = ImageCmsTransform(
|
||||
inputProfile, outputProfile, im.mode, outputMode, renderingIntent, flags=flags
|
||||
)
|
||||
inputProfile, outputProfile, im.mode, outputMode,
|
||||
renderingIntent, flags=flags
|
||||
)
|
||||
if inPlace:
|
||||
transform.apply_in_place(im)
|
||||
imOut = None
|
||||
|
@ -323,8 +342,8 @@ def getOpenProfile(profileFilename):
|
|||
If profileFilename is not a vaild filename for an ICC profile, a PyCMSError
|
||||
will be raised.
|
||||
|
||||
:param profileFilename: String, as a valid filename path to the ICC profile you
|
||||
wish to open, or a file-like object.
|
||||
:param profileFilename: String, as a valid filename path to the ICC profile
|
||||
you wish to open, or a file-like object.
|
||||
:returns: A CmsProfile class object.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
@ -334,7 +353,10 @@ def getOpenProfile(profileFilename):
|
|||
except (IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0):
|
||||
|
||||
def buildTransform(
|
||||
inputProfile, outputProfile, inMode, outMode,
|
||||
renderingIntent=INTENT_PERCEPTUAL, flags=0):
|
||||
"""
|
||||
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
|
||||
outputProfile. Use applyTransform to apply the transform to a given
|
||||
|
@ -367,14 +389,14 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent
|
|||
manually overridden if you really want to, but I don't know of any
|
||||
time that would be of use, or would even work).
|
||||
|
||||
:param inputProfile: String, as a valid filename path to the ICC input profile
|
||||
you wish to use for this transform, or a profile object
|
||||
:param inputProfile: String, as a valid filename path to the ICC input
|
||||
profile you wish to use for this transform, or a profile object
|
||||
:param outputProfile: String, as a valid filename path to the ICC output
|
||||
profile you wish to use for this transform, or a profile object
|
||||
:param inMode: String, as a valid PIL mode that the appropriate profile also
|
||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param outMode: String, as a valid PIL mode that the appropriate profile also
|
||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param inMode: String, as a valid PIL mode that the appropriate profile
|
||||
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param outMode: String, as a valid PIL mode that the appropriate profile
|
||||
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for the transform
|
||||
|
||||
|
@ -383,28 +405,37 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent
|
|||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what they do.
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param flags: Integer (0-...) specifying additional flags
|
||||
:returns: A CmsTransform class object.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
|
||||
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
|
||||
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
|
||||
|
||||
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
|
||||
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
|
||||
raise PyCMSError(
|
||||
"flags must be an integer between 0 and %s" + _MAX_FLAG)
|
||||
|
||||
try:
|
||||
if not isinstance(inputProfile, ImageCmsProfile):
|
||||
inputProfile = ImageCmsProfile(inputProfile)
|
||||
if not isinstance(outputProfile, ImageCmsProfile):
|
||||
outputProfile = ImageCmsProfile(outputProfile)
|
||||
return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags)
|
||||
return ImageCmsTransform(
|
||||
inputProfile, outputProfile, inMode, outMode,
|
||||
renderingIntent, flags=flags)
|
||||
except (IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]):
|
||||
|
||||
def buildProofTransform(
|
||||
inputProfile, outputProfile, proofProfile, inMode, outMode,
|
||||
renderingIntent=INTENT_PERCEPTUAL,
|
||||
proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
flags=FLAGS["SOFTPROOFING"]):
|
||||
"""
|
||||
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
|
||||
outputProfile, but tries to simulate the result that would be
|
||||
|
@ -443,17 +474,17 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
|
|||
when the simulated device has a much wider gamut than the output
|
||||
device, you may obtain marginal results.
|
||||
|
||||
:param inputProfile: String, as a valid filename path to the ICC input profile
|
||||
you wish to use for this transform, or a profile object
|
||||
:param inputProfile: String, as a valid filename path to the ICC input
|
||||
profile you wish to use for this transform, or a profile object
|
||||
:param outputProfile: String, as a valid filename path to the ICC output
|
||||
(monitor, usually) profile you wish to use for this transform, or a
|
||||
profile object
|
||||
:param proofProfile: String, as a valid filename path to the ICC proof profile
|
||||
you wish to use for this transform, or a profile object
|
||||
:param inMode: String, as a valid PIL mode that the appropriate profile also
|
||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param outMode: String, as a valid PIL mode that the appropriate profile also
|
||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param proofProfile: String, as a valid filename path to the ICC proof
|
||||
profile you wish to use for this transform, or a profile object
|
||||
:param inMode: String, as a valid PIL mode that the appropriate profile
|
||||
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param outMode: String, as a valid PIL mode that the appropriate profile
|
||||
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for the input->proof (simulated) transform
|
||||
|
||||
|
@ -462,7 +493,8 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
|
|||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what they do.
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent you
|
||||
wish to use for proof->output transform
|
||||
|
||||
|
@ -471,17 +503,19 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
|
|||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what they do.
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param flags: Integer (0-...) specifying additional flags
|
||||
:returns: A CmsTransform class object.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
|
||||
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
|
||||
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
|
||||
|
||||
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
|
||||
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
|
||||
raise PyCMSError(
|
||||
"flags must be an integer between 0 and %s" + _MAX_FLAG)
|
||||
|
||||
try:
|
||||
if not isinstance(inputProfile, ImageCmsProfile):
|
||||
|
@ -490,13 +524,16 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
|
|||
outputProfile = ImageCmsProfile(outputProfile)
|
||||
if not isinstance(proofProfile, ImageCmsProfile):
|
||||
proofProfile = ImageCmsProfile(proofProfile)
|
||||
return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags)
|
||||
return ImageCmsTransform(
|
||||
inputProfile, outputProfile, inMode, outMode, renderingIntent,
|
||||
proofProfile, proofRenderingIntent, flags)
|
||||
except (IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
buildTransformFromOpenProfiles = buildTransform
|
||||
buildProofTransformFromOpenProfiles = buildProofTransform
|
||||
|
||||
|
||||
def applyTransform(im, transform, inPlace=0):
|
||||
"""
|
||||
(pyCMS) Applies a transform to a given image.
|
||||
|
@ -514,8 +551,8 @@ def applyTransform(im, transform, inPlace=0):
|
|||
is raised.
|
||||
|
||||
This function applies a pre-calculated transform (from
|
||||
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an
|
||||
image. The transform can be used for multiple images, saving
|
||||
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
|
||||
to an image. The transform can be used for multiple images, saving
|
||||
considerable calcuation time if doing the same conversion multiple times.
|
||||
|
||||
If you want to modify im in-place instead of receiving a new image as
|
||||
|
@ -528,10 +565,12 @@ def applyTransform(im, transform, inPlace=0):
|
|||
:param im: A PIL Image object, and im.mode must be the same as the inMode
|
||||
supported by the transform.
|
||||
:param transform: A valid CmsTransform class object
|
||||
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is modified
|
||||
in place and None is returned, if False, a new Image object with the
|
||||
transform applied is returned (and im is not changed). The default is False.
|
||||
:returns: Either None, or a new PIL Image object, depending on the value of inPlace
|
||||
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is
|
||||
modified in place and None is returned, if False, a new Image object
|
||||
with the transform applied is returned (and im is not changed). The
|
||||
default is False.
|
||||
:returns: Either None, or a new PIL Image object, depending on the value of
|
||||
inPlace
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
|
@ -546,6 +585,7 @@ def applyTransform(im, transform, inPlace=0):
|
|||
|
||||
return imOut
|
||||
|
||||
|
||||
def createProfile(colorSpace, colorTemp=-1):
|
||||
"""
|
||||
(pyCMS) Creates a profile.
|
||||
|
@ -562,30 +602,36 @@ def createProfile(colorSpace, colorTemp=-1):
|
|||
ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
|
||||
to images.
|
||||
|
||||
:param colorSpace: String, the color space of the profile you wish to create.
|
||||
:param colorSpace: String, the color space of the profile you wish to
|
||||
create.
|
||||
Currently only "LAB", "XYZ", and "sRGB" are supported.
|
||||
:param colorTemp: Positive integer for the white point for the profile, in
|
||||
degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
|
||||
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles,
|
||||
and is ignored for XYZ and sRGB.
|
||||
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
|
||||
profiles, and is ignored for XYZ and sRGB.
|
||||
:returns: A CmsProfile class object
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
if colorSpace not in ["LAB", "XYZ", "sRGB"]:
|
||||
raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace)
|
||||
raise PyCMSError(
|
||||
"Color space not supported for on-the-fly profile creation (%s)"
|
||||
% colorSpace)
|
||||
|
||||
if colorSpace == "LAB":
|
||||
try:
|
||||
colorTemp = float(colorTemp)
|
||||
except:
|
||||
raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp)
|
||||
raise PyCMSError(
|
||||
"Color temperature must be numeric, \"%s\" not valid"
|
||||
% colorTemp)
|
||||
|
||||
try:
|
||||
return core.createProfile(colorSpace, colorTemp)
|
||||
except (TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def getProfileName(profile):
|
||||
"""
|
||||
|
||||
|
@ -600,10 +646,10 @@ def getProfileName(profile):
|
|||
profile was originally created. Sometimes this tag also contains
|
||||
additional information supplied by the creator.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: A string containing the internal name of the profile as stored in an
|
||||
ICC tag.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: A string containing the internal name of the profile as stored
|
||||
in an ICC tag.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
|
@ -619,7 +665,7 @@ def getProfileName(profile):
|
|||
manufacturer = profile.profile.product_manufacturer
|
||||
|
||||
if not (model or manufacturer):
|
||||
return profile.profile.product_description+"\n"
|
||||
return profile.profile.product_description + "\n"
|
||||
if not manufacturer or len(model) > 30:
|
||||
return model + "\n"
|
||||
return "%s - %s\n" % (model, manufacturer)
|
||||
|
@ -627,6 +673,7 @@ def getProfileName(profile):
|
|||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def getProfileInfo(profile):
|
||||
"""
|
||||
(pyCMS) Gets the internal product information for the given profile.
|
||||
|
@ -641,10 +688,10 @@ def getProfileInfo(profile):
|
|||
info tag. This often contains details about the profile, and how it
|
||||
was created, as supplied by the creator.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in an ICC
|
||||
tag.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in
|
||||
an ICC tag.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
|
@ -652,7 +699,8 @@ def getProfileInfo(profile):
|
|||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
# add an extra newline to preserve pyCMS compatibility
|
||||
# Python, not C. the white point bits weren't working well, so skipping.
|
||||
# Python, not C. the white point bits weren't working well,
|
||||
# so skipping.
|
||||
# // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
|
||||
description = profile.profile.product_description
|
||||
cpright = profile.profile.product_copyright
|
||||
|
@ -660,7 +708,7 @@ def getProfileInfo(profile):
|
|||
for elt in (description, cpright):
|
||||
if elt:
|
||||
arr.append(elt)
|
||||
return "\r\n\r\n".join(arr)+"\r\n\r\n"
|
||||
return "\r\n\r\n".join(arr) + "\r\n\r\n"
|
||||
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
@ -679,10 +727,10 @@ def getProfileCopyright(profile):
|
|||
Use this function to obtain the information stored in the profile's
|
||||
copyright tag.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in an ICC
|
||||
tag.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in
|
||||
an ICC tag.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
try:
|
||||
|
@ -693,6 +741,7 @@ def getProfileCopyright(profile):
|
|||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def getProfileManufacturer(profile):
|
||||
"""
|
||||
(pyCMS) Gets the manufacturer for the given profile.
|
||||
|
@ -700,16 +749,16 @@ def getProfileManufacturer(profile):
|
|||
If profile isn't a valid CmsProfile object or filename to a profile,
|
||||
a PyCMSError is raised.
|
||||
|
||||
If an error occurs while trying to obtain the manufacturer tag, a PyCMSError
|
||||
is raised
|
||||
If an error occurs while trying to obtain the manufacturer tag, a
|
||||
PyCMSError is raised
|
||||
|
||||
Use this function to obtain the information stored in the profile's
|
||||
manufacturer tag.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in an ICC
|
||||
tag.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in
|
||||
an ICC tag.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
try:
|
||||
|
@ -720,6 +769,7 @@ def getProfileManufacturer(profile):
|
|||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def getProfileModel(profile):
|
||||
"""
|
||||
(pyCMS) Gets the model for the given profile.
|
||||
|
@ -733,10 +783,10 @@ def getProfileModel(profile):
|
|||
Use this function to obtain the information stored in the profile's
|
||||
model tag.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in an ICC
|
||||
tag.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in
|
||||
an ICC tag.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
|
@ -748,6 +798,7 @@ def getProfileModel(profile):
|
|||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def getProfileDescription(profile):
|
||||
"""
|
||||
(pyCMS) Gets the description for the given profile.
|
||||
|
@ -761,10 +812,10 @@ def getProfileDescription(profile):
|
|||
Use this function to obtain the information stored in the profile's
|
||||
description tag.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in an ICC
|
||||
tag.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: A string containing the internal profile information stored in an
|
||||
ICC tag.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
|
@ -793,16 +844,18 @@ def getDefaultIntent(profile):
|
|||
If you wish to use a different intent than returned, use
|
||||
ImageCms.isIntentSupported() to verify it will work first.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:returns: Integer 0-3 specifying the default rendering intent for this profile.
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:returns: Integer 0-3 specifying the default rendering intent for this
|
||||
profile.
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what they do.
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:exception PyCMSError:
|
||||
"""
|
||||
|
||||
|
@ -813,6 +866,7 @@ def getDefaultIntent(profile):
|
|||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def isIntentSupported(profile, intent, direction):
|
||||
"""
|
||||
(pyCMS) Checks if a given intent is supported.
|
||||
|
@ -828,17 +882,18 @@ def isIntentSupported(profile, intent, direction):
|
|||
potential PyCMSError that will occur if they don't support the modes
|
||||
you select.
|
||||
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
||||
of an ICC profile.
|
||||
:param intent: Integer (0-3) specifying the rendering intent you wish to use
|
||||
with this profile
|
||||
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||
filename of an ICC profile.
|
||||
:param intent: Integer (0-3) specifying the rendering intent you wish to
|
||||
use with this profile
|
||||
|
||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
||||
|
||||
see the pyCMS documentation for details on rendering intents and what they do.
|
||||
see the pyCMS documentation for details on rendering intents and what
|
||||
they do.
|
||||
:param direction: Integer specifing if the profile is to be used for input,
|
||||
output, or proof
|
||||
|
||||
|
@ -862,6 +917,7 @@ def isIntentSupported(profile, intent, direction):
|
|||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
||||
def versions():
|
||||
"""
|
||||
(pyCMS) Fetches versions.
|
||||
|
@ -869,8 +925,9 @@ def versions():
|
|||
|
||||
import sys
|
||||
return (
|
||||
VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION
|
||||
)
|
||||
VERSION, core.littlecms_version,
|
||||
sys.version.split()[0], Image.VERSION
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
@ -880,14 +937,16 @@ if __name__ == "__main__":
|
|||
from PIL import ImageCms
|
||||
print(__doc__)
|
||||
|
||||
for f in dir(pyCMS):
|
||||
print("="*80)
|
||||
print("%s" %f)
|
||||
|
||||
for f in dir(ImageCms):
|
||||
doc = None
|
||||
try:
|
||||
exec ("doc = ImageCms.%s.__doc__" %(f))
|
||||
exec("doc = %s.__doc__" % (f))
|
||||
if "pyCMS" in doc:
|
||||
# so we don't get the __doc__ string for imported modules
|
||||
print("=" * 80)
|
||||
print("%s" % f)
|
||||
print(doc)
|
||||
except AttributeError:
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
|
||||
# End of file
|
||||
|
|
|
@ -20,6 +20,7 @@ import struct
|
|||
import os
|
||||
import io
|
||||
|
||||
|
||||
def _parse_codestream(fp):
|
||||
"""Parse the JPEG 2000 codestream to extract the size and component
|
||||
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
|
||||
|
@ -28,7 +29,7 @@ def _parse_codestream(fp):
|
|||
lsiz = struct.unpack('>H', hdr)[0]
|
||||
siz = hdr + fp.read(lsiz - 2)
|
||||
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
|
||||
xtosiz, ytosiz, csiz \
|
||||
xtosiz, ytosiz, csiz \
|
||||
= struct.unpack('>HHIIIIIIIIH', siz[:38])
|
||||
ssiz = [None]*csiz
|
||||
xrsiz = [None]*csiz
|
||||
|
@ -51,6 +52,7 @@ def _parse_codestream(fp):
|
|||
|
||||
return (size, mode)
|
||||
|
||||
|
||||
def _parse_jp2_header(fp):
|
||||
"""Parse the JP2 header box to extract size, component count and
|
||||
color space information, returning a PIL (size, mode) tuple."""
|
||||
|
@ -90,7 +92,7 @@ def _parse_jp2_header(fp):
|
|||
|
||||
if tbox == b'ihdr':
|
||||
height, width, nc, bpc, c, unkc, ipr \
|
||||
= struct.unpack('>IIHBBBB', content)
|
||||
= struct.unpack('>IIHBBBB', content)
|
||||
size = (width, height)
|
||||
if unkc:
|
||||
if nc == 1:
|
||||
|
@ -112,13 +114,13 @@ def _parse_jp2_header(fp):
|
|||
elif nc == 4:
|
||||
mode = 'RGBA'
|
||||
break
|
||||
elif cs == 17: # grayscale
|
||||
elif cs == 17: # grayscale
|
||||
if nc == 1:
|
||||
mode = 'L'
|
||||
elif nc == 2:
|
||||
mode = 'LA'
|
||||
break
|
||||
elif cs == 18: # sYCC
|
||||
elif cs == 18: # sYCC
|
||||
if nc == 3:
|
||||
mode = 'RGB'
|
||||
elif nc == 4:
|
||||
|
@ -127,6 +129,7 @@ def _parse_jp2_header(fp):
|
|||
|
||||
return (size, mode)
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for JPEG2000 images.
|
||||
|
||||
|
@ -180,10 +183,12 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
|
||||
ImageFile.ImageFile.load(self)
|
||||
|
||||
|
||||
def _accept(prefix):
|
||||
return (prefix[:4] == b'\xff\x4f\xff\x51'
|
||||
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Save support
|
||||
|
||||
|
@ -228,7 +233,7 @@ def _save(im, fp, filename):
|
|||
progression,
|
||||
cinema_mode,
|
||||
fd
|
||||
)
|
||||
)
|
||||
|
||||
ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -7,7 +7,7 @@ This is an improved version of the OleFileIO module from [PIL](http://www.python
|
|||
|
||||
As far as I know, this module is now the most complete and robust Python implementation to read MS OLE2 files, portable on several operating systems. (please tell me if you know other similar Python modules)
|
||||
|
||||
OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-imaging.github.io/), the friendly fork of PIL.
|
||||
OleFileIO_PL can be used as an independent module or with PIL. The goal is to have it integrated into [Pillow](http://python-pillow.github.io/), the friendly fork of PIL.
|
||||
|
||||
OleFileIO\_PL is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data, then please also check [python-oletools](http://www.decalage.info/python/oletools), which are built upon OleFileIO_PL.
|
||||
|
||||
|
|
|
@ -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 += "%02x%02x%02x " % (r, g, b)
|
||||
colorspace += b"> ]"
|
||||
procset = "/ImageI" # indexed color
|
||||
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())
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -859,7 +859,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
# libtiff handles the fillmode for us, so 1;IR should
|
||||
# actually be 1;I. Including the R double reverses the
|
||||
# bits, so stripes of the image are reversed. See
|
||||
# https://github.com/python-imaging/Pillow/issues/279
|
||||
# https://github.com/python-pillow/Pillow/issues/279
|
||||
if fillorder == 2:
|
||||
key = (
|
||||
self.tag.prefix, photo, format, 1,
|
||||
|
@ -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)
|
||||
|
|
|
@ -5,8 +5,8 @@ Pillow
|
|||
|
||||
Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
|
||||
|
||||
.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master
|
||||
:target: https://travis-ci.org/python-imaging/Pillow
|
||||
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
|
||||
:target: https://travis-ci.org/python-pillow/Pillow
|
||||
:alt: Travis CI build status
|
||||
|
||||
.. image:: https://pypip.in/v/Pillow/badge.png
|
||||
|
@ -17,7 +17,7 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt
|
|||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:alt: Number of PyPI downloads
|
||||
|
||||
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/python-imaging/Pillow?branch=master
|
||||
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
|
||||
|
||||
The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more.
|
||||
|
|
BIN
Tests/images/binary_preview_map.eps
Executable file
BIN
Tests/images/imagedraw_arc.png
Normal file
After Width: | Height: | Size: 284 B |
BIN
Tests/images/imagedraw_bitmap.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Tests/images/imagedraw_chord.png
Normal file
After Width: | Height: | Size: 326 B |
BIN
Tests/images/imagedraw_ellipse.png
Normal file
After Width: | Height: | Size: 491 B |
BIN
Tests/images/imagedraw_floodfill.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
Tests/images/imagedraw_floodfill2.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
Tests/images/imagedraw_line.png
Normal file
After Width: | Height: | Size: 286 B |
BIN
Tests/images/imagedraw_pieslice.png
Normal file
After Width: | Height: | Size: 405 B |
BIN
Tests/images/imagedraw_point.png
Normal file
After Width: | Height: | Size: 124 B |
BIN
Tests/images/imagedraw_polygon.png
Normal file
After Width: | Height: | Size: 292 B |
BIN
Tests/images/imagedraw_rectangle.png
Normal file
After Width: | Height: | Size: 228 B |
BIN
Tests/images/junk_jpeg_header.jpg
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
Tests/images/lena.spider
Normal 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
|
||||
|
|
|
@ -37,7 +37,7 @@ def test_roundtrip():
|
|||
assert_image_similar(reread.convert('RGB'), im, 50)
|
||||
|
||||
def test_roundtrip2():
|
||||
#see https://github.com/python-imaging/Pillow/issues/403
|
||||
#see https://github.com/python-pillow/Pillow/issues/403
|
||||
out = tempfile('temp.gif')
|
||||
im = Image.open('Images/lena.gif')
|
||||
im2 = im.copy()
|
||||
|
@ -48,7 +48,7 @@ def test_roundtrip2():
|
|||
|
||||
|
||||
def test_palette_handling():
|
||||
# see https://github.com/python-imaging/Pillow/issues/513
|
||||
# see https://github.com/python-pillow/Pillow/issues/513
|
||||
|
||||
im = Image.open('Images/lena.gif')
|
||||
im = im.convert('RGB')
|
||||
|
@ -64,7 +64,7 @@ def test_palette_handling():
|
|||
assert_image_similar(im, reloaded.convert('RGB'), 10)
|
||||
|
||||
def test_palette_434():
|
||||
# see https://github.com/python-imaging/Pillow/issues/434
|
||||
# see https://github.com/python-pillow/Pillow/issues/434
|
||||
|
||||
def roundtrip(im, *args, **kwargs):
|
||||
out = tempfile('temp.gif')
|
||||
|
|
|
@ -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-pillow/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-pillow/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-pillow/Pillow/issues/630
|
||||
filename = "Tests/images/junk_jpeg_header.jpg"
|
||||
assert_no_exception(lambda: Image.open(filename))
|
||||
|
||||
# End of file
|
||||
|
|
|
@ -71,7 +71,7 @@ def test_g4_eq_png():
|
|||
|
||||
assert_image_equal(g4, png)
|
||||
|
||||
# see https://github.com/python-imaging/Pillow/issues/279
|
||||
# see https://github.com/python-pillow/Pillow/issues/279
|
||||
def test_g4_fillorder_eq_png():
|
||||
""" Checking that we're actually getting the data that we expect"""
|
||||
png = Image.open('Tests/images/g4-fillorder-test.png')
|
||||
|
@ -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
|
@ -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
|
|
@ -101,7 +101,7 @@ def test_bad_text():
|
|||
assert_equal(im.info, {'spam': 'egg\x00'})
|
||||
|
||||
def test_bad_ztxt():
|
||||
# Test reading malformed zTXt chunks (python-imaging/Pillow#318)
|
||||
# Test reading malformed zTXt chunks (python-pillow/Pillow#318)
|
||||
|
||||
im = load(HEAD + chunk(b'zTXt') + TAIL)
|
||||
assert_equal(im.info, {})
|
||||
|
|
36
Tests/test_file_spider.py
Normal 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
|
|
@ -6,7 +6,7 @@ tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys()))
|
|||
def test_rt_metadata():
|
||||
""" Test writing arbitray metadata into the tiff image directory
|
||||
Use case is ImageJ private tags, one numeric, one arbitrary
|
||||
data. https://github.com/python-imaging/Pillow/issues/291
|
||||
data. https://github.com/python-pillow/Pillow/issues/291
|
||||
"""
|
||||
|
||||
img = lena()
|
||||
|
|
|
@ -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
|
||||
# ref https://github.com/python-pillow/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'))
|
||||
|
@ -55,21 +60,36 @@ def test_rgba_p():
|
|||
|
||||
assert_image_similar(im, comparable, 20)
|
||||
|
||||
|
||||
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-pillow/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
|
||||
|
@ -77,7 +97,7 @@ def test_trns_l():
|
|||
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,19 +105,19 @@ 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')
|
||||
|
@ -105,8 +125,6 @@ def test_trns_RGB():
|
|||
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))
|
||||
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ def test_basic():
|
|||
check(mode)
|
||||
|
||||
def test_signedness():
|
||||
# see https://github.com/python-imaging/Pillow/issues/452
|
||||
# see https://github.com/python-pillow/Pillow/issues/452
|
||||
# pixelaccess is using signed int* instead of uint*
|
||||
for mode in ("I;16", "I;16B"):
|
||||
check(mode, 2**15-1)
|
||||
|
|
|
@ -4,7 +4,7 @@ from PIL import Image
|
|||
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
# This takes _forever_ on pypy. Open Bug,
|
||||
# see https://github.com/python-imaging/Pillow/issues/484
|
||||
# see https://github.com/python-pillow/Pillow/issues/484
|
||||
skip()
|
||||
|
||||
def test_sanity():
|
||||
|
@ -26,7 +26,7 @@ def test_sanity():
|
|||
|
||||
def test_16bit_lut():
|
||||
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
||||
see https://github.com/python-imaging/Pillow/issues/440
|
||||
see https://github.com/python-pillow/Pillow/issues/440
|
||||
"""
|
||||
|
||||
im = lena("I")
|
||||
|
|
|
@ -94,7 +94,7 @@ def test_alpha_premult_transform():
|
|||
|
||||
def test_blank_fill():
|
||||
# attempting to hit
|
||||
# https://github.com/python-imaging/Pillow/issues/254 reported
|
||||
# https://github.com/python-pillow/Pillow/issues/254 reported
|
||||
#
|
||||
# issue is that transforms with transparent overflow area
|
||||
# contained junk from previous images, especially on systems with
|
||||
|
|
|
@ -9,12 +9,13 @@ except ImportError:
|
|||
|
||||
SRGB = "Tests/icc/sRGB.icm"
|
||||
|
||||
|
||||
def test_sanity():
|
||||
|
||||
# basic smoke test.
|
||||
# this mostly follows the cms_test outline.
|
||||
|
||||
v = ImageCms.versions() # should return four strings
|
||||
v = ImageCms.versions() # should return four strings
|
||||
assert_equal(v[0], '1.0.0 pil')
|
||||
assert_equal(list(map(type, v)), [str, str, str, str])
|
||||
|
||||
|
@ -24,10 +25,19 @@ def test_sanity():
|
|||
i = ImageCms.profileToProfile(lena(), SRGB, SRGB)
|
||||
assert_image(i, "RGB", (128, 128))
|
||||
|
||||
i = lena()
|
||||
ImageCms.profileToProfile(i, SRGB, SRGB, inPlace=True)
|
||||
assert_image(i, "RGB", (128, 128))
|
||||
|
||||
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
||||
i = ImageCms.applyTransform(lena(), t)
|
||||
assert_image(i, "RGB", (128, 128))
|
||||
|
||||
i = lena()
|
||||
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
||||
ImageCms.applyTransform(lena(), t, inPlace=True)
|
||||
assert_image(i, "RGB", (128, 128))
|
||||
|
||||
p = ImageCms.createProfile("sRGB")
|
||||
o = ImageCms.getOpenProfile(SRGB)
|
||||
t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB")
|
||||
|
@ -41,24 +51,47 @@ def test_sanity():
|
|||
assert_image(i, "RGB", (128, 128))
|
||||
|
||||
# test PointTransform convenience API
|
||||
im = lena().point(t)
|
||||
lena().point(t)
|
||||
|
||||
|
||||
def test_name():
|
||||
# get profile information for file
|
||||
assert_equal(ImageCms.getProfileName(SRGB).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
def x_test_info():
|
||||
|
||||
|
||||
def test_info():
|
||||
assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(),
|
||||
['sRGB IEC61966-2.1', '',
|
||||
'Copyright (c) 1998 Hewlett-Packard Company', '',
|
||||
'WhitePoint : D65 (daylight)', '',
|
||||
'Tests/icc/sRGB.icm'])
|
||||
'Copyright (c) 1998 Hewlett-Packard Company', ''])
|
||||
|
||||
|
||||
def test_copyright():
|
||||
assert_equal(ImageCms.getProfileCopyright(SRGB).strip(),
|
||||
'Copyright (c) 1998 Hewlett-Packard Company')
|
||||
|
||||
|
||||
def test_manufacturer():
|
||||
assert_equal(ImageCms.getProfileManufacturer(SRGB).strip(),
|
||||
'IEC http://www.iec.ch')
|
||||
|
||||
|
||||
def test_model():
|
||||
assert_equal(ImageCms.getProfileModel(SRGB).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
|
||||
|
||||
def test_description():
|
||||
assert_equal(ImageCms.getProfileDescription(SRGB).strip(),
|
||||
'sRGB IEC61966-2.1')
|
||||
|
||||
|
||||
def test_intent():
|
||||
assert_equal(ImageCms.getDefaultIntent(SRGB), 0)
|
||||
assert_equal(ImageCms.isIntentSupported(
|
||||
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
ImageCms.DIRECTION_INPUT), 1)
|
||||
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
ImageCms.DIRECTION_INPUT), 1)
|
||||
|
||||
|
||||
def test_profile_object():
|
||||
# same, using profile object
|
||||
|
@ -69,8 +102,9 @@ def test_profile_object():
|
|||
# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', ''])
|
||||
assert_equal(ImageCms.getDefaultIntent(p), 0)
|
||||
assert_equal(ImageCms.isIntentSupported(
|
||||
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
ImageCms.DIRECTION_INPUT), 1)
|
||||
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
ImageCms.DIRECTION_INPUT), 1)
|
||||
|
||||
|
||||
def test_extensions():
|
||||
# extensions
|
||||
|
@ -79,12 +113,21 @@ def test_extensions():
|
|||
assert_equal(ImageCms.getProfileName(p).strip(),
|
||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||
|
||||
|
||||
def test_exceptions():
|
||||
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None))
|
||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
|
||||
assert_exception(
|
||||
ImageCms.PyCMSError,
|
||||
lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
|
||||
assert_exception(
|
||||
ImageCms.PyCMSError,
|
||||
lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
|
||||
assert_exception(
|
||||
ImageCms.PyCMSError,
|
||||
lambda: ImageCms.getProfileName(None))
|
||||
assert_exception(
|
||||
ImageCms.PyCMSError,
|
||||
lambda: ImageCms.isIntentSupported(SRGB, None, None))
|
||||
|
||||
|
||||
def test_display_profile():
|
||||
|
@ -93,37 +136,37 @@ def test_display_profile():
|
|||
|
||||
|
||||
def test_lab_color_profile():
|
||||
pLab = ImageCms.createProfile("LAB", 5000)
|
||||
pLab = ImageCms.createProfile("LAB", 6500)
|
||||
ImageCms.createProfile("LAB", 5000)
|
||||
ImageCms.createProfile("LAB", 6500)
|
||||
|
||||
|
||||
def test_simple_lab():
|
||||
i = Image.new('RGB', (10,10), (128,128,128))
|
||||
i = Image.new('RGB', (10, 10), (128, 128, 128))
|
||||
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||
|
||||
i_lab = ImageCms.applyTransform(i, t)
|
||||
|
||||
|
||||
assert_equal(i_lab.mode, 'LAB')
|
||||
|
||||
k = i_lab.getpixel((0,0))
|
||||
assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128
|
||||
k = i_lab.getpixel((0, 0))
|
||||
assert_equal(k, (137, 128, 128)) # not a linear luminance map. so L != 128
|
||||
|
||||
L = i_lab.getdata(0)
|
||||
L = i_lab.getdata(0)
|
||||
a = i_lab.getdata(1)
|
||||
b = i_lab.getdata(2)
|
||||
|
||||
assert_equal(list(L), [137]*100)
|
||||
assert_equal(list(a), [128]*100)
|
||||
assert_equal(list(b), [128]*100)
|
||||
assert_equal(list(L), [137] * 100)
|
||||
assert_equal(list(a), [128] * 100)
|
||||
assert_equal(list(b), [128] * 100)
|
||||
|
||||
|
||||
def test_lab_color():
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||
# need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType,
|
||||
# and have that mapping work back to a PIL mode. (likely RGB)
|
||||
# Need to add a type mapping for some PIL type to TYPE_Lab_8 in
|
||||
# findLCMSType, and have that mapping work back to a PIL mode (likely RGB).
|
||||
i = ImageCms.applyTransform(lena(), t)
|
||||
assert_image(i, "LAB", (128, 128))
|
||||
|
||||
|
@ -133,6 +176,7 @@ def test_lab_color():
|
|||
|
||||
assert_image_similar(i, target, 30)
|
||||
|
||||
|
||||
def test_lab_srgb():
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
||||
|
@ -145,6 +189,7 @@ def test_lab_srgb():
|
|||
|
||||
assert_image_similar(lena(), img_srgb, 30)
|
||||
|
||||
|
||||
def test_lab_roundtrip():
|
||||
# check to see if we're at least internally consistent.
|
||||
pLab = ImageCms.createProfile("LAB")
|
||||
|
@ -156,5 +201,3 @@ def test_lab_roundtrip():
|
|||
out = ImageCms.applyTransform(i, t2)
|
||||
|
||||
assert_image_similar(lena(), out, 2)
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,27 @@
|
|||
from tester import *
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageColor
|
||||
from PIL import ImageDraw
|
||||
|
||||
# Image size
|
||||
w, h = 100, 100
|
||||
|
||||
# Bounding box points
|
||||
x0 = int(w / 4)
|
||||
x1 = int(x0 * 3)
|
||||
y0 = int(h / 4)
|
||||
y1 = int(x0 * 3)
|
||||
|
||||
# Two kinds of bounding box
|
||||
bbox1 = [(x0, y0), (x1, y1)]
|
||||
bbox2 = [x0, y0, x1, y1]
|
||||
|
||||
# Two kinds of coordinate sequences
|
||||
points1 = [(10, 10), (20, 40), (30, 30)]
|
||||
points2 = [10, 10, 20, 40, 30, 30]
|
||||
|
||||
|
||||
def test_sanity():
|
||||
|
||||
im = lena("RGB").copy()
|
||||
|
@ -17,6 +36,7 @@ def test_sanity():
|
|||
|
||||
success()
|
||||
|
||||
|
||||
def test_deprecated():
|
||||
|
||||
im = lena().copy()
|
||||
|
@ -26,3 +46,220 @@ def test_deprecated():
|
|||
assert_warning(DeprecationWarning, lambda: draw.setink(0))
|
||||
assert_warning(DeprecationWarning, lambda: draw.setfill(0))
|
||||
|
||||
|
||||
def helper_arc(bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
# FIXME Fill param should be named outline.
|
||||
draw.arc(bbox, 0, 180)
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_arc.png"))
|
||||
|
||||
|
||||
def test_arc1():
|
||||
helper_arc(bbox1)
|
||||
|
||||
|
||||
def test_arc2():
|
||||
helper_arc(bbox2)
|
||||
|
||||
|
||||
def test_bitmap():
|
||||
# Arrange
|
||||
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.bitmap((10, 10), small)
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png"))
|
||||
|
||||
|
||||
def helper_chord(bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.chord(bbox, 0, 180, fill="red", outline="yellow")
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_chord.png"))
|
||||
|
||||
|
||||
def test_chord1():
|
||||
helper_chord(bbox1)
|
||||
|
||||
|
||||
def test_chord2():
|
||||
helper_chord(bbox2)
|
||||
|
||||
|
||||
def helper_ellipse(bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.ellipse(bbox, fill="green", outline="blue")
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_ellipse.png"))
|
||||
|
||||
|
||||
def test_ellipse1():
|
||||
helper_ellipse(bbox1)
|
||||
|
||||
|
||||
def test_ellipse2():
|
||||
helper_ellipse(bbox2)
|
||||
|
||||
|
||||
def helper_line(points):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.line(points1, fill="yellow", width=2)
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png"))
|
||||
|
||||
|
||||
def test_line1():
|
||||
helper_line(points1)
|
||||
|
||||
|
||||
def test_line2():
|
||||
helper_line(points2)
|
||||
|
||||
|
||||
def helper_pieslice(bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.pieslice(bbox, -90, 45, fill="white", outline="blue")
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_pieslice.png"))
|
||||
|
||||
|
||||
def test_pieslice1():
|
||||
helper_pieslice(bbox1)
|
||||
|
||||
|
||||
def test_pieslice2():
|
||||
helper_pieslice(bbox2)
|
||||
|
||||
|
||||
def helper_point(points):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.point(points1, fill="yellow")
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png"))
|
||||
|
||||
|
||||
def test_point1():
|
||||
helper_point(points1)
|
||||
|
||||
|
||||
def test_point2():
|
||||
helper_point(points2)
|
||||
|
||||
|
||||
def helper_polygon(points):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.polygon(points1, fill="red", outline="blue")
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png"))
|
||||
|
||||
|
||||
def test_polygon1():
|
||||
helper_polygon(points1)
|
||||
|
||||
|
||||
def test_polygon2():
|
||||
helper_polygon(points2)
|
||||
|
||||
|
||||
def helper_rectangle(bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw.rectangle(bbox, fill="black", outline="green")
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png"))
|
||||
|
||||
|
||||
def test_rectangle1():
|
||||
helper_rectangle(bbox1)
|
||||
|
||||
|
||||
def test_rectangle2():
|
||||
helper_rectangle(bbox2)
|
||||
|
||||
|
||||
def test_floodfill():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.rectangle(bbox2, outline="yellow", fill="green")
|
||||
centre_point = (int(w/2), int(h/2))
|
||||
|
||||
# Act
|
||||
ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"))
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill.png"))
|
||||
|
||||
|
||||
def test_floodfill_border():
|
||||
# Arrange
|
||||
im = Image.new("RGB", (w, h))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.rectangle(bbox2, outline="yellow", fill="green")
|
||||
centre_point = (int(w/2), int(h/2))
|
||||
|
||||
# Act
|
||||
ImageDraw.floodfill(
|
||||
im, centre_point, ImageColor.getrgb("red"),
|
||||
border=ImageColor.getrgb("black"))
|
||||
del draw
|
||||
|
||||
# Assert
|
||||
assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png"))
|
||||
|
||||
|
||||
# End of file
|
||||
|
|
|
@ -56,7 +56,7 @@ def test_parser():
|
|||
assert_image(im1, im2.mode, im2.size)
|
||||
|
||||
# XXX Why assert exception and why does it fail?
|
||||
# https://github.com/python-imaging/Pillow/issues/78
|
||||
# https://github.com/python-pillow/Pillow/issues/78
|
||||
#assert_exception(IOError, lambda: roundtrip("PDF"))
|
||||
|
||||
def test_ico():
|
||||
|
|
|
@ -3,7 +3,7 @@ from PIL import Image
|
|||
|
||||
import locale
|
||||
|
||||
# ref https://github.com/python-imaging/Pillow/issues/272
|
||||
# ref https://github.com/python-pillow/Pillow/issues/272
|
||||
## on windows, in polish locale:
|
||||
|
||||
## import locale
|
||||
|
|
|
@ -108,7 +108,7 @@ def test_to_array():
|
|||
|
||||
|
||||
def test_point_lut():
|
||||
# see https://github.com/python-imaging/Pillow/issues/439
|
||||
# see https://github.com/python-pillow/Pillow/issues/439
|
||||
|
||||
data = list(range(256))*3
|
||||
lut = numpy.array(data, dtype='uint8')
|
||||
|
|
70
Tests/test_pickle.py
Normal 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
|
|
@ -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()
|
||||
|
||||
|
|
95
_imaging.c
|
@ -2399,17 +2399,35 @@ _draw_ink(ImagingDrawObject* self, PyObject* args)
|
|||
static PyObject*
|
||||
_draw_arc(ImagingDrawObject* self, PyObject* args)
|
||||
{
|
||||
int x0, y0, x1, y1;
|
||||
double* xy;
|
||||
int n;
|
||||
|
||||
PyObject* data;
|
||||
int ink;
|
||||
int start, end;
|
||||
int op = 0;
|
||||
if (!PyArg_ParseTuple(args, "(iiii)iii|i",
|
||||
&x0, &y0, &x1, &y1,
|
||||
&start, &end, &ink))
|
||||
if (!PyArg_ParseTuple(args, "Oiii|i", &data, &start, &end, &ink))
|
||||
return NULL;
|
||||
|
||||
if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end,
|
||||
&ink, op) < 0)
|
||||
n = PyPath_Flatten(data, &xy);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = ImagingDrawArc(self->image->image,
|
||||
(int) xy[0], (int) xy[1],
|
||||
(int) xy[2], (int) xy[3],
|
||||
start, end, &ink, op
|
||||
);
|
||||
|
||||
free(xy);
|
||||
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
|
@ -2455,15 +2473,35 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
|
|||
static PyObject*
|
||||
_draw_chord(ImagingDrawObject* self, PyObject* args)
|
||||
{
|
||||
int x0, y0, x1, y1;
|
||||
double* xy;
|
||||
int n;
|
||||
|
||||
PyObject* data;
|
||||
int ink, fill;
|
||||
int start, end;
|
||||
if (!PyArg_ParseTuple(args, "(iiii)iiii",
|
||||
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
|
||||
if (!PyArg_ParseTuple(args, "Oiiii",
|
||||
&data, &start, &end, &ink, &fill))
|
||||
return NULL;
|
||||
|
||||
if (ImagingDrawChord(self->image->image, x0, y0, x1, y1,
|
||||
start, end, &ink, fill, self->blend) < 0)
|
||||
n = PyPath_Flatten(data, &xy);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = ImagingDrawChord(self->image->image,
|
||||
(int) xy[0], (int) xy[1],
|
||||
(int) xy[2], (int) xy[3],
|
||||
start, end, &ink, fill, self->blend
|
||||
);
|
||||
|
||||
free(xy);
|
||||
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
|
@ -2656,15 +2694,34 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
|
|||
static PyObject*
|
||||
_draw_pieslice(ImagingDrawObject* self, PyObject* args)
|
||||
{
|
||||
int x0, y0, x1, y1;
|
||||
double* xy;
|
||||
int n;
|
||||
|
||||
PyObject* data;
|
||||
int ink, fill;
|
||||
int start, end;
|
||||
if (!PyArg_ParseTuple(args, "(iiii)iiii",
|
||||
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
|
||||
if (!PyArg_ParseTuple(args, "Oiiii", &data, &start, &end, &ink, &fill))
|
||||
return NULL;
|
||||
|
||||
if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1,
|
||||
start, end, &ink, fill, self->blend) < 0)
|
||||
n = PyPath_Flatten(data, &xy);
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 2 coordinates"
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = ImagingDrawPieslice(self->image->image,
|
||||
(int) xy[0], (int) xy[1],
|
||||
(int) xy[2], (int) xy[3],
|
||||
start, end, &ink, fill, self->blend
|
||||
);
|
||||
|
||||
free(xy);
|
||||
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
|
@ -3350,7 +3407,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 +3480,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},
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
3
decode.c
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
2
docs/_templates/sidebarhelp.html
vendored
|
@ -12,7 +12,7 @@
|
|||
|
||||
<p>
|
||||
If you've discovered a bug, you can
|
||||
<a href="https://github.com/python-imaging/Pillow/issues/new">open an issue
|
||||
<a href="https://github.com/python-pillow/Pillow/issues/new">open an issue
|
||||
on Github</a>.
|
||||
</p>
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ The fork authors' goal is to foster active development of PIL through:
|
|||
- Regular releases to the `Python Package Index`_
|
||||
- Solicitation for community contributions and involvement on `Image-SIG`_
|
||||
|
||||
.. _Travis CI: https://travis-ci.org/python-imaging/Pillow
|
||||
.. _GitHub: https://github.com/python-imaging/Pillow
|
||||
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow
|
||||
.. _GitHub: https://github.com/python-pillow/Pillow
|
||||
.. _Python Package Index: https://pypi.python.org/pypi/Pillow
|
||||
.. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig
|
||||
|
||||
|
@ -60,6 +60,6 @@ announcement. So if you still want to support PIL, please
|
|||
|
||||
.. _report issues here first: https://bitbucket.org/effbot/pil-2009-raclette/issues
|
||||
|
||||
.. _open the corresponding Pillow tickets here: https://github.com/python-imaging/Pillow/issues
|
||||
.. _open the corresponding Pillow tickets here: https://github.com/python-pillow/Pillow/issues
|
||||
|
||||
Please provide a link to the PIL ticket so we can track the issue(s) upstream.
|
||||
|
|
|
@ -4,8 +4,8 @@ Pillow
|
|||
Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the
|
||||
Python Imaging Library by Fredrik Lundh and Contributors.
|
||||
|
||||
.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master
|
||||
:target: https://travis-ci.org/python-imaging/Pillow
|
||||
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
|
||||
:target: https://travis-ci.org/python-pillow/Pillow
|
||||
:alt: Travis CI build status
|
||||
|
||||
.. image:: https://pypip.in/v/Pillow/badge.png
|
||||
|
@ -16,15 +16,15 @@ Python Imaging Library by Fredrik Lundh and Contributors.
|
|||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:alt: Number of PyPI downloads
|
||||
|
||||
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/python-imaging/Pillow?branch=master
|
||||
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
|
||||
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
|
||||
:alt: Test coverage
|
||||
|
||||
To start using Pillow, please read the :doc:`installation
|
||||
instructions <installation>`.
|
||||
|
||||
You can get the source and contribute at
|
||||
https://github.com/python-imaging/Pillow. You can download archives
|
||||
https://github.com/python-pillow/Pillow. You can download archives
|
||||
and old versions from `PyPI <https://pypi.python.org/pypi/Pillow>`_.
|
||||
|
||||
.. toctree::
|
||||
|
@ -42,7 +42,7 @@ Support Pillow!
|
|||
|
||||
PIL needs you! Please help us maintain the Python Imaging Library here:
|
||||
|
||||
- `GitHub <https://github.com/python-imaging/Pillow>`_
|
||||
- `GitHub <https://github.com/python-pillow/Pillow>`_
|
||||
- `Freenode <irc://irc.freenode.net#pil>`_
|
||||
- `Image-SIG <http://mail.python.org/mailman/listinfo/image-sig>`_
|
||||
|
||||
|
|
|
@ -185,6 +185,25 @@ to a specific version:
|
|||
|
||||
$ pip install --use-wheel Pillow==2.3.0
|
||||
|
||||
FreeBSD installation
|
||||
---------------------
|
||||
|
||||
.. Note:: Only FreeBSD 10 tested
|
||||
|
||||
|
||||
Make sure you have Python's development libraries installed.::
|
||||
|
||||
$ sudo pkg install python2
|
||||
|
||||
Or for Python 3::
|
||||
|
||||
$ sudo pkg install python3
|
||||
|
||||
Prerequisites are installed on **FreeBSD 10** with::
|
||||
|
||||
$ sudo pkg install jpeg tiff webp lcms2 freetype2
|
||||
|
||||
|
||||
|
||||
Platform support
|
||||
----------------
|
||||
|
@ -224,6 +243,8 @@ current versions of Linux, OS X, and Windows.
|
|||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||
| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 |
|
||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||
| FreeBSD 10 |Yes | 2.7,3.4 | 2.4,2.3.1 |x86-64 |
|
||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||
| Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 |
|
||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||
| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 |
|
||||
|
@ -232,4 +253,3 @@ current versions of Linux, OS X, and Windows.
|
|||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||
| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.3.0, 2.4.0 |x86,x86-64 |
|
||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||
|
||||
|
|
|
@ -91,9 +91,12 @@ Methods
|
|||
Draws an arc (a portion of a circle outline) between the start and end
|
||||
angles, inside the given bounding box.
|
||||
|
||||
:param xy: Four points to define the bounding box. Sequence of either
|
||||
:param xy: Four points to define the bounding box. Sequence of
|
||||
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
|
||||
:param outline: Color to use for the outline.
|
||||
:param start: Starting angle, in degrees. Angles are measured from
|
||||
3 o'clock, increasing clockwise.
|
||||
:param end: Ending angle, in degrees.
|
||||
:param fill: Color to use for the arc.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None)
|
||||
|
||||
|
@ -111,7 +114,7 @@ Methods
|
|||
Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points
|
||||
with a straight line.
|
||||
|
||||
:param xy: Four points to define the bounding box. Sequence of either
|
||||
:param xy: Four points to define the bounding box. Sequence of
|
||||
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
|
||||
:param outline: Color to use for the outline.
|
||||
:param fill: Color to use for the fill.
|
||||
|
@ -144,7 +147,7 @@ Methods
|
|||
Same as arc, but also draws straight lines between the end points and the
|
||||
center of the bounding box.
|
||||
|
||||
:param xy: Four points to define the bounding box. Sequence of either
|
||||
:param xy: Four points to define the bounding box. Sequence of
|
||||
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
|
||||
:param outline: Color to use for the outline.
|
||||
:param fill: Color to use for the fill.
|
||||
|
|
3
encode.c
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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)
|
||||
#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
|
||||
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
two cases. */
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
|
|
|
@ -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
|
@ -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)
|
||||
|
|
36
setup.py
|
@ -206,24 +206,30 @@ class pil_build_ext(build_ext):
|
|||
_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
|
||||
# Homebrew not installed
|
||||
prefix = None
|
||||
|
||||
# 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")
|
||||
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])
|
||||
|
@ -654,7 +660,7 @@ setup(
|
|||
_read('CHANGES.rst')).decode('utf-8'),
|
||||
author='Alex Clark (fork author)',
|
||||
author_email='aclark@aclark.net',
|
||||
url='http://python-imaging.github.io/',
|
||||
url='http://python-pillow.github.io/',
|
||||
classifiers=[
|
||||
"Development Status :: 6 - Mature",
|
||||
"Topic :: Multimedia :: Graphics",
|
||||
|
|