diff --git a/.travis.yml b/.travis.yml index 58f493b1b..88118b1b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ python: - 3.2 - 3.3 -install: "sudo apt-get -qq install libfreetype6-dev liblcms1-dev libwebp-dev" +install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev" script: - python setup.py clean diff --git a/CHANGES.rst b/CHANGES.rst index 1f10204e4..56d763432 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,11 +4,35 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ -- Port PIL Handbook tutorial and appendices [irksep] +- LCMS support upgraded from version 1 to version 2, fixes #343 + [wiredfool] -- Alpha Premultiplication support for transform and resize [wiredfool] +- Added more raw decoder 16 bit pixel formats + [svanheulen] -- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 [wiredfool] +- Document remaining Image* modules listed in PIL handbook + [irksep] + +- Document ImageEnhance, ImageFile, ImageFilter, ImageFont, ImageGrab, ImageMath, and ImageOps + [irksep] + +- Port and update docs for Image, ImageChops, ImageColor, and ImageDraw + [irksep] + +- Move or copy content from README.rst to docs/ + [irksep] + +- Respect CFLAGS/LDFLAGS when searching for headers/libs + [iElectric] + +- Port PIL Handbook tutorial and appendices + [irksep] + +- Alpha Premultiplication support for transform and resize + [wiredfool] + +- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 + [wiredfool] 2.2.1 (2013-10-02) ------------------ diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 778be7151..56efba356 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -27,6 +27,7 @@ Contributors (Pillow) - Sandro Mani - Simon Law - Stéphane Klein +- Steve Johnson - Takeshi KOMIYA - Tom Gross - Tom Payne diff --git a/PIL/Image.py b/PIL/Image.py index 58b85d5d2..e1d88fe59 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -57,7 +57,7 @@ try: except ImportError as v: core = _imaging_not_installed() - # Explanations for ways that we know we might have an import error + # Explanations for ways that we know we might have an import error if str(v).startswith("Module use of python"): # The _imaging C module is present, but not compiled for # the right version (windows only). Print a warning, if @@ -81,8 +81,8 @@ except ImportError as v: "recompile PIL or build Python --with-wide-unicode. ", RuntimeWarning ) - # Fail here anyway. Don't let people run with a mostly broken Pillow. - raise + # Fail here anyway. Don't let people run with a mostly broken Pillow. + raise try: import builtins @@ -200,6 +200,7 @@ _MODEINFO = { "RGBA": ("RGB", "L", ("R", "G", "B", "A")), "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + "LAB": ("RGB", "L", ("L", "A", "B")), # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and # BGR;24. Use these modes only if you know exactly what you're @@ -224,16 +225,18 @@ _MODE_CONV = { "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), - "I;16": ('=u2', None), + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + # I;16 == I;16L, and I;32 == I;32L + "I;16": ('u2', None), "I;16L": ('i2', None), "I;16LS": ('u4', None), "I;32L": ('i4', None), "I;32LS": ('ImageChops module contains a number of arithmetical image -# operations, called channel operations ("chops"). These can be -# used for various purposes, including special effects, image -# compositions, algorithmic painting, and more. -#

-# At this time, channel operations are only implemented for 8-bit -# images (e.g. "L" and "RGB"). -#

-# Most channel operations take one or two image arguments and returns -# a new image. Unless otherwise noted, the result of a channel -# operation is always clipped to the range 0 to MAX (which is 255 for -# all modes supported by the operations in this module). -## - -## -# Return an image with the same size as the given image, but filled -# with the given pixel value. -# -# @param image Reference image. -# @param value Pixel value. -# @return An image object. def constant(image, value): - "Fill a channel with a given grey level" + """Fill a channel with a given grey level. + + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.new("L", image.size, value) -## -# Copy image. -# -# @param image Source image. -# @return A copy of the source image. def duplicate(image): - "Create a copy of a channel" + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ return image.copy() -## -# Inverts an image -# (MAX - image). -# -# @param image Source image. -# @return An image object. def invert(image): - "Invert a channel" + """ + Invert an image (channel). + + .. code-block:: python + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ image.load() return image._new(image.im.chop_invert()) -## -# Compare images, and return lighter pixel value -# (max(image1, image2)). -#

-# Compares the two images, pixel by pixel, and returns a new image -# containing the lighter values. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def lighter(image1, image2): - "Select the lighter pixels from each image" + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. + + .. code-block:: python + + out = max(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_lighter(image2.im)) -## -# Compare images, and return darker pixel value -# (min(image1, image2)). -#

-# Compares the two images, pixel by pixel, and returns a new image -# containing the darker values. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def darker(image1, image2): - "Select the darker pixels from each image" + """ + Compares the two images, pixel by pixel, and returns a new image + containing the darker values. + + .. code-block:: python + + out = min(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_darker(image2.im)) -## -# Calculate absolute difference -# (abs(image1 - image2)). -#

-# Returns the absolute value of the difference between the two images. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def difference(image1, image2): - "Subtract one image from another" + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. + + .. code-block:: python + + out = abs(image1 - image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_difference(image2.im)) -## -# Superimpose positive images -# (image1 * image2 / MAX). -#

-# Superimposes two images on top of each other. If you multiply an -# image with a solid black image, the result is black. If you multiply -# with a solid white image, the image is unaffected. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def multiply(image1, image2): - "Superimpose two positive images" + """ + Superimposes two images on top of each other. + + If you multiply an image with a solid black image, the result is black. If + you multiply with a solid white image, the image is unaffected. + + .. code-block:: python + + out = image1 * image2 / MAX + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_multiply(image2.im)) -## -# Superimpose negative images -# (MAX - ((MAX - image1) * (MAX - image2) / MAX)). -#

-# Superimposes two inverted images on top of each other. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def screen(image1, image2): - "Superimpose two negative images" + """ + Superimposes two inverted images on top of each other. + + .. code-block:: python + + out = MAX - ((MAX - image1) * (MAX - image2) / MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_screen(image2.im)) -## -# Add images -# ((image1 + image2) / scale + offset). -#

-# Adds two images, dividing the result by scale and adding the -# offset. If omitted, scale defaults to 1.0, and offset to 0.0. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def add(image1, image2, scale=1.0, offset=0): - "Add two images" + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 + image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_add(image2.im, scale, offset)) -## -# Subtract images -# ((image1 - image2) / scale + offset). -#

-# Subtracts two images, dividing the result by scale and adding the -# offset. If omitted, scale defaults to 1.0, and offset to 0.0. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def subtract(image1, image2, scale=1.0, offset=0): - "Subtract two images" + """ + Subtracts two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 - image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) -## -# Add images without clipping -# ((image1 + image2) % MAX). -#

-# Adds two images, without clipping the result. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def add_modulo(image1, image2): - "Add two images without clipping" + """Add two images, without clipping the result. + + .. code-block:: python + + out = ((image1 + image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_add_modulo(image2.im)) -## -# Subtract images without clipping -# ((image1 - image2) % MAX). -#

-# Subtracts two images, without clipping the result. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def subtract_modulo(image1, image2): - "Subtract two images without clipping" + """Subtract two images, without clipping the result. + + .. code-block:: python + + out = ((image1 - image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_subtract_modulo(image2.im)) -## -# Logical AND -# (image1 and image2). def logical_and(image1, image2): - "Logical and between two images" + """Logical AND between two images. + + .. code-block:: python + + out = ((image1 and image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_and(image2.im)) -## -# Logical OR -# (image1 or image2). def logical_or(image1, image2): - "Logical or between two images" + """Logical OR between two images. + + .. code-block:: python + + out = ((image1 or image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_or(image2.im)) -## -# Logical XOR -# (image1 xor image2). def logical_xor(image1, image2): - "Logical xor between two images" + """Logical XOR between two images. + + .. code-block:: python + + out = ((bool(image1) != bool(image2)) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() return image1._new(image1.im.chop_xor(image2.im)) -## -# Blend images using constant transparency weight. -#

-# Same as the blend function in the Image module. def blend(image1, image2, alpha): - "Blend two images using a constant transparency weight" + """Blend images using constant transparency weight. Alias for + :py:meth:`PIL.Image.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.blend(image1, image2, alpha) -## -# Create composite using transparency mask. -#

-# Same as the composite function in the Image module. def composite(image1, image2, mask): - "Create composite image by blending images using a transparency mask" + """Create composite using transparency mask. Alias for + :py:meth:`PIL.Image.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.composite(image1, image2, mask) -## -# Offset image data. -#

-# Returns a copy of the image where data has been offset by the given -# distances. Data wraps around the edges. If yoffset is omitted, it -# is assumed to be equal to xoffset. -# -# @param image Source image. -# @param xoffset The horizontal distance. -# @param yoffset The vertical distance. If omitted, both -# distances are set to the same value. -# @return An Image object. def offset(image, xoffset, yoffset=None): - "Offset image in horizontal and/or vertical direction" + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + if yoffset is None: yoffset = xoffset image.load() diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index e3cb11f36..20ba6a11f 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -42,6 +42,8 @@ pyCMS Version History: + 1.0.0 pil Oct 2013 Port to LCMS 2. + 0.1.0 pil mod March 10, 2009 Renamed display profile to proof profile. The proof @@ -77,7 +79,7 @@ pyCMS """ -VERSION = "0.1.0 pil" +VERSION = "1.0.0 pil" # --------------------------------------------------------------------. @@ -151,8 +153,8 @@ class ImageCmsProfile: self.profile = profile self.filename = filename if profile: - self.product_name = profile.product_name - self.product_info = profile.product_info + self.product_name = None #profile.product_name + self.product_info = None #profile.product_info else: self.product_name = None self.product_info = None @@ -564,10 +566,10 @@ def createProfile(colorSpace, colorTemp=-1): raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) if colorSpace == "LAB": - if isinstance(colorTemp, float): - colorTemp = int(colorTemp + 0.5) - if not isinstance(colorTemp, int): - raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp) + try: + colorTemp = float(colorTemp) + except: + raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp) try: return core.createProfile(colorSpace, colorTemp) @@ -597,7 +599,19 @@ def getProfileName(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - return profile.profile.product_name + "\n" + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.product_model + manufacturer = profile.profile.product_manufacturer + + if not (model or manufacturer): + return profile.profile.product_description+"\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return "%s - %s\n" % (model, manufacturer) + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -625,10 +639,130 @@ def getProfileInfo(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) # add an extra newline to preserve pyCMS compatibility - return profile.product_info + "\n" + # Python, not C. the white point bits weren't working well, so skipping. + # // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.product_description + cpright = profile.profile.product_copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr)+"\r\n\r\n" + except (AttributeError, IOError, TypeError, ValueError) as v: 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): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_copyright + "\n" + 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): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_manufacturer + "\n" + 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): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_model + "\n" + 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): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_description + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + + ## # (pyCMS) Gets the default intent name for the given profile. # diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index 86f221adc..c14257151 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -30,6 +30,15 @@ import re # as an RGB value. def getrgb(color): + """ + Convert a color string to an RGB tuple. If the string cannot be parsed, + this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue)`` + """ try: rgb = colormap[color] except KeyError: @@ -97,6 +106,16 @@ def getrgb(color): raise ValueError("unknown color specifier: %r" % color) def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue)`` + """ # same as getrgb, but converts the result to the given mode color = getrgb(color) if mode == "RGB": diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 34d69d569..10433343e 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -20,71 +20,68 @@ from PIL import Image, ImageFilter, ImageStat + class _Enhance: - ## - # Returns an enhanced image. The enhancement factor is a floating - # point value controlling the enhancement. Factor 1.0 always - # returns a copy of the original image, lower factors mean less - # colour (brightness, contrast, etc), and higher values more. - # There are no restrictions on this value. - # - # @param factor Enhancement factor. - # @return An enhanced image. - def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.blend(self.degenerate, self.image, factor) -## -# Color enhancement object. -#

-# This class can be used to adjust the colour balance of an image, in -# a manner similar to the controls on a colour TV set. An enhancement -# factor of 0.0 gives a black and white image, a factor of 1.0 gives -# the original image. class Color(_Enhance): - "Adjust image colour balance" + """Adjust image color balance. + + This class can be used to adjust the colour balance of an image, in + a manner similar to the controls on a colour TV set. An enhancement + factor of 0.0 gives a black and white image. A factor of 1.0 gives + the original image. + """ def __init__(self, image): self.image = image self.degenerate = image.convert("L").convert(image.mode) -## -# Contrast enhancement object. -#

-# This class can be used to control the contrast of an image, similar -# to the contrast control on a TV set. An enhancement factor of 0.0 -# gives a solid grey image, factor 1.0 gives the original image. class Contrast(_Enhance): - "Adjust image contrast" + """Adjust image contrast. + + This class can be used to control the contrast of an image, similar + to the contrast control on a TV set. An enhancement factor of 0.0 + gives a solid grey image. A factor of 1.0 gives the original image. + """ def __init__(self, image): self.image = image mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) self.degenerate = Image.new("L", image.size, mean).convert(image.mode) -## -# Brightness enhancement object. -#

-# This class can be used to control the brighntess of an image. An -# enhancement factor of 0.0 gives a black image, factor 1.0 gives the -# original image. class Brightness(_Enhance): - "Adjust image brightness" + """Adjust image brightness. + + This class can be used to control the brighntess of an image. An + enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the + original image. + """ def __init__(self, image): self.image = image self.degenerate = Image.new(image.mode, image.size, 0) -## -# Sharpness enhancement object. -#

-# This class can be used to adjust the sharpness of an image. The -# enhancement factor 0.0 gives a blurred image, 1.0 gives the original -# image, and a factor of 2.0 gives a sharpened image. class Sharpness(_Enhance): - "Adjust image sharpness" + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ def __init__(self, image): self.image = image self.degenerate = image.filter(ImageFilter.SMOOTH) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index c86f0a177..70762b4b0 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -137,7 +137,7 @@ class ImageFile(Image.Image): readonly = 0 if self.filename and len(self.tile) == 1 and not hasattr(sys, 'pypy_version_info'): - # As of pypy 2.1.0, memory mapping was failing here. + # As of pypy 2.1.0, memory mapping was failing here. # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: @@ -299,6 +299,8 @@ class Parser: """ Incremental image parser. This class implements the standard feed/close consumer interface. + + In Python 2.x, this is an old-style class. """ incremental = None image = None @@ -318,7 +320,7 @@ class Parser: """ (Consumer) Feed data to the parser. - :param data" A string buffer. + :param data: A string buffer. :exception IOError: If the parser failed to parse the image file. """ # collect data @@ -404,7 +406,9 @@ class Parser: (Consumer) Close the stream. :returns: An image object. - :exception IOError: If the parser failed to parse the image file. + :exception IOError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. """ # finish decoding if self.decoder: diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index c13f75bbc..ac8fe9f19 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -17,31 +17,28 @@ from functools import reduce + class Filter(object): pass -## -# Convolution filter kernel. class Kernel(Filter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. - ## - # Create a convolution kernel. The current version only - # supports 3x3 and 5x5 integer and floating point kernels. - #

- # In the current version, kernels can only be applied to - # "L" and "RGB" images. - # - # @def __init__(size, kernel, **options) - # @param size Kernel size, given as (width, height). In - # the current version, this must be (3,3) or (5,5). - # @param kernel A sequence containing kernel weights. - # @param **options Optional keyword arguments. - # @keyparam scale Scale factor. If given, the result for each - # pixel is divided by this value. The default is the sum - # of the kernel weights. - # @keyparam offset Offset. If given, this value is added to the - # result, after it has been divided by the scale factor. + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. the default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ def __init__(self, size, kernel, scale=None, offset=0): if scale is None: @@ -56,24 +53,23 @@ class Kernel(Filter): raise ValueError("cannot filter palette images") return image.filter(*self.filterargs) + class BuiltinFilter(Kernel): def __init__(self): pass -## -# Rank filter. class RankFilter(Filter): - name = "Rank" + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the **rank**'th value. - ## - # Create a rank filter. The rank filter sorts all pixels in - # a window of the given size, and returns the rank'th value. - # - # @param size The kernel size, in pixels. - # @param rank What pixel value to pick. Use 0 for a min filter, - # size*size/2 for a median filter, size*size-1 for a max filter, - # etc. + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + name = "Rank" def __init__(self, size, rank): self.size = size @@ -85,99 +81,99 @@ class RankFilter(Filter): image = image.expand(self.size//2, self.size//2) return image.rankfilter(self.size, self.rank) -## -# Median filter. Picks the median pixel value in a window with the -# given size. class MedianFilter(RankFilter): - name = "Median" + """ + Create a median filter. Picks the median pixel value in a window with the + given size. - ## - # Create a median filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Median" def __init__(self, size=3): self.size = size self.rank = size*size//2 -## -# Min filter. Picks the lowest pixel value in a window with the given -# size. class MinFilter(RankFilter): - name = "Min" + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. - ## - # Create a min filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Min" def __init__(self, size=3): self.size = size self.rank = 0 -## -# Max filter. Picks the largest pixel value in a window with the -# given size. class MaxFilter(RankFilter): - name = "Max" + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. - ## - # Create a max filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Max" def __init__(self, size=3): self.size = size self.rank = size*size-1 -## -# Mode filter. Picks the most frequent pixel value in a box with the -# given size. Pixel values that occur only once or twice are ignored; -# if no pixel value occurs more than twice, the original pixel value -# is preserved. class ModeFilter(Filter): - name = "Mode" + """ - ## - # Create a mode filter. - # - # @param size The kernel size, in pixels. + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + name = "Mode" def __init__(self, size=3): self.size = size + def filter(self, image): return image.modefilter(self.size) -## -# Gaussian blur filter. class GaussianBlur(Filter): + """Gaussian blur filter. + + :param radius: Blur radius. + """ name = "GaussianBlur" def __init__(self, radius=2): self.radius = radius + def filter(self, image): return image.gaussian_blur(self.radius) -## -# Unsharp mask filter. class UnsharpMask(Filter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + """ name = "UnsharpMask" def __init__(self, radius=2, percent=150, threshold=3): self.radius = radius self.percent = percent self.threshold = threshold + def filter(self, image): return image.unsharp_mask(self.radius, self.percent, self.threshold) -## -# Simple blur filter. class BLUR(BuiltinFilter): name = "Blur" @@ -189,8 +185,6 @@ class BLUR(BuiltinFilter): 1, 1, 1, 1, 1 ) -## -# Simple contour filter. class CONTOUR(BuiltinFilter): name = "Contour" @@ -200,8 +194,6 @@ class CONTOUR(BuiltinFilter): -1, -1, -1 ) -## -# Simple detail filter. class DETAIL(BuiltinFilter): name = "Detail" @@ -211,8 +203,6 @@ class DETAIL(BuiltinFilter): 0, -1, 0 ) -## -# Simple edge enhancement filter. class EDGE_ENHANCE(BuiltinFilter): name = "Edge-enhance" @@ -222,8 +212,6 @@ class EDGE_ENHANCE(BuiltinFilter): -1, -1, -1 ) -## -# Simple stronger edge enhancement filter. class EDGE_ENHANCE_MORE(BuiltinFilter): name = "Edge-enhance More" @@ -233,8 +221,6 @@ class EDGE_ENHANCE_MORE(BuiltinFilter): -1, -1, -1 ) -## -# Simple embossing filter. class EMBOSS(BuiltinFilter): name = "Emboss" @@ -244,8 +230,6 @@ class EMBOSS(BuiltinFilter): 0, 0, 0 ) -## -# Simple edge-finding filter. class FIND_EDGES(BuiltinFilter): name = "Find Edges" @@ -255,8 +239,6 @@ class FIND_EDGES(BuiltinFilter): -1, -1, -1 ) -## -# Simple smoothing filter. class SMOOTH(BuiltinFilter): name = "Smooth" @@ -266,8 +248,6 @@ class SMOOTH(BuiltinFilter): 1, 1, 1 ) -## -# Simple stronger smoothing filter. class SMOOTH_MORE(BuiltinFilter): name = "Smooth More" @@ -279,8 +259,6 @@ class SMOOTH_MORE(BuiltinFilter): 1, 1, 1, 1, 1 ) -## -# Simple sharpening filter. class SHARPEN(BuiltinFilter): name = "Sharpen" diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 826b37d5c..9366937dd 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -61,21 +61,6 @@ except ImportError: # position according to dx, dy. # -------------------------------------------------------------------- -## -# The ImageFont module defines a class with the same name. -# Instances of this class store bitmap fonts, and are used with the -# text method of the ImageDraw class. -#

