diff --git a/CHANGES.rst b/CHANGES.rst index 8839a1782..49946c36c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,9 +1,15 @@ Changelog (Pillow) ================== -2.4.0 (unreleased) +2.4.0 (2014-04-01) ------------------ +- Indexed Transparency handled for conversions between L, RGB, and P modes. Fixes #510 + [wiredfool] + +- Conversions enabled from RGBA->P, Fixes #544 + [wiredfool] + - Improved icns support [al45tair] diff --git a/MANIFEST.in b/MANIFEST.in index 09f265250..c2358f76f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include tox.ini recursive-include Images *.bdf recursive-include Images *.fli recursive-include Images *.gif +recursive-include Images *.icns recursive-include Images *.ico recursive-include Images *.jpg recursive-include Images *.pbm @@ -34,7 +35,9 @@ recursive-include Tests *.gif recursive-include Tests *.gnuplot recursive-include Tests *.html recursive-include Tests *.icm +recursive-include Tests *.icns recursive-include Tests *.ico +recursive-include Tests *.jp2 recursive-include Tests *.jpg recursive-include Tests *.pcf recursive-include Tests *.pcx @@ -47,6 +50,7 @@ recursive-include Tests *.ttf recursive-include Tests *.txt recursive-include Tk *.c recursive-include Tk *.txt +recursive-include depends *.sh recursive-include docs *.bat recursive-include docs *.gitignore recursive-include docs *.html diff --git a/PIL/Image.py b/PIL/Image.py index 7713fe7d9..18d2c8267 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -735,21 +735,65 @@ class Image: im = self.im.convert_matrix(mode, matrix) return self._new(im) + if mode == "P" and self.mode == "RGBA": + return self.quantize(colors) + + trns = None + delete_trns = False + # transparency handling + if "transparency" in self.info and self.info['transparency'] is not None: + if self.mode in ('L', 'RGB') and mode == 'RGBA': + # Use transparent conversion to promote from transparent + # color to an alpha channel. + return self._new(self.im.convert_transparent( + mode, self.info['transparency'])) + elif self.mode in ('L', 'RGB', 'P') and mode in ('L', 'RGB', 'P'): + t = self.info['transparency'] + if isinstance(t, bytes): + # Dragons. This can't be represented by a single color + warnings.warn('Palette images with Transparency expressed '+ + ' in bytes should be converted to RGBA images') + delete_trns = True + else: + # get the new transparency color. + # use existing conversions + trns_im = Image()._new(core.new(self.mode, (1,1))) + if self.mode == 'P': + trns_im.putpalette(self.palette) + trns_im.putpixel((0,0), t) + + if mode in ('L','RGB'): + trns_im = trns_im.convert(mode) + else: + # can't just retrieve the palette number, got to do it + # after quantization. + trns_im = trns_im.convert('RGB') + trns = trns_im.getpixel((0,0)) + + if mode == "P" and palette == ADAPTIVE: im = self.im.quantize(colors) new = self._new(im) from PIL import ImagePalette new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB")) + if delete_trns: + # This could possibly happen if we requantize to fewer colors. + # The transparency would be totally off in that case. + del(new.info['transparency']) + if trns is not None: + try: + new.info['transparency'] = new.palette.getcolor(trns) + except: + # if we can't make a transparent color, don't leave the old + # transparency hanging around to mess us up. + del(new.info['transparency']) + warnings.warn("Couldn't allocate palette entry for transparency") return new # colorspace conversion if dither is None: dither = FLOYDSTEINBERG - - # Use transparent conversion to promote from transparent color to an alpha channel. - if self.mode in ("L", "RGB") and mode == "RGBA" and "transparency" in self.info: - return self._new(self.im.convert_transparent(mode, self.info['transparency'])) - + try: im = self.im.convert(mode, dither) except ValueError: @@ -760,9 +804,22 @@ class Image: except KeyError: raise ValueError("illegal conversion") - return self._new(im) + new_im = self._new(im) + if delete_trns: + #crash fail if we leave a bytes transparency in an rgb/l mode. + del(new.info['transparency']) + if trns is not None: + if new_im.mode == 'P': + try: + new_im.info['transparency'] = new_im.palette.getcolor(trns) + except: + del(new_im.info['transparency']) + warnings.warn("Couldn't allocate palette entry for transparency") + else: + new_im.info['transparency'] = trns + return new_im - def quantize(self, colors=256, method=0, kmeans=0, palette=None): + def quantize(self, colors=256, method=None, kmeans=0, palette=None): # methods: # 0 = median cut @@ -773,7 +830,18 @@ class Image: # quantizer interface in a later version of PIL. self.load() + + if method is None: + # defaults: + method = 0 + if self.mode == 'RGBA': + method = 2 + if self.mode == 'RGBA' and method != 2: + # Caller specified an invalid mode. + raise ValueError('Fast Octree (method == 2) is the ' + + ' only valid method for quantizing RGBA images') + if palette: # use palette from reference image palette.load() diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 20ba6a11f..c875712c1 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -84,7 +84,13 @@ VERSION = "1.0.0 pil" # --------------------------------------------------------------------. from PIL import Image -from PIL import _imagingcms +try: + from PIL import _imagingcms +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from _util import import_err + _imagingcms = import_err(ex) from PIL._util import isStringType core = _imagingcms @@ -159,11 +165,10 @@ class ImageCmsProfile: self.product_name = None self.product_info = None -## -# Transform. This can be used with the procedural API, or with the -# standard {@link Image.point} method. - class ImageCmsTransform(Image.ImagePointHandler): + """Transform. This can be used with the procedural API, or with the + standard Image.point() method. + """ def __init__(self, input, output, input_mode, output_mode, intent=INTENT_PERCEPTUAL, @@ -203,11 +208,11 @@ class ImageCmsTransform(Image.ImagePointHandler): result = self.transform.apply(im.im.id, im.im.id) return im -## -# (experimental) Fetches the profile for the current display device. -# @return None if the profile is not known. - def get_display_profile(handle=None): + """ (experimental) Fetches the profile for the current display device. + :returns: None if the profile is not known. + """ + import sys if sys.platform == "win32": from PIL import ImageWin @@ -228,59 +233,58 @@ def get_display_profile(handle=None): # pyCMS compatible layer # --------------------------------------------------------------------. -## -# (pyCMS) Exception class. This is used for all errors in the pyCMS API. - class PyCMSError(Exception): + """ (pyCMS) Exception class. This is used for all errors in the pyCMS API. """ pass -## -# (pyCMS) Applies an ICC transformation to a given image, mapping from -# inputProfile to outputProfile. -# -# If the input or output profiles specified are not valid filenames, a -# PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, -# a PyCMSError will be raised. If an error occurs during application of -# the profiles, a PyCMSError will be raised. If outputMode is not a mode -# supported by the outputProfile (or by pyCMS), a PyCMSError will be -# raised. -# -# This function applies an ICC transformation to im from inputProfile's -# color space to outputProfile's color space using the specified rendering -# intent to decide how to handle out-of-gamut colors. -# -# OutputMode can be used to specify that a color mode conversion is to -# be done using these profiles, but the specified profiles must be able -# to handle that mode. I.e., if converting im from RGB to CMYK using -# profiles, the input profile must handle RGB data, and the output -# profile must handle CMYK data. -# -# @param im An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.) -# @param inputProfile String, as a valid filename path to the ICC input profile -# you wish to use for this image, or a profile object -# @param outputProfile String, as a valid filename path to the ICC output -# profile you wish to use for this image, or a profile object -# @param renderingIntent Integer (0-3) specifying the rendering intent you wish -# to use for the transform -# -# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) -# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) -# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) -# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) -# -# see the pyCMS documentation for details on rendering intents and what they do. -# @param outputMode A valid PIL mode for the output image (i.e. "RGB", "CMYK", -# etc.). Note: if rendering the image "inPlace", outputMode MUST be the -# same mode as the input, or omitted completely. If omitted, the outputMode -# will be the same as the mode of the input image (im.mode) -# @param inPlace Boolean (1 = True, None or 0 = False). If True, the original -# image is modified in-place, and None is returned. If False (default), a -# new Image object is returned with the transform applied. -# @param flags Integer (0-...) specifying additional flags -# @return Either None or a new PIL image object, depending on value of inPlace -# @exception PyCMSError - def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0): + """ + (pyCMS) Applies an ICC transformation to a given image, mapping from + inputProfile to outputProfile. + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, + a PyCMSError will be raised. If an error occurs during application of + the profiles, a PyCMSError will be raised. If outputMode is not a mode + supported by the outputProfile (or by pyCMS), a PyCMSError will be + raised. + + This function applies an ICC transformation to im from inputProfile's + color space to outputProfile's color space using the specified rendering + intent to decide how to handle out-of-gamut colors. + + OutputMode can be used to specify that a color mode conversion is to + be done using these profiles, but the specified profiles must be able + to handle that mode. I.e., if converting im from RGB to CMYK using + profiles, the input profile must handle RGB data, and the output + profile must handle CMYK data. + + :param im: An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.) + :param inputProfile: String, as a valid filename path to the ICC input profile + you wish to use for this image, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this image, or a profile object + :param renderingIntent: Integer (0-3) specifying the rendering intent you wish + to use for the transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what they do. + :param outputMode: A valid PIL mode for the output image (i.e. "RGB", "CMYK", + etc.). Note: if rendering the image "inPlace", outputMode MUST be the + same mode as the input, or omitted completely. If omitted, the outputMode + will be the same as the mode of the input image (im.mode) + :param inPlace: Boolean (1 = True, None or 0 = False). If True, the original + image is modified in-place, and None is returned. If False (default), a + new Image object is returned with the transform applied. + :param flags: Integer (0-...) specifying additional flags + :returns: Either None or a new PIL image object, depending on value of inPlace + :exception PyCMSError: + """ + if outputMode is None: outputMode = im.mode @@ -308,80 +312,83 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER return imOut -## -# (pyCMS) Opens an ICC profile file. -# -# The PyCMSProfile object can be passed back into pyCMS for use in creating -# transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). -# -# If profileFilename is not a vaild filename for an ICC profile, a PyCMSError -# will be raised. -# -# @param profileFilename String, as a valid filename path to the ICC profile you -# wish to open, or a file-like object. -# @return A CmsProfile class object. -# @exception PyCMSError def getOpenProfile(profileFilename): + """ + (pyCMS) Opens an ICC profile file. + + The PyCMSProfile object can be passed back into pyCMS for use in creating + transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). + + If profileFilename is not a vaild filename for an ICC profile, a PyCMSError + will be raised. + + :param profileFilename: String, as a valid filename path to the ICC profile you + wish to open, or a file-like object. + :returns: A CmsProfile class object. + :exception PyCMSError: + """ + try: return ImageCmsProfile(profileFilename) except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Builds an ICC transform mapping from the inputProfile to the -# outputProfile. Use applyTransform to apply the transform to a given -# image. -# -# If the input or output profiles specified are not valid filenames, a -# PyCMSError will be raised. If an error occurs during creation of the -# transform, a PyCMSError will be raised. -# -# If inMode or outMode are not a mode supported by the outputProfile (or -# by pyCMS), a PyCMSError will be raised. -# -# This function builds and returns an ICC transform from the inputProfile -# to the outputProfile using the renderingIntent to determine what to do -# with out-of-gamut colors. It will ONLY work for converting images that -# are in inMode to images that are in outMode color format (PIL mode, -# i.e. "RGB", "RGBA", "CMYK", etc.). -# -# Building the transform is a fair part of the overhead in -# ImageCms.profileToProfile(), so if you're planning on converting multiple -# images using the same input/output settings, this can save you time. -# Once you have a transform object, it can be used with -# ImageCms.applyProfile() to convert images without the need to re-compute -# the lookup table for the transform. -# -# The reason pyCMS returns a class object rather than a handle directly -# to the transform is that it needs to keep track of the PIL input/output -# modes that the transform is meant for. These attributes are stored in -# the "inMode" and "outMode" attributes of the object (which can be -# manually overridden if you really want to, but I don't know of any -# time that would be of use, or would even work). -# -# @param inputProfile String, as a valid filename path to the ICC input profile -# you wish to use for this transform, or a profile object -# @param outputProfile String, as a valid filename path to the ICC output -# profile you wish to use for this transform, or a profile object -# @param inMode String, as a valid PIL mode that the appropriate profile also -# supports (i.e. "RGB", "RGBA", "CMYK", etc.) -# @param outMode String, as a valid PIL mode that the appropriate profile also -# supports (i.e. "RGB", "RGBA", "CMYK", etc.) -# @param renderingIntent Integer (0-3) specifying the rendering intent you -# wish to use for the transform -# -# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) -# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) -# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) -# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) -# -# see the pyCMS documentation for details on rendering intents and what they do. -# @param flags Integer (0-...) specifying additional flags -# @return A CmsTransform class object. -# @exception PyCMSError - def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0): + """ + (pyCMS) Builds an ICC transform mapping from the inputProfile to the + outputProfile. Use applyTransform to apply the transform to a given + image. + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If an error occurs during creation of the + transform, a PyCMSError will be raised. + + If inMode or outMode are not a mode supported by the outputProfile (or + by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile using the renderingIntent to determine what to do + with out-of-gamut colors. It will ONLY work for converting images that + are in inMode to images that are in outMode color format (PIL mode, + i.e. "RGB", "RGBA", "CMYK", etc.). + + Building the transform is a fair part of the overhead in + ImageCms.profileToProfile(), so if you're planning on converting multiple + images using the same input/output settings, this can save you time. + Once you have a transform object, it can be used with + ImageCms.applyProfile() to convert images without the need to re-compute + the lookup table for the transform. + + The reason pyCMS returns a class object rather than a handle directly + to the transform is that it needs to keep track of the PIL input/output + modes that the transform is meant for. These attributes are stored in + the "inMode" and "outMode" attributes of the object (which can be + manually overridden if you really want to, but I don't know of any + time that would be of use, or would even work). + + :param inputProfile: String, as a valid filename path to the ICC input profile + you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + profile you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") @@ -397,78 +404,79 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Builds an ICC transform mapping from the inputProfile to the -# outputProfile, but tries to simulate the result that would be -# obtained on the proofProfile device. -# -# If the input, output, or proof profiles specified are not valid -# filenames, a PyCMSError will be raised. -# -# If an error occurs during creation of the transform, a PyCMSError will -# be raised. -# -# If inMode or outMode are not a mode supported by the outputProfile -# (or by pyCMS), a PyCMSError will be raised. -# -# This function builds and returns an ICC transform from the inputProfile -# to the outputProfile, but tries to simulate the result that would be -# obtained on the proofProfile device using renderingIntent and -# proofRenderingIntent to determine what to do with out-of-gamut -# colors. This is known as "soft-proofing". It will ONLY work for -# converting images that are in inMode to images that are in outMode -# color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). -# -# Usage of the resulting transform object is exactly the same as with -# ImageCms.buildTransform(). -# -# Proof profiling is generally used when using an output device to get a -# good idea of what the final printed/displayed image would look like on -# the proofProfile device when it's quicker and easier to use the -# output device for judging color. Generally, this means that the -# output device is a monitor, or a dye-sub printer (etc.), and the simulated -# device is something more expensive, complicated, or time consuming -# (making it difficult to make a real print for color judgement purposes). -# -# Soft-proofing basically functions by adjusting the colors on the -# output device to match the colors of the device being simulated. However, -# when the simulated device has a much wider gamut than the output -# device, you may obtain marginal results. -# -# @param inputProfile String, as a valid filename path to the ICC input profile -# you wish to use for this transform, or a profile object -# @param outputProfile String, as a valid filename path to the ICC output -# (monitor, usually) profile you wish to use for this transform, or a -# profile object -# @param proofProfile String, as a valid filename path to the ICC proof profile -# you wish to use for this transform, or a profile object -# @param inMode String, as a valid PIL mode that the appropriate profile also -# supports (i.e. "RGB", "RGBA", "CMYK", etc.) -# @param outMode String, as a valid PIL mode that the appropriate profile also -# supports (i.e. "RGB", "RGBA", "CMYK", etc.) -# @param renderingIntent Integer (0-3) specifying the rendering intent you -# wish to use for the input->proof (simulated) transform -# -# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) -# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) -# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) -# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) -# -# see the pyCMS documentation for details on rendering intents and what they do. -# @param proofRenderingIntent Integer (0-3) specifying the rendering intent you -# wish to use for proof->output transform -# -# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) -# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) -# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) -# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) -# -# see the pyCMS documentation for details on rendering intents and what they do. -# @param flags Integer (0-...) specifying additional flags -# @return A CmsTransform class object. -# @exception PyCMSError - def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]): + """ + (pyCMS) Builds an ICC transform mapping from the inputProfile to the + outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device. + + If the input, output, or proof profiles specified are not valid + filenames, a PyCMSError will be raised. + + If an error occurs during creation of the transform, a PyCMSError will + be raised. + + If inMode or outMode are not a mode supported by the outputProfile + (or by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device using renderingIntent and + proofRenderingIntent to determine what to do with out-of-gamut + colors. This is known as "soft-proofing". It will ONLY work for + converting images that are in inMode to images that are in outMode + color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). + + Usage of the resulting transform object is exactly the same as with + ImageCms.buildTransform(). + + Proof profiling is generally used when using an output device to get a + good idea of what the final printed/displayed image would look like on + the proofProfile device when it's quicker and easier to use the + output device for judging color. Generally, this means that the + output device is a monitor, or a dye-sub printer (etc.), and the simulated + device is something more expensive, complicated, or time consuming + (making it difficult to make a real print for color judgement purposes). + + Soft-proofing basically functions by adjusting the colors on the + output device to match the colors of the device being simulated. However, + when the simulated device has a much wider gamut than the output + device, you may obtain marginal results. + + :param inputProfile: String, as a valid filename path to the ICC input profile + you wish to use for this transform, or a profile object + :param outputProfile: String, as a valid filename path to the ICC output + (monitor, usually) profile you wish to use for this transform, or a + profile object + :param proofProfile: String, as a valid filename path to the ICC proof profile + you wish to use for this transform, or a profile object + :param inMode: String, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param outMode: String, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + :param renderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for the input->proof (simulated) transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what they do. + :param proofRenderingIntent: Integer (0-3) specifying the rendering intent you + wish to use for proof->output transform + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what they do. + :param flags: Integer (0-...) specifying additional flags + :returns: A CmsTransform class object. + :exception PyCMSError: + """ + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") @@ -489,43 +497,44 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo buildTransformFromOpenProfiles = buildTransform buildProofTransformFromOpenProfiles = buildProofTransform -## -# (pyCMS) Applies a transform to a given image. -# -# If im.mode != transform.inMode, a PyCMSError is raised. -# -# If inPlace == TRUE and transform.inMode != transform.outMode, a -# PyCMSError is raised. -# -# If im.mode, transfer.inMode, or transfer.outMode is not supported by -# pyCMSdll or the profiles you used for the transform, a PyCMSError is -# raised. -# -# If an error occurs while the transform is being applied, a PyCMSError -# is raised. -# -# This function applies a pre-calculated transform (from -# ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an -# image. The transform can be used for multiple images, saving -# 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 -# the return value, set inPlace to TRUE. This can only be done if -# transform.inMode and transform.outMode are the same, because we can't -# change the mode in-place (the buffer sizes for some modes are -# different). The default behavior is to return a new Image object of -# the same dimensions in mode transform.outMode. -# -# @param im A PIL Image object, and im.mode must be the same as the inMode -# supported by the transform. -# @param transform A valid CmsTransform class object -# @param inPlace Bool (1 == True, 0 or None == False). If True, im is modified -# in place and None is returned, if False, a new Image object with the -# transform applied is returned (and im is not changed). The default is False. -# @return Either None, or a new PIL Image object, depending on the value of inPlace -# @exception PyCMSError - def applyTransform(im, transform, inPlace=0): + """ + (pyCMS) Applies a transform to a given image. + + If im.mode != transform.inMode, a PyCMSError is raised. + + If inPlace == TRUE and transform.inMode != transform.outMode, a + PyCMSError is raised. + + If im.mode, transfer.inMode, or transfer.outMode is not supported by + pyCMSdll or the profiles you used for the transform, a PyCMSError is + raised. + + If an error occurs while the transform is being applied, a PyCMSError + is raised. + + This function applies a pre-calculated transform (from + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an + image. The transform can be used for multiple images, saving + 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 + the return value, set inPlace to TRUE. This can only be done if + transform.inMode and transform.outMode are the same, because we can't + change the mode in-place (the buffer sizes for some modes are + different). The default behavior is to return a new Image object of + the same dimensions in mode transform.outMode. + + :param im: A PIL Image object, and im.mode must be the same as the inMode + supported by the transform. + :param transform: A valid CmsTransform class object + :param inPlace: Bool (1 == True, 0 or None == False). If True, im is modified + in place and None is returned, if False, a new Image object with the + transform applied is returned (and im is not changed). The default is False. + :returns: Either None, or a new PIL Image object, depending on the value of inPlace + :exception PyCMSError: + """ + try: if inPlace: transform.apply_in_place(im) @@ -537,31 +546,32 @@ def applyTransform(im, transform, inPlace=0): return imOut -## -# (pyCMS) Creates a profile. -# -# If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised -# -# If using LAB and colorTemp != a positive integer, a PyCMSError is raised. -# -# If an error occurs while creating the profile, a PyCMSError is raised. -# -# Use this function to create common profiles on-the-fly instead of -# having to supply a profile on disk and knowing the path to it. It -# returns a normal CmsProfile object that can be passed to -# ImageCms.buildTransformFromOpenProfiles() to create a transform to apply -# to images. -# -# @param colorSpace String, the color space of the profile you wish to create. -# Currently only "LAB", "XYZ", and "sRGB" are supported. -# @param colorTemp Positive integer for the white point for the profile, in -# degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 -# illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles, -# and is ignored for XYZ and sRGB. -# @return A CmsProfile class object -# @exception PyCMSError - def createProfile(colorSpace, colorTemp=-1): + """ + (pyCMS) Creates a profile. + + If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised + + If using LAB and colorTemp != a positive integer, a PyCMSError is raised. + + If an error occurs while creating the profile, a PyCMSError is raised. + + Use this function to create common profiles on-the-fly instead of + having to supply a profile on disk and knowing the path to it. It + returns a normal CmsProfile object that can be passed to + ImageCms.buildTransformFromOpenProfiles() to create a transform to apply + to images. + + :param colorSpace: String, the color space of the profile you wish to create. + Currently only "LAB", "XYZ", and "sRGB" are supported. + :param colorTemp: Positive integer for the white point for the profile, in + degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 + illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles, + and is ignored for XYZ and sRGB. + :returns: A CmsProfile class object + :exception PyCMSError: + """ + if colorSpace not in ["LAB", "XYZ", "sRGB"]: raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) @@ -576,25 +586,27 @@ def createProfile(colorSpace, colorTemp=-1): except (TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Gets the internal product name for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised If an error occurs while trying to obtain the -# name tag, a PyCMSError is raised. -# -# Use this function to obtain the INTERNAL name of the profile (stored -# in an ICC tag in the profile itself), usually the one used when the -# profile was originally created. Sometimes this tag also contains -# additional information supplied by the creator. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return A string containing the internal name of the profile as stored in an -# ICC tag. -# @exception PyCMSError - def getProfileName(profile): + """ + + (pyCMS) Gets the internal product name for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised If an error occurs while trying to obtain the + name tag, a PyCMSError is raised. + + Use this function to obtain the INTERNAL name of the profile (stored + in an ICC tag in the profile itself), usually the one used when the + profile was originally created. Sometimes this tag also contains + additional information supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: A string containing the internal name of the profile as stored in an + ICC tag. + :exception PyCMSError: + """ + try: # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): @@ -615,26 +627,27 @@ def getProfileName(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Gets the internal product information for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised. -# -# If an error occurs while trying to obtain the info tag, a PyCMSError -# is raised -# -# Use this function to obtain the information stored in the profile's -# info tag. This often contains details about the profile, and how it -# was created, as supplied by the creator. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return A string containing the internal profile information stored in an ICC -# tag. -# @exception PyCMSError - def getProfileInfo(profile): + """ + (pyCMS) Gets the internal product information for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the info tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + info tag. This often contains details about the profile, and how it + was created, as supplied by the creator. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: A string containing the internal profile information stored in an ICC + tag. + :exception PyCMSError: + """ + try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) @@ -653,25 +666,25 @@ def getProfileInfo(profile): raise PyCMSError(v) -## -# (pyCMS) Gets the copyright for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised. -# -# If an error occurs while trying to obtain the copyright tag, a PyCMSError -# is raised -# -# Use this function to obtain the information stored in the profile's -# copyright tag. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return A string containing the internal profile information stored in an ICC -# tag. -# @exception PyCMSError - def getProfileCopyright(profile): + """ + (pyCMS) Gets the copyright for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the copyright tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + copyright tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: A string containing the internal profile information stored in an ICC + tag. + :exception PyCMSError: + """ try: # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): @@ -680,25 +693,25 @@ def getProfileCopyright(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Gets the manufacturer for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised. -# -# If an error occurs while trying to obtain the manufacturer tag, a PyCMSError -# is raised -# -# Use this function to obtain the information stored in the profile's -# manufacturer tag. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return A string containing the internal profile information stored in an ICC -# tag. -# @exception PyCMSError - def getProfileManufacturer(profile): + """ + (pyCMS) Gets the manufacturer for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the manufacturer tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + manufacturer tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: A string containing the internal profile information stored in an ICC + tag. + :exception PyCMSError: + """ try: # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): @@ -707,25 +720,26 @@ def getProfileManufacturer(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Gets the model for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised. -# -# If an error occurs while trying to obtain the model tag, a PyCMSError -# is raised -# -# Use this function to obtain the information stored in the profile's -# model tag. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return A string containing the internal profile information stored in an ICC -# tag. -# @exception PyCMSError - def getProfileModel(profile): + """ + (pyCMS) Gets the model for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the model tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + model tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: A string containing the internal profile information stored in an ICC + tag. + :exception PyCMSError: + """ + try: # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): @@ -734,25 +748,26 @@ def getProfileModel(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Gets the description for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised. -# -# If an error occurs while trying to obtain the description tag, a PyCMSError -# is raised -# -# Use this function to obtain the information stored in the profile's -# description tag. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return A string containing the internal profile information stored in an ICC -# tag. -# @exception PyCMSError - def getProfileDescription(profile): + """ + (pyCMS) Gets the description for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the description tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + description tag. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: A string containing the internal profile information stored in an ICC + tag. + :exception PyCMSError: + """ + try: # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): @@ -762,35 +777,35 @@ def getProfileDescription(profile): raise PyCMSError(v) - -## -# (pyCMS) Gets the default intent name for the given profile. -# -# If profile isn't a valid CmsProfile object or filename to a profile, -# a PyCMSError is raised. -# -# If an error occurs while trying to obtain the default intent, a -# PyCMSError is raised. -# -# Use this function to determine the default (and usually best optomized) -# rendering intent for this profile. Most profiles support multiple -# rendering intents, but are intended mostly for one type of conversion. -# If you wish to use a different intent than returned, use -# ImageCms.isIntentSupported() to verify it will work first. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @return Integer 0-3 specifying the default rendering intent for this profile. -# -# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) -# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) -# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) -# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) -# -# see the pyCMS documentation for details on rendering intents and what they do. -# @exception PyCMSError - def getDefaultIntent(profile): + """ + (pyCMS) Gets the default intent name for the given profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the default intent, a + PyCMSError is raised. + + Use this function to determine the default (and usually best optomized) + rendering intent for this profile. Most profiles support multiple + rendering intents, but are intended mostly for one type of conversion. + If you wish to use a different intent than returned, use + ImageCms.isIntentSupported() to verify it will work first. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :returns: Integer 0-3 specifying the default rendering intent for this profile. + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what they do. + :exception PyCMSError: + """ + try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) @@ -798,42 +813,43 @@ def getDefaultIntent(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Checks if a given intent is supported. -# -# Use this function to verify that you can use your desired -# renderingIntent with profile, and that profile can be used for the -# input/output/proof profile as you desire. -# -# Some profiles are created specifically for one "direction", can cannot -# be used for others. Some profiles can only be used for certain -# rendering intents... so it's best to either verify this before trying -# to create a transform with them (using this function), or catch the -# potential PyCMSError that will occur if they don't support the modes -# you select. -# -# @param profile EITHER a valid CmsProfile object, OR a string of the filename -# of an ICC profile. -# @param intent Integer (0-3) specifying the rendering intent you wish to use -# with this profile -# -# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) -# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) -# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) -# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) -# -# see the pyCMS documentation for details on rendering intents and what they do. -# @param direction Integer specifing if the profile is to be used for input, -# output, or proof -# -# INPUT = 0 (or use ImageCms.DIRECTION_INPUT) -# OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) -# PROOF = 2 (or use ImageCms.DIRECTION_PROOF) -# -# @return 1 if the intent/direction are supported, -1 if they are not. -# @exception PyCMSError - def isIntentSupported(profile, intent, direction): + """ + (pyCMS) Checks if a given intent is supported. + + Use this function to verify that you can use your desired + renderingIntent with profile, and that profile can be used for the + input/output/proof profile as you desire. + + Some profiles are created specifically for one "direction", can cannot + be used for others. Some profiles can only be used for certain + rendering intents... so it's best to either verify this before trying + to create a transform with them (using this function), or catch the + potential PyCMSError that will occur if they don't support the modes + you select. + + :param profile: EITHER a valid CmsProfile object, OR a string of the filename + of an ICC profile. + :param intent: Integer (0-3) specifying the rendering intent you wish to use + with this profile + + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and what they do. + :param direction: Integer specifing if the profile is to be used for input, + output, or proof + + INPUT = 0 (or use ImageCms.DIRECTION_INPUT) + OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) + PROOF = 2 (or use ImageCms.DIRECTION_PROOF) + + :returns: 1 if the intent/direction are supported, -1 if they are not. + :exception PyCMSError: + """ + try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) @@ -846,10 +862,11 @@ def isIntentSupported(profile, intent, direction): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) -## -# (pyCMS) Fetches versions. - def versions(): + """ + (pyCMS) Fetches versions. + """ + import sys return ( VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION diff --git a/PIL/__init__.py b/PIL/__init__.py index b83220097..c35f6207e 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -12,7 +12,7 @@ # ;-) VERSION = '1.1.7' # PIL version -PILLOW_VERSION = '2.3.0' # Pillow +PILLOW_VERSION = '2.4.0' # Pillow _plugins = ['ArgImagePlugin', 'BmpImagePlugin', diff --git a/PIL/_util.py b/PIL/_util.py index 220ac6c52..761c258f1 100644 --- a/PIL/_util.py +++ b/PIL/_util.py @@ -14,3 +14,9 @@ else: # Checks if an object is a string, and that it points to a directory. def isDirectory(f): return isPath(f) and os.path.isdir(f) + +class import_err(object): + def __init__(self, ex): + self.ex = ex + def __getattr__(self, elt): + raise self.ex diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index fd3d39bc5..4d40e43b2 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -46,5 +46,67 @@ def test_16bit_workaround(): im = Image.open('Tests/images/16bit.cropped.tif') _test_float_conversion(im.convert('I')) +def test_rgba_p(): + im = lena('RGBA') + im.putalpha(lena('L')) + + converted = im.convert('P') + comparable = converted.convert('RGBA') + + assert_image_similar(im, comparable, 20) + +def test_trns_p(): + im = lena('P') + im.info['transparency']=0 + + f = tempfile('temp.png') + + l = im.convert('L') + assert_equal(l.info['transparency'], 0) # undone + assert_no_exception(lambda: l.save(f)) + + + rgb = im.convert('RGB') + assert_equal(rgb.info['transparency'], (0,0,0)) # undone + assert_no_exception(lambda: rgb.save(f)) + +def test_trns_l(): + im = lena('L') + im.info['transparency'] = 128 + + f = tempfile('temp.png') + + rgb = im.convert('RGB') + assert_equal(rgb.info['transparency'], (128,128,128)) # undone + assert_no_exception(lambda: rgb.save(f)) + + p = im.convert('P') + assert_true('transparency' in p.info) + assert_no_exception(lambda: p.save(f)) + + p = assert_warning(UserWarning, + lambda: im.convert('P', palette = Image.ADAPTIVE)) + assert_false('transparency' in p.info) + assert_no_exception(lambda: p.save(f)) +def test_trns_RGB(): + im = lena('RGB') + im.info['transparency'] = im.getpixel((0,0)) + + f = tempfile('temp.png') + + l = im.convert('L') + assert_equal(l.info['transparency'], l.getpixel((0,0))) # undone + assert_no_exception(lambda: l.save(f)) + + p = im.convert('P') + assert_true('transparency' in p.info) + assert_no_exception(lambda: p.save(f)) + + p = assert_warning(UserWarning, + lambda: im.convert('P', palette = Image.ADAPTIVE)) + assert_false('transparency' in p.info) + assert_no_exception(lambda: p.save(f)) + + diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 70b5eb503..dbf68a25e 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -19,4 +19,9 @@ def test_octree_quantize(): im = im.quantize(100, Image.FASTOCTREE) assert_image(im, "P", im.size) - assert len(im.getcolors()) == 100 \ No newline at end of file + assert len(im.getcolors()) == 100 + +def test_rgba_quantize(): + im = lena('RGBA') + assert_no_exception(lambda: im.quantize()) + assert_exception(Exception, lambda: im.quantize(method=0)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index d18132598..dcb445c9f 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -3,6 +3,7 @@ from tester import * from PIL import Image try: from PIL import ImageCms + ImageCms.core.profile_open except ImportError: skip() diff --git a/Tests/tester.py b/Tests/tester.py index d4309e3e6..f7e2c26c6 100644 --- a/Tests/tester.py +++ b/Tests/tester.py @@ -121,9 +121,9 @@ def assert_no_exception(func): def assert_warning(warn_class, func): # note: this assert calls func three times! import warnings - def warn_error(message, category, **options): + def warn_error(message, category=UserWarning, **options): raise category(message) - def warn_ignore(message, category, **options): + def warn_ignore(message, category=UserWarning, **options): pass warn = warnings.warn result = None diff --git a/_imaging.c b/_imaging.c index 138ce0101..c47868b81 100644 --- a/_imaging.c +++ b/_imaging.c @@ -71,7 +71,7 @@ * See the README file for information on usage and redistribution. */ -#define PILLOW_VERSION "2.3.0" +#define PILLOW_VERSION "2.4.0" #include "Python.h" diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index cdb9e2ca4..fddc134e6 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -156,6 +156,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: JPEG 2000 ^^^^^^^^^ +.. versionadded:: 2.4.0 + PIL reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or ``RGBA`` data. It can also read files containing ``YCbCr`` data, which it converts on read into ``RGB`` or ``RGBA`` depending on whether or not there is diff --git a/docs/index.rst b/docs/index.rst index 36f600c85..a59cda115 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,12 +18,9 @@ Python Imaging Library by Fredrik Lundh and Contributors. To start using Pillow, please read the :doc:`installation instructions `. -If you can't find the information you need, try the old `PIL Handbook`_, but be -aware that it was last updated for PIL 1.1.5. You can download archives and old -versions from `PyPI `_. You can get the -source and contribute at https://github.com/python-imaging/Pillow. - -.. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm +You can get the source and contribute at +https://github.com/python-imaging/Pillow. You can download archives +and old versions from `PyPI `_. .. toctree:: :maxdepth: 2 diff --git a/docs/installation.rst b/docs/installation.rst index 1cab14c14..715a6ac30 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -66,10 +66,15 @@ Many of Pillow's features require external libraries: * **libwebp** provides the Webp format. * Pillow has been tested with version **0.1.3**, which does not read - transparent webp files. Version **0.3.0** supports transparency. + transparent webp files. Versions **0.3.0** and **0.4.0** support + transparency. * **tcl/tk** provides support for tkinter bitmap and photo images. +* **openjpeg** provides JPEG 2000 functionality. + + * Pillow has been tested with openjpeg **2.0.0**. + If the prerequisites are installed in the standard library locations for your machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration should be required. If they are installed in a non-standard location, you may @@ -225,6 +230,6 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 8.1 Pro |Yes | 2.6,2.7,3.2,3.3,3.4 | 2.3.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 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ diff --git a/docs/requirements.txt b/docs/requirements.txt index d825a5fcc..7611ea463 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ # requirements for working on docs # install pillow from master if you're into that, but RtD needs this -pillow>=2.2.1 +pillow>=2.4.0 Jinja2==2.7.1 MarkupSafe==0.18 diff --git a/setup.py b/setup.py index efe684e05..93918debd 100644 --- a/setup.py +++ b/setup.py @@ -87,7 +87,7 @@ except (ImportError, OSError): NAME = 'Pillow' -VERSION = '2.3.0' +VERSION = '2.4.0' TCL_ROOT = None JPEG_ROOT = None JPEG2K_ROOT = None @@ -226,45 +226,50 @@ class pil_build_ext(build_ext): _add_directory(include_dirs, "/usr/X11/include") elif sys.platform.startswith("linux"): - for platform_ in (plat.architecture()[0], plat.processor()): - - if not platform_: - continue - - if platform_ in ["x86_64", "64bit"]: - _add_directory(library_dirs, "/lib64") - _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/x86_64-linux-gnu") - break - elif platform_ in ["i386", "i686", "32bit"]: - _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") - break - elif platform_ in ["aarch64"]: - _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/aarch64-linux-gnu") - break - elif platform_ in ["arm", "armv7l"]: - _add_directory(library_dirs, "/usr/lib/arm-linux-gnueabi") - break - elif platform_ in ["ppc64"]: - _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/ppc64-linux-gnu") - _add_directory(library_dirs, "/usr/lib/powerpc64-linux-gnu") - break - elif platform_ in ["ppc"]: - _add_directory(library_dirs, "/usr/lib/ppc-linux-gnu") - _add_directory(library_dirs, "/usr/lib/powerpc-linux-gnu") - break - elif platform_ in ["s390x"]: - _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/s390x-linux-gnu") - break - elif platform_ in ["s390"]: - _add_directory(library_dirs, "/usr/lib/s390-linux-gnu") - break + arch_tp = (plat.processor(), plat.architecture()[0]) + if arch_tp == ("x86_64","32bit"): + # 32 bit build on 64 bit machine. + _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") else: - raise ValueError( - "Unable to identify Linux platform: `%s`" % platform_) + for platform_ in arch_tp: + + if not platform_: + continue + + if platform_ in ["x86_64", "64bit"]: + _add_directory(library_dirs, "/lib64") + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/x86_64-linux-gnu") + break + elif platform_ in ["i386", "i686", "32bit"]: + _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") + break + elif platform_ in ["aarch64"]: + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/aarch64-linux-gnu") + break + elif platform_ in ["arm", "armv7l"]: + _add_directory(library_dirs, "/usr/lib/arm-linux-gnueabi") + break + elif platform_ in ["ppc64"]: + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/ppc64-linux-gnu") + _add_directory(library_dirs, "/usr/lib/powerpc64-linux-gnu") + break + elif platform_ in ["ppc"]: + _add_directory(library_dirs, "/usr/lib/ppc-linux-gnu") + _add_directory(library_dirs, "/usr/lib/powerpc-linux-gnu") + break + elif platform_ in ["s390x"]: + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/s390x-linux-gnu") + break + elif platform_ in ["s390"]: + _add_directory(library_dirs, "/usr/lib/s390-linux-gnu") + break + else: + raise ValueError( + "Unable to identify Linux platform: `%s`" % platform_) # XXX Kludge. Above /\ we brute force support multiarch. Here we # try Barry's more general approach. Afterward, something should