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__.:
|
|
@ -39,7 +39,7 @@ after_success:
|
||||||
- coverage report
|
- coverage report
|
||||||
- coveralls
|
- coveralls
|
||||||
- pip install pep8 pyflakes
|
- pip install pep8 pyflakes
|
||||||
- pep8 PIL/*.py
|
- pep8 --statistics --count PIL/*.py
|
||||||
- pyflakes PIL/*.py
|
- pep8 --statistics --count Tests/*.py
|
||||||
- pep8 Tests/*.py
|
- pyflakes PIL/*.py | tee >(wc -l)
|
||||||
- pyflakes Tests/*.py
|
- pyflakes Tests/*.py | tee >(wc -l)
|
||||||
|
|
42
CHANGES.rst
|
@ -3,33 +3,39 @@ Changelog (Pillow)
|
||||||
|
|
||||||
2.5.0 (unreleased)
|
2.5.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
- Added more ImageDraw tests
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Added tests for Spider files
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
- Use libtiff to write any compressed tiff files
|
- Use libtiff to write any compressed tiff files
|
||||||
[wiredfool]
|
[wiredfool]
|
||||||
|
|
||||||
- Support for pickling Image objects
|
- Support for pickling Image objects
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Fixed resolution handling for EPS thumbnails
|
- Fixed resolution handling for EPS thumbnails
|
||||||
[eliempje]
|
[eliempje]
|
||||||
|
|
||||||
- Fixed rendering of some binary EPS files (Issue #302)
|
- Fixed rendering of some binary EPS files (Issue #302)
|
||||||
[eliempje]
|
[eliempje]
|
||||||
|
|
||||||
- Rename variables not to use built-in function names
|
- Rename variables not to use built-in function names
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Ignore junk JPEG markers
|
- Ignore junk JPEG markers
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Change default interpolation for Image.thumbnail to Image.ANTIALIAS
|
- Change default interpolation for Image.thumbnail to Image.ANTIALIAS
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Add tests and fixes for saving PDFs
|
- Add tests and fixes for saving PDFs
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Remove transparency resource after P->RGBA conversion
|
- Remove transparency resource after P->RGBA conversion
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Clean up preprocessor cruft for Windows
|
- Clean up preprocessor cruft for Windows
|
||||||
[CounterPillow]
|
[CounterPillow]
|
||||||
|
|
||||||
|
@ -39,13 +45,13 @@ Changelog (Pillow)
|
||||||
- Added Image.close, context manager support.
|
- Added Image.close, context manager support.
|
||||||
[wiredfool]
|
[wiredfool]
|
||||||
|
|
||||||
- Added support for 16 bit PGM files.
|
- Added support for 16 bit PGM files.
|
||||||
[wiredfool]
|
[wiredfool]
|
||||||
|
|
||||||
- Updated OleFileIO to version 0.30 from upstream
|
- Updated OleFileIO to version 0.30 from upstream
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
- Added support for additional TIFF floating point format
|
- Added support for additional TIFF floating point format
|
||||||
[Hijackal]
|
[Hijackal]
|
||||||
|
|
||||||
- Have the tempfile use a suffix with a dot
|
- Have the tempfile use a suffix with a dot
|
||||||
|
@ -75,7 +81,7 @@ Changelog (Pillow)
|
||||||
- Added support for JPEG 2000
|
- Added support for JPEG 2000
|
||||||
[al45tair]
|
[al45tair]
|
||||||
|
|
||||||
- Add more detailed error messages to Image.py
|
- Add more detailed error messages to Image.py
|
||||||
[larsmans]
|
[larsmans]
|
||||||
|
|
||||||
- Avoid conflicting _expand functions in PIL & MINGW, fixes #538
|
- Avoid conflicting _expand functions in PIL & MINGW, fixes #538
|
||||||
|
@ -103,7 +109,7 @@ Changelog (Pillow)
|
||||||
[wiredfool]
|
[wiredfool]
|
||||||
|
|
||||||
- Fixed palette handling when converting from mode P->RGB->P
|
- Fixed palette handling when converting from mode P->RGB->P
|
||||||
[d_schmidt]
|
[d_schmidt]
|
||||||
|
|
||||||
- Fixed saving mode P image as a PNG with transparency = palette color 0
|
- Fixed saving mode P image as a PNG with transparency = palette color 0
|
||||||
[d-schmidt]
|
[d-schmidt]
|
||||||
|
@ -113,7 +119,7 @@ Changelog (Pillow)
|
||||||
|
|
||||||
- Fixed DOS with invalid palette size or invalid image size in BMP file
|
- Fixed DOS with invalid palette size or invalid image size in BMP file
|
||||||
[wiredfool]
|
[wiredfool]
|
||||||
|
|
||||||
- Added support for BMP version 4 and 5
|
- Added support for BMP version 4 and 5
|
||||||
[eddwardo, wiredfool]
|
[eddwardo, wiredfool]
|
||||||
|
|
||||||
|
@ -146,7 +152,7 @@ Changelog (Pillow)
|
||||||
|
|
||||||
- Prefer homebrew freetype over X11 freetype (but still allow both)
|
- Prefer homebrew freetype over X11 freetype (but still allow both)
|
||||||
[dmckeone]
|
[dmckeone]
|
||||||
|
|
||||||
2.3.1 (2014-03-14)
|
2.3.1 (2014-03-14)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -271,7 +277,7 @@ Changelog (Pillow)
|
||||||
[nikmolnar]
|
[nikmolnar]
|
||||||
|
|
||||||
- Fix for encoding of b_whitespace, similar to closed issue #272
|
- Fix for encoding of b_whitespace, similar to closed issue #272
|
||||||
[mhogg]
|
[mhogg]
|
||||||
|
|
||||||
- Fix #273: Add numpy array interface support for 16 and 32 bit integer modes
|
- Fix #273: Add numpy array interface support for 16 and 32 bit integer modes
|
||||||
[cgohlke]
|
[cgohlke]
|
||||||
|
@ -431,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.)
|
- 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]
|
[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.
|
- Add WebP support.
|
||||||
[lqs]
|
[lqs]
|
||||||
|
|
|
@ -1529,7 +1529,7 @@ class Image:
|
||||||
clockwise around its centre.
|
clockwise around its centre.
|
||||||
|
|
||||||
:param angle: In degrees counter clockwise.
|
: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),
|
one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
|
||||||
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
|
:py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
|
||||||
environment), or :py:attr:`PIL.Image.BICUBIC`
|
environment), or :py:attr:`PIL.Image.BICUBIC`
|
||||||
|
@ -1550,7 +1550,6 @@ class Image:
|
||||||
math.cos(angle), math.sin(angle), 0.0,
|
math.cos(angle), math.sin(angle), 0.0,
|
||||||
-math.sin(angle), math.cos(angle), 0.0
|
-math.sin(angle), math.cos(angle), 0.0
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def transform(x, y, matrix=matrix):
|
def transform(x, y, matrix=matrix):
|
||||||
(a, b, c, d, e, f) = matrix
|
(a, b, c, d, e, f) = matrix
|
||||||
|
|
381
PIL/ImageCms.py
|
@ -1,19 +1,19 @@
|
||||||
#
|
"""
|
||||||
# The Python Imaging Library.
|
The Python Imaging Library.
|
||||||
# $Id$
|
$Id$
|
||||||
#
|
|
||||||
# optional color managment support, based on Kevin Cazabon's PyCMS
|
Optional color managment support, based on Kevin Cazabon's PyCMS
|
||||||
# library.
|
library.
|
||||||
#
|
|
||||||
# History:
|
History:
|
||||||
# 2009-03-08 fl Added to PIL.
|
2009-03-08 fl Added to PIL.
|
||||||
#
|
|
||||||
# Copyright (C) 2002-2003 Kevin Cazabon
|
Copyright (C) 2002-2003 Kevin Cazabon
|
||||||
# Copyright (c) 2009 by Fredrik Lundh
|
Copyright (c) 2009 by Fredrik Lundh
|
||||||
#
|
|
||||||
# See the README file for information on usage and redistribution. See
|
See the README file for information on usage and redistribution. See
|
||||||
# below for the original description.
|
below for the original description.
|
||||||
#
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
@ -66,7 +66,8 @@ pyCMS
|
||||||
|
|
||||||
Added try/except statements arount type() checks of
|
Added try/except statements arount type() checks of
|
||||||
potential CObjects... Python won't let you use type()
|
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.
|
Added buildProofTransformFromOpenProfiles() function.
|
||||||
Additional fixes in DLL, see DLL code for details.
|
Additional fixes in DLL, see DLL code for details.
|
||||||
|
@ -88,9 +89,9 @@ try:
|
||||||
from PIL import _imagingcms
|
from PIL import _imagingcms
|
||||||
except ImportError as ex:
|
except ImportError as ex:
|
||||||
# Allow error import for doc purposes, but error out when accessing
|
# Allow error import for doc purposes, but error out when accessing
|
||||||
# anything in core.
|
# anything in core.
|
||||||
from _util import deferred_error
|
from _util import import_err
|
||||||
_imagingcms = deferred_error(ex)
|
_imagingcms = import_err(ex)
|
||||||
from PIL._util import isStringType
|
from PIL._util import isStringType
|
||||||
|
|
||||||
core = _imagingcms
|
core = _imagingcms
|
||||||
|
@ -113,22 +114,24 @@ DIRECTION_PROOF = 2
|
||||||
FLAGS = {
|
FLAGS = {
|
||||||
"MATRIXINPUT": 1,
|
"MATRIXINPUT": 1,
|
||||||
"MATRIXOUTPUT": 2,
|
"MATRIXOUTPUT": 2,
|
||||||
"MATRIXONLY": (1|2),
|
"MATRIXONLY": (1 | 2),
|
||||||
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
|
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
|
||||||
"NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use)
|
# Don't create prelinearization tables on precalculated transforms
|
||||||
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
|
# (internal use):
|
||||||
"NOTCACHE": 64, # Inhibit 1-pixel cache
|
"NOPRELINEARIZATION": 16,
|
||||||
|
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
|
||||||
|
"NOTCACHE": 64, # Inhibit 1-pixel cache
|
||||||
"NOTPRECALC": 256,
|
"NOTPRECALC": 256,
|
||||||
"NULLTRANSFORM": 512, # Don't transform anyway
|
"NULLTRANSFORM": 512, # Don't transform anyway
|
||||||
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
|
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
|
||||||
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
|
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
|
||||||
"WHITEBLACKCOMPENSATION": 8192,
|
"WHITEBLACKCOMPENSATION": 8192,
|
||||||
"BLACKPOINTCOMPENSATION": 8192,
|
"BLACKPOINTCOMPENSATION": 8192,
|
||||||
"GAMUTCHECK": 4096, # Out of Gamut alarm
|
"GAMUTCHECK": 4096, # Out of Gamut alarm
|
||||||
"SOFTPROOFING": 16384, # Do softproofing
|
"SOFTPROOFING": 16384, # Do softproofing
|
||||||
"PRESERVEBLACK": 32768, # Black preservation
|
"PRESERVEBLACK": 32768, # Black preservation
|
||||||
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
|
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
|
||||||
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
|
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
_MAX_FLAG = 0
|
_MAX_FLAG = 0
|
||||||
|
@ -136,6 +139,7 @@ for flag in FLAGS.values():
|
||||||
if isinstance(flag, int):
|
if isinstance(flag, int):
|
||||||
_MAX_FLAG = _MAX_FLAG | flag
|
_MAX_FLAG = _MAX_FLAG | flag
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------.
|
# --------------------------------------------------------------------.
|
||||||
# Experimental PIL-level API
|
# Experimental PIL-level API
|
||||||
# --------------------------------------------------------------------.
|
# --------------------------------------------------------------------.
|
||||||
|
@ -153,40 +157,42 @@ class ImageCmsProfile:
|
||||||
elif hasattr(profile, "read"):
|
elif hasattr(profile, "read"):
|
||||||
self._set(core.profile_frombytes(profile.read()))
|
self._set(core.profile_frombytes(profile.read()))
|
||||||
else:
|
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):
|
def _set(self, profile, filename=None):
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
if profile:
|
if profile:
|
||||||
self.product_name = None #profile.product_name
|
self.product_name = None # profile.product_name
|
||||||
self.product_info = None #profile.product_info
|
self.product_info = None # profile.product_info
|
||||||
else:
|
else:
|
||||||
self.product_name = None
|
self.product_name = None
|
||||||
self.product_info = None
|
self.product_info = None
|
||||||
|
|
||||||
|
|
||||||
class ImageCmsTransform(Image.ImagePointHandler):
|
class ImageCmsTransform(Image.ImagePointHandler):
|
||||||
|
|
||||||
"""Transform. This can be used with the procedural API, or with the
|
"""Transform. This can be used with the procedural API, or with the
|
||||||
standard Image.point() method.
|
standard Image.point() method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, input, output, input_mode, output_mode,
|
def __init__(self, input, output, input_mode, output_mode,
|
||||||
intent=INTENT_PERCEPTUAL,
|
intent=INTENT_PERCEPTUAL, proof=None,
|
||||||
proof=None, proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
|
proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
|
||||||
if proof is None:
|
if proof is None:
|
||||||
self.transform = core.buildTransform(
|
self.transform = core.buildTransform(
|
||||||
input.profile, output.profile,
|
input.profile, output.profile,
|
||||||
input_mode, output_mode,
|
input_mode, output_mode,
|
||||||
intent,
|
intent,
|
||||||
flags
|
flags
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.transform = core.buildProofTransform(
|
self.transform = core.buildProofTransform(
|
||||||
input.profile, output.profile, proof.profile,
|
input.profile, output.profile, proof.profile,
|
||||||
input_mode, output_mode,
|
input_mode, output_mode,
|
||||||
intent, proof_intent,
|
intent, proof_intent,
|
||||||
flags
|
flags
|
||||||
)
|
)
|
||||||
# Note: inputMode and outputMode are for pyCMS compatibility only
|
# Note: inputMode and outputMode are for pyCMS compatibility only
|
||||||
self.input_mode = self.inputMode = input_mode
|
self.input_mode = self.inputMode = input_mode
|
||||||
self.output_mode = self.outputMode = output_mode
|
self.output_mode = self.outputMode = output_mode
|
||||||
|
@ -198,21 +204,22 @@ class ImageCmsTransform(Image.ImagePointHandler):
|
||||||
im.load()
|
im.load()
|
||||||
if imOut is None:
|
if imOut is None:
|
||||||
imOut = Image.new(self.output_mode, im.size, 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
|
return imOut
|
||||||
|
|
||||||
def apply_in_place(self, im):
|
def apply_in_place(self, im):
|
||||||
im.load()
|
im.load()
|
||||||
if im.mode != self.output_mode:
|
if im.mode != self.output_mode:
|
||||||
raise ValueError("mode mismatch") # wrong output mode
|
raise ValueError("mode mismatch") # wrong output mode
|
||||||
result = self.transform.apply(im.im.id, im.im.id)
|
self.transform.apply(im.im.id, im.im.id)
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
def get_display_profile(handle=None):
|
def get_display_profile(handle=None):
|
||||||
""" (experimental) Fetches the profile for the current display device.
|
""" (experimental) Fetches the profile for the current display device.
|
||||||
:returns: None if the profile is not known.
|
:returns: None if the profile is not known.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
from PIL import ImageWin
|
from PIL import ImageWin
|
||||||
|
@ -229,15 +236,21 @@ def get_display_profile(handle=None):
|
||||||
profile = get()
|
profile = get()
|
||||||
return ImageCmsProfile(profile)
|
return ImageCmsProfile(profile)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------.
|
# --------------------------------------------------------------------.
|
||||||
# pyCMS compatible layer
|
# pyCMS compatible layer
|
||||||
# --------------------------------------------------------------------.
|
# --------------------------------------------------------------------.
|
||||||
|
|
||||||
class PyCMSError(Exception):
|
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
|
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
|
(pyCMS) Applies an ICC transformation to a given image, mapping from
|
||||||
inputProfile to outputProfile.
|
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
|
profiles, the input profile must handle RGB data, and the output
|
||||||
profile must handle CMYK data.
|
profile must handle CMYK data.
|
||||||
|
|
||||||
:param im: An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.)
|
:param im: An open PIL image object (i.e. Image.new(...) or
|
||||||
:param inputProfile: String, as a valid filename path to the ICC input profile
|
Image.open(...), etc.)
|
||||||
you wish to use for this image, or a profile object
|
: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
|
:param outputProfile: String, as a valid filename path to the ICC output
|
||||||
profile you wish to use for this image, or a profile object
|
profile you wish to use for this image, or a profile object
|
||||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you wish
|
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||||
to use for the transform
|
wish to use for the transform
|
||||||
|
|
||||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
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
|
||||||
:param outputMode: A valid PIL mode for the output image (i.e. "RGB", "CMYK",
|
they do.
|
||||||
etc.). Note: if rendering the image "inPlace", outputMode MUST be the
|
:param outputMode: A valid PIL mode for the output image (i.e. "RGB",
|
||||||
same mode as the input, or omitted completely. If omitted, the outputMode
|
"CMYK", etc.). Note: if rendering the image "inPlace", outputMode
|
||||||
will be the same as the mode of the input image (im.mode)
|
MUST be the same mode as the input, or omitted completely. If
|
||||||
:param inPlace: Boolean (1 = True, None or 0 = False). If True, the original
|
omitted, the outputMode will be the same as the mode of the input
|
||||||
image is modified in-place, and None is returned. If False (default), a
|
image (im.mode)
|
||||||
new Image object is returned with the transform applied.
|
: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
|
: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:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if outputMode is None:
|
if outputMode is None:
|
||||||
outputMode = im.mode
|
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")
|
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
|
||||||
|
|
||||||
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
|
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:
|
try:
|
||||||
if not isinstance(inputProfile, ImageCmsProfile):
|
if not isinstance(inputProfile, ImageCmsProfile):
|
||||||
|
@ -300,8 +318,9 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER
|
||||||
if not isinstance(outputProfile, ImageCmsProfile):
|
if not isinstance(outputProfile, ImageCmsProfile):
|
||||||
outputProfile = ImageCmsProfile(outputProfile)
|
outputProfile = ImageCmsProfile(outputProfile)
|
||||||
transform = ImageCmsTransform(
|
transform = ImageCmsTransform(
|
||||||
inputProfile, outputProfile, im.mode, outputMode, renderingIntent, flags=flags
|
inputProfile, outputProfile, im.mode, outputMode,
|
||||||
)
|
renderingIntent, flags=flags
|
||||||
|
)
|
||||||
if inPlace:
|
if inPlace:
|
||||||
transform.apply_in_place(im)
|
transform.apply_in_place(im)
|
||||||
imOut = None
|
imOut = None
|
||||||
|
@ -323,8 +342,8 @@ def getOpenProfile(profileFilename):
|
||||||
If profileFilename is not a vaild filename for an ICC profile, a PyCMSError
|
If profileFilename is not a vaild filename for an ICC profile, a PyCMSError
|
||||||
will be raised.
|
will be raised.
|
||||||
|
|
||||||
:param profileFilename: String, as a valid filename path to the ICC profile you
|
:param profileFilename: String, as a valid filename path to the ICC profile
|
||||||
wish to open, or a file-like object.
|
you wish to open, or a file-like object.
|
||||||
:returns: A CmsProfile class object.
|
:returns: A CmsProfile class object.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
@ -334,7 +353,10 @@ def getOpenProfile(profileFilename):
|
||||||
except (IOError, TypeError, ValueError) as v:
|
except (IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(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
|
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
|
||||||
outputProfile. Use applyTransform to apply the transform to a given
|
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
|
manually overridden if you really want to, but I don't know of any
|
||||||
time that would be of use, or would even work).
|
time that would be of use, or would even work).
|
||||||
|
|
||||||
:param inputProfile: String, as a valid filename path to the ICC input profile
|
:param inputProfile: String, as a valid filename path to the ICC input
|
||||||
you wish to use for this transform, or a profile object
|
profile you wish to use for this transform, or a profile object
|
||||||
:param outputProfile: String, as a valid filename path to the ICC output
|
:param outputProfile: String, as a valid filename path to the ICC output
|
||||||
profile you wish to use for this transform, or a profile object
|
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
|
:param inMode: String, as a valid PIL mode that the appropriate profile
|
||||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||||
:param outMode: String, as a valid PIL mode that the appropriate profile also
|
:param outMode: String, as a valid PIL mode that the appropriate profile
|
||||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||||
wish to use for the transform
|
wish to use for the transform
|
||||||
|
|
||||||
|
@ -383,28 +405,37 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent
|
||||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
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
|
:param flags: Integer (0-...) specifying additional flags
|
||||||
:returns: A CmsTransform class object.
|
:returns: A CmsTransform class object.
|
||||||
:exception PyCMSError:
|
: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")
|
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
|
||||||
|
|
||||||
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
|
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:
|
try:
|
||||||
if not isinstance(inputProfile, ImageCmsProfile):
|
if not isinstance(inputProfile, ImageCmsProfile):
|
||||||
inputProfile = ImageCmsProfile(inputProfile)
|
inputProfile = ImageCmsProfile(inputProfile)
|
||||||
if not isinstance(outputProfile, ImageCmsProfile):
|
if not isinstance(outputProfile, ImageCmsProfile):
|
||||||
outputProfile = ImageCmsProfile(outputProfile)
|
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:
|
except (IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(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
|
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
|
||||||
outputProfile, but tries to simulate the result that would be
|
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
|
when the simulated device has a much wider gamut than the output
|
||||||
device, you may obtain marginal results.
|
device, you may obtain marginal results.
|
||||||
|
|
||||||
:param inputProfile: String, as a valid filename path to the ICC input profile
|
:param inputProfile: String, as a valid filename path to the ICC input
|
||||||
you wish to use for this transform, or a profile object
|
profile you wish to use for this transform, or a profile object
|
||||||
:param outputProfile: String, as a valid filename path to the ICC output
|
:param outputProfile: String, as a valid filename path to the ICC output
|
||||||
(monitor, usually) profile you wish to use for this transform, or a
|
(monitor, usually) profile you wish to use for this transform, or a
|
||||||
profile object
|
profile object
|
||||||
:param proofProfile: String, as a valid filename path to the ICC proof profile
|
:param proofProfile: String, as a valid filename path to the ICC proof
|
||||||
you wish to use for this transform, or a profile object
|
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
|
:param inMode: String, as a valid PIL mode that the appropriate profile
|
||||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||||
:param outMode: String, as a valid PIL mode that the appropriate profile also
|
:param outMode: String, as a valid PIL mode that the appropriate profile
|
||||||
supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
|
||||||
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
:param renderingIntent: Integer (0-3) specifying the rendering intent you
|
||||||
wish to use for the input->proof (simulated) transform
|
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_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
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
|
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent you
|
||||||
wish to use for proof->output transform
|
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_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
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
|
:param flags: Integer (0-...) specifying additional flags
|
||||||
:returns: A CmsTransform class object.
|
:returns: A CmsTransform class object.
|
||||||
:exception PyCMSError:
|
: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")
|
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
|
||||||
|
|
||||||
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
|
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:
|
try:
|
||||||
if not isinstance(inputProfile, ImageCmsProfile):
|
if not isinstance(inputProfile, ImageCmsProfile):
|
||||||
|
@ -490,13 +524,16 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
|
||||||
outputProfile = ImageCmsProfile(outputProfile)
|
outputProfile = ImageCmsProfile(outputProfile)
|
||||||
if not isinstance(proofProfile, ImageCmsProfile):
|
if not isinstance(proofProfile, ImageCmsProfile):
|
||||||
proofProfile = ImageCmsProfile(proofProfile)
|
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:
|
except (IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
buildTransformFromOpenProfiles = buildTransform
|
buildTransformFromOpenProfiles = buildTransform
|
||||||
buildProofTransformFromOpenProfiles = buildProofTransform
|
buildProofTransformFromOpenProfiles = buildProofTransform
|
||||||
|
|
||||||
|
|
||||||
def applyTransform(im, transform, inPlace=0):
|
def applyTransform(im, transform, inPlace=0):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Applies a transform to a given image.
|
(pyCMS) Applies a transform to a given image.
|
||||||
|
@ -514,8 +551,8 @@ def applyTransform(im, transform, inPlace=0):
|
||||||
is raised.
|
is raised.
|
||||||
|
|
||||||
This function applies a pre-calculated transform (from
|
This function applies a pre-calculated transform (from
|
||||||
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an
|
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
|
||||||
image. The transform can be used for multiple images, saving
|
to an image. The transform can be used for multiple images, saving
|
||||||
considerable calcuation time if doing the same conversion multiple times.
|
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
|
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
|
:param im: A PIL Image object, and im.mode must be the same as the inMode
|
||||||
supported by the transform.
|
supported by the transform.
|
||||||
:param transform: A valid CmsTransform class object
|
:param transform: A valid CmsTransform class object
|
||||||
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is modified
|
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is
|
||||||
in place and None is returned, if False, a new Image object with the
|
modified in place and None is returned, if False, a new Image object
|
||||||
transform applied is returned (and im is not changed). The default is False.
|
with the transform applied is returned (and im is not changed). The
|
||||||
:returns: Either None, or a new PIL Image object, depending on the value of inPlace
|
default is False.
|
||||||
|
:returns: Either None, or a new PIL Image object, depending on the value of
|
||||||
|
inPlace
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -546,6 +585,7 @@ def applyTransform(im, transform, inPlace=0):
|
||||||
|
|
||||||
return imOut
|
return imOut
|
||||||
|
|
||||||
|
|
||||||
def createProfile(colorSpace, colorTemp=-1):
|
def createProfile(colorSpace, colorTemp=-1):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Creates a profile.
|
(pyCMS) Creates a profile.
|
||||||
|
@ -562,30 +602,36 @@ def createProfile(colorSpace, colorTemp=-1):
|
||||||
ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
|
ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
|
||||||
to images.
|
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.
|
Currently only "LAB", "XYZ", and "sRGB" are supported.
|
||||||
:param colorTemp: Positive integer for the white point for the profile, in
|
: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
|
degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
|
||||||
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles,
|
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
|
||||||
and is ignored for XYZ and sRGB.
|
profiles, and is ignored for XYZ and sRGB.
|
||||||
:returns: A CmsProfile class object
|
:returns: A CmsProfile class object
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if colorSpace not in ["LAB", "XYZ", "sRGB"]:
|
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":
|
if colorSpace == "LAB":
|
||||||
try:
|
try:
|
||||||
colorTemp = float(colorTemp)
|
colorTemp = float(colorTemp)
|
||||||
except:
|
except:
|
||||||
raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp)
|
raise PyCMSError(
|
||||||
|
"Color temperature must be numeric, \"%s\" not valid"
|
||||||
|
% colorTemp)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return core.createProfile(colorSpace, colorTemp)
|
return core.createProfile(colorSpace, colorTemp)
|
||||||
except (TypeError, ValueError) as v:
|
except (TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def getProfileName(profile):
|
def getProfileName(profile):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -600,10 +646,10 @@ def getProfileName(profile):
|
||||||
profile was originally created. Sometimes this tag also contains
|
profile was originally created. Sometimes this tag also contains
|
||||||
additional information supplied by the creator.
|
additional information supplied by the creator.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: A string containing the internal name of the profile as stored in an
|
:returns: A string containing the internal name of the profile as stored
|
||||||
ICC tag.
|
in an ICC tag.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -612,14 +658,14 @@ def getProfileName(profile):
|
||||||
if not isinstance(profile, ImageCmsProfile):
|
if not isinstance(profile, ImageCmsProfile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
# do it in python, not c.
|
# do it in python, not c.
|
||||||
# // name was "%s - %s" (model, manufacturer) || Description ,
|
# // name was "%s - %s" (model, manufacturer) || Description ,
|
||||||
# // but if the Model and Manufacturer were the same or the model
|
# // but if the Model and Manufacturer were the same or the model
|
||||||
# // was long, Just the model, in 1.x
|
# // was long, Just the model, in 1.x
|
||||||
model = profile.profile.product_model
|
model = profile.profile.product_model
|
||||||
manufacturer = profile.profile.product_manufacturer
|
manufacturer = profile.profile.product_manufacturer
|
||||||
|
|
||||||
if not (model or 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:
|
if not manufacturer or len(model) > 30:
|
||||||
return model + "\n"
|
return model + "\n"
|
||||||
return "%s - %s\n" % (model, manufacturer)
|
return "%s - %s\n" % (model, manufacturer)
|
||||||
|
@ -627,6 +673,7 @@ def getProfileName(profile):
|
||||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def getProfileInfo(profile):
|
def getProfileInfo(profile):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Gets the internal product information for the given profile.
|
(pyCMS) Gets the internal product information for the given profile.
|
||||||
|
@ -641,18 +688,19 @@ def getProfileInfo(profile):
|
||||||
info tag. This often contains details about the profile, and how it
|
info tag. This often contains details about the profile, and how it
|
||||||
was created, as supplied by the creator.
|
was created, as supplied by the creator.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: A string containing the internal profile information stored in an ICC
|
:returns: A string containing the internal profile information stored in
|
||||||
tag.
|
an ICC tag.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not isinstance(profile, ImageCmsProfile):
|
if not isinstance(profile, ImageCmsProfile):
|
||||||
profile = ImageCmsProfile(profile)
|
profile = ImageCmsProfile(profile)
|
||||||
# add an extra newline to preserve pyCMS compatibility
|
# 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
|
# // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
|
||||||
description = profile.profile.product_description
|
description = profile.profile.product_description
|
||||||
cpright = profile.profile.product_copyright
|
cpright = profile.profile.product_copyright
|
||||||
|
@ -660,7 +708,7 @@ def getProfileInfo(profile):
|
||||||
for elt in (description, cpright):
|
for elt in (description, cpright):
|
||||||
if elt:
|
if elt:
|
||||||
arr.append(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:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
@ -677,12 +725,12 @@ def getProfileCopyright(profile):
|
||||||
is raised
|
is raised
|
||||||
|
|
||||||
Use this function to obtain the information stored in the profile's
|
Use this function to obtain the information stored in the profile's
|
||||||
copyright tag.
|
copyright tag.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: A string containing the internal profile information stored in an ICC
|
:returns: A string containing the internal profile information stored in
|
||||||
tag.
|
an ICC tag.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -693,6 +741,7 @@ def getProfileCopyright(profile):
|
||||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def getProfileManufacturer(profile):
|
def getProfileManufacturer(profile):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Gets the manufacturer for the given 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,
|
If profile isn't a valid CmsProfile object or filename to a profile,
|
||||||
a PyCMSError is raised.
|
a PyCMSError is raised.
|
||||||
|
|
||||||
If an error occurs while trying to obtain the manufacturer tag, a PyCMSError
|
If an error occurs while trying to obtain the manufacturer tag, a
|
||||||
is raised
|
PyCMSError is raised
|
||||||
|
|
||||||
Use this function to obtain the information stored in the profile's
|
Use this function to obtain the information stored in the profile's
|
||||||
manufacturer tag.
|
manufacturer tag.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: A string containing the internal profile information stored in an ICC
|
:returns: A string containing the internal profile information stored in
|
||||||
tag.
|
an ICC tag.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -720,23 +769,24 @@ def getProfileManufacturer(profile):
|
||||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def getProfileModel(profile):
|
def getProfileModel(profile):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Gets the model for the given profile.
|
(pyCMS) Gets the model for the given profile.
|
||||||
|
|
||||||
If profile isn't a valid CmsProfile object or filename to a profile,
|
If profile isn't a valid CmsProfile object or filename to a profile,
|
||||||
a PyCMSError is raised.
|
a PyCMSError is raised.
|
||||||
|
|
||||||
If an error occurs while trying to obtain the model tag, a PyCMSError
|
If an error occurs while trying to obtain the model tag, a PyCMSError
|
||||||
is raised
|
is raised
|
||||||
|
|
||||||
Use this function to obtain the information stored in the profile's
|
Use this function to obtain the information stored in the profile's
|
||||||
model tag.
|
model tag.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: A string containing the internal profile information stored in an ICC
|
:returns: A string containing the internal profile information stored in
|
||||||
tag.
|
an ICC tag.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -748,6 +798,7 @@ def getProfileModel(profile):
|
||||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def getProfileDescription(profile):
|
def getProfileDescription(profile):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Gets the description for the given profile.
|
(pyCMS) Gets the description for the given profile.
|
||||||
|
@ -759,12 +810,12 @@ def getProfileDescription(profile):
|
||||||
is raised
|
is raised
|
||||||
|
|
||||||
Use this function to obtain the information stored in the profile's
|
Use this function to obtain the information stored in the profile's
|
||||||
description tag.
|
description tag.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: A string containing the internal profile information stored in an ICC
|
:returns: A string containing the internal profile information stored in an
|
||||||
tag.
|
ICC tag.
|
||||||
:exception PyCMSError:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -793,16 +844,18 @@ def getDefaultIntent(profile):
|
||||||
If you wish to use a different intent than returned, use
|
If you wish to use a different intent than returned, use
|
||||||
ImageCms.isIntentSupported() to verify it will work first.
|
ImageCms.isIntentSupported() to verify it will work first.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:returns: Integer 0-3 specifying the default rendering intent for this profile.
|
:returns: Integer 0-3 specifying the default rendering intent for this
|
||||||
|
profile.
|
||||||
|
|
||||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
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:
|
:exception PyCMSError:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -813,6 +866,7 @@ def getDefaultIntent(profile):
|
||||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def isIntentSupported(profile, intent, direction):
|
def isIntentSupported(profile, intent, direction):
|
||||||
"""
|
"""
|
||||||
(pyCMS) Checks if a given intent is supported.
|
(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
|
potential PyCMSError that will occur if they don't support the modes
|
||||||
you select.
|
you select.
|
||||||
|
|
||||||
:param profile: EITHER a valid CmsProfile object, OR a string of the filename
|
:param profile: EITHER a valid CmsProfile object, OR a string of the
|
||||||
of an ICC profile.
|
filename of an ICC profile.
|
||||||
:param intent: Integer (0-3) specifying the rendering intent you wish to use
|
:param intent: Integer (0-3) specifying the rendering intent you wish to
|
||||||
with this profile
|
use with this profile
|
||||||
|
|
||||||
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
|
||||||
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
|
||||||
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
|
||||||
INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
|
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,
|
:param direction: Integer specifing if the profile is to be used for input,
|
||||||
output, or proof
|
output, or proof
|
||||||
|
|
||||||
|
@ -862,15 +917,17 @@ def isIntentSupported(profile, intent, direction):
|
||||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||||
raise PyCMSError(v)
|
raise PyCMSError(v)
|
||||||
|
|
||||||
|
|
||||||
def versions():
|
def versions():
|
||||||
"""
|
"""
|
||||||
(pyCMS) Fetches versions.
|
(pyCMS) Fetches versions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
return (
|
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
|
from PIL import ImageCms
|
||||||
print(__doc__)
|
print(__doc__)
|
||||||
|
|
||||||
for f in dir(pyCMS):
|
for f in dir(ImageCms):
|
||||||
print("="*80)
|
doc = None
|
||||||
print("%s" %f)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exec ("doc = ImageCms.%s.__doc__" %(f))
|
exec("doc = %s.__doc__" % (f))
|
||||||
if "pyCMS" in doc:
|
if "pyCMS" in doc:
|
||||||
# so we don't get the __doc__ string for imported modules
|
# so we don't get the __doc__ string for imported modules
|
||||||
|
print("=" * 80)
|
||||||
|
print("%s" % f)
|
||||||
print(doc)
|
print(doc)
|
||||||
except AttributeError:
|
except (AttributeError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# End of file
|
||||||
|
|
|
@ -15,20 +15,21 @@
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from PIL import Image, ImageFile
|
||||||
import struct
|
import struct
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
|
||||||
def _parse_codestream(fp):
|
def _parse_codestream(fp):
|
||||||
"""Parse the JPEG 2000 codestream to extract the size and component
|
"""Parse the JPEG 2000 codestream to extract the size and component
|
||||||
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
|
count from the SIZ marker segment, returning a PIL (size, mode) tuple."""
|
||||||
|
|
||||||
hdr = fp.read(2)
|
hdr = fp.read(2)
|
||||||
lsiz = struct.unpack('>H', hdr)[0]
|
lsiz = struct.unpack('>H', hdr)[0]
|
||||||
siz = hdr + fp.read(lsiz - 2)
|
siz = hdr + fp.read(lsiz - 2)
|
||||||
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
|
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
|
||||||
xtosiz, ytosiz, csiz \
|
xtosiz, ytosiz, csiz \
|
||||||
= struct.unpack('>HHIIIIIIIIH', siz[:38])
|
= struct.unpack('>HHIIIIIIIIH', siz[:38])
|
||||||
ssiz = [None]*csiz
|
ssiz = [None]*csiz
|
||||||
xrsiz = [None]*csiz
|
xrsiz = [None]*csiz
|
||||||
|
@ -48,13 +49,14 @@ def _parse_codestream(fp):
|
||||||
mode == 'RGBA'
|
mode == 'RGBA'
|
||||||
else:
|
else:
|
||||||
mode = None
|
mode = None
|
||||||
|
|
||||||
return (size, mode)
|
return (size, mode)
|
||||||
|
|
||||||
|
|
||||||
def _parse_jp2_header(fp):
|
def _parse_jp2_header(fp):
|
||||||
"""Parse the JP2 header box to extract size, component count and
|
"""Parse the JP2 header box to extract size, component count and
|
||||||
color space information, returning a PIL (size, mode) tuple."""
|
color space information, returning a PIL (size, mode) tuple."""
|
||||||
|
|
||||||
# Find the JP2 header box
|
# Find the JP2 header box
|
||||||
header = None
|
header = None
|
||||||
while True:
|
while True:
|
||||||
|
@ -76,7 +78,7 @@ def _parse_jp2_header(fp):
|
||||||
|
|
||||||
size = None
|
size = None
|
||||||
mode = None
|
mode = None
|
||||||
|
|
||||||
hio = io.BytesIO(header)
|
hio = io.BytesIO(header)
|
||||||
while True:
|
while True:
|
||||||
lbox, tbox = struct.unpack('>I4s', hio.read(8))
|
lbox, tbox = struct.unpack('>I4s', hio.read(8))
|
||||||
|
@ -90,7 +92,7 @@ def _parse_jp2_header(fp):
|
||||||
|
|
||||||
if tbox == b'ihdr':
|
if tbox == b'ihdr':
|
||||||
height, width, nc, bpc, c, unkc, ipr \
|
height, width, nc, bpc, c, unkc, ipr \
|
||||||
= struct.unpack('>IIHBBBB', content)
|
= struct.unpack('>IIHBBBB', content)
|
||||||
size = (width, height)
|
size = (width, height)
|
||||||
if unkc:
|
if unkc:
|
||||||
if nc == 1:
|
if nc == 1:
|
||||||
|
@ -112,13 +114,13 @@ def _parse_jp2_header(fp):
|
||||||
elif nc == 4:
|
elif nc == 4:
|
||||||
mode = 'RGBA'
|
mode = 'RGBA'
|
||||||
break
|
break
|
||||||
elif cs == 17: # grayscale
|
elif cs == 17: # grayscale
|
||||||
if nc == 1:
|
if nc == 1:
|
||||||
mode = 'L'
|
mode = 'L'
|
||||||
elif nc == 2:
|
elif nc == 2:
|
||||||
mode = 'LA'
|
mode = 'LA'
|
||||||
break
|
break
|
||||||
elif cs == 18: # sYCC
|
elif cs == 18: # sYCC
|
||||||
if nc == 3:
|
if nc == 3:
|
||||||
mode = 'RGB'
|
mode = 'RGB'
|
||||||
elif nc == 4:
|
elif nc == 4:
|
||||||
|
@ -127,6 +129,7 @@ def _parse_jp2_header(fp):
|
||||||
|
|
||||||
return (size, mode)
|
return (size, mode)
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Image plugin for JPEG2000 images.
|
# Image plugin for JPEG2000 images.
|
||||||
|
|
||||||
|
@ -141,16 +144,16 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
self.size, self.mode = _parse_codestream(self.fp)
|
self.size, self.mode = _parse_codestream(self.fp)
|
||||||
else:
|
else:
|
||||||
sig = sig + self.fp.read(8)
|
sig = sig + self.fp.read(8)
|
||||||
|
|
||||||
if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
|
if sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
|
||||||
self.codec = "jp2"
|
self.codec = "jp2"
|
||||||
self.size, self.mode = _parse_jp2_header(self.fp)
|
self.size, self.mode = _parse_jp2_header(self.fp)
|
||||||
else:
|
else:
|
||||||
raise SyntaxError('not a JPEG 2000 file')
|
raise SyntaxError('not a JPEG 2000 file')
|
||||||
|
|
||||||
if self.size is None or self.mode is None:
|
if self.size is None or self.mode is None:
|
||||||
raise SyntaxError('unable to determine size/mode')
|
raise SyntaxError('unable to determine size/mode')
|
||||||
|
|
||||||
self.reduce = 0
|
self.reduce = 0
|
||||||
self.layers = 0
|
self.layers = 0
|
||||||
|
|
||||||
|
@ -187,13 +190,15 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
t = self.tile[0]
|
t = self.tile[0]
|
||||||
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
|
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
|
||||||
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
|
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
|
||||||
|
|
||||||
ImageFile.ImageFile.load(self)
|
ImageFile.ImageFile.load(self)
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return (prefix[:4] == b'\xff\x4f\xff\x51'
|
return (prefix[:4] == b'\xff\x4f\xff\x51'
|
||||||
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
|
or prefix[:12] == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a')
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Save support
|
# Save support
|
||||||
|
|
||||||
|
@ -224,7 +229,7 @@ def _save(im, fp, filename):
|
||||||
fd = fp.fileno()
|
fd = fp.fileno()
|
||||||
except:
|
except:
|
||||||
fd = -1
|
fd = -1
|
||||||
|
|
||||||
im.encoderconfig = (
|
im.encoderconfig = (
|
||||||
offset,
|
offset,
|
||||||
tile_offset,
|
tile_offset,
|
||||||
|
@ -238,10 +243,10 @@ def _save(im, fp, filename):
|
||||||
progression,
|
progression,
|
||||||
cinema_mode,
|
cinema_mode,
|
||||||
fd
|
fd
|
||||||
)
|
)
|
||||||
|
|
||||||
ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])
|
ImageFile._save(im, fp, [('jpeg2k', (0, 0)+im.size, 0, kind)])
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Registry stuff
|
# 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)
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -348,4 +348,4 @@ By obtaining, using, and/or copying this software and/or its associated document
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
|
Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
|
||||||
|
|
||||||
SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
|
@ -36,17 +36,23 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from PIL import Image, ImageFile
|
||||||
import os, struct, sys
|
import os
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def isInt(f):
|
def isInt(f):
|
||||||
try:
|
try:
|
||||||
i = int(f)
|
i = int(f)
|
||||||
if f-i == 0: return 1
|
if f-i == 0:
|
||||||
else: return 0
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
except:
|
except:
|
||||||
return 0
|
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
|
# There is no magic number to identify Spider files, so just check a
|
||||||
# series of header locations to see if they have reasonable values.
|
# 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):
|
def isSpiderHeader(t):
|
||||||
h = (99,) + t # add 1 value so can use spider header index start=1
|
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
|
# header values 1,2,5,12,13,22,23 should be integers
|
||||||
for i in [1,2,5,12,13,22,23]:
|
for i in [1, 2, 5, 12, 13, 22, 23]:
|
||||||
if not isInt(h[i]): return 0
|
if not isInt(h[i]):
|
||||||
|
return 0
|
||||||
# check iform
|
# check iform
|
||||||
iform = int(h[5])
|
iform = int(h[5])
|
||||||
if not iform in iforms: return 0
|
if iform not in iforms:
|
||||||
|
return 0
|
||||||
# check other header values
|
# check other header values
|
||||||
labrec = int(h[13]) # no. records in file header
|
labrec = int(h[13]) # no. records in file header
|
||||||
labbyt = int(h[22]) # total no. of bytes in header
|
labbyt = int(h[22]) # total no. of bytes in header
|
||||||
lenbyt = int(h[23]) # record length in bytes
|
lenbyt = int(h[23]) # record length in bytes
|
||||||
#print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
|
# print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
|
||||||
if labbyt != (labrec * lenbyt): return 0
|
if labbyt != (labrec * lenbyt):
|
||||||
|
return 0
|
||||||
# looks like a valid header
|
# looks like a valid header
|
||||||
return labbyt
|
return labbyt
|
||||||
|
|
||||||
|
|
||||||
def isSpiderImage(filename):
|
def isSpiderImage(filename):
|
||||||
fp = open(filename,'rb')
|
fp = open(filename, 'rb')
|
||||||
f = fp.read(92) # read 23 * 4 bytes
|
f = fp.read(92) # read 23 * 4 bytes
|
||||||
fp.close()
|
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)
|
hdrlen = isSpiderHeader(t)
|
||||||
if hdrlen == 0:
|
if hdrlen == 0:
|
||||||
bigendian = 0
|
t = struct.unpack('<23f', f) # little-endian
|
||||||
t = struct.unpack('<23f',f) # little-endian
|
|
||||||
hdrlen = isSpiderHeader(t)
|
hdrlen = isSpiderHeader(t)
|
||||||
return hdrlen
|
return hdrlen
|
||||||
|
|
||||||
|
@ -96,11 +104,11 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.bigendian = 1
|
self.bigendian = 1
|
||||||
t = struct.unpack('>27f',f) # try big-endian first
|
t = struct.unpack('>27f', f) # try big-endian first
|
||||||
hdrlen = isSpiderHeader(t)
|
hdrlen = isSpiderHeader(t)
|
||||||
if hdrlen == 0:
|
if hdrlen == 0:
|
||||||
self.bigendian = 0
|
self.bigendian = 0
|
||||||
t = struct.unpack('<27f',f) # little-endian
|
t = struct.unpack('<27f', f) # little-endian
|
||||||
hdrlen = isSpiderHeader(t)
|
hdrlen = isSpiderHeader(t)
|
||||||
if hdrlen == 0:
|
if hdrlen == 0:
|
||||||
raise SyntaxError("not a valid Spider file")
|
raise SyntaxError("not a valid Spider file")
|
||||||
|
@ -112,7 +120,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
if iform != 1:
|
if iform != 1:
|
||||||
raise SyntaxError("not a Spider 2D image")
|
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.istack = int(h[24])
|
||||||
self.imgnumber = int(h[27])
|
self.imgnumber = int(h[27])
|
||||||
|
|
||||||
|
@ -141,9 +149,10 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
self.rawmode = "F;32F"
|
self.rawmode = "F;32F"
|
||||||
self.mode = "F"
|
self.mode = "F"
|
||||||
|
|
||||||
self.tile = [("raw", (0, 0) + self.size, offset,
|
self.tile = [
|
||||||
(self.rawmode, 0, 1))]
|
("raw", (0, 0) + self.size, offset,
|
||||||
self.__fp = self.fp # FIXME: hack
|
(self.rawmode, 0, 1))]
|
||||||
|
self.__fp = self.fp # FIXME: hack
|
||||||
|
|
||||||
# 1st image index is zero (although SPIDER imgnumber starts at 1)
|
# 1st image index is zero (although SPIDER imgnumber starts at 1)
|
||||||
def tell(self):
|
def tell(self):
|
||||||
|
@ -176,6 +185,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
from PIL import ImageTk
|
from PIL import ImageTk
|
||||||
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
|
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Image series
|
# Image series
|
||||||
|
|
||||||
|
@ -200,17 +210,19 @@ def loadImageSeries(filelist=None):
|
||||||
imglist.append(im)
|
imglist.append(im)
|
||||||
return imglist
|
return imglist
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# For saving images in Spider format
|
# For saving images in Spider format
|
||||||
|
|
||||||
def makeSpiderHeader(im):
|
def makeSpiderHeader(im):
|
||||||
nsam,nrow = im.size
|
nsam, nrow = im.size
|
||||||
lenbyt = nsam * 4 # There are labrec records in the header
|
lenbyt = nsam * 4 # There are labrec records in the header
|
||||||
labrec = 1024 / lenbyt
|
labrec = 1024 / lenbyt
|
||||||
if 1024%lenbyt != 0: labrec += 1
|
if 1024 % lenbyt != 0:
|
||||||
|
labrec += 1
|
||||||
labbyt = labrec * lenbyt
|
labbyt = labrec * lenbyt
|
||||||
hdr = []
|
hdr = []
|
||||||
nvalues = labbyt / 4
|
nvalues = int(labbyt / 4)
|
||||||
for i in range(nvalues):
|
for i in range(nvalues):
|
||||||
hdr.append(0.0)
|
hdr.append(0.0)
|
||||||
|
|
||||||
|
@ -218,13 +230,13 @@ def makeSpiderHeader(im):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# NB these are Fortran indices
|
# NB these are Fortran indices
|
||||||
hdr[1] = 1.0 # nslice (=1 for an image)
|
hdr[1] = 1.0 # nslice (=1 for an image)
|
||||||
hdr[2] = float(nrow) # number of rows per slice
|
hdr[2] = float(nrow) # number of rows per slice
|
||||||
hdr[5] = 1.0 # iform for 2D image
|
hdr[5] = 1.0 # iform for 2D image
|
||||||
hdr[12] = float(nsam) # number of pixels per line
|
hdr[12] = float(nsam) # number of pixels per line
|
||||||
hdr[13] = float(labrec) # number of records in file header
|
hdr[13] = float(labrec) # number of records in file header
|
||||||
hdr[22] = float(labbyt) # total number of bytes in header
|
hdr[22] = float(labbyt) # total number of bytes in header
|
||||||
hdr[23] = float(lenbyt) # record length in bytes
|
hdr[23] = float(lenbyt) # record length in bytes
|
||||||
|
|
||||||
# adjust for Fortran indexing
|
# adjust for Fortran indexing
|
||||||
hdr = hdr[1:]
|
hdr = hdr[1:]
|
||||||
|
@ -232,9 +244,10 @@ def makeSpiderHeader(im):
|
||||||
# pack binary data into a string
|
# pack binary data into a string
|
||||||
hdrstr = []
|
hdrstr = []
|
||||||
for v in hdr:
|
for v in hdr:
|
||||||
hdrstr.append(struct.pack('f',v))
|
hdrstr.append(struct.pack('f', v))
|
||||||
return hdrstr
|
return hdrstr
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
def _save(im, fp, filename):
|
||||||
if im.mode[0] != "F":
|
if im.mode[0] != "F":
|
||||||
im = im.convert('F')
|
im = im.convert('F')
|
||||||
|
@ -250,11 +263,12 @@ def _save(im, fp, filename):
|
||||||
raise IOError("Unable to open %s for writing" % filename)
|
raise IOError("Unable to open %s for writing" % filename)
|
||||||
fp.writelines(hdr)
|
fp.writelines(hdr)
|
||||||
|
|
||||||
rawmode = "F;32NF" #32-bit native floating point
|
rawmode = "F;32NF" # 32-bit native floating point
|
||||||
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))])
|
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
|
||||||
|
|
||||||
fp.close()
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
def _save_spider(im, fp, filename):
|
def _save_spider(im, fp, filename):
|
||||||
# get the filename extension and register it with Image
|
# get the filename extension and register it with Image
|
||||||
fn, ext = os.path.splitext(filename)
|
fn, ext = os.path.splitext(filename)
|
||||||
|
@ -292,5 +306,7 @@ if __name__ == "__main__":
|
||||||
if outfile != "":
|
if outfile != "":
|
||||||
# perform some image operation
|
# perform some image operation
|
||||||
im = im.transpose(Image.FLIP_LEFT_RIGHT)
|
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")
|
im.save(outfile, "SPIDER")
|
||||||
|
|
|
@ -54,7 +54,7 @@ import collections
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Set these to true to force use of libtiff for reading or writing.
|
# Set these to true to force use of libtiff for reading or writing.
|
||||||
READ_LIBTIFF = False
|
READ_LIBTIFF = False
|
||||||
WRITE_LIBTIFF= False
|
WRITE_LIBTIFF= False
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
Value: integer corresponding to the data type from
|
Value: integer corresponding to the data type from
|
||||||
`TiffTags.TYPES`
|
`TiffTags.TYPES`
|
||||||
|
|
||||||
'internal'
|
'internal'
|
||||||
* self.tags = {} Key: numerical tiff tag number
|
* self.tags = {} Key: numerical tiff tag number
|
||||||
Value: Decoded data, Generally a tuple.
|
Value: Decoded data, Generally a tuple.
|
||||||
* If set from __setval__ -- always a tuple
|
* If set from __setval__ -- always a tuple
|
||||||
|
@ -489,10 +489,10 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
|
|
||||||
if tag in self.tagtype:
|
if tag in self.tagtype:
|
||||||
typ = self.tagtype[tag]
|
typ = self.tagtype[tag]
|
||||||
|
|
||||||
if Image.DEBUG:
|
if Image.DEBUG:
|
||||||
print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
|
print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
|
||||||
|
|
||||||
if typ == 1:
|
if typ == 1:
|
||||||
# byte data
|
# byte data
|
||||||
if isinstance(value, tuple):
|
if isinstance(value, tuple):
|
||||||
|
@ -512,7 +512,7 @@ class ImageFileDirectory(collections.MutableMapping):
|
||||||
# and doesn't match the tiff spec: 8-bit byte that
|
# and doesn't match the tiff spec: 8-bit byte that
|
||||||
# contains a 7-bit ASCII code; the last byte must be
|
# contains a 7-bit ASCII code; the last byte must be
|
||||||
# NUL (binary zero). Also, I don't think this was well
|
# NUL (binary zero). Also, I don't think this was well
|
||||||
# excersized before.
|
# excersized before.
|
||||||
data = value = b"" + value.encode('ascii', 'replace') + b"\0"
|
data = value = b"" + value.encode('ascii', 'replace') + b"\0"
|
||||||
else:
|
else:
|
||||||
# integer data
|
# integer data
|
||||||
|
@ -859,7 +859,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
# libtiff handles the fillmode for us, so 1;IR should
|
# libtiff handles the fillmode for us, so 1;IR should
|
||||||
# actually be 1;I. Including the R double reverses the
|
# actually be 1;I. Including the R double reverses the
|
||||||
# bits, so stripes of the image are reversed. See
|
# 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:
|
if fillorder == 2:
|
||||||
key = (
|
key = (
|
||||||
self.tag.prefix, photo, format, 1,
|
self.tag.prefix, photo, format, 1,
|
||||||
|
@ -984,11 +984,11 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
|
compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
|
||||||
|
|
||||||
libtiff = WRITE_LIBTIFF or compression != 'raw'
|
libtiff = WRITE_LIBTIFF or compression != 'raw'
|
||||||
|
|
||||||
# required for color libtiff images
|
# required for color libtiff images
|
||||||
ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)
|
ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)
|
||||||
|
|
||||||
# -- multi-page -- skip TIFF header on subsequent pages
|
# -- multi-page -- skip TIFF header on subsequent pages
|
||||||
if not libtiff and fp.tell() == 0:
|
if not libtiff and fp.tell() == 0:
|
||||||
# tiff header (write via IFD to get everything right)
|
# tiff header (write via IFD to get everything right)
|
||||||
|
@ -1025,7 +1025,7 @@ def _save(im, fp, filename):
|
||||||
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
|
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
|
||||||
if "icc_profile" in im.info:
|
if "icc_profile" in im.info:
|
||||||
ifd[ICCPROFILE] = im.info["icc_profile"]
|
ifd[ICCPROFILE] = im.info["icc_profile"]
|
||||||
|
|
||||||
if "description" in im.encoderinfo:
|
if "description" in im.encoderinfo:
|
||||||
ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
|
ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
|
||||||
if "resolution" in im.encoderinfo:
|
if "resolution" in im.encoderinfo:
|
||||||
|
@ -1091,7 +1091,7 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes.
|
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes.
|
||||||
atts={}
|
atts={}
|
||||||
# bits per sample is a single short in the tiff directory, not a list.
|
# bits per sample is a single short in the tiff directory, not a list.
|
||||||
atts[BITSPERSAMPLE] = bits[0]
|
atts[BITSPERSAMPLE] = bits[0]
|
||||||
# Merge the ones that we have with (optional) more bits from
|
# Merge the ones that we have with (optional) more bits from
|
||||||
# the original file, e.g x,y resolution so that we can
|
# the original file, e.g x,y resolution so that we can
|
||||||
|
|
|
@ -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.
|
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
|
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
|
||||||
:target: https://travis-ci.org/python-imaging/Pillow
|
:target: https://travis-ci.org/python-pillow/Pillow
|
||||||
:alt: Travis CI build status
|
:alt: Travis CI build status
|
||||||
|
|
||||||
.. image:: https://pypip.in/v/Pillow/badge.png
|
.. 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/
|
:target: https://pypi.python.org/pypi/Pillow/
|
||||||
:alt: Number of PyPI downloads
|
:alt: Number of PyPI downloads
|
||||||
|
|
||||||
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master
|
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
|
||||||
:target: https://coveralls.io/r/python-imaging/Pillow?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.
|
The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more.
|
||||||
|
|
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/lena.spider
Normal file
|
@ -37,7 +37,7 @@ def test_roundtrip():
|
||||||
assert_image_similar(reread.convert('RGB'), im, 50)
|
assert_image_similar(reread.convert('RGB'), im, 50)
|
||||||
|
|
||||||
def test_roundtrip2():
|
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')
|
out = tempfile('temp.gif')
|
||||||
im = Image.open('Images/lena.gif')
|
im = Image.open('Images/lena.gif')
|
||||||
im2 = im.copy()
|
im2 = im.copy()
|
||||||
|
@ -48,11 +48,11 @@ def test_roundtrip2():
|
||||||
|
|
||||||
|
|
||||||
def test_palette_handling():
|
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 = Image.open('Images/lena.gif')
|
||||||
im = im.convert('RGB')
|
im = im.convert('RGB')
|
||||||
|
|
||||||
im = im.resize((100,100), Image.ANTIALIAS)
|
im = im.resize((100,100), Image.ANTIALIAS)
|
||||||
im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256)
|
im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256)
|
||||||
|
|
||||||
|
@ -60,11 +60,11 @@ def test_palette_handling():
|
||||||
im2.save(f, optimize=True)
|
im2.save(f, optimize=True)
|
||||||
|
|
||||||
reloaded = Image.open(f)
|
reloaded = Image.open(f)
|
||||||
|
|
||||||
assert_image_similar(im, reloaded.convert('RGB'), 10)
|
assert_image_similar(im, reloaded.convert('RGB'), 10)
|
||||||
|
|
||||||
def test_palette_434():
|
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):
|
def roundtrip(im, *args, **kwargs):
|
||||||
out = tempfile('temp.gif')
|
out = tempfile('temp.gif')
|
||||||
|
@ -78,10 +78,10 @@ def test_palette_434():
|
||||||
|
|
||||||
assert_image_equal(*roundtrip(im))
|
assert_image_equal(*roundtrip(im))
|
||||||
assert_image_equal(*roundtrip(im, optimize=True))
|
assert_image_equal(*roundtrip(im, optimize=True))
|
||||||
|
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
# check automatic P conversion
|
# check automatic P conversion
|
||||||
reloaded = roundtrip(im)[1].convert('RGB')
|
reloaded = roundtrip(im)[1].convert('RGB')
|
||||||
assert_image_equal(im, reloaded)
|
assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ def test_optimize():
|
||||||
|
|
||||||
|
|
||||||
def test_optimize_large_buffer():
|
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')
|
f = tempfile('temp.jpg')
|
||||||
# this requires ~ 1.5x Image.MAXBLOCK
|
# this requires ~ 1.5x Image.MAXBLOCK
|
||||||
im = Image.new("RGB", (4096, 4096), 0xff3333)
|
im = Image.new("RGB", (4096, 4096), 0xff3333)
|
||||||
|
@ -159,7 +159,7 @@ def test_progressive_large_buffer_highest_quality():
|
||||||
|
|
||||||
|
|
||||||
def test_large_exif():
|
def test_large_exif():
|
||||||
# https://github.com/python-imaging/Pillow/issues/148
|
# https://github.com/python-pillow/Pillow/issues/148
|
||||||
f = tempfile('temp.jpg')
|
f = tempfile('temp.jpg')
|
||||||
im = lena()
|
im = lena()
|
||||||
im.save(f, 'JPEG', quality=90, exif=b"1"*65532)
|
im.save(f, 'JPEG', quality=90, exif=b"1"*65532)
|
||||||
|
@ -231,7 +231,7 @@ def test_quality_keep():
|
||||||
|
|
||||||
|
|
||||||
def test_junk_jpeg_header():
|
def test_junk_jpeg_header():
|
||||||
# https://github.com/python-imaging/Pillow/issues/630
|
# https://github.com/python-pillow/Pillow/issues/630
|
||||||
filename = "Tests/images/junk_jpeg_header.jpg"
|
filename = "Tests/images/junk_jpeg_header.jpg"
|
||||||
assert_no_exception(lambda: Image.open(filename))
|
assert_no_exception(lambda: Image.open(filename))
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ def test_g4_eq_png():
|
||||||
|
|
||||||
assert_image_equal(g4, 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():
|
def test_g4_fillorder_eq_png():
|
||||||
""" Checking that we're actually getting the data that we expect"""
|
""" Checking that we're actually getting the data that we expect"""
|
||||||
png = Image.open('Tests/images/g4-fillorder-test.png')
|
png = Image.open('Tests/images/g4-fillorder-test.png')
|
||||||
|
@ -96,7 +96,7 @@ def test_g4_write():
|
||||||
assert_equal(reread.info['compression'], 'group4')
|
assert_equal(reread.info['compression'], 'group4')
|
||||||
|
|
||||||
assert_equal(reread.info['compression'], orig.info['compression'])
|
assert_equal(reread.info['compression'], orig.info['compression'])
|
||||||
|
|
||||||
assert_false(orig.tobytes() == reread.tobytes())
|
assert_false(orig.tobytes() == reread.tobytes())
|
||||||
|
|
||||||
def test_adobe_deflate_tiff():
|
def test_adobe_deflate_tiff():
|
||||||
|
@ -120,7 +120,7 @@ def test_write_metadata():
|
||||||
original = img.tag.named()
|
original = img.tag.named()
|
||||||
reloaded = loaded.tag.named()
|
reloaded = loaded.tag.named()
|
||||||
|
|
||||||
# PhotometricInterpretation is set from SAVE_INFO, not the original image.
|
# PhotometricInterpretation is set from SAVE_INFO, not the original image.
|
||||||
ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation']
|
ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation']
|
||||||
|
|
||||||
for tag, value in reloaded.items():
|
for tag, value in reloaded.items():
|
||||||
|
@ -133,7 +133,7 @@ def test_write_metadata():
|
||||||
assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
|
assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
|
||||||
|
|
||||||
for tag, value in original.items():
|
for tag, value in original.items():
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
if tag.endswith('Resolution'):
|
if tag.endswith('Resolution'):
|
||||||
val = reloaded[tag]
|
val = reloaded[tag]
|
||||||
assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1],
|
assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1],
|
||||||
|
@ -164,7 +164,7 @@ def test_little_endian():
|
||||||
else:
|
else:
|
||||||
assert_equal(b[0], b'\xe0')
|
assert_equal(b[0], b'\xe0')
|
||||||
assert_equal(b[1], b'\x01')
|
assert_equal(b[1], b'\x01')
|
||||||
|
|
||||||
|
|
||||||
out = tempfile("temp.tif")
|
out = tempfile("temp.tif")
|
||||||
#out = "temp.le.tif"
|
#out = "temp.le.tif"
|
||||||
|
@ -174,8 +174,8 @@ def test_little_endian():
|
||||||
assert_equal(reread.info['compression'], im.info['compression'])
|
assert_equal(reread.info['compression'], im.info['compression'])
|
||||||
assert_equal(reread.getpixel((0,0)), 480)
|
assert_equal(reread.getpixel((0,0)), 480)
|
||||||
# UNDONE - libtiff defaults to writing in native endian, so
|
# UNDONE - libtiff defaults to writing in native endian, so
|
||||||
# on big endian, we'll get back mode = 'I;16B' here.
|
# on big endian, we'll get back mode = 'I;16B' here.
|
||||||
|
|
||||||
def test_big_endian():
|
def test_big_endian():
|
||||||
im = Image.open('Tests/images/16bit.MM.deflate.tif')
|
im = Image.open('Tests/images/16bit.MM.deflate.tif')
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ def test_big_endian():
|
||||||
else:
|
else:
|
||||||
assert_equal(b[0], b'\x01')
|
assert_equal(b[0], b'\x01')
|
||||||
assert_equal(b[1], b'\xe0')
|
assert_equal(b[1], b'\xe0')
|
||||||
|
|
||||||
out = tempfile("temp.tif")
|
out = tempfile("temp.tif")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
reread = Image.open(out)
|
||||||
|
@ -203,12 +203,12 @@ def test_g4_string_info():
|
||||||
"""Tests String data in info directory"""
|
"""Tests String data in info directory"""
|
||||||
file = "Tests/images/lena_g4_500.tif"
|
file = "Tests/images/lena_g4_500.tif"
|
||||||
orig = Image.open(file)
|
orig = Image.open(file)
|
||||||
|
|
||||||
out = tempfile("temp.tif")
|
out = tempfile("temp.tif")
|
||||||
|
|
||||||
orig.tag[269] = 'temp.tif'
|
orig.tag[269] = 'temp.tif'
|
||||||
orig.save(out)
|
orig.save(out)
|
||||||
|
|
||||||
reread = Image.open(out)
|
reread = Image.open(out)
|
||||||
assert_equal('temp.tif', reread.tag[269])
|
assert_equal('temp.tif', reread.tag[269])
|
||||||
|
|
||||||
|
@ -223,8 +223,8 @@ def test_12bit_rawmode():
|
||||||
# convert 12bit.cropped.tif -depth 16 tmp.tif
|
# convert 12bit.cropped.tif -depth 16 tmp.tif
|
||||||
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
|
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
|
||||||
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
|
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
|
||||||
# so we need to unshift so that the integer values are the same.
|
# so we need to unshift so that the integer values are the same.
|
||||||
|
|
||||||
im2 = Image.open('Tests/images/12in16bit.tif')
|
im2 = Image.open('Tests/images/12in16bit.tif')
|
||||||
|
|
||||||
if Image.DEBUG:
|
if Image.DEBUG:
|
||||||
|
@ -235,11 +235,11 @@ def test_12bit_rawmode():
|
||||||
print (im2.getpixel((0,0)))
|
print (im2.getpixel((0,0)))
|
||||||
print (im2.getpixel((0,1)))
|
print (im2.getpixel((0,1)))
|
||||||
print (im2.getpixel((0,2)))
|
print (im2.getpixel((0,2)))
|
||||||
|
|
||||||
assert_image_equal(im, im2)
|
assert_image_equal(im, im2)
|
||||||
|
|
||||||
def test_blur():
|
def test_blur():
|
||||||
# test case from irc, how to do blur on b/w image and save to compressed tif.
|
# test case from irc, how to do blur on b/w image and save to compressed tif.
|
||||||
from PIL import ImageFilter
|
from PIL import ImageFilter
|
||||||
out = tempfile('temp.tif')
|
out = tempfile('temp.tif')
|
||||||
im = Image.open('Tests/images/pport_g4.tif')
|
im = Image.open('Tests/images/pport_g4.tif')
|
||||||
|
@ -266,7 +266,7 @@ def test_compressions():
|
||||||
im.save(out, compression='jpeg')
|
im.save(out, compression='jpeg')
|
||||||
im2 = Image.open(out)
|
im2 = Image.open(out)
|
||||||
assert_image_similar(im, im2, 30)
|
assert_image_similar(im, im2, 30)
|
||||||
|
|
||||||
|
|
||||||
def test_cmyk_save():
|
def test_cmyk_save():
|
||||||
im = lena('CMYK')
|
im = lena('CMYK')
|
||||||
|
@ -280,7 +280,7 @@ def xtest_bw_compression_wRGB():
|
||||||
""" This test passes, but when running all tests causes a failure due to
|
""" This test passes, but when running all tests causes a failure due to
|
||||||
output on stderr from the error thrown by libtiff. We need to capture that
|
output on stderr from the error thrown by libtiff. We need to capture that
|
||||||
but not now"""
|
but not now"""
|
||||||
|
|
||||||
im = lena('RGB')
|
im = lena('RGB')
|
||||||
out = tempfile('temp.tif')
|
out = tempfile('temp.tif')
|
||||||
|
|
||||||
|
@ -293,8 +293,8 @@ def test_fp_leak():
|
||||||
fn = im.fp.fileno()
|
fn = im.fp.fileno()
|
||||||
|
|
||||||
assert_no_exception(lambda: os.fstat(fn))
|
assert_no_exception(lambda: os.fstat(fn))
|
||||||
im.load() # this should close it.
|
im.load() # this should close it.
|
||||||
assert_exception(OSError, lambda: os.fstat(fn))
|
assert_exception(OSError, lambda: os.fstat(fn))
|
||||||
im = None # this should force even more closed.
|
im = None # this should force even more closed.
|
||||||
assert_exception(OSError, lambda: os.fstat(fn))
|
assert_exception(OSError, lambda: os.fstat(fn))
|
||||||
assert_exception(OSError, lambda: os.close(fn))
|
assert_exception(OSError, lambda: os.close(fn))
|
||||||
|
|
|
@ -101,7 +101,7 @@ def test_bad_text():
|
||||||
assert_equal(im.info, {'spam': 'egg\x00'})
|
assert_equal(im.info, {'spam': 'egg\x00'})
|
||||||
|
|
||||||
def test_bad_ztxt():
|
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)
|
im = load(HEAD + chunk(b'zTXt') + TAIL)
|
||||||
assert_equal(im.info, {})
|
assert_equal(im.info, {})
|
||||||
|
@ -182,7 +182,7 @@ def test_save_l_transparency():
|
||||||
file = tempfile("temp.png")
|
file = tempfile("temp.png")
|
||||||
assert_no_exception(lambda: im.save(file))
|
assert_no_exception(lambda: im.save(file))
|
||||||
|
|
||||||
# There are 559 transparent pixels.
|
# There are 559 transparent pixels.
|
||||||
im = im.convert('RGBA')
|
im = im.convert('RGBA')
|
||||||
assert_equal(im.split()[3].getcolors()[0][0], 559)
|
assert_equal(im.split()[3].getcolors()[0][0], 559)
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ def test_trns_p():
|
||||||
# Check writing a transparency of 0, issue #528
|
# Check writing a transparency of 0, issue #528
|
||||||
im = lena('P')
|
im = lena('P')
|
||||||
im.info['transparency']=0
|
im.info['transparency']=0
|
||||||
|
|
||||||
f = tempfile("temp.png")
|
f = tempfile("temp.png")
|
||||||
im.save(f)
|
im.save(f)
|
||||||
|
|
||||||
|
@ -263,8 +263,8 @@ def test_trns_p():
|
||||||
assert_true('transparency' in im2.info)
|
assert_true('transparency' in im2.info)
|
||||||
|
|
||||||
assert_image_equal(im2.convert('RGBA'), im.convert('RGBA'))
|
assert_image_equal(im2.convert('RGBA'), im.convert('RGBA'))
|
||||||
|
|
||||||
|
|
||||||
def test_save_icc_profile_none():
|
def test_save_icc_profile_none():
|
||||||
# check saving files with an ICC profile set to None (omit profile)
|
# check saving files with an ICC profile set to None (omit profile)
|
||||||
in_file = "Tests/images/icc_profile_none.png"
|
in_file = "Tests/images/icc_profile_none.png"
|
||||||
|
|
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,9 +6,9 @@ tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys()))
|
||||||
def test_rt_metadata():
|
def test_rt_metadata():
|
||||||
""" Test writing arbitray metadata into the tiff image directory
|
""" Test writing arbitray metadata into the tiff image directory
|
||||||
Use case is ImageJ private tags, one numeric, one arbitrary
|
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()
|
img = lena()
|
||||||
|
|
||||||
textdata = "This is some arbitrary metadata for a text field"
|
textdata = "This is some arbitrary metadata for a text field"
|
||||||
|
@ -20,15 +20,15 @@ def test_rt_metadata():
|
||||||
f = tempfile("temp.tif")
|
f = tempfile("temp.tif")
|
||||||
|
|
||||||
img.save(f, tiffinfo=info)
|
img.save(f, tiffinfo=info)
|
||||||
|
|
||||||
loaded = Image.open(f)
|
loaded = Image.open(f)
|
||||||
|
|
||||||
assert_equal(loaded.tag[50838], (len(textdata),))
|
assert_equal(loaded.tag[50838], (len(textdata),))
|
||||||
assert_equal(loaded.tag[50839], textdata)
|
assert_equal(loaded.tag[50839], textdata)
|
||||||
|
|
||||||
def test_read_metadata():
|
def test_read_metadata():
|
||||||
img = Image.open('Tests/images/lena_g4.tif')
|
img = Image.open('Tests/images/lena_g4.tif')
|
||||||
|
|
||||||
known = {'YResolution': ((1207959552, 16777216),),
|
known = {'YResolution': ((1207959552, 16777216),),
|
||||||
'PlanarConfiguration': (1,),
|
'PlanarConfiguration': (1,),
|
||||||
'BitsPerSample': (1,),
|
'BitsPerSample': (1,),
|
||||||
|
@ -48,7 +48,7 @@ def test_read_metadata():
|
||||||
'StripOffsets': (8,),
|
'StripOffsets': (8,),
|
||||||
'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'}
|
'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'}
|
||||||
|
|
||||||
# assert_equal is equivalent, but less helpful in telling what's wrong.
|
# assert_equal is equivalent, but less helpful in telling what's wrong.
|
||||||
named = img.tag.named()
|
named = img.tag.named()
|
||||||
for tag, value in named.items():
|
for tag, value in named.items():
|
||||||
assert_equal(known[tag], value)
|
assert_equal(known[tag], value)
|
||||||
|
@ -70,11 +70,11 @@ def test_write_metadata():
|
||||||
reloaded = loaded.tag.named()
|
reloaded = loaded.tag.named()
|
||||||
|
|
||||||
ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets']
|
ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets']
|
||||||
|
|
||||||
for tag, value in reloaded.items():
|
for tag, value in reloaded.items():
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
|
assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
|
||||||
|
|
||||||
for tag, value in original.items():
|
for tag, value in original.items():
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)
|
assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)
|
||||||
|
|
|
@ -28,7 +28,7 @@ def test_default():
|
||||||
assert_image(im, "RGB", im.size)
|
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):
|
def _test_float_conversion(im):
|
||||||
orig = im.getpixel((5, 5))
|
orig = im.getpixel((5, 5))
|
||||||
|
@ -76,7 +76,7 @@ def test_trns_p():
|
||||||
assert_no_exception(lambda: rgb.save(f))
|
assert_no_exception(lambda: rgb.save(f))
|
||||||
|
|
||||||
|
|
||||||
# ref https://github.com/python-imaging/Pillow/issues/664
|
# ref https://github.com/python-pillow/Pillow/issues/664
|
||||||
|
|
||||||
def test_trns_p_rgba():
|
def test_trns_p_rgba():
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -16,27 +16,27 @@ def color(mode):
|
||||||
def check(mode, c=None):
|
def check(mode, c=None):
|
||||||
if not c:
|
if not c:
|
||||||
c = color(mode)
|
c = color(mode)
|
||||||
|
|
||||||
#check putpixel
|
#check putpixel
|
||||||
im = Image.new(mode, (1, 1), None)
|
im = Image.new(mode, (1, 1), None)
|
||||||
im.putpixel((0, 0), c)
|
im.putpixel((0, 0), c)
|
||||||
assert_equal(im.getpixel((0, 0)), c,
|
assert_equal(im.getpixel((0, 0)), c,
|
||||||
"put/getpixel roundtrip failed for mode %s, color %s" %
|
"put/getpixel roundtrip failed for mode %s, color %s" %
|
||||||
(mode, c))
|
(mode, c))
|
||||||
|
|
||||||
# check inital color
|
# check inital color
|
||||||
im = Image.new(mode, (1, 1), c)
|
im = Image.new(mode, (1, 1), c)
|
||||||
assert_equal(im.getpixel((0, 0)), c,
|
assert_equal(im.getpixel((0, 0)), c,
|
||||||
"initial color failed for mode %s, color %s " %
|
"initial color failed for mode %s, color %s " %
|
||||||
(mode, color))
|
(mode, color))
|
||||||
|
|
||||||
def test_basic():
|
def test_basic():
|
||||||
for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F",
|
for mode in ("1", "L", "LA", "I", "I;16", "I;16B", "F",
|
||||||
"P", "PA", "RGB", "RGBA", "RGBX", "CMYK","YCbCr"):
|
"P", "PA", "RGB", "RGBA", "RGBX", "CMYK","YCbCr"):
|
||||||
check(mode)
|
check(mode)
|
||||||
|
|
||||||
def test_signedness():
|
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*
|
# pixelaccess is using signed int* instead of uint*
|
||||||
for mode in ("I;16", "I;16B"):
|
for mode in ("I;16", "I;16B"):
|
||||||
check(mode, 2**15-1)
|
check(mode, 2**15-1)
|
||||||
|
@ -44,6 +44,6 @@ def test_signedness():
|
||||||
check(mode, 2**15+1)
|
check(mode, 2**15+1)
|
||||||
check(mode, 2**16-1)
|
check(mode, 2**16-1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from PIL import Image
|
||||||
|
|
||||||
if hasattr(sys, 'pypy_version_info'):
|
if hasattr(sys, 'pypy_version_info'):
|
||||||
# This takes _forever_ on pypy. Open Bug,
|
# 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()
|
skip()
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
|
@ -26,7 +26,7 @@ def test_sanity():
|
||||||
|
|
||||||
def test_16bit_lut():
|
def test_16bit_lut():
|
||||||
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
""" 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")
|
im = lena("I")
|
||||||
|
|
|
@ -10,9 +10,9 @@ def test_extent():
|
||||||
w//2,h//2), # ul -> lr
|
w//2,h//2), # ul -> lr
|
||||||
Image.BILINEAR)
|
Image.BILINEAR)
|
||||||
|
|
||||||
|
|
||||||
scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h))
|
scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h))
|
||||||
|
|
||||||
assert_image_similar(transformed, scaled, 10) # undone -- precision?
|
assert_image_similar(transformed, scaled, 10) # undone -- precision?
|
||||||
|
|
||||||
def test_quad():
|
def test_quad():
|
||||||
|
@ -23,9 +23,9 @@ def test_quad():
|
||||||
(0,0,0,h//2,
|
(0,0,0,h//2,
|
||||||
w//2,h//2,w//2,0), # ul -> ccw around quad
|
w//2,h//2,w//2,0), # ul -> ccw around quad
|
||||||
Image.BILINEAR)
|
Image.BILINEAR)
|
||||||
|
|
||||||
scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h))
|
scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h))
|
||||||
|
|
||||||
assert_image_equal(transformed, scaled)
|
assert_image_equal(transformed, scaled)
|
||||||
|
|
||||||
def test_mesh():
|
def test_mesh():
|
||||||
|
@ -48,8 +48,8 @@ def test_mesh():
|
||||||
checker = Image.new('RGBA', im.size)
|
checker = Image.new('RGBA', im.size)
|
||||||
checker.paste(scaled, (0,0))
|
checker.paste(scaled, (0,0))
|
||||||
checker.paste(scaled, (w//2,h//2))
|
checker.paste(scaled, (w//2,h//2))
|
||||||
|
|
||||||
assert_image_equal(transformed, checker)
|
assert_image_equal(transformed, checker)
|
||||||
|
|
||||||
# now, check to see that the extra area is (0,0,0,0)
|
# now, check to see that the extra area is (0,0,0,0)
|
||||||
blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0))
|
blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0))
|
||||||
|
@ -59,34 +59,34 @@ def test_mesh():
|
||||||
|
|
||||||
def _test_alpha_premult(op):
|
def _test_alpha_premult(op):
|
||||||
# create image with half white, half black, with the black half transparent.
|
# create image with half white, half black, with the black half transparent.
|
||||||
# do op,
|
# do op,
|
||||||
# there should be no darkness in the white section.
|
# there should be no darkness in the white section.
|
||||||
im = Image.new('RGBA', (10,10), (0,0,0,0));
|
im = Image.new('RGBA', (10,10), (0,0,0,0));
|
||||||
im2 = Image.new('RGBA', (5,10), (255,255,255,255));
|
im2 = Image.new('RGBA', (5,10), (255,255,255,255));
|
||||||
im.paste(im2, (0,0))
|
im.paste(im2, (0,0))
|
||||||
|
|
||||||
im = op(im, (40,10))
|
im = op(im, (40,10))
|
||||||
im_background = Image.new('RGB', (40,10), (255,255,255))
|
im_background = Image.new('RGB', (40,10), (255,255,255))
|
||||||
im_background.paste(im, (0,0), im)
|
im_background.paste(im, (0,0), im)
|
||||||
|
|
||||||
hist = im_background.histogram()
|
hist = im_background.histogram()
|
||||||
assert_equal(40*10, hist[-1])
|
assert_equal(40*10, hist[-1])
|
||||||
|
|
||||||
|
|
||||||
def test_alpha_premult_resize():
|
def test_alpha_premult_resize():
|
||||||
|
|
||||||
def op (im, sz):
|
def op (im, sz):
|
||||||
return im.resize(sz, Image.LINEAR)
|
return im.resize(sz, Image.LINEAR)
|
||||||
|
|
||||||
_test_alpha_premult(op)
|
_test_alpha_premult(op)
|
||||||
|
|
||||||
def test_alpha_premult_transform():
|
def test_alpha_premult_transform():
|
||||||
|
|
||||||
def op(im, sz):
|
def op(im, sz):
|
||||||
(w,h) = im.size
|
(w,h) = im.size
|
||||||
return im.transform(sz, Image.EXTENT,
|
return im.transform(sz, Image.EXTENT,
|
||||||
(0,0,
|
(0,0,
|
||||||
w,h),
|
w,h),
|
||||||
Image.BILINEAR)
|
Image.BILINEAR)
|
||||||
|
|
||||||
_test_alpha_premult(op)
|
_test_alpha_premult(op)
|
||||||
|
@ -94,7 +94,7 @@ def test_alpha_premult_transform():
|
||||||
|
|
||||||
def test_blank_fill():
|
def test_blank_fill():
|
||||||
# attempting to hit
|
# 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
|
# issue is that transforms with transparent overflow area
|
||||||
# contained junk from previous images, especially on systems with
|
# contained junk from previous images, especially on systems with
|
||||||
|
@ -106,11 +106,11 @@ def test_blank_fill():
|
||||||
#
|
#
|
||||||
# Running by default, but I'd totally understand not doing it in
|
# Running by default, but I'd totally understand not doing it in
|
||||||
# the future
|
# the future
|
||||||
|
|
||||||
foo = [Image.new('RGBA',(1024,1024), (a,a,a,a))
|
|
||||||
for a in range(1,65)]
|
|
||||||
|
|
||||||
# Yeah. Watch some JIT optimize this out.
|
foo = [Image.new('RGBA',(1024,1024), (a,a,a,a))
|
||||||
|
for a in range(1,65)]
|
||||||
|
|
||||||
|
# Yeah. Watch some JIT optimize this out.
|
||||||
foo = None
|
foo = None
|
||||||
|
|
||||||
test_mesh()
|
test_mesh()
|
||||||
|
|
|
@ -9,12 +9,13 @@ except ImportError:
|
||||||
|
|
||||||
SRGB = "Tests/icc/sRGB.icm"
|
SRGB = "Tests/icc/sRGB.icm"
|
||||||
|
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
|
|
||||||
# basic smoke test.
|
# basic smoke test.
|
||||||
# this mostly follows the cms_test outline.
|
# 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(v[0], '1.0.0 pil')
|
||||||
assert_equal(list(map(type, v)), [str, str, str, str])
|
assert_equal(list(map(type, v)), [str, str, str, str])
|
||||||
|
|
||||||
|
@ -24,10 +25,19 @@ def test_sanity():
|
||||||
i = ImageCms.profileToProfile(lena(), SRGB, SRGB)
|
i = ImageCms.profileToProfile(lena(), SRGB, SRGB)
|
||||||
assert_image(i, "RGB", (128, 128))
|
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")
|
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
||||||
i = ImageCms.applyTransform(lena(), t)
|
i = ImageCms.applyTransform(lena(), t)
|
||||||
assert_image(i, "RGB", (128, 128))
|
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")
|
p = ImageCms.createProfile("sRGB")
|
||||||
o = ImageCms.getOpenProfile(SRGB)
|
o = ImageCms.getOpenProfile(SRGB)
|
||||||
t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB")
|
t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB")
|
||||||
|
@ -41,24 +51,47 @@ def test_sanity():
|
||||||
assert_image(i, "RGB", (128, 128))
|
assert_image(i, "RGB", (128, 128))
|
||||||
|
|
||||||
# test PointTransform convenience API
|
# test PointTransform convenience API
|
||||||
im = lena().point(t)
|
lena().point(t)
|
||||||
|
|
||||||
|
|
||||||
def test_name():
|
def test_name():
|
||||||
# get profile information for file
|
# get profile information for file
|
||||||
assert_equal(ImageCms.getProfileName(SRGB).strip(),
|
assert_equal(ImageCms.getProfileName(SRGB).strip(),
|
||||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||||
def x_test_info():
|
|
||||||
|
|
||||||
|
def test_info():
|
||||||
assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(),
|
assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(),
|
||||||
['sRGB IEC61966-2.1', '',
|
['sRGB IEC61966-2.1', '',
|
||||||
'Copyright (c) 1998 Hewlett-Packard Company', '',
|
'Copyright (c) 1998 Hewlett-Packard Company', ''])
|
||||||
'WhitePoint : D65 (daylight)', '',
|
|
||||||
'Tests/icc/sRGB.icm'])
|
|
||||||
|
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():
|
def test_intent():
|
||||||
assert_equal(ImageCms.getDefaultIntent(SRGB), 0)
|
assert_equal(ImageCms.getDefaultIntent(SRGB), 0)
|
||||||
assert_equal(ImageCms.isIntentSupported(
|
assert_equal(ImageCms.isIntentSupported(
|
||||||
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||||
ImageCms.DIRECTION_INPUT), 1)
|
ImageCms.DIRECTION_INPUT), 1)
|
||||||
|
|
||||||
|
|
||||||
def test_profile_object():
|
def test_profile_object():
|
||||||
# same, using profile object
|
# same, using profile object
|
||||||
|
@ -69,8 +102,9 @@ def test_profile_object():
|
||||||
# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', ''])
|
# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', ''])
|
||||||
assert_equal(ImageCms.getDefaultIntent(p), 0)
|
assert_equal(ImageCms.getDefaultIntent(p), 0)
|
||||||
assert_equal(ImageCms.isIntentSupported(
|
assert_equal(ImageCms.isIntentSupported(
|
||||||
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC,
|
||||||
ImageCms.DIRECTION_INPUT), 1)
|
ImageCms.DIRECTION_INPUT), 1)
|
||||||
|
|
||||||
|
|
||||||
def test_extensions():
|
def test_extensions():
|
||||||
# extensions
|
# extensions
|
||||||
|
@ -79,12 +113,21 @@ def test_extensions():
|
||||||
assert_equal(ImageCms.getProfileName(p).strip(),
|
assert_equal(ImageCms.getProfileName(p).strip(),
|
||||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||||
|
|
||||||
|
|
||||||
def test_exceptions():
|
def test_exceptions():
|
||||||
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
||||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
|
assert_exception(
|
||||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
|
ImageCms.PyCMSError,
|
||||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None))
|
lambda: ImageCms.profileToProfile(lena(), "foo", "bar"))
|
||||||
assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None))
|
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():
|
def test_display_profile():
|
||||||
|
@ -93,61 +136,63 @@ def test_display_profile():
|
||||||
|
|
||||||
|
|
||||||
def test_lab_color_profile():
|
def test_lab_color_profile():
|
||||||
pLab = ImageCms.createProfile("LAB", 5000)
|
ImageCms.createProfile("LAB", 5000)
|
||||||
pLab = ImageCms.createProfile("LAB", 6500)
|
ImageCms.createProfile("LAB", 6500)
|
||||||
|
|
||||||
|
|
||||||
def test_simple_lab():
|
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")
|
pLab = ImageCms.createProfile("LAB")
|
||||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||||
|
|
||||||
i_lab = ImageCms.applyTransform(i, t)
|
i_lab = ImageCms.applyTransform(i, t)
|
||||||
|
|
||||||
|
|
||||||
assert_equal(i_lab.mode, 'LAB')
|
assert_equal(i_lab.mode, 'LAB')
|
||||||
|
|
||||||
k = i_lab.getpixel((0,0))
|
k = i_lab.getpixel((0, 0))
|
||||||
assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128
|
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)
|
a = i_lab.getdata(1)
|
||||||
b = i_lab.getdata(2)
|
b = i_lab.getdata(2)
|
||||||
|
|
||||||
assert_equal(list(L), [137]*100)
|
assert_equal(list(L), [137] * 100)
|
||||||
assert_equal(list(a), [128]*100)
|
assert_equal(list(a), [128] * 100)
|
||||||
assert_equal(list(b), [128]*100)
|
assert_equal(list(b), [128] * 100)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_lab_color():
|
def test_lab_color():
|
||||||
pLab = ImageCms.createProfile("LAB")
|
pLab = ImageCms.createProfile("LAB")
|
||||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||||
# need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType,
|
# Need to add a type mapping for some PIL type to TYPE_Lab_8 in
|
||||||
# and have that mapping work back to a PIL mode. (likely RGB)
|
# findLCMSType, and have that mapping work back to a PIL mode (likely RGB).
|
||||||
i = ImageCms.applyTransform(lena(), t)
|
i = ImageCms.applyTransform(lena(), t)
|
||||||
assert_image(i, "LAB", (128, 128))
|
assert_image(i, "LAB", (128, 128))
|
||||||
|
|
||||||
# i.save('temp.lab.tif') # visually verified vs PS.
|
# i.save('temp.lab.tif') # visually verified vs PS.
|
||||||
|
|
||||||
target = Image.open('Tests/images/lena.Lab.tif')
|
target = Image.open('Tests/images/lena.Lab.tif')
|
||||||
|
|
||||||
assert_image_similar(i, target, 30)
|
assert_image_similar(i, target, 30)
|
||||||
|
|
||||||
|
|
||||||
def test_lab_srgb():
|
def test_lab_srgb():
|
||||||
pLab = ImageCms.createProfile("LAB")
|
pLab = ImageCms.createProfile("LAB")
|
||||||
t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
||||||
|
|
||||||
img = Image.open('Tests/images/lena.Lab.tif')
|
img = Image.open('Tests/images/lena.Lab.tif')
|
||||||
|
|
||||||
img_srgb = ImageCms.applyTransform(img, t)
|
img_srgb = ImageCms.applyTransform(img, t)
|
||||||
|
|
||||||
# img_srgb.save('temp.srgb.tif') # visually verified vs ps.
|
# img_srgb.save('temp.srgb.tif') # visually verified vs ps.
|
||||||
|
|
||||||
assert_image_similar(lena(), img_srgb, 30)
|
assert_image_similar(lena(), img_srgb, 30)
|
||||||
|
|
||||||
|
|
||||||
def test_lab_roundtrip():
|
def test_lab_roundtrip():
|
||||||
# check to see if we're at least internally consistent.
|
# check to see if we're at least internally consistent.
|
||||||
pLab = ImageCms.createProfile("LAB")
|
pLab = ImageCms.createProfile("LAB")
|
||||||
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB")
|
||||||
|
|
||||||
t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB")
|
||||||
|
@ -156,5 +201,3 @@ def test_lab_roundtrip():
|
||||||
out = ImageCms.applyTransform(i, t2)
|
out = ImageCms.applyTransform(i, t2)
|
||||||
|
|
||||||
assert_image_similar(lena(), out, 2)
|
assert_image_similar(lena(), out, 2)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,27 @@
|
||||||
from tester import *
|
from tester import *
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from PIL import ImageColor
|
||||||
from PIL import ImageDraw
|
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():
|
def test_sanity():
|
||||||
|
|
||||||
im = lena("RGB").copy()
|
im = lena("RGB").copy()
|
||||||
|
@ -17,6 +36,7 @@ def test_sanity():
|
||||||
|
|
||||||
success()
|
success()
|
||||||
|
|
||||||
|
|
||||||
def test_deprecated():
|
def test_deprecated():
|
||||||
|
|
||||||
im = lena().copy()
|
im = lena().copy()
|
||||||
|
@ -26,3 +46,220 @@ def test_deprecated():
|
||||||
assert_warning(DeprecationWarning, lambda: draw.setink(0))
|
assert_warning(DeprecationWarning, lambda: draw.setink(0))
|
||||||
assert_warning(DeprecationWarning, lambda: draw.setfill(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
|
||||||
|
|
|
@ -49,14 +49,14 @@ def test_parser():
|
||||||
|
|
||||||
if EpsImagePlugin.has_ghostscript():
|
if EpsImagePlugin.has_ghostscript():
|
||||||
im1, im2 = roundtrip("EPS")
|
im1, im2 = roundtrip("EPS")
|
||||||
assert_image_similar(im1, im2.convert('L'),20) # EPS comes back in RGB
|
assert_image_similar(im1, im2.convert('L'),20) # EPS comes back in RGB
|
||||||
|
|
||||||
if "jpeg_encoder" in codecs:
|
if "jpeg_encoder" in codecs:
|
||||||
im1, im2 = roundtrip("JPEG") # lossy compression
|
im1, im2 = roundtrip("JPEG") # lossy compression
|
||||||
assert_image(im1, im2.mode, im2.size)
|
assert_image(im1, im2.mode, im2.size)
|
||||||
|
|
||||||
# XXX Why assert exception and why does it fail?
|
# 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"))
|
#assert_exception(IOError, lambda: roundtrip("PDF"))
|
||||||
|
|
||||||
def test_ico():
|
def test_ico():
|
||||||
|
|
|
@ -3,7 +3,7 @@ from PIL import Image
|
||||||
|
|
||||||
import locale
|
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:
|
## on windows, in polish locale:
|
||||||
|
|
||||||
## import locale
|
## import locale
|
||||||
|
@ -16,7 +16,7 @@ import locale
|
||||||
## 7
|
## 7
|
||||||
## 160
|
## 160
|
||||||
|
|
||||||
# one of string.whitespace is not freely convertable into ascii.
|
# one of string.whitespace is not freely convertable into ascii.
|
||||||
|
|
||||||
path = "Images/lena.jpg"
|
path = "Images/lena.jpg"
|
||||||
|
|
||||||
|
|
|
@ -83,12 +83,12 @@ def test_16bit():
|
||||||
def test_to_array():
|
def test_to_array():
|
||||||
|
|
||||||
def _to_array(mode, dtype):
|
def _to_array(mode, dtype):
|
||||||
img = lena(mode)
|
img = lena(mode)
|
||||||
np_img = numpy.array(img)
|
np_img = numpy.array(img)
|
||||||
_test_img_equals_nparray(img, np_img)
|
_test_img_equals_nparray(img, np_img)
|
||||||
assert_equal(np_img.dtype, numpy.dtype(dtype))
|
assert_equal(np_img.dtype, numpy.dtype(dtype))
|
||||||
|
|
||||||
|
|
||||||
modes = [("L", 'uint8'),
|
modes = [("L", 'uint8'),
|
||||||
("I", 'int32'),
|
("I", 'int32'),
|
||||||
("F", 'float32'),
|
("F", 'float32'),
|
||||||
|
@ -101,20 +101,20 @@ def test_to_array():
|
||||||
("I;16B", '>u2'),
|
("I;16B", '>u2'),
|
||||||
("I;16L", '<u2'),
|
("I;16L", '<u2'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
assert_no_exception(lambda: _to_array(*mode))
|
assert_no_exception(lambda: _to_array(*mode))
|
||||||
|
|
||||||
|
|
||||||
def test_point_lut():
|
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
|
data = list(range(256))*3
|
||||||
lut = numpy.array(data, dtype='uint8')
|
lut = numpy.array(data, dtype='uint8')
|
||||||
|
|
||||||
im = lena()
|
im = lena()
|
||||||
|
|
||||||
assert_no_exception(lambda: im.point(lut))
|
assert_no_exception(lambda: im.point(lut))
|
||||||
|
|
||||||
|
|
||||||
|
|
109
_imaging.c
|
@ -2252,17 +2252,17 @@ void _font_text_asBytes(PyObject* encoded_string, unsigned char** text){
|
||||||
if (bytes) {
|
if (bytes) {
|
||||||
*text = (unsigned char*)PyBytes_AsString(bytes);
|
*text = (unsigned char*)PyBytes_AsString(bytes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PY_VERSION_HEX < 0x03000000
|
#if PY_VERSION_HEX < 0x03000000
|
||||||
/* likely case here is py2.x with an ordinary string.
|
/* likely case here is py2.x with an ordinary string.
|
||||||
but this isn't defined in Py3.x */
|
but this isn't defined in Py3.x */
|
||||||
if (PyString_Check(encoded_string)) {
|
if (PyString_Check(encoded_string)) {
|
||||||
*text = (unsigned char *)PyString_AsString(encoded_string);
|
*text = (unsigned char *)PyString_AsString(encoded_string);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_font_getmask(ImagingFontObject* self, PyObject* args)
|
_font_getmask(ImagingFontObject* self, PyObject* args)
|
||||||
|
@ -2336,7 +2336,7 @@ _font_getsize(ImagingFontObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Py_BuildValue("ii", textwidth(self, text), self->ysize);
|
return Py_BuildValue("ii", textwidth(self, text), self->ysize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct PyMethodDef _font_methods[] = {
|
static struct PyMethodDef _font_methods[] = {
|
||||||
|
@ -2399,17 +2399,35 @@ _draw_ink(ImagingDrawObject* self, PyObject* args)
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_draw_arc(ImagingDrawObject* self, PyObject* args)
|
_draw_arc(ImagingDrawObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int x0, y0, x1, y1;
|
double* xy;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
PyObject* data;
|
||||||
int ink;
|
int ink;
|
||||||
int start, end;
|
int start, end;
|
||||||
int op = 0;
|
int op = 0;
|
||||||
if (!PyArg_ParseTuple(args, "(iiii)iii|i",
|
if (!PyArg_ParseTuple(args, "Oiii|i", &data, &start, &end, &ink))
|
||||||
&x0, &y0, &x1, &y1,
|
|
||||||
&start, &end, &ink))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end,
|
n = PyPath_Flatten(data, &xy);
|
||||||
&ink, op) < 0)
|
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;
|
return NULL;
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
|
@ -2455,15 +2473,35 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_draw_chord(ImagingDrawObject* self, PyObject* args)
|
_draw_chord(ImagingDrawObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int x0, y0, x1, y1;
|
double* xy;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
PyObject* data;
|
||||||
int ink, fill;
|
int ink, fill;
|
||||||
int start, end;
|
int start, end;
|
||||||
if (!PyArg_ParseTuple(args, "(iiii)iiii",
|
if (!PyArg_ParseTuple(args, "Oiiii",
|
||||||
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
|
&data, &start, &end, &ink, &fill))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (ImagingDrawChord(self->image->image, x0, y0, x1, y1,
|
n = PyPath_Flatten(data, &xy);
|
||||||
start, end, &ink, fill, self->blend) < 0)
|
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;
|
return NULL;
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
|
@ -2492,8 +2530,8 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = ImagingDrawEllipse(self->image->image,
|
n = ImagingDrawEllipse(self->image->image,
|
||||||
(int) xy[0], (int) xy[1],
|
(int) xy[0], (int) xy[1],
|
||||||
(int) xy[2], (int) xy[3],
|
(int) xy[2], (int) xy[3],
|
||||||
&ink, fill, self->blend
|
&ink, fill, self->blend
|
||||||
);
|
);
|
||||||
|
@ -2656,15 +2694,34 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_draw_pieslice(ImagingDrawObject* self, PyObject* args)
|
_draw_pieslice(ImagingDrawObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int x0, y0, x1, y1;
|
double* xy;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
PyObject* data;
|
||||||
int ink, fill;
|
int ink, fill;
|
||||||
int start, end;
|
int start, end;
|
||||||
if (!PyArg_ParseTuple(args, "(iiii)iiii",
|
if (!PyArg_ParseTuple(args, "Oiiii", &data, &start, &end, &ink, &fill))
|
||||||
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1,
|
n = PyPath_Flatten(data, &xy);
|
||||||
start, end, &ink, fill, self->blend) < 0)
|
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;
|
return NULL;
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
|
@ -2738,9 +2795,9 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = ImagingDrawRectangle(self->image->image,
|
n = ImagingDrawRectangle(self->image->image,
|
||||||
(int) xy[0], (int) xy[1],
|
(int) xy[0], (int) xy[1],
|
||||||
(int) xy[2], (int) xy[3],
|
(int) xy[2], (int) xy[3],
|
||||||
&ink, fill, self->blend
|
&ink, fill, self->blend
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3117,8 +3174,8 @@ _getattr_ptr(ImagingObject* self, void* closure)
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_getattr_unsafe_ptrs(ImagingObject* self, void* closure)
|
_getattr_unsafe_ptrs(ImagingObject* self, void* closure)
|
||||||
{
|
{
|
||||||
return Py_BuildValue("(ss)(si)(si)(si)(si)(si)(sn)(sn)(sn)(sn)(sn)(si)(si)(sn)",
|
return Py_BuildValue("(ss)(si)(si)(si)(si)(si)(sn)(sn)(sn)(sn)(sn)(si)(si)(sn)",
|
||||||
"mode", self->image->mode,
|
"mode", self->image->mode,
|
||||||
"type", self->image->type,
|
"type", self->image->type,
|
||||||
"depth", self->image->depth,
|
"depth", self->image->depth,
|
||||||
"bands", self->image->bands,
|
"bands", self->image->bands,
|
||||||
|
|
2
docs/_templates/sidebarhelp.html
vendored
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If you've discovered a bug, you can
|
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>.
|
on Github</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ The fork authors' goal is to foster active development of PIL through:
|
||||||
- Regular releases to the `Python Package Index`_
|
- Regular releases to the `Python Package Index`_
|
||||||
- Solicitation for community contributions and involvement on `Image-SIG`_
|
- Solicitation for community contributions and involvement on `Image-SIG`_
|
||||||
|
|
||||||
.. _Travis CI: https://travis-ci.org/python-imaging/Pillow
|
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow
|
||||||
.. _GitHub: https://github.com/python-imaging/Pillow
|
.. _GitHub: https://github.com/python-pillow/Pillow
|
||||||
.. _Python Package Index: https://pypi.python.org/pypi/Pillow
|
.. _Python Package Index: https://pypi.python.org/pypi/Pillow
|
||||||
.. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig
|
.. _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
|
.. _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.
|
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
|
Pillow is the 'friendly' PIL fork by Alex Clark and Contributors. PIL is the
|
||||||
Python Imaging Library by Fredrik Lundh and Contributors.
|
Python Imaging Library by Fredrik Lundh and Contributors.
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/python-imaging/Pillow.svg?branch=master
|
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
|
||||||
:target: https://travis-ci.org/python-imaging/Pillow
|
:target: https://travis-ci.org/python-pillow/Pillow
|
||||||
:alt: Travis CI build status
|
:alt: Travis CI build status
|
||||||
|
|
||||||
.. image:: https://pypip.in/v/Pillow/badge.png
|
.. 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/
|
:target: https://pypi.python.org/pypi/Pillow/
|
||||||
:alt: Number of PyPI downloads
|
:alt: Number of PyPI downloads
|
||||||
|
|
||||||
.. image:: https://coveralls.io/repos/python-imaging/Pillow/badge.png?branch=master
|
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
|
||||||
:target: https://coveralls.io/r/python-imaging/Pillow?branch=master
|
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
|
||||||
:alt: Test coverage
|
:alt: Test coverage
|
||||||
|
|
||||||
To start using Pillow, please read the :doc:`installation
|
To start using Pillow, please read the :doc:`installation
|
||||||
instructions <installation>`.
|
instructions <installation>`.
|
||||||
|
|
||||||
You can get the source and contribute at
|
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>`_.
|
and old versions from `PyPI <https://pypi.python.org/pypi/Pillow>`_.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@ -42,7 +42,7 @@ Support Pillow!
|
||||||
|
|
||||||
PIL needs you! Please help us maintain the Python Imaging Library here:
|
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>`_
|
- `Freenode <irc://irc.freenode.net#pil>`_
|
||||||
- `Image-SIG <http://mail.python.org/mailman/listinfo/image-sig>`_
|
- `Image-SIG <http://mail.python.org/mailman/listinfo/image-sig>`_
|
||||||
|
|
||||||
|
|
|
@ -67,11 +67,11 @@ Many of Pillow's features require external libraries:
|
||||||
|
|
||||||
* Pillow has been tested with version **0.1.3**, which does not read
|
* Pillow has been tested with version **0.1.3**, which does not read
|
||||||
transparent webp files. Versions **0.3.0** and **0.4.0** support
|
transparent webp files. Versions **0.3.0** and **0.4.0** support
|
||||||
transparency.
|
transparency.
|
||||||
|
|
||||||
* **tcl/tk** provides support for tkinter bitmap and photo images.
|
* **tcl/tk** provides support for tkinter bitmap and photo images.
|
||||||
|
|
||||||
* **openjpeg** provides JPEG 2000 functionality.
|
* **openjpeg** provides JPEG 2000 functionality.
|
||||||
|
|
||||||
* Pillow has been tested with openjpeg **2.0.0** and **2.1.0**.
|
* Pillow has been tested with openjpeg **2.0.0** and **2.1.0**.
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ Or for Python 3::
|
||||||
$ sudo apt-get install python3-dev python3-setuptools
|
$ sudo apt-get install python3-dev python3-setuptools
|
||||||
|
|
||||||
In Fedora, the command is::
|
In Fedora, the command is::
|
||||||
|
|
||||||
$ sudo yum install python-devel
|
$ sudo yum install python-devel
|
||||||
|
|
||||||
Prerequisites are installed on **Ubuntu 10.04 LTS** with::
|
Prerequisites are installed on **Ubuntu 10.04 LTS** with::
|
||||||
|
@ -185,6 +185,25 @@ to a specific version:
|
||||||
|
|
||||||
$ pip install --use-wheel Pillow==2.3.0
|
$ 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
|
Platform support
|
||||||
----------------
|
----------------
|
||||||
|
@ -199,7 +218,7 @@ current versions of Linux, OS X, and Windows.
|
||||||
Contributors please test on your platform, edit this document, and send a
|
Contributors please test on your platform, edit this document, and send a
|
||||||
pull request.
|
pull request.
|
||||||
|
|
||||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||||
|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** |
|
|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** |
|
||||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||||
| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 |
|
| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 |
|
||||||
|
@ -224,6 +243,8 @@ current versions of Linux, OS X, and Windows.
|
||||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||||
| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 |
|
| 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 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 |
|
||||||
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+
|
||||||
| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |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 |
|
| 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
|
Draws an arc (a portion of a circle outline) between the start and end
|
||||||
angles, inside the given bounding box.
|
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]``.
|
``[(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)
|
.. 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
|
Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points
|
||||||
with a straight line.
|
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]``.
|
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
|
||||||
:param outline: Color to use for the outline.
|
:param outline: Color to use for the outline.
|
||||||
:param fill: Color to use for the fill.
|
: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
|
Same as arc, but also draws straight lines between the end points and the
|
||||||
center of the bounding box.
|
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]``.
|
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
|
||||||
:param outline: Color to use for the outline.
|
:param outline: Color to use for the outline.
|
||||||
:param fill: Color to use for the fill.
|
:param fill: Color to use for the fill.
|
||||||
|
|
4
setup.py
|
@ -234,7 +234,7 @@ class pil_build_ext(build_ext):
|
||||||
elif sys.platform.startswith("linux"):
|
elif sys.platform.startswith("linux"):
|
||||||
arch_tp = (plat.processor(), plat.architecture()[0])
|
arch_tp = (plat.processor(), plat.architecture()[0])
|
||||||
if arch_tp == ("x86_64","32bit"):
|
if arch_tp == ("x86_64","32bit"):
|
||||||
# 32 bit build on 64 bit machine.
|
# 32 bit build on 64 bit machine.
|
||||||
_add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
|
_add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
|
||||||
else:
|
else:
|
||||||
for platform_ in arch_tp:
|
for platform_ in arch_tp:
|
||||||
|
@ -691,7 +691,7 @@ setup(
|
||||||
_read('CHANGES.rst')).decode('utf-8'),
|
_read('CHANGES.rst')).decode('utf-8'),
|
||||||
author='Alex Clark (fork author)',
|
author='Alex Clark (fork author)',
|
||||||
author_email='aclark@aclark.net',
|
author_email='aclark@aclark.net',
|
||||||
url='http://python-imaging.github.io/',
|
url='http://python-pillow.github.io/',
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 6 - Mature",
|
"Development Status :: 6 - Mature",
|
||||||
"Topic :: Multimedia :: Graphics",
|
"Topic :: Multimedia :: Graphics",
|
||||||
|
|