-# PIL uses it's own font file format to store bitmap fonts. You can -# use the pilfont utility to convert BDF and PCF font -# descriptors (X window font formats) to this format. -#

-# Starting with version 1.1.4, PIL can be configured to support -# TrueType and OpenType fonts. For earlier version, TrueType -# support is only available as part of the imToolkit package -# -# @see ImageDraw#ImageDraw.text -# @see pilfont class ImageFont: "PIL font wrapper" @@ -186,7 +171,7 @@ class TransposedFont: self.orientation = orientation # any 'transpose' argument, or None def getsize(self, text): - w, h = self.font.getsize(text)[0] + w, h = self.font.getsize(text) if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): return h, w return w, h @@ -197,41 +182,42 @@ class TransposedFont: return im.transpose(self.orientation) return im -## -# Load font file. This function loads a font object from the given -# bitmap font file, and returns the corresponding font object. -# -# @param filename Name of font file. -# @return A font object. -# @exception IOError If the file could not be read. def load(filename): - "Load a font file." + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ f = ImageFont() f._load_pilfont(filename) return f -## -# Load a TrueType or OpenType font file, and create a font object. -# This function loads a font object from the given file, and creates -# a font object for a font of the given size. -#

-# This function requires the _imagingft service. -# -# @param filename A truetype font file. Under Windows, if the file -# is not found in this filename, the loader also looks in Windows -# fonts directory -# @param size The requested size, in points. -# @param index Which font face to load (default is first available face). -# @param encoding Which font encoding to use (default is Unicode). Common -# encodings are "unic" (Unicode), "symb" (Microsoft Symbol), "ADOB" -# (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). -# See the FreeType documentation for more information. -# @return A font object. -# @exception IOError If the file could not be read. def truetype(font=None, size=10, index=0, encoding="", filename=None): - "Load a truetype font file." + """ + Load a TrueType or OpenType font file, and create a font object. + This function loads a font object from the given file, and creates + a font object for a font of the given size. + + This function requires the _imagingft service. + + :param filename: A truetype font file. Under Windows, if the file + is not found in this filename, the loader also looks in + Windows :file:`fonts/` directory. + :param size: The requested size, in points. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Common + encodings are "unic" (Unicode), "symb" (Microsoft + Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), + and "armn" (Apple Roman). See the FreeType documentation + for more information. + :return: A font object. + :exception IOError: If the file could not be read. + """ if filename: if warnings: @@ -251,17 +237,16 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): return FreeTypeFont(filename, size, index, encoding) raise -## -# Load font file. Same as load, but searches for a bitmap font along -# the Python path. -# -# @param filename Name of font file. -# @return A font object. -# @exception IOError If the file could not be read. -# @see #load def load_path(filename): - "Load a font file, searching along the Python path." + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ for dir in sys.path: if isDirectory(dir): if not isinstance(filename, str): @@ -275,13 +260,14 @@ def load_path(filename): pass raise IOError("cannot find font file") -## -# Load a (probably rather ugly) default font. -# -# @return A font object. def load_default(): - "Load a default font." + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ from io import BytesIO import base64 f = ImageFont() @@ -406,6 +392,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg== ''')))) return f + if __name__ == "__main__": # create font data chunk for embedding import base64, os, sys diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index 59cc04706..9bb190934 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -17,14 +17,6 @@ from PIL import Image -## -# (New in 1.1.3) The ImageGrab module can be used to copy -# the contents of the screen to a PIL image memory. -#

-# The current version works on Windows only.

-# -# @since 1.1.3 -## try: # built-in driver (1.1.3 and later) @@ -34,14 +26,6 @@ except AttributeError: import _grabscreen grabber = _grabscreen.grab -## -# (New in 1.1.3) Take a snapshot of the screen. The pixels inside the -# bounding box are returned as an "RGB" image. If the bounding box is -# omitted, the entire screen is copied. -# -# @param bbox What region to copy. Default is the entire screen. -# @return An image -# @since 1.1.3 def grab(bbox=None): size, data = grabber() @@ -54,13 +38,6 @@ def grab(bbox=None): im = im.crop(bbox) return im -## -# (New in 1.1.4) Take a snapshot of the clipboard image, if any. -# -# @return An image, a list of filenames, or None if the clipboard does -# not contain image data or filenames. Note that if a list is -# returned, the filenames may not represent image files. -# @since 1.1.4 def grabclipboard(): debug = 0 # temporary interface diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 395323fd3..b2355ed1d 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -199,17 +199,19 @@ for k, v in list(globals().items()): if k[:10] == "imagemath_": ops[k[10:]] = v -## -# Evaluates an image expression. -# -# @param expression A string containing a Python-style expression. -# @keyparam options Values to add to the evaluation context. You -# can either use a dictionary, or one or more keyword arguments. -# @return The evaluated expression. This is usually an image object, -# but can also be an integer, a floating point value, or a pixel -# tuple, depending on the expression. def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ # build execution namespace args = ops.copy() diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 943c6cafb..0d22f8c64 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -22,14 +22,6 @@ from PIL._util import isStringType import operator from functools import reduce -## -# (New in 1.1.3) The ImageOps module contains a number of -# 'ready-made' image processing operations. This module is somewhat -# experimental, and most operators only work on L and RGB images. -# -# @since 1.1.3 -## - # # helpers @@ -63,20 +55,20 @@ def _lut(image, lut): # # actions -## -# Maximize (normalize) image contrast. This function calculates a -# histogram of the input image, removes cutoff percent of the -# lightest and darkest pixels from the histogram, and remaps the image -# so that the darkest pixel becomes black (0), and the lightest -# becomes white (255). -# -# @param image The image to process. -# @param cutoff How many percent to cut off from the histogram. -# @param ignore The background pixel value (use None for no background). -# @return An image. def autocontrast(image, cutoff=0, ignore=None): - "Maximize image contrast, based on histogram" + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image, removes **cutoff** percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: How many percent to cut off from the histogram. + :param ignore: The background pixel value (use None for no background). + :return: An image. + """ histogram = image.histogram() lut = [] for layer in range(0, len(histogram), 256): @@ -139,19 +131,19 @@ def autocontrast(image, cutoff=0, ignore=None): lut.append(ix) return _lut(image, lut) -## -# Colorize grayscale image. The black and white -# arguments should be RGB tuples; this function calculates a colour -# wedge mapping all black pixels in the source image to the first -# colour, and all white pixels to the second colour. -# -# @param image The image to colourize. -# @param black The colour to use for black input pixels. -# @param white The colour to use for white input pixels. -# @return An image. def colorize(image, black, white): - "Colorize a grayscale image" + """ + Colorize grayscale image. The **black** and **white** + arguments should be RGB tuples; this function calculates a color + wedge mapping all black pixels in the source image to the first + color, and all white pixels to the second color. + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :return: An image. + """ assert image.mode == "L" black = _color(black, "RGB") white = _color(white, "RGB") @@ -163,49 +155,50 @@ def colorize(image, black, white): image = image.convert("RGB") return _lut(image, red + green + blue) -## -# Remove border from image. The same amount of pixels are removed -# from all four sides. This function works on all image modes. -# -# @param image The image to crop. -# @param border The number of pixels to remove. -# @return An image. -# @see Image#Image.crop def crop(image, border=0): - "Crop border off image" + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ left, top, right, bottom = _border(border) return image.crop( (left, top, image.size[0]-right, image.size[1]-bottom) ) -## -# Deform the image. -# -# @param image The image to deform. -# @param deformer A deformer object. Any object that implements a -# getmesh method can be used. -# @param resample What resampling filter to use. -# @return An image. def deform(image, deformer, resample=Image.BILINEAR): - "Deform image using the given deformer" + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + **getmesh** method can be used. + :param resample: What resampling filter to use. + :return: An image. + """ return image.transform( image.size, Image.MESH, deformer.getmesh(image), resample ) -## -# Equalize the image histogram. This function applies a non-linear -# mapping to the input image, in order to create a uniform -# distribution of grayscale values in the output image. -# -# @param image The image to equalize. -# @param mask An optional mask. If given, only the pixels selected by -# the mask are included in the analysis. -# @return An image. def equalize(image, mask=None): - "Equalize image histogram" + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ if image.mode == "P": image = image.convert("RGB") h = image.histogram(mask) @@ -225,15 +218,16 @@ def equalize(image, mask=None): n = n + h[i+b] return _lut(image, lut) -## -# Add border to the image -# -# @param image The image to expand. -# @param border Border width, in pixels. -# @param fill Pixel fill value (a colour value). Default is 0 (black). -# @return An image. def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ "Add border to image" left, top, right, bottom = _border(border) width = left + image.size[0] + right @@ -242,33 +236,32 @@ def expand(image, border=0, fill=0): out.paste(image, (left, top)) return out -## -# Returns a sized and cropped version of the image, cropped to the -# requested aspect ratio and size. -#

-# The fit function was contributed by Kevin Cazabon. -# -# @param size The requested output size in pixels, given as a -# (width, height) tuple. -# @param method What resampling method to use. Default is Image.NEAREST. -# @param bleed Remove a border around the outside of the image (from all -# four edges. The value is a decimal percentage (use 0.01 for one -# percent). The default value is 0 (no border). -# @param centering Control the cropping position. Use (0.5, 0.5) for -# center cropping (e.g. if cropping the width, take 50% off of the -# left side, and therefore 50% off the right side). (0.0, 0.0) -# will crop from the top left corner (i.e. if cropping the width, -# take all of the crop off of the right side, and if cropping the -# height, take all of it off the bottom). (1.0, 0.0) will crop -# from the bottom left corner, etc. (i.e. if cropping the width, -# take all of the crop off the left side, and if cropping the height -# take none from the top, and therefore all off the bottom). -# @return An image. def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): """ - This method returns a sized and cropped version of the image, - cropped to the aspect ratio and size that you request. + Returns a sized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.NEAREST`. + :param bleed: Remove a border around the outside of the image (from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. """ # by Kevin Cazabon, Feb 17/2000 @@ -336,73 +329,73 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # resize the image and return it return out.resize(size, method) -## -# Flip the image vertically (top to bottom). -# -# @param image The image to flip. -# @return An image. def flip(image): - "Flip image vertically" + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ return image.transpose(Image.FLIP_TOP_BOTTOM) -## -# Convert the image to grayscale. -# -# @param image The image to convert. -# @return An image. def grayscale(image): - "Convert to grayscale" + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ return image.convert("L") -## -# Invert (negate) the image. -# -# @param image The image to invert. -# @return An image. def invert(image): - "Invert image (negate)" + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ lut = [] for i in range(256): lut.append(255-i) return _lut(image, lut) -## -# Flip image horizontally (left to right). -# -# @param image The image to mirror. -# @return An image. def mirror(image): - "Flip image horizontally" + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ return image.transpose(Image.FLIP_LEFT_RIGHT) -## -# Reduce the number of bits for each colour channel. -# -# @param image The image to posterize. -# @param bits The number of bits to keep for each channel (1-8). -# @return An image. def posterize(image, bits): - "Reduce the number of bits per color channel" + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ lut = [] mask = ~(2**(8-bits)-1) for i in range(256): lut.append(i & mask) return _lut(image, lut) -## -# Invert all pixel values above a threshold. -# -# @param image The image to posterize. -# @param threshold All pixels above this greyscale level are inverted. -# @return An image. def solarize(image, threshold=128): - "Invert all values above threshold" + """ + Invert all pixel values above a threshold. + + :param image: The image to posterize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ lut = [] for i in range(256): if i < threshold: diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 2fa051407..61affdb19 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -19,11 +19,9 @@ import array from PIL import Image, ImageColor -## -# Colour palette wrapper for palette mapped images. class ImagePalette: - "Colour palette for palette mapped images" + "Color palette for palette mapped images" def __init__(self, mode = "RGB", palette = None): self.mode = mode @@ -35,14 +33,21 @@ class ImagePalette: raise ValueError("wrong palette size") def getdata(self): - # experimental: get palette contents in format suitable - # for the low-level im.putpalette primitive + """ + Get palette contents in format suitable # for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ if self.rawmode: return self.rawmode, self.palette return self.mode + ";L", self.tobytes() def tobytes(self): - # experimental: convert palette to bytes + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(self.palette, bytes): @@ -57,7 +62,10 @@ class ImagePalette: tostring = tobytes def getcolor(self, color): - # experimental: given an rgb tuple, allocate palette entry + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(color, tuple): @@ -80,7 +88,10 @@ class ImagePalette: raise ValueError("unknown color specifier: %r" % color) def save(self, fp): - # (experimental) save palette to text file + """Save palette to text file. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(fp, str): @@ -192,6 +203,3 @@ def load(filename): raise IOError("cannot load palette") return lut # data, rawmode - - -# add some psuedocolour palettes as well diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py index 63e367063..656d5ce61 100644 --- a/PIL/ImagePath.py +++ b/PIL/ImagePath.py @@ -16,17 +16,12 @@ from PIL import Image -## -# Path wrapper. + +# the Python class below is overridden by the C implementation. + class Path: - ## - # Creates a path object. - # - # @param xy Sequence. The sequence can contain 2-tuples [(x, y), ...] - # or a flat list of numbers [x, y, ...]. - def __init__(self, xy): pass diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py index e94ca0b1e..513c9247b 100644 --- a/PIL/ImageSequence.py +++ b/PIL/ImageSequence.py @@ -14,15 +14,18 @@ # ## -# This class implements an iterator object that can be used to loop -# over an image sequence. class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. - ## - # Create an iterator. - # - # @param im An image object. + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ def __init__(self, im): if not hasattr(im, "seek"): diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 508d6b46e..ef63b7857 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -25,25 +25,8 @@ from PIL import Image import operator, math from functools import reduce -## -# The ImageStat module calculates global statistics for an -# image, or a region of an image. -## - -## -# Calculate statistics for the given image. If a mask is included, -# only the regions covered by that mask are included in the -# statistics. class Stat: - "Get image or feature statistics" - - ## - # Create a statistics object. - # - # @def __init__(image, mask=None) - # @param image A PIL image, or a precalculate histogram. - # @param mask An optional mask. def __init__(self, image_or_list, mask = None): try: diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 1b75422fc..1e81d240e 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -34,13 +34,6 @@ except ImportError: from PIL import Image -## -# The ImageTk module contains support to create and modify -# Tkinter BitmapImage and PhotoImage objects. -#

-# For examples, see the demo programs in the Scripts -# directory. -## # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -61,28 +54,25 @@ def _pilbitmap_check(): # -------------------------------------------------------------------- # PhotoImage -## -# Creates a Tkinter-compatible photo image. This can be used -# everywhere Tkinter expects an image object. If the image is an RGBA -# image, pixels having alpha 0 are treated as transparent. - class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. - ## - # Create a photo image object. The constructor takes either - # a PIL image, or a mode and a size. Alternatively, you can - # use the file or data options to initialize - # the photo image object. - #

- # @def __init__(image=None, size=None, **options) - # @param image Either a PIL image, or a mode string. If a - # mode string is used, a size must also be given. - # @param size If the first argument is a mode string, this - # defines the size of the image. - # @keyparam file A filename to load the image from (using - # Image.open(file)). - # @keyparam data An 8-bit string containing image data (as - # loaded from an image file). + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the **file** or **data** options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ def __init__(self, image=None, size=None, **kw): @@ -130,44 +120,48 @@ class PhotoImage: except: pass # ignore internal errors - ## - # Get the Tkinter photo image identifier. This method is - # automatically called by Tkinter whenever a PhotoImage object is - # passed to a Tkinter method. - # - # @return A Tkinter photo image identifier (a string). def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ return str(self.__photo) - ## - # Get the width of the image. - # - # @return The width, in pixels. def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ return self.__size[0] - ## - # Get the height of the image. - # - # @return The height, in pixels. def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ return self.__size[1] - ## - # Paste a PIL image into the photo image. Note that this can - # be very slow if the photo image is displayed. - # - # @param im A PIL image. The size must match the target region. - # If the mode does not match, the image is converted to the - # mode of the bitmap image. - # @param box A 4-tuple defining the left, upper, right, and - # lower pixel coordinate. If None is given instead of a - # tuple, all of the image is assumed. def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. If None is given instead of a tuple, all of + the image is assumed. + """ # convert to blittable im.load() @@ -197,24 +191,21 @@ class PhotoImage: # -------------------------------------------------------------------- # BitmapImage -## -# Create a Tkinter-compatible bitmap image. This can be used -# everywhere Tkinter expects an image object. class BitmapImage: + """ - ## - # Create a Tkinter-compatible bitmap image. - #

- # The given image must have mode "1". Pixels having value 0 are - # treated as transparent. Options, if any, are passed on to - # Tkinter. The most commonly used option is foreground, - # which is used to specify the colour for the non-transparent - # parts. See the Tkinter documentation for information on how to - # specify colours. - # - # @def __init__(image=None, **options) - # @param image A PIL image. + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is **foreground**, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ def __init__(self, image=None, **kw): @@ -249,36 +240,38 @@ class BitmapImage: except: pass # ignore internal errors - ## - # Get the width of the image. - # - # @return The width, in pixels. def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ return self.__size[0] - ## - # Get the height of the image. - # - # @return The height, in pixels. def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ return self.__size[1] - ## - # Get the Tkinter bitmap image identifier. This method is - # automatically called by Tkinter whenever a BitmapImage object - # is passed to a Tkinter method. - # - # @return A Tkinter bitmap image identifier (a string). def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ return str(self.__photo) -## -# Copies the contents of a PhotoImage to a PIL image memory. def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" photo.tk.call("PyImagingPhotoGet", photo) # -------------------------------------------------------------------- diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index d83763e01..aa90b887b 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -20,44 +20,49 @@ import warnings from PIL import Image -## -# The ImageWin module contains support to create and display -# images under Windows 95/98, NT, 2000 and later. class HDC: + """ + Wraps a HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ def __init__(self, dc): self.dc = dc def __int__(self): return self.dc class HWND: + """ + Wraps a HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ def __init__(self, wnd): self.wnd = wnd def __int__(self): return self.wnd -## -# Create a Windows bitmap with the given mode and size. The mode can -# be one of "1", "L", "P", or "RGB". -# -# If the display requires a palette, this constructor creates a -# suitable palette and associates it with the image. For an "L" image, -# 128 greylevels are allocated. For an "RGB" image, a 6x6x6 colour -# cube is used, together with 20 greylevels. -# -# To make sure that palettes work properly under Windows, you must -# call the palette method upon certain events from Windows. class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". - ## - # Create Windows bitmap. - # - # @param image Either a PIL image, or a mode string. If a - # mode string is used, a size must also be given. The - # mode can be one of "1", "L", "P", or "RGB". - # @param size If the first argument is a mode string, this - # defines the size of the image. + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + **palette** method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ def __init__(self, image, size=None): if hasattr(image, "mode") and hasattr(image, "size"): @@ -74,15 +79,15 @@ class Dib: if image: self.paste(image) - ## - # Copy the bitmap contents to a device context. - # - # @param handle Device context (HDC), cast to a Python integer, - # or a HDC or HWND instance. In PythonWin, you can use the - # GetHandleAttrib method of the CDC class to get - # a suitable handle. def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or a HDC + or HWND instance. In PythonWin, you can use the + :py:meth:`CDC.GetHandleAttrib` to get a suitable handle. + """ if isinstance(handle, HWND): dc = self.image.getdc(handle) try: @@ -94,6 +99,15 @@ class Dib: return result def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ if not src: src = (0,0) + self.size if isinstance(handle, HWND): @@ -106,22 +120,22 @@ class Dib: result = self.image.draw(handle, dst, src) return result - ## - # Installs the palette associated with the image in the - # given device context. - #

- # This method should be called upon QUERYNEWPALETTE - # and PALETTECHANGED events from Windows. If this - # method returns a non-zero value, one or more display - # palette entries were changed, and the image should be - # redrawn. - # - # @param handle Device context (HDC), cast to a Python integer, - # or an HDC or HWND instance. - # @return A true value if one or more entries were changed - # (this indicates that the image should be redrawn). def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ if isinstance(handle, HWND): handle = self.image.getdc(handle) try: @@ -132,17 +146,18 @@ class Dib: result = self.image.query_palette(handle) return result - ## - # Paste a PIL image into the bitmap image. - # - # @param im A PIL image. The size must match the target region. - # If the mode does not match, the image is converted to the - # mode of the bitmap image. - # @param box A 4-tuple defining the left, upper, right, and - # lower pixel coordinate. If None is given instead of a - # tuple, all of the image is assumed. def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. If None is given instead of a + tuple, all of the image is assumed. + """ im.load() if self.mode != im.mode: im = im.convert(self.mode) @@ -151,21 +166,23 @@ class Dib: else: self.image.paste(im.im) - ## - # Load display memory contents from byte data. - # - # @param buffer A buffer containing display data (usually - # data returned from tobytes) def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from tobytes) + """ return self.image.frombytes(buffer) - ## - # Copy display memory contents to bytes object. - # - # @return A bytes object containing display data. def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ return self.image.tobytes() ## diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index dd4acd8af..88593bb9d 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -23,6 +23,10 @@ from PIL import EpsImagePlugin # Simple Postscript graphics interface. class PSDraw: + """ + Sets up printing to the given file. If **file** is omitted, + :py:attr:`sys.stdout` is assumed. + """ def __init__(self, fp=None): if not fp: @@ -31,7 +35,7 @@ class PSDraw: self.fp = fp def begin_document(self, id = None): - "Write Postscript DSC header" + """Set up printing of a document. (Write Postscript DSC header.)""" # FIXME: incomplete self.fp.write("%!PS-Adobe-3.0\n" "save\n" @@ -45,7 +49,7 @@ class PSDraw: self.isofont = {} def end_document(self): - "Write Postscript DSC footer" + """Ends printing. (Write Postscript DSC footer.)""" self.fp.write("%%EndDocument\n" "restore showpage\n" "%%End\n") @@ -53,6 +57,12 @@ class PSDraw: self.fp.flush() def setfont(self, font, size): + """ + Selects which font to use. + + :param font: A Postscript font name + :param size: Size in points. + """ if font not in self.isofont: # reencode font self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\ @@ -62,23 +72,49 @@ class PSDraw: self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font)) def setink(self, ink): + """ + .. warning:: + + This has been in the PIL API for ages but was never implemented. + """ print("*** NOT YET IMPLEMENTED ***") def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + Postscript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ xy = xy0 + xy1 self.fp.write("%d %d %d %d Vl\n" % xy) def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ self.fp.write("%d %d M %d %d 0 Vr\n" % box) def text(self, xy, text): + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ text = "\\(".join(text.split("(")) text = "\\)".join(text.split(")")) xy = xy + (text,) self.fp.write("%d %d M (%s) S\n" % xy) def image(self, box, im, dpi = None): - "Write an PIL image" + """Draw a PIL image, centered in the given box.""" # default resolution depends on mode if not dpi: if im.mode == "1": diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 220812ffe..410e29303 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -863,6 +863,12 @@ class TiffImageFile(ImageFile.ImageFile): # fillorder==2 modes have a corresponding # fillorder=1 mode self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if self.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' # Offset in the tile tuple is 0, we go from 0,0 to # w,h, and we only do this once -- eds @@ -1075,41 +1081,54 @@ def _save(im, fp, filename): _fp = os.dup(fp.fileno()) blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. - - items = itertools.chain(getattr(im, 'ifd', {}).items(), ifd.items()) - atts = {} - try: - # pull in more bits from the original file, e.g x,y resolution - # so that we can save(load('')) == original file. - for k,v in items: - if k not in atts and k not in blocklist: - if type(v[0]) == tuple and len(v) > 1: - # A tuple of more than one rational tuples - # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = [float(elt[0])/float(elt[1]) for elt in v] - continue - if type(v[0]) == tuple and len(v) == 1: - # A tuple of one rational tuples - # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = float(v[0][0])/float(v[0][1]) - continue - if type(v) == tuple and len(v) == 1: - # int or similar - atts[k] = v[0] - continue - if isStringType(v): - atts[k] = v.encode('ascii', 'ignore') - continue + atts={} + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + for k,v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): + if k not in atts and k not in blocklist: + if type(v[0]) == tuple and len(v) > 1: + # A tuple of more than one rational tuples + # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = [float(elt[0])/float(elt[1]) for elt in v] + continue + if type(v[0]) == tuple and len(v) == 1: + # A tuple of one rational tuples + # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0][0])/float(v[0][1]) + continue + if type(v) == tuple and len(v) > 2: + # List of ints? + # BitsPerSample is one example, I get (8,8,8) + # UNDONE + continue + if type(v) == tuple and len(v) == 2: + # one rational tuple + # flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0])/float(v[1]) + continue + if type(v) == tuple and len(v) == 1: + v = v[0] + # drop through + if isStringType(v): + atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" + continue + else: + # int or similar + atts[k] = v - except Exception as msg: - # if we don't have an ifd here, just punt. - if Image.DEBUG: - print (msg) - #raise msg - pass if Image.DEBUG: print (atts) + + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' + a = (rawmode, compression, _fp, filename, atts) + # print (im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0,0)+im.size) while 1: diff --git a/README.rst b/README.rst index 5917d6a21..78a256c0d 100644 --- a/README.rst +++ b/README.rst @@ -16,547 +16,4 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -Introduction ------------- - -.. Note:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. - -.. Note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. - -The fork author's goal is to foster active development of PIL through: - -- Continuous integration testing via `Travis CI `_ -- Publicized development activity on `GitHub `_ -- Regular releases to the `Python Package Index `_ -- Solicitation for community contributions and involvement on `Image-SIG `_ - -Why a fork? -~~~~~~~~~~~ - -PIL is not setuptools compatible. Please see http://mail.python.org/pipermail/image-sig/2010-August/006480.html for a more detailed explanation. Also, PIL's current bi-yearly (or greater) release schedule is too infrequent to accomodate the large number and frequency of issues reported. - -What about the official PIL? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. Note:: Prior to Pillow 2.0.0, very few image code changes were made. Pillow 2.0.0 added Python 3 support and includes many bug fixes from many contributors. - -As more time passes since the last PIL release, the likelyhood of a new PIL release decreases. However, we've yet to hear an official "PIL is dead" announcement. So if you still want to support PIL, please report issues here first: - -- https://bitbucket.org/effbot/pil-2009-raclette/issues - -Then open a Pillow ticket here: - -- https://github.com/python-imaging/Pillow/issues - -Please provide a link to the PIL ticket so we can track the issue(s) upstream. - -Installation ------------- - -.. Note:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. - -You can install Pillow with ``pip``:: - - $ pip install Pillow - -Or ``easy_install`` (for installing `Python Eggs `_, as pip does not support them):: - - $ easy_install Pillow - -Or download the compressed archive from PyPI, extract it, and inside it run:: - - $ python setup.py install - -For more information, please see http://pillow.readthedocs.org/en/latest/ or below. - -Documentation -------------- - -The API documentation included with PIL has been converted (from HTML generated by pythondoc) to reStructured text (via pandoc) and is now `hosted by readthedocs.org `_. This is a work in progress: in order to re-generate new API documentation, either `pythondoc `_ will have to be run again or the pythondoc functionality must be converted to Sphinx. - -Community Support ------------------ - -Developer -~~~~~~~~~ - -PIL needs you! Please help us maintain the Python Imaging Library here: - -- GitHub (https://github.com/python-imaging/Pillow) -- Freenode (irc://irc.freenode.net#pil) -- Image-SIG (http://mail.python.org/mailman/listinfo/image-sig) - -Financial -~~~~~~~~~ - -Pillow is a volunteer effort led by Alex Clark. If you can't help with development, please help us financially; your assistance is very much needed and appreciated! - -.. Note:: Contributors: please add your name and donation preference here. - -+--------------------------------------+---------------------------------------+ -| **Developer** | **Preference** | -+--------------------------------------+---------------------------------------+ -| Alex Clark (fork author) | http://gittip.com/aclark4life | -+--------------------------------------+---------------------------------------+ - -Developer Notes ---------------- - -.. Note:: If there is a binary package for your system, that is the easiest way to install Pillow. Currently we only provide binaries for Windows (via Python eggs). - -Build from source -~~~~~~~~~~~~~~~~~ - -Many of Pillow's features require external libraries: - -* **libjpeg** provides JPEG functionality. - - * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** - -* **zlib** provides access to compressed PNGs - -* **libtiff** provides group4 tiff functionality - - * Pillow has been tested with libtiff versions **3.x** and **4.0** - -* **libfreetype** provides type related services - -* **littlecms** provides color management - -* **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. - -* **tcl/tk** provides support for tkinter bitmap and photo images. - -If the prerequisites are installed in the standard library locations for your machine (e.g. /usr or /usr/local), no additional configuration should be required. If they are installed in a non-standard location, you may need to configure setuptools to use those locations (i.e. by editing setup.py and/or setup.cfg). Once you have installed the prerequisites, run:: - - $ pip install Pillow - -Platform-specific instructions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Linux -+++++ - -**We do not provide binaries for Linux.** If you didn't build Python from source, make sure you have Python's development libraries installed. In Debian or Ubuntu:: - - $ sudo apt-get install python-dev python-setuptools - -Or for Python 3:: - - $ sudo apt-get install python3-dev python3-setuptools - -Prerequisites are installed on **Ubuntu 10.04 LTS** with:: - - $ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev libfreetype6-dev liblcms1-dev tcl8.5-dev tk8.5-dev - -Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy 7.0** with:: - - $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev - -Distributions -^^^^^^^^^^^^^ - -.. Note:: XXX Provide links - -Additionally, many Linux distributions now include Pillow (instead of PIL) with their distribution: - -- Fedora -- Debian/Ubuntu -- ArchLinux - -Mac OS X -++++++++ - -**We do not provide binaries for OS X.** So you'll need XCode to install Pillow. (XCode 4.2 on 10.6 will work with the Official Python binary distribution. Otherwise, use whatever XCode you used to compile Python.) - -The easiest way to install the prerequisites is via `Homebrew `_. After you install Homebrew, run:: - - $ brew install libtiff libjpeg webp littlecms - -If you've built your own Python, then you should be able to install Pillow using:: - - $ pip install Pillow - -Windows -+++++++ - -We provide binaries for Windows in the form of Python Eggs and `Python Wheels `_: - -Python Eggs -^^^^^^^^^^^ - -.. Note:: Pip does not support Python Eggs; use easy_install instead. - -:: - - $ easy_install Pillow - -Python Wheels -^^^^^^^^^^^^^ - -.. Note:: Experimental. Requires Setuptools >=0.8 and Pip >=1.4.1 - -:: - - $ pip install --use-wheel Pillow - -Platform support -~~~~~~~~~~~~~~~~ - -Current platform support for Pillow. Binary distributions are contributed for each release on a volunteer basis, but the source should compile and run everywhere platform support is listed. In general, we aim to support all current versions of Linux, OS X, and Windows. - -.. Note:: Contributors please test on your platform, edit this document and send a pull request - -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.7 Lion |Yes | 2.6,2.7,3.2,3.3 | 2.2.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Redhat Linux 6 |Yes | 2.6 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Raspian Wheezy |Yes | 2.7,3.2 | 2.2.0 |arm | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 7 Pro |Yes | 2.7,3.2 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ - - -Port existing PIL-based code to Pillow -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Pillow is a functional drop-in replacement for the Python Imaging Library. To run your existing PIL-compatible code with Pillow, it needs to be modified to import the ``Imaging`` module from the ``PIL`` namespace *instead* of the global namespace. I.e. change:: - - import Image - -to:: - - from PIL import Image - -.. Note:: If your code imports from ``_imaging``, it will no longer work. - -The preferred, future proof method of importing the private ``_imaging`` module is:: - - from PIL import Image - _imaging = Image.core - -Python Imaging Library -====================== - -.. Note:: What follows is the original PIL 1.1.7 README file contents. - -:: - - The Python Imaging Library - $Id$ - - Release 1.1.7 (November 15, 2009) - - ==================================================================== - The Python Imaging Library 1.1.7 - ==================================================================== - - Contents - -------- - - + Introduction - + Support Options - - Commercial support - - Free support - + Software License - + Build instructions (all platforms) - - Additional notes for Mac OS X - - Additional notes for Windows - - -------------------------------------------------------------------- - Introduction - -------------------------------------------------------------------- - - The Python Imaging Library (PIL) adds image processing capabilities - to your Python environment. This library provides extensive file - format support, an efficient internal representation, and powerful - image processing capabilities. - - This source kit has been built and tested with Python 2.0 and newer, - on Windows, Mac OS X, and major Unix platforms. Large parts of the - library also work on 1.5.2 and 1.6. - - The main distribution site for this software is: - - http://www.pythonware.com/products/pil/ - - That site also contains information about free and commercial support - options, PIL add-ons, answers to frequently asked questions, and more. - - - Development versions (alphas, betas) are available here: - - http://effbot.org/downloads/ - - - The PIL handbook is not included in this distribution; to get the - latest version, check: - - http://www.pythonware.com/library/ - http://effbot.org/books/imagingbook/ (drafts) - - - For installation and licensing details, see below. - - - -------------------------------------------------------------------- - Support Options - -------------------------------------------------------------------- - - + Commercial Support - - Secret Labs (PythonWare) offers support contracts for companies using - the Python Imaging Library in commercial applications, and in mission- - critical environments. The support contract includes technical support, - bug fixes, extensions to the PIL library, sample applications, and more. - - For the full story, check: - - http://www.pythonware.com/products/pil/support.htm - - - + Free Support - - For support and general questions on the Python Imaging Library, send - e-mail to the Image SIG mailing list: - - image-sig@python.org - - You can join the Image SIG by sending a mail to: - - image-sig-request@python.org - - Put "subscribe" in the message body to automatically subscribe to the - list, or "help" to get additional information. Alternatively, you can - send your questions to the Python mailing list, python-list@python.org, - or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT - QUESTIONS TO PYTHONWARE ADDRESSES. - - - -------------------------------------------------------------------- - Software License - -------------------------------------------------------------------- - - The Python Imaging Library is - - Copyright (c) 1997-2009 by Secret Labs AB - Copyright (c) 1995-2009 by Fredrik Lundh - - By obtaining, using, and/or copying this software and/or its - associated documentation, you agree that you have read, understood, - and will comply with the following terms and conditions: - - 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. - - - -------------------------------------------------------------------- - Build instructions (all platforms) - -------------------------------------------------------------------- - - For a list of changes in this release, see the CHANGES document. - - 0. If you're in a hurry, try this: - - $ tar xvfz Imaging-1.1.7.tar.gz - $ cd Imaging-1.1.7 - $ python setup.py install - - If you prefer to know what you're doing, read on. - - - 1. Prerequisites. - - If you need any of the features described below, make sure you - have the necessary libraries before building PIL. - - feature library - ----------------------------------------------------------------- - JPEG support libjpeg (6a or 6b) - - http://www.ijg.org - http://www.ijg.org/files/jpegsrc.v6b.tar.gz - ftp://ftp.uu.net/graphics/jpeg/ - - PNG support zlib (1.2.3 or later is recommended) - - http://www.gzip.org/zlib/ - - OpenType/TrueType freetype2 (2.3.9 or later is recommended) - support - http://www.freetype.org - http://freetype.sourceforge.net - - CMS support littleCMS (1.1.5 or later is recommended) - support - http://www.littlecms.com/ - - If you have a recent Linux version, the libraries provided with the - operating system usually work just fine. If some library is - missing, installing a prebuilt version (jpeg-devel, zlib-devel, - etc) is usually easier than building from source. For example, for - Ubuntu 9.10 (karmic), you can install the following libraries: - - sudo apt-get install libjpeg62-dev - sudo apt-get install zlib1g-dev - sudo apt-get install libfreetype6-dev - sudo apt-get install liblcms1-dev - - If you're using Mac OS X, you can use the 'fink' tool to install - missing libraries (also see the Mac OS X section below). - - Similar tools are available for many other platforms. - - - 2. To build under Python 1.5.2, you need to install the stand-alone - version of the distutils library: - - http://www.python.org/sigs/distutils-sig/download.html - - You can fetch distutils 1.0.2 from the Python source repository: - - svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ - - For newer releases, the distutils library is included in the - Python standard library. - - NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some - more recent additions to the library may not work, but the core - functionality is available. - - - 3. If you didn't build Python from sources, make sure you have - Python's build support files on your machine. If you've down- - loaded a prebuilt package (e.g. a Linux RPM), you probably - need additional developer packages. Look for packages named - "python-dev", "python-devel", or similar. For example, for - Ubuntu 9.10 (karmic), use the following command: - - sudo apt-get install python-dev - - - 4. When you have everything you need, unpack the PIL distribution - (the file Imaging-1.1.7.tar.gz) in a suitable work directory: - - $ cd MyExtensions # example - $ gunzip Imaging-1.1.7.tar.gz - $ tar xvf Imaging-1.1.7.tar - - - 5. Build the library. We recommend that you do an in-place build, - and run the self test before installing. - - $ cd Imaging-1.1.7 - $ python setup.py build_ext -i - $ python selftest.py - - During the build process, the setup.py will display a summary - report that lists what external components it found. The self- - test will display a similar report, with what external components - the tests found in the actual build files: - - ---------------------------------------------------------------- - PIL 1.1.7 SETUP SUMMARY - ---------------------------------------------------------------- - *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) - --- JPEG support available - --- ZLIB (PNG/ZIP) support available - --- FREETYPE support available - ---------------------------------------------------------------- - - Make sure that the optional components you need are included. - - If the build script won't find a given component, you can edit the - setup.py file and set the appropriate ROOT variable. For details, - see instructions in the file. - - If the build script finds the component, but the tests cannot - identify it, try rebuilding *all* modules: - - $ python setup.py clean - $ python setup.py build_ext -i - - - 6. If the setup.py and selftest.py commands finish without any - errors, you're ready to install the library: - - $ python setup.py install - - (depending on how Python has been installed on your machine, - you might have to log in as a superuser to run the 'install' - command, or use the 'sudo' command to run 'install'.) - - - -------------------------------------------------------------------- - Additional notes for Mac OS X - -------------------------------------------------------------------- - - On Mac OS X you will usually install additional software such as - libjpeg or freetype with the "fink" tool, and then it ends up in - "/sw". If you have installed the libraries elsewhere, you may have - to tweak the "setup.py" file before building. - - - -------------------------------------------------------------------- - Additional notes for Windows - -------------------------------------------------------------------- - - On Windows, you need to tweak the ROOT settings in the "setup.py" - file, to make it find the external libraries. See comments in the - file for details. - - Make sure to build PIL and the external libraries with the same - runtime linking options as was used for the Python interpreter - (usually /MD, under Visual Studio). - - - Note that most Python distributions for Windows include libraries - compiled for Microsoft Visual Studio. You can get the free Express - edition of Visual Studio from: - - http://www.microsoft.com/Express/ - - To build extensions using other tool chains, see the "Using - non-Microsoft compilers on Windows" section in the distutils handbook: - - http://www.python.org/doc/current/inst/non-ms-compilers.html - - For additional information on how to build extensions using the - popular MinGW compiler, see: - - http://mingw.org (compiler) - http://sebsauvage.net/python/mingw.html (build instructions) - http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) +The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more. diff --git a/Tests/images/12bit.MM.cropped.tif b/Tests/images/12bit.MM.cropped.tif new file mode 100644 index 000000000..c4c24e2eb Binary files /dev/null and b/Tests/images/12bit.MM.cropped.tif differ diff --git a/Tests/images/12bit.MM.deflate.tif b/Tests/images/12bit.MM.deflate.tif new file mode 100644 index 000000000..90a62e94f Binary files /dev/null and b/Tests/images/12bit.MM.deflate.tif differ diff --git a/Tests/images/12bit.deflate.tif b/Tests/images/12bit.deflate.tif new file mode 100644 index 000000000..4f9f4ec06 Binary files /dev/null and b/Tests/images/12bit.deflate.tif differ diff --git a/Tests/images/lab-green.tif b/Tests/images/lab-green.tif new file mode 100644 index 000000000..76c129ee9 Binary files /dev/null and b/Tests/images/lab-green.tif differ diff --git a/Tests/images/lab-red.tif b/Tests/images/lab-red.tif new file mode 100644 index 000000000..fc1953006 Binary files /dev/null and b/Tests/images/lab-red.tif differ diff --git a/Tests/images/lab.tif b/Tests/images/lab.tif new file mode 100644 index 000000000..7dab9b2ba Binary files /dev/null and b/Tests/images/lab.tif differ diff --git a/Tests/images/lena.Lab.tif b/Tests/images/lena.Lab.tif new file mode 100644 index 000000000..335598210 Binary files /dev/null and b/Tests/images/lena.Lab.tif differ diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 976f97f34..cb81282c2 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -92,6 +92,9 @@ def test_g4_write(): assert_equal(reread.size,(500,500)) _assert_noerr(reread) assert_image_equal(reread, rot) + + assert_equal(reread.info['compression'], orig.info['compression']) + assert_false(orig.tobytes() == reread.tobytes()) def test_adobe_deflate_tiff(): @@ -124,3 +127,67 @@ def test_write_metadata(): for tag, value in original.items(): if tag not in ignored: assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) + + +def test_little_endian(): + im = Image.open('Tests/images/12bit.deflate.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + + + out = tempfile("temp.tif") + out = "temp.le.tif" + im.save(out) + reread = Image.open(out) + + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + # UNDONE - libtiff defaults to writing in native endian, so + # on big endian, we'll get back mode = 'I;16B' here. + +def test_big_endian(): + im = Image.open('Tests/images/12bit.MM.deflate.tif') + + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') + + out = tempfile("temp.tif") + im.save(out) + reread = Image.open(out) + + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + +def test_g4_string_info(): + """Tests String data in info directory""" + file = "Tests/images/lena_g4_500.tif" + orig = Image.open(file) + + out = tempfile("temp.tif") + + orig.tag[269] = 'temp.tif' + orig.save(out) + + reread = Image.open(out) + assert_equal('temp.tif', reread.tag[269]) + + diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index ddca24876..88c1aa8fd 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -71,3 +71,34 @@ def test_xyres_tiff(): im.tag.tags[Y_RESOLUTION] = (72,) im._setup() assert_equal(im.info['dpi'], (72., 72.)) + + +def test_little_endian(): + im = Image.open('Tests/images/12bit.cropped.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + + +def test_big_endian(): + im = Image.open('Tests/images/12bit.MM.cropped.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py new file mode 100644 index 000000000..371b06a0b --- /dev/null +++ b/Tests/test_format_lab.py @@ -0,0 +1,41 @@ +from tester import * + +from PIL import Image + +def test_white(): + i = Image.open('Tests/images/lab.tif') + + bits = i.load() + + assert_equal(i.mode, 'LAB') + + assert_equal(i.getbands(), ('L','A', 'B')) + + k = i.getpixel((0,0)) + assert_equal(k, (255,128,128)) + + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) + + assert_equal(list(L), [255]*100) + assert_equal(list(a), [128]*100) + assert_equal(list(b), [128]*100) + + +def test_green(): + # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS + # == RGB: 0, 152, 117 + i = Image.open('Tests/images/lab-green.tif') + + k = i.getpixel((0,0)) + assert_equal(k, (128,28,128)) + + +def test_red(): + # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS + # == RGB: 255, 0, 124 + i = Image.open('Tests/images/lab-red.tif') + + k = i.getpixel((0,0)) + assert_equal(k, (128,228,128)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 29e578192..d18132598 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -14,7 +14,7 @@ def test_sanity(): # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings - assert_equal(v[0], '0.1.0 pil') + assert_equal(v[0], '1.0.0 pil') assert_equal(list(map(type, v)), [str, str, str, str]) # internal version number @@ -39,44 +39,121 @@ def test_sanity(): i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) + # test PointTransform convenience API + im = lena().point(t) + +def test_name(): # get profile information for file assert_equal(ImageCms.getProfileName(SRGB).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') +def x_test_info(): assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), ['sRGB IEC61966-2.1', '', 'Copyright (c) 1998 Hewlett-Packard Company', '', 'WhitePoint : D65 (daylight)', '', 'Tests/icc/sRGB.icm']) + +def test_intent(): assert_equal(ImageCms.getDefaultIntent(SRGB), 0) assert_equal(ImageCms.isIntentSupported( SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) +def test_profile_object(): # same, using profile object p = ImageCms.createProfile("sRGB") - assert_equal(ImageCms.getProfileName(p).strip(), - 'sRGB built-in - (lcms internal)') - assert_equal(ImageCms.getProfileInfo(p).splitlines(), - ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) +# assert_equal(ImageCms.getProfileName(p).strip(), +# 'sRGB built-in - (lcms internal)') +# assert_equal(ImageCms.getProfileInfo(p).splitlines(), +# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) assert_equal(ImageCms.getDefaultIntent(p), 0) assert_equal(ImageCms.isIntentSupported( p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) +def test_extensions(): # extensions i = Image.open("Tests/images/rgb.jpg") p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) assert_equal(ImageCms.getProfileName(p).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') +def test_exceptions(): # the procedural pyCMS API uses PyCMSError for all sorts of errors assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) - # test PointTransform convenience API - im = lena().point(t) +def test_display_profile(): # try fetching the profile for the current display device assert_no_exception(lambda: ImageCms.get_display_profile()) + + +def test_lab_color_profile(): + pLab = ImageCms.createProfile("LAB", 5000) + pLab = ImageCms.createProfile("LAB", 6500) + +def test_simple_lab(): + i = Image.new('RGB', (10,10), (128,128,128)) + + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + + assert_equal(i_lab.mode, 'LAB') + + k = i_lab.getpixel((0,0)) + assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128 + + L = i_lab.getdata(0) + a = i_lab.getdata(1) + b = i_lab.getdata(2) + + assert_equal(list(L), [137]*100) + assert_equal(list(a), [128]*100) + assert_equal(list(b), [128]*100) + + +def test_lab_color(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + # need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, + # and have that mapping work back to a PIL mode. (likely RGB) + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "LAB", (128, 128)) + + # i.save('temp.lab.tif') # visually verified vs PS. + + target = Image.open('Tests/images/lena.Lab.tif') + + assert_image_similar(i, target, 30) + +def test_lab_srgb(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + img = Image.open('Tests/images/lena.Lab.tif') + + img_srgb = ImageCms.applyTransform(img, t) + + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + assert_image_similar(lena(), img_srgb, 30) + +def test_lab_roundtrip(): + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(lena(), t) + out = ImageCms.applyTransform(i, t2) + + assert_image_similar(lena(), out, 2) + + diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 85940d087..9ac2cdd89 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -88,5 +88,48 @@ def test_render_multiline(): # setting a tight epsilon, I'm showing the original test failure # at epsilon = ~38. assert_image_similar(im, target_img,.5) - - + + +def test_rotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check (w,h) of box a is (h,w) of box b + assert_equal(box_size_a[0], box_size_b[1]) + assert_equal(box_size_a[1], box_size_b[0]) + + +def test_unrotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = None + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check boxes a and b are same size + assert_equal(box_size_a, box_size_b) + + diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index b3355b65f..7675348b3 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -101,8 +101,11 @@ def test_unpack(): assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) + assert_equal(unpack("RGB", "RGB;15", 2), (8, 131, 0)) assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + assert_equal(unpack("RGB", "RGB;16", 2), (8, 64, 0)) assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + assert_equal(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) @@ -113,11 +116,17 @@ def test_unpack(): assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + assert_equal(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) + assert_equal(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) + assert_equal(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + assert_equal(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) + assert_equal(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) + assert_equal(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 377595751..61b3cf480 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -78,7 +78,7 @@ def test_16bit(): img = Image.open('Tests/images/12bit.cropped.tif') np_img = numpy.array(img) _test_img_equals_nparray(img, np_img) - assert_equal(np_img.dtype, numpy.dtype('uint16')) + assert_equal(np_img.dtype, numpy.dtype('u2'), - ("I;16L", 'uint16'), + ("I;16L", ' #include #endif -#define PYCMSVERSION "0.1.0 pil" +#define PYCMSVERSION "1.0.0 pil" /* version history */ /* + 1.0.0 pil Integrating littleCMS2 0.1.0 pil integration & refactoring 0.0.2 alpha: Minor updates, added interfaces to littleCMS features, Jan 6, 2003 - fixed some memory holes in how transforms/profiles were created and passed back to Python @@ -107,8 +104,6 @@ cms_profile_open(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - hProfile = cmsOpenProfileFromFile(sProfile, "r"); if (!hProfile) { PyErr_SetString(PyExc_IOError, "cannot open profile file"); @@ -133,8 +128,6 @@ cms_profile_fromstring(PyObject* self, PyObject* args) return NULL; #endif - cmsErrorAction(LCMS_ERROR_IGNORE); - hProfile = cmsOpenProfileFromMem(pProfile, nProfile); if (!hProfile) { PyErr_SetString(PyExc_IOError, "cannot open profile from string"); @@ -192,25 +185,25 @@ cms_transform_dealloc(CmsTransformObject* self) /* internal functions */ static const char* -findICmode(icColorSpaceSignature cs) +findICmode(cmsColorSpaceSignature cs) { switch (cs) { - case icSigXYZData: return "XYZ"; - case icSigLabData: return "LAB"; - case icSigLuvData: return "LUV"; - case icSigYCbCrData: return "YCbCr"; - case icSigYxyData: return "YXY"; - case icSigRgbData: return "RGB"; - case icSigGrayData: return "L"; - case icSigHsvData: return "HSV"; - case icSigHlsData: return "HLS"; - case icSigCmykData: return "CMYK"; - case icSigCmyData: return "CMY"; + case cmsSigXYZData: return "XYZ"; + case cmsSigLabData: return "LAB"; + case cmsSigLuvData: return "LUV"; + case cmsSigYCbCrData: return "YCbCr"; + case cmsSigYxyData: return "YXY"; + case cmsSigRgbData: return "RGB"; + case cmsSigGrayData: return "L"; + case cmsSigHsvData: return "HSV"; + case cmsSigHlsData: return "HLS"; + case cmsSigCmykData: return "CMYK"; + case cmsSigCmyData: return "CMY"; default: return ""; /* other TBA */ } } -static DWORD +static cmsUInt32Number findLCMStype(char* PILmode) { if (strcmp(PILmode, "RGB") == 0) { @@ -243,6 +236,10 @@ findLCMStype(char* PILmode) else if (strcmp(PILmode, "YCC") == 0) { return TYPE_YCbCr_8; } + else if (strcmp(PILmode, "LAB") == 0) { + // LabX equvalent like ALab, but not reversed -- no #define in lcms2 + return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)); + } else { /* take a wild guess... but you probably should fail instead. */ @@ -269,12 +266,10 @@ pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) } static cmsHTRANSFORM -_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, DWORD cmsFLAGS) +_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, cmsUInt32Number cmsFLAGS) { cmsHTRANSFORM hTransform; - cmsErrorAction(LCMS_ERROR_IGNORE); - Py_BEGIN_ALLOW_THREADS /* create the transform */ @@ -293,12 +288,10 @@ _buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sIn } static cmsHTRANSFORM -_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, DWORD cmsFLAGS) +_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, cmsUInt32Number cmsFLAGS) { cmsHTRANSFORM hTransform; - cmsErrorAction(LCMS_ERROR_IGNORE); - Py_BEGIN_ALLOW_THREADS /* create the transform */ @@ -336,8 +329,6 @@ buildTransform(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - transform = _buildTransform(pInputProfile->profile, pOutputProfile->profile, sInMode, sOutMode, iRenderingIntent, cmsFLAGS); if (!transform) @@ -363,8 +354,6 @@ buildProofTransform(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - transform = _buildProofTransform(pInputProfile->profile, pOutputProfile->profile, pProofProfile->profile, sInMode, sOutMode, iRenderingIntent, iProofIntent, cmsFLAGS); if (!transform) @@ -390,8 +379,6 @@ cms_transform_apply(CmsTransformObject *self, PyObject *args) im = (Imaging) idIn; imOut = (Imaging) idOut; - cmsErrorAction(LCMS_ERROR_IGNORE); - result = pyCMSdoTransform(im, imOut, self->transform); return Py_BuildValue("i", result); @@ -405,32 +392,34 @@ createProfile(PyObject *self, PyObject *args) { char *sColorSpace; cmsHPROFILE hProfile; - int iColorTemp = 0; - LPcmsCIExyY whitePoint = NULL; - LCMSBOOL result; + cmsFloat64Number dColorTemp = 0.0; + cmsCIExyY whitePoint; + cmsBool result; - if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp)) + if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - if (strcmp(sColorSpace, "LAB") == 0) { - if (iColorTemp > 0) { - result = cmsWhitePointFromTemp(iColorTemp, whitePoint); + if (dColorTemp > 0.0) { + result = cmsWhitePointFromTemp(&whitePoint, dColorTemp); if (!result) { - PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be integer in degrees Kelvin"); + PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be float in degrees Kelvin"); return NULL; } - hProfile = cmsCreateLabProfile(whitePoint); - } else - hProfile = cmsCreateLabProfile(NULL); + hProfile = cmsCreateLab2Profile(&whitePoint); + } else { + hProfile = cmsCreateLab2Profile(NULL); + } } - else if (strcmp(sColorSpace, "XYZ") == 0) + else if (strcmp(sColorSpace, "XYZ") == 0) { hProfile = cmsCreateXYZProfile(); - else if (strcmp(sColorSpace, "sRGB") == 0) + } + else if (strcmp(sColorSpace, "sRGB") == 0) { hProfile = cmsCreate_sRGBProfile(); - else + } + else { hProfile = NULL; + } if (!hProfile) { PyErr_SetString(PyExc_ValueError, "failed to create requested color space"); @@ -446,7 +435,7 @@ createProfile(PyObject *self, PyObject *args) static PyObject * cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) { - LCMSBOOL result; + cmsBool result; int intent; int direction; @@ -465,7 +454,7 @@ static PyObject * cms_get_display_profile_win32(PyObject* self, PyObject* args) { char filename[MAX_PATH]; - DWORD filename_size; + cmsUInt32Number filename_size; BOOL ok; int handle = 0; @@ -519,27 +508,63 @@ static struct PyMethodDef cms_profile_methods[] = { }; static PyObject* -cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) +_profile_getattr(CmsProfileObject* self, cmsInfoType field) { - return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); + // UNDONE -- check that I'm getting the right fields on these. + // return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); + //wchar_t buf[256]; -- UNDONE need wchar_t for unicode version. + char buf[256]; + cmsUInt32Number written; + written = cmsGetProfileInfoASCII(self->profile, + field, + "en", + "us", + buf, + 256); + if (written) { + return PyUnicode_FromString(buf); + } + // UNDONE suppressing error here by sending back blank string. + return PyUnicode_FromString(""); } static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) -{ - return PyUnicode_DecodeFSDefault(cmsTakeProductDesc(self->profile)); +{ + // description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x + return _profile_getattr(self, cmsInfoDescription); +} + +/* use these four for the individual fields. + */ +static PyObject* +cms_profile_getattr_product_description(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoDescription); } static PyObject* -cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) -{ - return PyUnicode_DecodeFSDefault(cmsTakeProductInfo(self->profile)); +cms_profile_getattr_product_model(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoModel); +} + +static PyObject* +cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoManufacturer); +} + +static PyObject* +cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoCopyright); } static PyObject* cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) { - return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); + return PyInt_FromLong(cmsGetHeaderRenderingIntent(self->profile)); } static PyObject* @@ -556,9 +581,11 @@ cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) /* FIXME: add more properties (creation_datetime etc) */ static struct PyGetSetDef cms_profile_getsetters[] = { - { "product_name", (getter) cms_profile_getattr_product_name }, { "product_desc", (getter) cms_profile_getattr_product_desc }, - { "product_info", (getter) cms_profile_getattr_product_info }, + { "product_description", (getter) cms_profile_getattr_product_description }, + { "product_manufacturer", (getter) cms_profile_getattr_product_manufacturer }, + { "product_model", (getter) cms_profile_getattr_product_model }, + { "product_copyright", (getter) cms_profile_getattr_product_copyright }, { "rendering_intent", (getter) cms_profile_getattr_rendering_intent }, { "pcs", (getter) cms_profile_getattr_pcs }, { "color_space", (getter) cms_profile_getattr_color_space }, diff --git a/docs/Guardfile b/docs/Guardfile index 2bbb2ef91..f8f3051ed 100644 --- a/docs/Guardfile +++ b/docs/Guardfile @@ -5,5 +5,6 @@ from livereload.compiler import shell Task.add('*.rst', shell('make html')) Task.add('*/*.rst', shell('make html')) Task.add('_static/*.css', shell('make clean html')) +Task.add('_templates/*', shell('make clean html')) Task.add('Makefile', shell('make html')) Task.add('conf.py', shell('make html')) diff --git a/docs/PIL.rst b/docs/PIL.rst index 8918fce0e..6726f661f 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -1,13 +1,8 @@ -PIL Package -=========== +PIL Package (autodoc of remaining modules) +========================================== -:mod:`Image` Module -------------------- - -.. automodule:: PIL.Image - :members: - :undoc-members: - :show-inheritance: +Reference for modules whose documentation has not yet been ported or written +can be found here. :mod:`BdfFontFile` Module ------------------------- @@ -65,14 +60,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageChops` Module ------------------------- - -.. automodule:: PIL.ImageChops - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageCms` Module ---------------------- @@ -81,22 +68,7 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageColor` Module ------------------------- - -.. automodule:: PIL.ImageColor - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageDraw` Module ------------------------ - -.. automodule:: PIL.ImageDraw - :members: - :undoc-members: - :show-inheritance: - +.. intentionally skipped documenting this because it's not documented anywhere :mod:`ImageDraw2` Module ------------------------ @@ -105,22 +77,7 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageEnhance` Module --------------------------- - -.. automodule:: PIL.ImageEnhance - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageFile` Module ------------------------ - -.. automodule:: PIL.ImageFile - :members: - :undoc-members: - :show-inheritance: - +.. intentionally skipped documenting this because it's deprecated :mod:`ImageFileIO` Module ------------------------- @@ -129,78 +86,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageFilter` Module -------------------------- - -.. automodule:: PIL.ImageFilter - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageFont` Module ------------------------ - -.. automodule:: PIL.ImageFont - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageMath` Module ------------------------ - -.. automodule:: PIL.ImageMath - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageMode` Module ------------------------ - -.. automodule:: PIL.ImageMode - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageOps` Module ----------------------- - -.. automodule:: PIL.ImageOps - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImagePalette` Module --------------------------- - -.. automodule:: PIL.ImagePalette - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImagePath` Module ------------------------ - -.. automodule:: PIL.ImagePath - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageQt` Module ---------------------- - -.. automodule:: PIL.ImageQt - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageSequence` Module ---------------------------- - -.. automodule:: PIL.ImageSequence - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageShow` Module ----------------------- @@ -209,22 +94,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageStat` Module ------------------------ - -.. automodule:: PIL.ImageStat - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageTk` Module ---------------------- - -.. automodule:: PIL.ImageTk - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageTransform` Module ---------------------------- @@ -233,14 +102,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageWin` Module ----------------------- - -.. automodule:: PIL.ImageWin - :members: - :undoc-members: - :show-inheritance: - :mod:`JpegPresets` Module ------------------------- @@ -257,14 +118,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`PSDraw` Module --------------------- - -.. automodule:: PIL.PSDraw - :members: - :undoc-members: - :show-inheritance: - :mod:`PaletteFile` Module ------------------------- diff --git a/docs/_templates/sidebarhelp.html b/docs/_templates/sidebarhelp.html new file mode 100644 index 000000000..330b3de45 --- /dev/null +++ b/docs/_templates/sidebarhelp.html @@ -0,0 +1,18 @@ +

Need help?

+ +

+ You can seek realtime assistance via IRC at + irc://irc.freenode.net#pil. You can + also post to the + + Image-SIG mailing list. And, of course, there's + + Stack Overflow. +

+ +

+ If you've discovered a bug, you can + open an issue + on Github. +

+ diff --git a/docs/about.rst b/docs/about.rst new file mode 100644 index 000000000..dfd88b605 --- /dev/null +++ b/docs/about.rst @@ -0,0 +1,47 @@ +About Pillow +============ + +Goals +----- + +The fork authors' goal is to foster active development of PIL through: + +- Continuous integration testing via `Travis CI`_ +- Publicized development activity on `GitHub`_ +- Regular releases to the `Python Package Index`_ +- Solicitation for community contributions and involvement on `Image-SIG`_ + +.. _Travis CI: https://travis-ci.org/python-imaging/Pillow +.. _GitHub: https://github.com/python-imaging/Pillow +.. _Python Package Index: https://pypi.python.org/pypi/Pillow +.. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig + +Why a fork? +----------- + +PIL is not setuptools compatible. Please see `this Image-SIG post`_ for a more +detailed explanation. Also, PIL's current bi-yearly (or greater) release +schedule is too infrequent to accomodate the large number and frequency of +issues reported. + +.. _this Image-SIG post: https://mail.python.org/pipermail/image-sig/2010-August/006480.html + +What about the official PIL? +---------------------------- + +.. note:: + + Prior to Pillow 2.0.0, very few image code changes were made. Pillow 2.0.0 + added Python 3 support and includes many bug fixes from many contributors. + +As more time passes since the last PIL release, the likelyhood of a new PIL +release decreases. However, we've yet to hear an official "PIL is dead" +announcement. So if you still want to support PIL, please +`report issues here first`_, then +`open the corresponding Pillow tickets here`_. + +.. _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 + +Please provide a link to the PIL ticket so we can track the issue(s) upstream. diff --git a/docs/conf.py b/docs/conf.py index a9ae431bb..987b6dbb3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,96 +1,39 @@ # -*- coding: utf-8 -*- -# -# Pillow (PIL fork) documentation build configuration file, created by -# sphinx-quickstart on Fri Apr 12 19:51:26 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +import os +import sys -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../')) import PIL -# -- General configuration ----------------------------------------------------- +### general configuration ### -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +needs_sphinx = '1.0' -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx'] intersphinx_mapping = {'http://docs.python.org/2/': None} -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. source_suffix = '.rst' - -# The encoding of source files. +templates_path = ['_templates'] #source_encoding = 'utf-8-sig' - -# The master toctree document. master_doc = 'index' -# General information about the project. project = u'Pillow (PIL fork)' -copyright = u'1997-2011 by Secret Labs AB, 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark' +copyright = (u'1997-2011 by Secret Labs AB,' + u' 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark') -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# # The short X.Y version. version = PIL.PILLOW_VERSION # The full version, including alpha/beta/rc tags. release = version -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. +# currently excluding autodoc'd plugs exclude_patterns = ['_build', 'plugins.rst'] -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- +### HTML output ### from better import better_theme_path html_theme_path = [better_theme_path] @@ -100,140 +43,27 @@ html_title = "Pillow v{release} (PIL fork)".format(release=release) html_short_title = "Home" html_static_path = ['_static'] -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. html_theme_options = {} html_sidebars = { - '**': ['localtoc.html', 'sourcelink.html', 'searchbox.html'], - 'index': ['searchbox.html'], + '**': ['localtoc.html', 'sourcelink.html', 'sidebarhelp.html', + 'searchbox.html'], + 'index': ['globaltoc.html', 'sidebarhelp.html', 'searchbox.html'], } -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - # Output file base name for HTML help builder. -htmlhelp_basename = 'PillowPILforkdoc' +htmlhelp_basename = 'Pillowdoc' -# -- Options for LaTeX output -------------------------------------------------- +### LaTeX output (RtD PDF output as well) ### -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', +latex_elements = {} -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'PillowPILfork.tex', u'Pillow (PIL fork) Documentation', - u'Author', 'manual'), + ('index', 'Pillow.tex', u'Pillow (PIL fork) Documentation', u'Author', + 'manual'), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'pillowpilfork', u'Pillow (PIL fork) Documentation', - [u'Author'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'PillowPILfork', u'Pillow (PIL fork) Documentation', - u'Author', 'PillowPILfork', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - - -# -- Options for Epub output --------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = u'Pillow (PIL fork)' -epub_author = u'Author' -epub_publisher = u'Author' -epub_copyright = u'2013, Author' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -#epub_cover = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -# Allow duplicate toc entries. -#epub_tocdup = True # skip_api_docs setting will skip PIL.rst if True. Used for working on the # guides; makes livereload basically instantaneous. diff --git a/docs/guides.rst b/docs/guides.rst new file mode 100644 index 000000000..926d3cfcc --- /dev/null +++ b/docs/guides.rst @@ -0,0 +1,10 @@ +Guides +====== + +.. toctree:: + :maxdepth: 2 + + handbook/overview + handbook/tutorial + handbook/concepts + porting-pil-to-pillow diff --git a/docs/handbook/guides.rst b/docs/handbook/guides.rst deleted file mode 100644 index edec8bea8..000000000 --- a/docs/handbook/guides.rst +++ /dev/null @@ -1,9 +0,0 @@ -Guides -====== - -.. toctree:: - :maxdepth: 2 - - overview - tutorial - concepts diff --git a/docs/index.rst b/docs/index.rst index 883b1bdb3..e2243450d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,24 +4,60 @@ Pillow: a modern fork of PIL Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. +.. image:: https://travis-ci.org/python-imaging/Pillow.png + :target: https://travis-ci.org/python-imaging/Pillow -Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +To start using Pillow, read the :doc:`installation +instructions `. -For general information including installation instructions, see `README.rst`_. 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. +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. -.. _README.rst: https://github.com/python-imaging/Pillow/blob/master/README.rst .. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm .. toctree:: :maxdepth: 2 - handbook/guides.rst - handbook/appendices.rst - PIL - plugins + installation + about + guides + reference/index.rst + handbook/appendices + original-readme + +Support Pillow! +=============== + +PIL needs you! Please help us maintain the Python Imaging Library here: + +- `GitHub `_ +- `Freenode `_ +- `Image-SIG `_ + +Financial +--------- + +Pillow is a volunteer effort led by Alex Clark. If you can't help with +development, please help us financially; your assistance is very much needed +and appreciated! + +.. note:: Contributors: please add your name and donation preference here. + +======================================= ======================================= +**Developer** **Preference** +======================================= ======================================= +Alex Clark (fork author) http://gittip.com/aclark4life +======================================= ======================================= Indices and tables ================== diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 000000000..b28c74b56 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,199 @@ +Installation +============ + +.. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. + +.. warning:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. + +.. warning:: PIL and Pillow currently cannot co-exist in the same environment. + If you want to use Pillow, please remove PIL first. + +.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. + +.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. + +Simple installation +------------------- + +.. note:: + + The following instructions will install Pillow with support for most formats. + See :ref:`external-libraries` for the features you would gain by installing + the external libraries first. This page probably also include specific + instructions for your platform. + +You can install Pillow with :command:`pip`:: + + $ pip install Pillow + +Or :command:`easy_install` (for installing `Python Eggs +`_, as :command:`pip` does +not support them):: + + $ easy_install Pillow + +Or download the `compressed archive from PyPI`_, extract it, and inside it +run:: + + $ python setup.py install + +.. _compressed archive from PyPI: https://pypi.python.org/pypi/Pillow + +.. _external-libraries: + +External libraries +------------------ + +Many of Pillow's features require external libraries: + +* **libjpeg** provides JPEG functionality. + + * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** + +* **zlib** provides access to compressed PNGs + +* **libtiff** provides group4 tiff functionality + + * Pillow has been tested with libtiff versions **3.x** and **4.0** + +* **libfreetype** provides type related services + +* **littlecms** provides color management + +* **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. + +* **tcl/tk** provides support for tkinter bitmap and photo images. + +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 +need to configure setuptools to use those locations (i.e. by editing +:file:`setup.py` and/or :file:`setup.cfg`). Once you have installed the +prerequisites, run:: + + $ pip install Pillow + +Linux installation +------------------ + +.. note:: + + Fedora, Debian/Ubuntu, and ArchLinux include Pillow (instead of PIL) with + their distributions. Consider using those instead of installing manually. + +.. note:: + + You *do not* need to install all of the external libraries to get Pillow's + basics to work. + +**We do not provide binaries for Linux.** If you didn't build Python from +source, make sure you have Python's development libraries installed. In Debian +or Ubuntu:: + + $ sudo apt-get install python-dev python-setuptools + +Or for Python 3:: + + $ sudo apt-get install python3-dev python3-setuptools + +Prerequisites are installed on **Ubuntu 10.04 LTS** with:: + + $ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev \ + libfreetype6-dev liblcms1-dev tcl8.5-dev tk8.5-dev + +Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy +7.0** with:: + + $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev + +Mac OS X installation +--------------------- + +.. note:: + + You *do not* need to install all of the external libraries to get Pillow's + basics to work. + +**We do not provide binaries for OS X**, so you'll need XCode to install +Pillow. (XCode 4.2 on 10.6 will work with the Official Python binary +distribution. Otherwise, use whatever XCode you used to compile Python.) + +The easiest way to install the prerequisites is via `Homebrew +`_. After you install Homebrew, run:: + + $ brew install libtiff libjpeg webp littlecms + +If you've built your own Python, then you should be able to install Pillow +using:: + + $ pip install Pillow + +Windows installation +-------------------- + +We provide binaries for Windows in the form of Python Eggs and `Python Wheels +`_: + +Python Eggs +^^^^^^^^^^^ + +.. note:: + + :command:`pip` does not support Python Eggs; use :command:`easy_install` + instead. + +:: + + $ easy_install Pillow + +Python Wheels +^^^^^^^^^^^^^ + +.. Note:: Experimental. Requires setuptools >=0.8 and pip >=1.4.1 + +:: + + $ pip install --use-wheel Pillow + +Platform support +---------------- + +Current platform support for Pillow. Binary distributions are contributed for +each release on a volunteer basis, but the source should compile and run +everywhere platform support is listed. In general, we aim to support all +current versions of Linux, OS X, and Windows. + +.. note:: + + Contributors please test on your platform, edit this document, and send a + pull request. + ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.7 Lion |Yes | 2.6,2.7,3.2,3.3 | 2.2.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Redhat Linux 6 |Yes | 2.6 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Raspian Wheezy |Yes | 2.7,3.2 | 2.2.0 |arm | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |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 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ + diff --git a/docs/original-readme.rst b/docs/original-readme.rst new file mode 100644 index 000000000..73b941f37 --- /dev/null +++ b/docs/original-readme.rst @@ -0,0 +1,306 @@ +Original PIL README +=================== + +What follows is the original PIL 1.1.7 README file contents. + +:: + + The Python Imaging Library + $Id$ + + Release 1.1.7 (November 15, 2009) + + ==================================================================== + The Python Imaging Library 1.1.7 + ==================================================================== + + Contents + -------- + + + Introduction + + Support Options + - Commercial support + - Free support + + Software License + + Build instructions (all platforms) + - Additional notes for Mac OS X + - Additional notes for Windows + + -------------------------------------------------------------------- + Introduction + -------------------------------------------------------------------- + + The Python Imaging Library (PIL) adds image processing capabilities + to your Python environment. This library provides extensive file + format support, an efficient internal representation, and powerful + image processing capabilities. + + This source kit has been built and tested with Python 2.0 and newer, + on Windows, Mac OS X, and major Unix platforms. Large parts of the + library also work on 1.5.2 and 1.6. + + The main distribution site for this software is: + + http://www.pythonware.com/products/pil/ + + That site also contains information about free and commercial support + options, PIL add-ons, answers to frequently asked questions, and more. + + + Development versions (alphas, betas) are available here: + + http://effbot.org/downloads/ + + + The PIL handbook is not included in this distribution; to get the + latest version, check: + + http://www.pythonware.com/library/ + http://effbot.org/books/imagingbook/ (drafts) + + + For installation and licensing details, see below. + + + -------------------------------------------------------------------- + Support Options + -------------------------------------------------------------------- + + + Commercial Support + + Secret Labs (PythonWare) offers support contracts for companies using + the Python Imaging Library in commercial applications, and in mission- + critical environments. The support contract includes technical support, + bug fixes, extensions to the PIL library, sample applications, and more. + + For the full story, check: + + http://www.pythonware.com/products/pil/support.htm + + + + Free Support + + For support and general questions on the Python Imaging Library, send + e-mail to the Image SIG mailing list: + + image-sig@python.org + + You can join the Image SIG by sending a mail to: + + image-sig-request@python.org + + Put "subscribe" in the message body to automatically subscribe to the + list, or "help" to get additional information. Alternatively, you can + send your questions to the Python mailing list, python-list@python.org, + or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT + QUESTIONS TO PYTHONWARE ADDRESSES. + + + -------------------------------------------------------------------- + Software License + -------------------------------------------------------------------- + + The Python Imaging Library is + + Copyright (c) 1997-2009 by Secret Labs AB + Copyright (c) 1995-2009 by Fredrik Lundh + + By obtaining, using, and/or copying this software and/or its + associated documentation, you agree that you have read, understood, + and will comply with the following terms and conditions: + + 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. + + + -------------------------------------------------------------------- + Build instructions (all platforms) + -------------------------------------------------------------------- + + For a list of changes in this release, see the CHANGES document. + + 0. If you're in a hurry, try this: + + $ tar xvfz Imaging-1.1.7.tar.gz + $ cd Imaging-1.1.7 + $ python setup.py install + + If you prefer to know what you're doing, read on. + + + 1. Prerequisites. + + If you need any of the features described below, make sure you + have the necessary libraries before building PIL. + + feature library + ----------------------------------------------------------------- + JPEG support libjpeg (6a or 6b) + + http://www.ijg.org + http://www.ijg.org/files/jpegsrc.v6b.tar.gz + ftp://ftp.uu.net/graphics/jpeg/ + + PNG support zlib (1.2.3 or later is recommended) + + http://www.gzip.org/zlib/ + + OpenType/TrueType freetype2 (2.3.9 or later is recommended) + support + http://www.freetype.org + http://freetype.sourceforge.net + + CMS support littleCMS (1.1.5 or later is recommended) + support + http://www.littlecms.com/ + + If you have a recent Linux version, the libraries provided with the + operating system usually work just fine. If some library is + missing, installing a prebuilt version (jpeg-devel, zlib-devel, + etc) is usually easier than building from source. For example, for + Ubuntu 9.10 (karmic), you can install the following libraries: + + sudo apt-get install libjpeg62-dev + sudo apt-get install zlib1g-dev + sudo apt-get install libfreetype6-dev + sudo apt-get install liblcms1-dev + + If you're using Mac OS X, you can use the 'fink' tool to install + missing libraries (also see the Mac OS X section below). + + Similar tools are available for many other platforms. + + + 2. To build under Python 1.5.2, you need to install the stand-alone + version of the distutils library: + + http://www.python.org/sigs/distutils-sig/download.html + + You can fetch distutils 1.0.2 from the Python source repository: + + svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ + + For newer releases, the distutils library is included in the + Python standard library. + + NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some + more recent additions to the library may not work, but the core + functionality is available. + + + 3. If you didn't build Python from sources, make sure you have + Python's build support files on your machine. If you've down- + loaded a prebuilt package (e.g. a Linux RPM), you probably + need additional developer packages. Look for packages named + "python-dev", "python-devel", or similar. For example, for + Ubuntu 9.10 (karmic), use the following command: + + sudo apt-get install python-dev + + + 4. When you have everything you need, unpack the PIL distribution + (the file Imaging-1.1.7.tar.gz) in a suitable work directory: + + $ cd MyExtensions # example + $ gunzip Imaging-1.1.7.tar.gz + $ tar xvf Imaging-1.1.7.tar + + + 5. Build the library. We recommend that you do an in-place build, + and run the self test before installing. + + $ cd Imaging-1.1.7 + $ python setup.py build_ext -i + $ python selftest.py + + During the build process, the setup.py will display a summary + report that lists what external components it found. The self- + test will display a similar report, with what external components + the tests found in the actual build files: + + ---------------------------------------------------------------- + PIL 1.1.7 SETUP SUMMARY + ---------------------------------------------------------------- + *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) + --- JPEG support available + --- ZLIB (PNG/ZIP) support available + --- FREETYPE support available + ---------------------------------------------------------------- + + Make sure that the optional components you need are included. + + If the build script won't find a given component, you can edit the + setup.py file and set the appropriate ROOT variable. For details, + see instructions in the file. + + If the build script finds the component, but the tests cannot + identify it, try rebuilding *all* modules: + + $ python setup.py clean + $ python setup.py build_ext -i + + + 6. If the setup.py and selftest.py commands finish without any + errors, you're ready to install the library: + + $ python setup.py install + + (depending on how Python has been installed on your machine, + you might have to log in as a superuser to run the 'install' + command, or use the 'sudo' command to run 'install'.) + + + -------------------------------------------------------------------- + Additional notes for Mac OS X + -------------------------------------------------------------------- + + On Mac OS X you will usually install additional software such as + libjpeg or freetype with the "fink" tool, and then it ends up in + "/sw". If you have installed the libraries elsewhere, you may have + to tweak the "setup.py" file before building. + + + -------------------------------------------------------------------- + Additional notes for Windows + -------------------------------------------------------------------- + + On Windows, you need to tweak the ROOT settings in the "setup.py" + file, to make it find the external libraries. See comments in the + file for details. + + Make sure to build PIL and the external libraries with the same + runtime linking options as was used for the Python interpreter + (usually /MD, under Visual Studio). + + + Note that most Python distributions for Windows include libraries + compiled for Microsoft Visual Studio. You can get the free Express + edition of Visual Studio from: + + http://www.microsoft.com/Express/ + + To build extensions using other tool chains, see the "Using + non-Microsoft compilers on Windows" section in the distutils handbook: + + http://www.python.org/doc/current/inst/non-ms-compilers.html + + For additional information on how to build extensions using the + popular MinGW compiler, see: + + http://mingw.org (compiler) + http://sebsauvage.net/python/mingw.html (build instructions) + http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) diff --git a/docs/porting-pil-to-pillow.rst b/docs/porting-pil-to-pillow.rst new file mode 100644 index 000000000..93bc672af --- /dev/null +++ b/docs/porting-pil-to-pillow.rst @@ -0,0 +1,17 @@ +Porting existing PIL-based code to Pillow +========================================= + +Pillow is a functional drop-in replacement for the Python Imaging Library. To +run your existing PIL-compatible code with Pillow, it needs to be modified to +import the ``Imaging`` module from the ``PIL`` namespace *instead* of the +global namespace. Change this:: + + import Image + +to this:: + + from PIL import Image + +The :py:mod:`_imaging` module has been moved. You can now import it like this:: + + from PIL.Image import core as _imaging diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst new file mode 100644 index 000000000..fe13c882b --- /dev/null +++ b/docs/reference/Image.rst @@ -0,0 +1,189 @@ +.. py:module:: PIL.Image +.. py:currentmodule:: PIL.Image + +:py:mod:`Image` Module +====================== + +The :py:mod:`~PIL.Image` module provides a class with the same name which is +used to represent a PIL image. The module also provides a number of factory +functions, including functions to load images from files, and to create new +images. + +Examples +-------- + +The following script loads an image, rotates it 45 degrees, and displays it +using an external viewer (usually xv on Unix, and the paint program on +Windows). + +Open, rotate, and display an image (using the default viewer) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from PIL import Image + im = Image.open("bride.jpg") + im.rotate(45).show() + +The following script creates nice 128x128 thumbnails of all JPEG images in the +current directory. + +Create thumbnails +^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from PIL import Image + import glob, os + + size = 128, 128 + + for infile in glob.glob("*.jpg"): + file, ext = os.path.splitext(infile) + im = Image.open(infile) + im.thumbnail(size, Image.ANTIALIAS) + im.save(file + ".thumbnail", "JPEG") + +Functions +--------- + +.. autofunction:: open + +Image processing +^^^^^^^^^^^^^^^^ + +.. autofunction:: alpha_composite +.. autofunction:: blend +.. autofunction:: composite +.. autofunction:: eval +.. autofunction:: merge + +Constructing images +^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: new +.. autofunction:: fromarray +.. autofunction:: frombytes +.. autofunction:: fromstring +.. autofunction:: frombuffer + +Registering plugins +^^^^^^^^^^^^^^^^^^^ + +.. note:: + + These functions are for use by plugin authors. Application authors can + ignore them. + +.. autofunction:: register_open +.. autofunction:: register_mime +.. autofunction:: register_save +.. autofunction:: register_extension + +The Image Class +--------------- + +.. autoclass:: PIL.Image.Image + +An instance of the :py:class:`~PIL.Image.Image` class has the following +methods. Unless otherwise stated, all methods return a new instance of the +:py:class:`~PIL.Image.Image` class, holding the resulting image. + +.. automethod:: PIL.Image.Image.convert + +The following example converts an RGB image (linearly calibrated according to +ITU-R 709, using the D65 luminant) to the CIE XYZ color space: + +.. code-block:: python + + rgb2xyz = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0 ) + out = im.convert("RGB", rgb2xyz) + +.. automethod:: PIL.Image.Image.copy +.. automethod:: PIL.Image.Image.crop +.. automethod:: PIL.Image.Image.draft +.. automethod:: PIL.Image.Image.filter +.. automethod:: PIL.Image.Image.getbands +.. automethod:: PIL.Image.Image.getbbox +.. automethod:: PIL.Image.Image.getcolors +.. automethod:: PIL.Image.Image.getdata +.. automethod:: PIL.Image.Image.getextrema +.. automethod:: PIL.Image.Image.getpixel +.. automethod:: PIL.Image.Image.histogram +.. automethod:: PIL.Image.Image.offset +.. automethod:: PIL.Image.Image.paste +.. automethod:: PIL.Image.Image.point +.. automethod:: PIL.Image.Image.putalpha +.. automethod:: PIL.Image.Image.putdata +.. automethod:: PIL.Image.Image.putpalette +.. automethod:: PIL.Image.Image.putpixel +.. automethod:: PIL.Image.Image.quantize +.. automethod:: PIL.Image.Image.resize +.. automethod:: PIL.Image.Image.rotate +.. automethod:: PIL.Image.Image.save +.. automethod:: PIL.Image.Image.seek +.. automethod:: PIL.Image.Image.show +.. automethod:: PIL.Image.Image.split +.. automethod:: PIL.Image.Image.tell +.. automethod:: PIL.Image.Image.thumbnail +.. automethod:: PIL.Image.Image.tobitmap +.. automethod:: PIL.Image.Image.tostring +.. automethod:: PIL.Image.Image.transform +.. automethod:: PIL.Image.Image.transpose +.. automethod:: PIL.Image.Image.verify + +.. automethod:: PIL.Image.Image.fromstring +.. deprecated:: 2.0 + +.. automethod:: PIL.Image.Image.load + +Attributes +---------- + +Instances of the :py:class:`Image` class have the following attributes: + +.. py:attribute:: format + + The file format of the source file. For images created by the library + itself (via a factory function, or by running a method on an existing + image), this attribute is set to ``None``. + + :type: :py:class:`string` or ``None`` + +.. py:attribute:: mode + + Image mode. This is a string specifying the pixel format used by the image. + Typical values are “1”, “L”, “RGB”, or “CMYK.” See + :doc:`../handbook/concepts` for a full list. + + :type: :py:class:`string` + +.. py:attribute:: size + + Image size, in pixels. The size is given as a 2-tuple (width, height). + + :type: ``(width, height)`` + +.. py:attribute:: palette + + Colour palette table, if any. If mode is “P”, this should be an instance of + the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should + be set to ``None``. + + :type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None`` + +.. py:attribute:: info + + A dictionary holding data associated with the image. This dictionary is + used by file handlers to pass on various non-image information read from + the file. See documentation for the various file handlers for details. + + Most methods ignore the dictionary when returning new images; since the + keys are not standardized, it’s not possible for a method to know if the + operation affects the dictionary. If you need the information later on, + keep a reference to the info dictionary returned from the open method. + + :type: :py:class:`dict` diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst new file mode 100644 index 000000000..8d08315b0 --- /dev/null +++ b/docs/reference/ImageChops.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageChops +.. py:currentmodule:: PIL.ImageChops + +:py:mod:`ImageChops` ("Channel Operations") Module +================================================== + +The :py:mod:`ImageChops` module contains a number of arithmetical image +operations, called channel operations (“chops”). These can be used for various +purposes, including special effects, image compositions, algorithmic painting, +and more. + +For more pre-made operations, see :py:mod:`ImageOps`. + +At this time, most channel operations are only implemented for 8-bit images +(e.g. “L” and “RGB”). + +Functions +--------- + +Most channel operations take one or two image arguments and returns a new +image. Unless otherwise noted, the result of a channel operation is always +clipped to the range 0 to MAX (which is 255 for all modes supported by the +operations in this module). + +.. autofunction:: PIL.ImageChops.add +.. autofunction:: PIL.ImageChops.add_modulo +.. autofunction:: PIL.ImageChops.blend +.. autofunction:: PIL.ImageChops.composite +.. autofunction:: PIL.ImageChops.constant +.. autofunction:: PIL.ImageChops.darker +.. autofunction:: PIL.ImageChops.difference +.. autofunction:: PIL.ImageChops.duplicate +.. autofunction:: PIL.ImageChops.invert +.. autofunction:: PIL.ImageChops.lighter +.. autofunction:: PIL.ImageChops.logical_and +.. autofunction:: PIL.ImageChops.logical_or +.. autofunction:: PIL.ImageChops.multiply +.. autofunction:: PIL.ImageChops.offset +.. autofunction:: PIL.ImageChops.screen +.. autofunction:: PIL.ImageChops.subtract +.. autofunction:: PIL.ImageChops.subtract_modulo diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst new file mode 100644 index 000000000..da9b406e1 --- /dev/null +++ b/docs/reference/ImageColor.rst @@ -0,0 +1,43 @@ +.. py:module:: PIL.ImageColor +.. py:currentmodule:: PIL.ImageColor + +:py:mod:`ImageColor` Module +=========================== + +The :py:mod:`ImageColor` module contains color tables and converters from +CSS3-style color specifiers to RGB tuples. This module is used by +:py:meth:`PIL.Image.Image.new` and the :py:mod:`~PIL.ImageDraw` module, among +others. + +.. _color-names: + +Color Names +----------- + +The ImageColor module supports the following string formats: + +* Hexadecimal color specifiers, given as ``#rgb`` or ``#rrggbb``. For example, + ``#ff0000`` specifies pure red. + +* RGB functions, given as ``rgb(red, green, blue)`` where the color values are + integers in the range 0 to 255. Alternatively, the color values can be given + as three percentages (0% to 100%). For example, ``rgb(255,0,0)`` and + ``rgb(100%,0%,0%)`` both specify pure red. + +* Hue-Saturation-Lightness (HSL) functions, given as ``hsl(hue, saturation%, + lightness%)`` where hue is the color given as an angle between 0 and 360 + (red=0, green=120, blue=240), saturation is a value between 0% and 100% + (gray=0%, full color=100%), and lightness is a value between 0% and 100% + (black=0%, normal=50%, white=100%). For example, ``hsl(0,100%,50%)`` is pure + red. + +* Common HTML color names. The :py:mod:`~PIL.ImageColor` module provides some + 140 standard color names, based on the colors supported by the X Window + system and most web browsers. color names are case insensitive. For example, + ``red`` and ``Red`` both specify pure red. + +Functions +--------- + +.. autofunction:: getrgb +.. autofunction:: getcolor diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst new file mode 100644 index 000000000..68855eb5b --- /dev/null +++ b/docs/reference/ImageDraw.rst @@ -0,0 +1,239 @@ +.. py:module:: PIL.ImageDraw +.. py:currentmodule:: PIL.ImageDraw + +:py:mod:`ImageDraw` Module +========================== + +The :py:mod:`ImageDraw` module provide simple 2D graphics for +:py:class:`~PIL.Image.Image` objects. You can use this module to create new +images, annotate or retouch existing images, and to generate graphics on the +fly for web use. + +For a more advanced drawing library for PIL, see the `aggdraw module`_. + +.. _aggdraw module: http://effbot.org/zone/aggdraw-index.htm + +Example: Draw a gray cross over an image +---------------------------------------- + +.. code-block:: python + + from PIL import Image, ImageDraw + + im = Image.open("lena.pgm") + + draw = ImageDraw.Draw(im) + draw.line((0, 0) + im.size, fill=128) + draw.line((0, im.size[1], im.size[0], 0), fill=128) + del draw + + # write to stdout + im.save(sys.stdout, "PNG") + + +Concepts +-------- + +Coordinates +^^^^^^^^^^^ + +The graphics interface uses the same coordinate system as PIL itself, with (0, +0) in the upper left corner. + +Colors +^^^^^^ + +To specify colors, you can use numbers or tuples just as you would use with +:py:meth:`PIL.Image.Image.new` or :py:meth:`PIL.Image.Image.putpixel`. For “1”, +“L”, and “I” images, use integers. For “RGB” images, use a 3-tuple containing +integer values. For “F” images, use integer or floating point values. + +For palette images (mode “P”), use integers as color indexes. In 1.1.4 and +later, you can also use RGB 3-tuples or color names (see below). The drawing +layer will automatically assign color indexes, as long as you don’t draw with +more than 256 colors. + +Color Names +^^^^^^^^^^^ + +See :ref:`color-names` for the color names supported by Pillow. + +Fonts +^^^^^ + +PIL can use bitmap fonts or OpenType/TrueType fonts. + +Bitmap fonts are stored in PIL’s own format, where each font typically consists +of a two files, one named .pil and the other usually named .pbm. The former +contains font metrics, the latter raster data. + +To load a bitmap font, use the load functions in the :py:mod:`~PIL.ImageFont` +module. + +To load a OpenType/TrueType font, use the truetype function in the +:py:mod:`~PIL.ImageFont` module. Note that this function depends on third-party +libraries, and may not available in all PIL builds. + +Functions +--------- + +.. py:class:: PIL.ImageDraw.Draw(im, mode=None) + + Creates an object that can be used to draw in the given image. + + Note that the image will be modified in place. + +Methods +------- + +.. py:method:: PIL.ImageDraw.Draw.arc(xy, start, end, fill=None) + + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + +.. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None) + + Draws a bitmap (mask) at the given position, using the current fill color + for the non-zero portions. The bitmap should be a valid transparency mask + (mode “1”) or matte (mode “L” or “RGBA”). + + This is equivalent to doing ``image.paste(xy, color, bitmap)``. + + To paste pixel data into an image, use the + :py:meth:`~PIL.Image.Image.paste` method on the image itself. + +.. py:method:: PIL.ImageDraw.Draw.chord(xy, start, end, fill=None, outline=None) + + Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points + with a straight line. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.ellipse(xy, fill=None, outline=None) + + Draws an ellipse inside the given bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.line(xy, fill=None, width=0) + + Draws a line between the coordinates in the **xy** list. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param fill: Color to use for the line. + :param width: The line width, in pixels. Note that line + joins are not handled well, so wide polylines will not look good. + + .. versionadded:: 1.1.5 + + .. note:: This option was broken until version 1.1.6. + +.. py:method:: PIL.ImageDraw.Draw.pieslice(xy, start, end, fill=None, outline=None) + + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.point(xy, fill=None) + + Draws points (individual pixels) at the given coordinates. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param fill: Color to use for the point. + +.. py:method:: PIL.ImageDraw.Draw.polygon(xy, fill=None, outline=None) + + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.rectangle(xy, fill=None, outline=None) + + Draws a rectangle. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. The second point + is just outside the drawn rectangle. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.shape(shape, fill=None, outline=None) + + .. warning:: This method is experimental. + + Draw a shape. + +.. py:method:: PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None) + + Draws the string at the given position. + + :param xy: Top left corner of the text. + :param text: Text to be drawn. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param fill: Color to use for the text. + +.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None) + + Return the size of the given string, in pixels. + + :param text: Text to be measured. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + +Legacy API +---------- + +The :py:class:`~PIL.ImageDraw.Draw` class contains a constructor and a number +of methods which are provided for backwards compatibility only. For this to +work properly, you should either use options on the drawing primitives, or +these methods. Do not mix the old and new calling conventions. + + +.. py:function:: PIL.ImageDraw.ImageDraw(image) + + :rtype: :py:class:`~PIL.ImageDraw.Draw` + +.. py:method:: PIL.ImageDraw.Draw.setink(ink) + + .. deprecated:: 1.1.5 + + Sets the color to use for subsequent draw and fill operations. + +.. py:method:: PIL.ImageDraw.Draw.setfill(fill) + + .. deprecated:: 1.1.5 + + Sets the fill mode. + + If the mode is 0, subsequently drawn shapes (like polygons and rectangles) + are outlined. If the mode is 1, they are filled. + +.. py:method:: PIL.ImageDraw.Draw.setfont(font) + + .. deprecated:: 1.1.5 + + Sets the default font to use for the text method. + + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst new file mode 100644 index 000000000..e6eae85f0 --- /dev/null +++ b/docs/reference/ImageEnhance.rst @@ -0,0 +1,38 @@ +.. py:module:: PIL.ImageEnhance +.. py:currentmodule:: PIL.ImageEnhance + +:py:mod:`ImageEnhance` Module +============================= + +The :py:mod:`ImageEnhance` module contains a number of classes that can be used +for image enhancement. + +Example: Vary the sharpness of an image +--------------------------------------- + +.. code-block:: python + + from PIL import ImageEnhance + + enhancer = ImageEnhance.Sharpness(image) + + for i in range(8): + factor = i / 4.0 + enhancer.enhance(factor).show("Sharpness %f" % factor) + +Also see the :file:`enhancer.py` demo program in the :file:`Scripts/` +directory. + +Classes +------- + +All enhancement classes implement a common interface, containing a single +method: + +.. autoclass:: PIL.ImageEnhance._Enhance + :members: + +.. autoclass:: PIL.ImageEnhance.Color +.. autoclass:: PIL.ImageEnhance.Contrast +.. autoclass:: PIL.ImageEnhance.Brightness +.. autoclass:: PIL.ImageEnhance.Sharpness diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst new file mode 100644 index 000000000..9612658e9 --- /dev/null +++ b/docs/reference/ImageFile.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageFile +.. py:currentmodule:: PIL.ImageFile + +:py:mod:`ImageFile` Module +========================== + +The :py:mod:`ImageFile` module provides support functions for the image open +and save functions. + +In addition, it provides a :py:class:`Parser` class which can be used to decode +an image piece by piece (e.g. while receiving it over a network connection). +This class implements the same consumer interface as the standard **sgmllib** +and **xmllib** modules. + +Example: Parse an image +----------------------- + +.. code-block:: python + + from PIL import ImageFile + + fp = open("lena.pgm", "rb") + + p = ImageFile.Parser() + + while 1: + s = fp.read(1024) + if not s: + break + p.feed(s) + + im = p.close() + + im.save("copy.jpg") + + +:py:class:`~PIL.ImageFile.Parser` +--------------------------------- + +.. autoclass:: PIL.ImageFile.Parser() + :members: diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst new file mode 100644 index 000000000..e89fafbcf --- /dev/null +++ b/docs/reference/ImageFilter.rst @@ -0,0 +1,47 @@ +.. py:module:: PIL.ImageFilter +.. py:currentmodule:: PIL.ImageFilter + +:py:mod:`ImageFilter` Module +============================ + +The :py:mod:`ImageFilter` module contains definitions for a pre-defined set of +filters, which can be be used with the :py:meth:`Image.filter() +` method. + +Example: Filter an image +------------------------ + +.. code-block:: python + + from PIL import ImageFilter + + im1 = im.filter(ImageFilter.BLUR) + + im2 = im.filter(ImageFilter.MinFilter(3)) + im3 = im.filter(ImageFilter.MinFilter) # same as MinFilter(3) + +Filters +------- + +The current version of the library provides the following set of predefined +image enhancement filters: + +* **BLUR** +* **CONTOUR** +* **DETAIL** +* **EDGE_ENHANCE** +* **EDGE_ENHANCE_MORE** +* **EMBOSS** +* **FIND_EDGES** +* **SMOOTH** +* **SMOOTH_MORE** +* **SHARPEN** + +.. autoclass:: PIL.ImageFilter.GaussianBlur +.. autoclass:: PIL.ImageFilter.UnsharpMask +.. autoclass:: PIL.ImageFilter.Kernel +.. autoclass:: PIL.ImageFilter.RankFilter +.. autoclass:: PIL.ImageFilter.MedianFilter +.. autoclass:: PIL.ImageFilter.MinFilter +.. autoclass:: PIL.ImageFilter.MaxFilter +.. autoclass:: PIL.ImageFilter.ModeFilter diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst new file mode 100644 index 000000000..166d977a6 --- /dev/null +++ b/docs/reference/ImageFont.rst @@ -0,0 +1,69 @@ +.. py:module:: PIL.ImageFont +.. py:currentmodule:: PIL.ImageFont + +:py:mod:`ImageFont` Module +========================== + +The :py:mod:`ImageFont` module defines a class with the same name. Instances of +this class store bitmap fonts, and are used with the +:py:meth:`PIL.ImageDraw.Draw.text` method. + +PIL uses its own font file format to store bitmap fonts. You can use the +:command`pilfont` utility to convert BDF and PCF font descriptors (X window +font formats) to this format. + +Starting with version 1.1.4, PIL can be configured to support TrueType and +OpenType fonts (as well as other font formats supported by the FreeType +library). For earlier versions, TrueType support is only available as part of +the imToolkit package + +Example +------- + +.. code-block:: python + + from PIL import ImageFont, ImageDraw + + draw = ImageDraw.Draw(image) + + # use a bitmap font + font = ImageFont.load("arial.pil") + + draw.text((10, 10), "hello", font=font) + + # use a truetype font + font = ImageFont.truetype("arial.ttf", 15) + + draw.text((10, 25), "world", font=font) + +Functions +--------- + +.. autofunction:: PIL.ImageFont.load +.. autofunction:: PIL.ImageFont.load_path +.. autofunction:: PIL.ImageFont.truetype +.. autofunction:: PIL.ImageFont.load_default + +Methods +------- + +.. py:method:: PIL.ImageFont.ImageFont.getsize(text) + + :return: (width, height) + +.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='') + + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode “L” and use a + maximum value of 255. Otherwise, it should have mode “1”. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst new file mode 100644 index 000000000..117be885b --- /dev/null +++ b/docs/reference/ImageGrab.rst @@ -0,0 +1,33 @@ +.. py:module:: PIL.ImageGrab +.. py:currentmodule:: PIL.ImageGrab + +:py:mod:`ImageGrab` Module (Windows-only) +========================================= + +The :py:mod:`ImageGrab` module can be used to copy the contents of the screen +or the clipboard to a PIL image memory. + +.. note:: The current version works on Windows only. + +.. versionadded:: 1.1.3 + +.. py:function:: PIL.ImageGrab.grab(bbox=None) + + Take a snapshot of the screen. The pixels inside the bounding box are + returned as an "RGB" image. If the bounding box is omitted, the entire + screen is copied. + + .. versionadded:: 1.1.3 + + :param bbox: What region to copy. Default is the entire screen. + :return: An image + +.. py:function:: PIL.ImageGrab.grabclipboard() + + Take a snapshot of the clipboard image, if any. + + .. versionadded:: 1.1.4 + + :return: An image, a list of filenames, or None if the clipboard does + not contain image data or filenames. Note that if a list is + returned, the filenames may not represent image files. diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst new file mode 100644 index 000000000..e3f9ed8d6 --- /dev/null +++ b/docs/reference/ImageMath.rst @@ -0,0 +1,127 @@ +.. py:module:: PIL.ImageMath +.. py:currentmodule:: PIL.ImageMath + +:py:mod:`ImageMath` Module +========================== + +The :py:mod:`ImageMath` module can be used to evaluate “image expressions”. The +module provides a single eval function, which takes an expression string and +one or more images. + +Example: Using the :py:mod:`~PIL.ImageMath` module +-------------------------------------------------- + +.. code-block:: python + + import Image, ImageMath + + im1 = Image.open("image1.jpg") + im2 = Image.open("image2.jpg") + + out = ImageMath.eval("convert(min(a, b), 'L')", a=im1, b=im2) + out.save("result.png") + +.. py:function:: eval(expression, environment) + + Evaluate expression in the given environment. + + In the current version, :py:mod:`~PIL.ImageMath` only supports + single-layer images. To process multi-band images, use the + :py:meth:`~PIL.Image.Image.split` method or :py:func:`~PIL.Image.merge` + function. + + :param expression: A string which uses the standard Python expression + syntax. In addition to the standard operators, you can + also use the functions described below. + :param environment: A dictionary that maps image names to Image instances. + You can use one or more keyword arguments instead of a + dictionary, as shown in the above example. Note that + the names must be valid Python identifiers. + :return: An image, an integer value, a floating point value, + or a pixel tuple, depending on the expression. + +Expression syntax +----------------- + +Expressions are standard Python expressions, but they’re evaluated in a +non-standard environment. You can use PIL methods as usual, plus the following +set of operators and functions: + +Standard Operators +^^^^^^^^^^^^^^^^^^ + +You can use standard arithmetical operators for addition (+), subtraction (-), +multiplication (*), and division (/). + +The module also supports unary minus (-), modulo (%), and power (**) operators. + +Note that all operations are done with 32-bit integers or 32-bit floating +point values, as necessary. For example, if you add two 8-bit images, the +result will be a 32-bit integer image. If you add a floating point constant to +an 8-bit image, the result will be a 32-bit floating point image. + +You can force conversion using the :py:func:`~PIL.ImageMath.convert`, +:py:func:`~PIL.ImageMath.float`, and :py:func:`~PIL.ImageMath.int` functions +described below. + +Bitwise Operators +^^^^^^^^^^^^^^^^^ + +The module also provides operations that operate on individual bits. This +includes and (&), or (|), and exclusive or (^). You can also invert (~) all +pixel bits. + +Note that the operands are converted to 32-bit signed integers before the +bitwise operation is applied. This means that you’ll get negative values if +you invert an ordinary greyscale image. You can use the and (&) operator to +mask off unwanted bits. + +Bitwise operators don’t work on floating point images. + +Logical Operators +^^^^^^^^^^^^^^^^^ + +Logical operators like :keyword:`and`, :keyword:`or`, and :keyword:`not` work +on entire images, rather than individual pixels. + +An empty image (all pixels zero) is treated as false. All other images are +treated as true. + +Note that :keyword:`and` and :keyword:`or` return the last evaluated operand, +while not always returns a boolean value. + +Built-in Functions +^^^^^^^^^^^^^^^^^^ + +These functions are applied to each individual pixel. + +.. py:currentmodule:: None + +.. py:function:: abs(image) + + Absolute value. + +.. py:function:: convert(image, mode) + + Convert image to the given mode. The mode must be given as a string + constant. + +.. py:function:: float(image) + + Convert image to 32-bit floating point. This is equivalent to + convert(image, “F”). + +.. py:function:: int(image) + + Convert image to 32-bit integer. This is equivalent to convert(image, “I”). + + Note that 1-bit and 8-bit images are automatically converted to 32-bit + integers if necessary to get a correct result. + +.. py:function:: max(image1, image2) + + Maximum value. + +.. py:function:: min(image1, image2) + + Minimum value. diff --git a/docs/reference/ImageOps.rst b/docs/reference/ImageOps.rst new file mode 100644 index 000000000..50cea90ca --- /dev/null +++ b/docs/reference/ImageOps.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageOps +.. py:currentmodule:: PIL.ImageOps + +:py:mod:`ImageOps` Module +========================== + +The :py:mod:`ImageOps` module contains a number of ‘ready-made’ image +processing operations. This module is somewhat experimental, and most operators +only work on L and RGB images. + +Only bug fixes have been added since the Pillow fork. + +.. versionadded:: 1.1.3 + +.. autofunction:: autocontrast +.. autofunction:: colorize +.. autofunction:: crop +.. autofunction:: deform +.. autofunction:: equalize +.. autofunction:: expand +.. autofunction:: fit +.. autofunction:: flip +.. autofunction:: grayscale +.. autofunction:: invert +.. autofunction:: mirror +.. autofunction:: posterize +.. autofunction:: solarize diff --git a/docs/reference/ImagePalette.rst b/docs/reference/ImagePalette.rst new file mode 100644 index 000000000..15b8aed8f --- /dev/null +++ b/docs/reference/ImagePalette.rst @@ -0,0 +1,21 @@ +.. py:module:: PIL.ImagePalette +.. py:currentmodule:: PIL.ImagePalette + +:py:mod:`ImagePalette` Module +============================= + +The :py:mod:`ImagePalette` module contains a class of the same name to +represent the color palette of palette mapped images. + +.. note:: + + This module was never well-documented. It hasn't changed since 2001, + though, so it's probably safe for you to read the source code and puzzle + out the internals if you need to. + + The :py:class:`~PIL.ImagePalette.ImagePalette` class has several methods, + but they are all marked as "experimental." Read that as you will. The + ``[source]`` link is there for a reason. + +.. autoclass:: PIL.ImagePalette.ImagePalette + :members: diff --git a/docs/reference/ImagePath.rst b/docs/reference/ImagePath.rst new file mode 100644 index 000000000..700464144 --- /dev/null +++ b/docs/reference/ImagePath.rst @@ -0,0 +1,68 @@ +.. py:module:: PIL.ImagePath +.. py:currentmodule:: PIL.ImagePath + +:py:mod:`ImagePath` Module +========================== + +The :py:mod:`ImagePath` module is used to store and manipulate 2-dimensional +vector data. Path objects can be passed to the methods on the +:py:mod:`~PIL.ImageDraw` module. + +.. py:class:: PIL.ImagePath.Path + + A path object. The coordinate list can be any sequence object containing + either 2-tuples [(x, y), …] or numeric values [x, y, …]. + + You can also create a path object from another path object. + + In 1.1.6 and later, you can also pass in any object that implements + Python’s buffer API. The buffer should provide read access, and contain C + floats in machine byte order. + + The path object implements most parts of the Python sequence interface, and + behaves like a list of (x, y) pairs. You can use len(), item access, and + slicing as usual. However, the current version does not support slice + assignment, or item and slice deletion. + + :param xy: A sequence. The sequence can contain 2-tuples [(x, y), ...] + or a flat list of numbers [x, y, ...]. + +.. py:method:: PIL.ImagePath.Path.compact(distance=2) + + Compacts the path, by removing points that are close to each other. This + method modifies the path in place, and returns the number of points left in + the path. + + **distance** is measured as `Manhattan distance`_ and defaults to two + pixels. + +.. _Manhattan distance: http://en.wikipedia.org/wiki/Manhattan_distance + +.. py:method:: PIL.ImagePath.Path.getbbox() + + Gets the bounding box of the path. + + :return: ``(x0, y0, x1, y1)`` + +.. py:method:: PIL.ImagePath.Path.map(function) + + Maps the path through a function. + +.. py:method:: PIL.ImagePath.Path.tolist(flat=0) + + Converts the path to a Python list [(x, y), …]. + + :param flat: By default, this function returns a list of 2-tuples + [(x, y), ...]. If this argument is :keyword:`True`, it + returns a flat list [x, y, ...] instead. + :return: A list of coordinates. See **flat**. + +.. py:method:: PIL.ImagePath.Path.transform(matrix) + + Transforms the path in place, using an affine transform. The matrix is a + 6-tuple (a, b, c, d, e, f), and each point is mapped as follows: + + .. code-block:: python + + xOut = xIn * a + yIn * b + c + yOut = xIn * d + yIn * e + f diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst new file mode 100644 index 000000000..2f5cccf45 --- /dev/null +++ b/docs/reference/ImageQt.rst @@ -0,0 +1,20 @@ +.. py:module:: PIL.ImageQt +.. py:currentmodule:: PIL.ImageQt + +:py:mod:`ImageQt` Module +======================== + +The :py:mod:`ImageQt` module contains support for creating PyQt4 QImage objects +from PIL images. + +.. versionadded:: 1.1.6 + +.. py:class:: ImageQt.ImageQt(image) + + Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL + :py:class:`~PIL.Image.Image` object. This class is a subclass of + QtGui.QImage, which means that you can pass the resulting objects directly + to PyQt4 API functions and methods. + + This operation is currently supported for mode 1, L, P, RGB, and RGBA + images. To handle other modes, you need to convert the image first. diff --git a/docs/reference/ImageSequence.rst b/docs/reference/ImageSequence.rst new file mode 100644 index 000000000..85539cb9e --- /dev/null +++ b/docs/reference/ImageSequence.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageSequence +.. py:currentmodule:: PIL.ImageSequence + +:py:mod:`ImageSequence` Module +============================== + +The :py:mod:`ImageSequence` module contains a wrapper class that lets you +iterate over the frames of an image sequence. + +Extracting frames from an animation +----------------------------------- + +.. code-block:: python + + from PIL import Image, ImageSequence + + im = Image.open("animation.fli") + + index = 1 + for frame in ImageSequence.Iterator(im): + frame.save("frame%d.png" % index) + index = index + 1 + +The :py:class:`~PIL.ImageSequence.Iterator` class +------------------------------------------------- + +.. autoclass:: PIL.ImageSequence.Iterator diff --git a/docs/reference/ImageStat.rst b/docs/reference/ImageStat.rst new file mode 100644 index 000000000..c8dfe3062 --- /dev/null +++ b/docs/reference/ImageStat.rst @@ -0,0 +1,53 @@ +.. py:module:: PIL.ImageStat +.. py:currentmodule:: PIL.ImageStat + +:py:mod:`ImageStat` Module +========================== + +The :py:mod:`ImageStat` module calculates global statistics for an image, or +for a region of an image. + +.. py:class:: PIL.ImageStat.Stat(image_or_list, mask=None) + + Calculate statistics for the given image. If a mask is included, + only the regions covered by that mask are included in the + statistics. You can also pass in a previously calculated histogram. + + :param image: A PIL image, or a precalculated histogram. + :param mask: An optional mask. + + .. py:attribute:: extrema + + Min/max values for each band in the image. + + .. py:attribute:: count + + Total number of pixels. + + .. py:attribute:: sum + + Sum of all pixels. + + .. py:attribute:: sum2 + + Squared sum of all pixels. + + .. py:attribute:: pixel + + Average pixel level. + + .. py:attribute:: median + + Median pixel level. + + .. py:attribute:: rms + + RMS (root-mean-square). + + .. py:attribute:: var + + Variance. + + .. py:attribute:: stddev + + Standard deviation. diff --git a/docs/reference/ImageTk.rst b/docs/reference/ImageTk.rst new file mode 100644 index 000000000..7ee4af029 --- /dev/null +++ b/docs/reference/ImageTk.rst @@ -0,0 +1,16 @@ +.. py:module:: PIL.ImageTk +.. py:currentmodule:: PIL.ImageTk + +:py:mod:`ImageTk` Module +======================== + +The :py:mod:`ImageTk` module contains support to create and modify Tkinter +BitmapImage and PhotoImage objects from PIL images. + +For examples, see the demo programs in the Scripts directory. + +.. autoclass:: PIL.ImageTk.BitmapImage + :members: + +.. autoclass:: PIL.ImageTk.PhotoImage + :members: diff --git a/docs/reference/ImageWin.rst b/docs/reference/ImageWin.rst new file mode 100644 index 000000000..2696e7e99 --- /dev/null +++ b/docs/reference/ImageWin.rst @@ -0,0 +1,29 @@ +.. py:module:: PIL.ImageWin +.. py:currentmodule:: PIL.ImageWin + +:py:mod:`ImageWin` Module (Windows-only) +======================================== + +The :py:mod:`ImageWin` module contains support to create and display images on +Windows. + +ImageWin can be used with PythonWin and other user interface toolkits that +provide access to Windows device contexts or window handles. For example, +Tkinter makes the window handle available via the winfo_id method: + +.. code-block:: python + + from PIL import ImageWin + + dib = ImageWin.Dib(...) + + hwnd = ImageWin.HWND(widget.winfo_id()) + dib.draw(hwnd, xy) + + +.. autoclass:: PIL.ImageWin.Dib + :members: + + +.. autoclass:: PIL.ImageWin.HDC +.. autoclass:: PIL.ImageWin.HWND diff --git a/docs/reference/PSDraw.rst b/docs/reference/PSDraw.rst new file mode 100644 index 000000000..2b5b9b340 --- /dev/null +++ b/docs/reference/PSDraw.rst @@ -0,0 +1,11 @@ +.. py:module:: PIL.PSDraw +.. py:currentmodule:: PIL.PSDraw + +:py:mod:`PSDraw` Module +======================= + +The :py:mod:`PSDraw` module provides simple print support for Postscript +printers. You can print text, graphics and images through this module. + +.. autoclass:: PIL.PSDraw.PSDraw + :members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst new file mode 100644 index 000000000..2d57e37be --- /dev/null +++ b/docs/reference/index.rst @@ -0,0 +1,26 @@ +Reference +========= + +.. toctree:: + :maxdepth: 2 + + Image + ImageChops + ImageColor + ImageDraw + ImageEnhance + ImageFile + ImageFilter + ImageFont + ImageGrab + ImageMath + ImageOps + ImagePalette + ImagePath + ImageQt + ImageSequence + ImageStat + ImageTk + ImageWin + PSDraw + ../PIL diff --git a/docs/requirements.txt b/docs/requirements.txt index af38b4b69..d825a5fcc 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.0 +pillow>=2.2.1 Jinja2==2.7.1 MarkupSafe==0.18 diff --git a/encode.c b/encode.c index 783e67a8e..10ed90d12 100644 --- a/encode.c +++ b/encode.c @@ -773,11 +773,10 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) (ttag_t) PyInt_AsLong(key), PyInt_AsLong(value)); } else if(PyBytes_Check(value)) { - TRACE(("Setting from String: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); + TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), PyBytes_AsString(value)); - } else if(PyList_Check(value)) { int len,i; float *floatav; @@ -795,12 +794,12 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) free(floatav); } } else if (PyFloat_Check(value)) { - TRACE(("Setting from String: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); + TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), (float)PyFloat_AsDouble(value)); } else { - TRACE(("Unhandled type for key %d : %s ", + TRACE(("Unhandled type for key %d : %s \n", (int)PyInt_AsLong(key), PyBytes_AsString(PyObject_Str(value)))); } diff --git a/libImaging/Access.c b/libImaging/Access.c index fdc1a8886..70eb1af4c 100644 --- a/libImaging/Access.c +++ b/libImaging/Access.c @@ -237,6 +237,7 @@ ImagingAccessInit() ADD("RGBX", line_32, get_pixel_32, put_pixel_32); ADD("CMYK", line_32, get_pixel_32, put_pixel_32); ADD("YCbCr", line_32, get_pixel_32, put_pixel_32); + ADD("LAB", line_32, get_pixel_32, put_pixel_32); } ImagingAccess diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 0da13baab..c139aed88 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -40,6 +40,7 @@ extern "C" { * RGBA 4 R, G, B, A * CMYK 4 C, M, Y, K * YCbCr 4 Y, Cb, Cr, - + * Lab 4 L, a, b, - * * experimental modes (incomplete): * LA 4 L, -, -, A diff --git a/libImaging/Pack.c b/libImaging/Pack.c index 478de7499..1cc1f3a94 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -361,6 +361,27 @@ packI16B(UINT8* out, const UINT8* in_, int pixels) } } +static void +packI16N_I16B(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C16B; + out += 2; tmp += 2; + } + +} +static void +packI16N_I16(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C16L; + out += 2; tmp += 2; + } +} + + static void packI32S(UINT8* out, const UINT8* in, int pixels) { @@ -372,6 +393,19 @@ packI32S(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingPackLAB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LAB triplets */ + for (i = 0; i < pixels; i++) { + out[0] = in[0]; + out[1] = in[1] ^ 128; /* signed in outside world */ + out[2] = in[2] ^ 128; + out += 3; in += 4; + } +} + static void copy1(UINT8* out, const UINT8* in, int pixels) { @@ -526,6 +560,12 @@ static struct { {"YCbCr", "Cb", 8, band1}, {"YCbCr", "Cr", 8, band2}, + /* LAB Color */ + {"LAB", "LAB", 24, ImagingPackLAB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, + {"LAB", "B", 8, band2}, + /* integer */ {"I", "I", 32, copy4}, {"I", "I;16B", 16, packI16B}, @@ -541,6 +581,9 @@ static struct { {"I;16", "I;16", 16, copy2}, {"I;16B", "I;16B", 16, copy2}, {"I;16L", "I;16L", 16, copy2}, + {"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, packI16N_I16}, + {"I;16B", "I;16N", 16, packI16N_I16B}, {"BGR;15", "BGR;15", 16, copy2}, {"BGR;16", "BGR;16", 16, copy2}, {"BGR;24", "BGR;24", 24, copy3}, diff --git a/libImaging/Storage.c b/libImaging/Storage.c index eb3be4322..314f462c8 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -105,7 +105,8 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, im->linesize = xsize * 4; im->type = IMAGING_TYPE_INT32; - } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 || strcmp(mode, "I;16B") == 0) { + } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 \ + || strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) { /* EXPERIMENTAL */ /* 16-bit raw integer images */ im->bands = 1; @@ -178,9 +179,16 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, im->pixelsize = 4; im->linesize = xsize * 4; + } else if (strcmp(mode, "LAB") == 0) { + /* 24-bit color, luminance, + 2 color channels */ + /* L is uint8, a,b are int8 */ + im->bands = 3; + im->pixelsize = 4; + im->linesize = xsize * 4; + } else { free(im); - return (Imaging) ImagingError_ValueError("unrecognized mode"); + return (Imaging) ImagingError_ValueError("unrecognized mode"); } /* Setup image descriptor */ diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index d014fb815..f6d6718d1 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -441,6 +441,36 @@ ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingUnpackRGB15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[B] = ((pixel>>10) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGBA15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5/5/5/1 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[B] = ((pixel>>10) & 31) * 255 / 31; + out[A] = (pixel>>15) * 255; + out += 4; in += 2; + } +} + void ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels) { @@ -456,6 +486,36 @@ ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingUnpackBGRA15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, reversed bytes, 5/5/5/1 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[R] = ((pixel>>10) & 31) * 255 / 31; + out[A] = (pixel>>15) * 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGB16(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5/6/5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 63) * 255 / 63; + out[B] = ((pixel>>11) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + void ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels) { @@ -471,6 +531,36 @@ ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingUnpackRGB4B(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 4 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 15) * 17; + out[G] = ((pixel>>4) & 15) * 17; + out[B] = ((pixel>>8) & 15) * 17; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGBA4B(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGBA, 4 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 15) * 17; + out[G] = ((pixel>>4) & 15) * 17; + out[B] = ((pixel>>8) & 15) * 17; + out[A] = ((pixel>>12) & 15) * 17; + out += 4; in += 2; + } +} + static void ImagingUnpackBGRX(UINT8* out, const UINT8* in, int pixels) { @@ -660,6 +750,51 @@ unpackCMYKI(UINT8* out, const UINT8* in, int pixels) } } +/* Unpack to "LAB" image */ +/* There are two representations of LAB images for whatever precision: + L: Uint (in PS, it's 0-100) + A: Int (in ps, -128 .. 128, or elsewhere 0..255, with 128 as middle. + Channels in PS display a 0 value as middle grey, + LCMS appears to use 128 as the 0 value for these channels) + B: Int (as above) + + Since we don't have any signed ints, we're going with the shifted versions + internally, and we'll unshift for saving and whatnot. +*/ +void +ImagingUnpackLAB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LAB triplets */ + for (i = 0; i < pixels; i++) { + out[0] = in[0]; + out[1] = in[1] ^ 128; /* signed in outside world */ + out[2] = in[2] ^ 128; + out[3] = 255; + out += 4; in += 3; + } +} + +static void +unpackI16N_I16B(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) out; + for (i = 0; i < pixels; i++) { + C16B; + in += 2; tmp += 2; + } + +} +static void +unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) out; + for (i = 0; i < pixels; i++) { + C16L; + in += 2; tmp += 2; + } +} + static void copy1(UINT8* out, const UINT8* in, int pixels) { @@ -674,6 +809,13 @@ copy2(UINT8* out, const UINT8* in, int pixels) memcpy(out, in, pixels*2); } +static void +copy3(UINT8* out, const UINT8* in, int pixels) +{ + /* LAB triples, 24bit */ + memcpy(out, in, 3 * pixels); +} + static void copy4(UINT8* out, const UINT8* in, int pixels) { @@ -889,8 +1031,11 @@ static struct { {"RGB", "RGB;R", 24, unpackRGBR}, {"RGB", "RGB;16B", 48, unpackRGB16B}, {"RGB", "BGR", 24, ImagingUnpackBGR}, + {"RGB", "RGB;15", 16, ImagingUnpackRGB15}, {"RGB", "BGR;15", 16, ImagingUnpackBGR15}, + {"RGB", "RGB;16", 16, ImagingUnpackRGB16}, {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGB", "RGB;4B", 16, ImagingUnpackRGB4B}, {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ {"RGB", "RGBX", 32, copy4}, {"RGB", "RGBX;L", 32, unpackRGBAL}, @@ -909,6 +1054,9 @@ static struct { {"RGBA", "RGBa", 32, unpackRGBa}, {"RGBA", "RGBA;I", 32, unpackRGBAI}, {"RGBA", "RGBA;L", 32, unpackRGBAL}, + {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15}, + {"RGBA", "BGRA;15", 16, ImagingUnpackBGRA15}, + {"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B}, {"RGBA", "RGBA;16B", 64, unpackRGBA16B}, {"RGBA", "BGRA", 32, unpackBGRA}, {"RGBA", "ARGB", 32, unpackARGB}, @@ -924,8 +1072,9 @@ static struct { {"RGBX", "RGB;L", 24, unpackRGBL}, {"RGBX", "RGB;16B", 48, unpackRGB16B}, {"RGBX", "BGR", 24, ImagingUnpackBGR}, + {"RGBX", "RGB;15", 16, ImagingUnpackRGB15}, {"RGBX", "BGR;15", 16, ImagingUnpackBGR15}, - {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGBX", "RGB;4B", 16, ImagingUnpackRGB4B}, {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ {"RGBX", "RGBX", 32, copy4}, {"RGBX", "RGBX;L", 32, unpackRGBAL}, @@ -957,6 +1106,12 @@ static struct { {"YCbCr", "YCbCrX", 32, copy4}, {"YCbCr", "YCbCrK", 32, copy4}, + /* LAB Color */ + {"LAB", "LAB", 24, ImagingUnpackLAB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, + {"LAB", "B", 8, band2}, + /* integer variations */ {"I", "I", 32, copy4}, {"I", "I;8", 8, unpackI8}, @@ -1004,6 +1159,10 @@ static struct { {"I;16B", "I;16B", 16, copy2}, {"I;16L", "I;16L", 16, copy2}, + {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16B", "I;16N", 16, unpackI16N_I16B}, + {NULL} /* sentinel */ }; diff --git a/selftest.py b/selftest.py index 463902648..1eb7738e3 100644 --- a/selftest.py +++ b/selftest.py @@ -195,7 +195,7 @@ if __name__ == "__main__": check_codec("ZLIB (PNG/ZIP)", "zip") check_codec("G4 TIFF", "group4") check_module("FREETYPE2", "PIL._imagingft") - check_module("LITTLECMS", "PIL._imagingcms") + check_module("LITTLECMS2", "PIL._imagingcms") check_module("WEBP", "PIL._webp") try: from PIL import _webp diff --git a/setup.py b/setup.py index ad1dbdb49..6c677bbd3 100644 --- a/setup.py +++ b/setup.py @@ -214,13 +214,21 @@ class pil_build_ext(build_ext): _add_directory(library_dirs, "/usr/local/lib") # FIXME: check /opt/stuff directories here? + # respect CFLAGS/LDFLAGS + for k in ('CFLAGS', 'LDFLAGS'): + if k in os.environ: + for match in re.finditer(r'-I([^\s]+)', os.environ[k]): + _add_directory(include_dirs, match.group(1)) + for match in re.finditer(r'-L([^\s]+)', os.environ[k]): + _add_directory(library_dirs, match.group(1)) + # include, rpath, if set as environment variables: - for k in 'C_INCLUDE_PATH INCLUDE'.split(): + for k in ('C_INCLUDE_PATH', 'INCLUDE'): if k in os.environ: for d in os.environ[k].split(os.path.pathsep): _add_directory(include_dirs, d) - for k in 'LD_RUN_PATH LIBRARY_PATH LIB'.split(): + for k in ('LD_RUN_PATH', 'LIBRARY_PATH', 'LIB'): if k in os.environ: for d in os.environ[k].split(os.path.pathsep): _add_directory(library_dirs, d) @@ -334,8 +342,8 @@ class pil_build_ext(build_ext): _add_directory(self.compiler.include_dirs, dir, 0) if feature.want('lcms'): - if _find_include_file(self, "lcms.h"): - if _find_library_file(self, "lcms"): + if _find_include_file(self, "lcms2.h"): + if _find_library_file(self, "lcms2"): feature.lcms = "lcms" if _tkinter and _find_include_file(self, "tk.h"): @@ -418,7 +426,7 @@ class pil_build_ext(build_ext): if sys.platform == "win32": extra.extend(["user32", "gdi32"]) exts.append(Extension( - "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra)) + "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms2"] + extra)) if os.path.isfile("_webp.c") and feature.webp: libs = ["webp"] @@ -494,7 +502,7 @@ class pil_build_ext(build_ext): (feature.zlib, "ZLIB (PNG/ZIP)"), (feature.tiff, "TIFF G3/G4 (experimental)"), (feature.freetype, "FREETYPE2"), - (feature.lcms, "LITTLECMS"), + (feature.lcms, "LITTLECMS2"), (feature.webp, "WEBP"), (feature.webpmux, "WEBPMUX"), ]